--- /dev/null
+From 86814d8ffd55fd4ad19c512eccd721522a370fb2 Mon Sep 17 00:00:00 2001
+From: Konstantin Shkolnyy <kshk@linux.ibm.com>
+Date: Tue, 3 Dec 2024 09:06:56 -0600
+Subject: vsock/test: verify socket options after setting them
+
+From: Konstantin Shkolnyy <kshk@linux.ibm.com>
+
+commit 86814d8ffd55fd4ad19c512eccd721522a370fb2 upstream.
+
+Replace setsockopt() calls with calls to functions that follow
+setsockopt() with getsockopt() and check that the returned value and its
+size are the same as have been set. (Except in vsock_perf.)
+
+Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
+Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+[Stefano: patch needed to avoid vsock test build failure reported by
+ Johan Korsnes after backporting commit 0a98de8013696 ("vsock/test: fix
+ seqpacket message bounds test") in 6.12-stable tree]
+Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
+Tested-by: Johan Korsnes <johan.korsnes@remarkable.no>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ tools/testing/vsock/control.c | 9 -
+ tools/testing/vsock/msg_zerocopy_common.c | 10 --
+ tools/testing/vsock/msg_zerocopy_common.h | 1
+ tools/testing/vsock/util.c | 142 ++++++++++++++++++++++++++++++
+ tools/testing/vsock/util.h | 7 +
+ tools/testing/vsock/vsock_perf.c | 10 ++
+ tools/testing/vsock/vsock_test.c | 53 +++--------
+ tools/testing/vsock/vsock_test_zerocopy.c | 2
+ tools/testing/vsock/vsock_uring_test.c | 2
+ 9 files changed, 182 insertions(+), 54 deletions(-)
+
+--- a/tools/testing/vsock/control.c
++++ b/tools/testing/vsock/control.c
+@@ -27,6 +27,7 @@
+
+ #include "timeout.h"
+ #include "control.h"
++#include "util.h"
+
+ static int control_fd = -1;
+
+@@ -50,7 +51,6 @@ void control_init(const char *control_ho
+
+ for (ai = result; ai; ai = ai->ai_next) {
+ int fd;
+- int val = 1;
+
+ fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (fd < 0)
+@@ -65,11 +65,8 @@ void control_init(const char *control_ho
+ break;
+ }
+
+- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+- &val, sizeof(val)) < 0) {
+- perror("setsockopt");
+- exit(EXIT_FAILURE);
+- }
++ setsockopt_int_check(fd, SOL_SOCKET, SO_REUSEADDR, 1,
++ "setsockopt SO_REUSEADDR");
+
+ if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0)
+ goto next;
+--- a/tools/testing/vsock/msg_zerocopy_common.c
++++ b/tools/testing/vsock/msg_zerocopy_common.c
+@@ -14,16 +14,6 @@
+
+ #include "msg_zerocopy_common.h"
+
+-void enable_so_zerocopy(int fd)
+-{
+- int val = 1;
+-
+- if (setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val))) {
+- perror("setsockopt");
+- exit(EXIT_FAILURE);
+- }
+-}
+-
+ void vsock_recv_completion(int fd, const bool *zerocopied)
+ {
+ struct sock_extended_err *serr;
+--- a/tools/testing/vsock/msg_zerocopy_common.h
++++ b/tools/testing/vsock/msg_zerocopy_common.h
+@@ -12,7 +12,6 @@
+ #define VSOCK_RECVERR 1
+ #endif
+
+-void enable_so_zerocopy(int fd);
+ void vsock_recv_completion(int fd, const bool *zerocopied);
+
+ #endif /* MSG_ZEROCOPY_COMMON_H */
+--- a/tools/testing/vsock/util.c
++++ b/tools/testing/vsock/util.c
+@@ -663,3 +663,145 @@ void free_test_iovec(const struct iovec
+
+ free(iovec);
+ }
++
++/* Set "unsigned long long" socket option and check that it's indeed set */
++void setsockopt_ull_check(int fd, int level, int optname,
++ unsigned long long val, char const *errmsg)
++{
++ unsigned long long chkval;
++ socklen_t chklen;
++ int err;
++
++ err = setsockopt(fd, level, optname, &val, sizeof(val));
++ if (err) {
++ fprintf(stderr, "setsockopt err: %s (%d)\n",
++ strerror(errno), errno);
++ goto fail;
++ }
++
++ chkval = ~val; /* just make storage != val */
++ chklen = sizeof(chkval);
++
++ err = getsockopt(fd, level, optname, &chkval, &chklen);
++ if (err) {
++ fprintf(stderr, "getsockopt err: %s (%d)\n",
++ strerror(errno), errno);
++ goto fail;
++ }
++
++ if (chklen != sizeof(chkval)) {
++ fprintf(stderr, "size mismatch: set %zu got %d\n", sizeof(val),
++ chklen);
++ goto fail;
++ }
++
++ if (chkval != val) {
++ fprintf(stderr, "value mismatch: set %llu got %llu\n", val,
++ chkval);
++ goto fail;
++ }
++ return;
++fail:
++ fprintf(stderr, "%s val %llu\n", errmsg, val);
++ exit(EXIT_FAILURE);
++;
++}
++
++/* Set "int" socket option and check that it's indeed set */
++void setsockopt_int_check(int fd, int level, int optname, int val,
++ char const *errmsg)
++{
++ int chkval;
++ socklen_t chklen;
++ int err;
++
++ err = setsockopt(fd, level, optname, &val, sizeof(val));
++ if (err) {
++ fprintf(stderr, "setsockopt err: %s (%d)\n",
++ strerror(errno), errno);
++ goto fail;
++ }
++
++ chkval = ~val; /* just make storage != val */
++ chklen = sizeof(chkval);
++
++ err = getsockopt(fd, level, optname, &chkval, &chklen);
++ if (err) {
++ fprintf(stderr, "getsockopt err: %s (%d)\n",
++ strerror(errno), errno);
++ goto fail;
++ }
++
++ if (chklen != sizeof(chkval)) {
++ fprintf(stderr, "size mismatch: set %zu got %d\n", sizeof(val),
++ chklen);
++ goto fail;
++ }
++
++ if (chkval != val) {
++ fprintf(stderr, "value mismatch: set %d got %d\n", val, chkval);
++ goto fail;
++ }
++ return;
++fail:
++ fprintf(stderr, "%s val %d\n", errmsg, val);
++ exit(EXIT_FAILURE);
++}
++
++static void mem_invert(unsigned char *mem, size_t size)
++{
++ size_t i;
++
++ for (i = 0; i < size; i++)
++ mem[i] = ~mem[i];
++}
++
++/* Set "timeval" socket option and check that it's indeed set */
++void setsockopt_timeval_check(int fd, int level, int optname,
++ struct timeval val, char const *errmsg)
++{
++ struct timeval chkval;
++ socklen_t chklen;
++ int err;
++
++ err = setsockopt(fd, level, optname, &val, sizeof(val));
++ if (err) {
++ fprintf(stderr, "setsockopt err: %s (%d)\n",
++ strerror(errno), errno);
++ goto fail;
++ }
++
++ /* just make storage != val */
++ chkval = val;
++ mem_invert((unsigned char *)&chkval, sizeof(chkval));
++ chklen = sizeof(chkval);
++
++ err = getsockopt(fd, level, optname, &chkval, &chklen);
++ if (err) {
++ fprintf(stderr, "getsockopt err: %s (%d)\n",
++ strerror(errno), errno);
++ goto fail;
++ }
++
++ if (chklen != sizeof(chkval)) {
++ fprintf(stderr, "size mismatch: set %zu got %d\n", sizeof(val),
++ chklen);
++ goto fail;
++ }
++
++ if (memcmp(&chkval, &val, sizeof(val)) != 0) {
++ fprintf(stderr, "value mismatch: set %ld:%ld got %ld:%ld\n",
++ val.tv_sec, val.tv_usec, chkval.tv_sec, chkval.tv_usec);
++ goto fail;
++ }
++ return;
++fail:
++ fprintf(stderr, "%s val %ld:%ld\n", errmsg, val.tv_sec, val.tv_usec);
++ exit(EXIT_FAILURE);
++}
++
++void enable_so_zerocopy_check(int fd)
++{
++ setsockopt_int_check(fd, SOL_SOCKET, SO_ZEROCOPY, 1,
++ "setsockopt SO_ZEROCOPY");
++}
+--- a/tools/testing/vsock/util.h
++++ b/tools/testing/vsock/util.h
+@@ -68,4 +68,11 @@ unsigned long iovec_hash_djb2(const stru
+ struct iovec *alloc_test_iovec(const struct iovec *test_iovec, int iovnum);
+ void free_test_iovec(const struct iovec *test_iovec,
+ struct iovec *iovec, int iovnum);
++void setsockopt_ull_check(int fd, int level, int optname,
++ unsigned long long val, char const *errmsg);
++void setsockopt_int_check(int fd, int level, int optname, int val,
++ char const *errmsg);
++void setsockopt_timeval_check(int fd, int level, int optname,
++ struct timeval val, char const *errmsg);
++void enable_so_zerocopy_check(int fd);
+ #endif /* UTIL_H */
+--- a/tools/testing/vsock/vsock_perf.c
++++ b/tools/testing/vsock/vsock_perf.c
+@@ -251,6 +251,16 @@ static void run_receiver(int rcvlowat_by
+ close(fd);
+ }
+
++static void enable_so_zerocopy(int fd)
++{
++ int val = 1;
++
++ if (setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val))) {
++ perror("setsockopt");
++ exit(EXIT_FAILURE);
++ }
++}
++
+ static void run_sender(int peer_cid, unsigned long to_send_bytes)
+ {
+ time_t tx_begin_ns;
+--- a/tools/testing/vsock/vsock_test.c
++++ b/tools/testing/vsock/vsock_test.c
+@@ -455,17 +455,13 @@ static void test_seqpacket_msg_bounds_se
+
+ sock_buf_size = SOCK_BUF_SIZE;
+
+- if (setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE,
+- &sock_buf_size, sizeof(sock_buf_size))) {
+- perror("setsockopt(SO_VM_SOCKETS_BUFFER_MAX_SIZE)");
+- exit(EXIT_FAILURE);
+- }
+-
+- if (setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
+- &sock_buf_size, sizeof(sock_buf_size))) {
+- perror("setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");
+- exit(EXIT_FAILURE);
+- }
++ setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE,
++ sock_buf_size,
++ "setsockopt(SO_VM_SOCKETS_BUFFER_MAX_SIZE)");
++
++ setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
++ sock_buf_size,
++ "setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");
+
+ /* Ready to receive data. */
+ control_writeln("SRVREADY");
+@@ -597,10 +593,8 @@ static void test_seqpacket_timeout_clien
+ tv.tv_sec = RCVTIMEO_TIMEOUT_SEC;
+ tv.tv_usec = 0;
+
+- if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv)) == -1) {
+- perror("setsockopt(SO_RCVTIMEO)");
+- exit(EXIT_FAILURE);
+- }
++ setsockopt_timeval_check(fd, SOL_SOCKET, SO_RCVTIMEO, tv,
++ "setsockopt(SO_RCVTIMEO)");
+
+ read_enter_ns = current_nsec();
+
+@@ -866,11 +860,8 @@ static void test_stream_poll_rcvlowat_cl
+ exit(EXIT_FAILURE);
+ }
+
+- if (setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT,
+- &lowat_val, sizeof(lowat_val))) {
+- perror("setsockopt(SO_RCVLOWAT)");
+- exit(EXIT_FAILURE);
+- }
++ setsockopt_int_check(fd, SOL_SOCKET, SO_RCVLOWAT,
++ lowat_val, "setsockopt(SO_RCVLOWAT)");
+
+ control_expectln("SRVSENT");
+
+@@ -1398,11 +1389,9 @@ static void test_stream_credit_update_te
+ /* size_t can be < unsigned long long */
+ sock_buf_size = buf_size;
+
+- if (setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
+- &sock_buf_size, sizeof(sock_buf_size))) {
+- perror("setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");
+- exit(EXIT_FAILURE);
+- }
++ setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
++ sock_buf_size,
++ "setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");
+
+ if (low_rx_bytes_test) {
+ /* Set new SO_RCVLOWAT here. This enables sending credit
+@@ -1411,11 +1400,8 @@ static void test_stream_credit_update_te
+ */
+ recv_buf_size = 1 + VIRTIO_VSOCK_MAX_PKT_BUF_SIZE;
+
+- if (setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT,
+- &recv_buf_size, sizeof(recv_buf_size))) {
+- perror("setsockopt(SO_RCVLOWAT)");
+- exit(EXIT_FAILURE);
+- }
++ setsockopt_int_check(fd, SOL_SOCKET, SO_RCVLOWAT,
++ recv_buf_size, "setsockopt(SO_RCVLOWAT)");
+ }
+
+ /* Send one dummy byte here, because 'setsockopt()' above also
+@@ -1457,11 +1443,8 @@ static void test_stream_credit_update_te
+ recv_buf_size++;
+
+ /* Updating SO_RCVLOWAT will send credit update. */
+- if (setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT,
+- &recv_buf_size, sizeof(recv_buf_size))) {
+- perror("setsockopt(SO_RCVLOWAT)");
+- exit(EXIT_FAILURE);
+- }
++ setsockopt_int_check(fd, SOL_SOCKET, SO_RCVLOWAT,
++ recv_buf_size, "setsockopt(SO_RCVLOWAT)");
+ }
+
+ fds.fd = fd;
+--- a/tools/testing/vsock/vsock_test_zerocopy.c
++++ b/tools/testing/vsock/vsock_test_zerocopy.c
+@@ -162,7 +162,7 @@ static void test_client(const struct tes
+ }
+
+ if (test_data->so_zerocopy)
+- enable_so_zerocopy(fd);
++ enable_so_zerocopy_check(fd);
+
+ iovec = alloc_test_iovec(test_data->vecs, test_data->vecs_cnt);
+
+--- a/tools/testing/vsock/vsock_uring_test.c
++++ b/tools/testing/vsock/vsock_uring_test.c
+@@ -73,7 +73,7 @@ static void vsock_io_uring_client(const
+ }
+
+ if (msg_zerocopy)
+- enable_so_zerocopy(fd);
++ enable_so_zerocopy_check(fd);
+
+ iovec = alloc_test_iovec(test_data->vecs, test_data->vecs_cnt);
+