]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
vsock/test: Add test for a linear and non-linear skb getting coalesced
authorMichal Luczaj <mhal@rbox.co>
Tue, 13 Jan 2026 15:08:19 +0000 (16:08 +0100)
committerJakub Kicinski <kuba@kernel.org>
Fri, 16 Jan 2026 03:44:44 +0000 (19:44 -0800)
Loopback transport can mangle data in rx queue when a linear skb is
followed by a small MSG_ZEROCOPY packet.

To exercise the logic, send out two packets: a weirdly sized one (to ensure
some spare tail room in the skb) and a zerocopy one that's small enough to
fit in the spare room of its predecessor. Then, wait for both to land in
the rx queue, and check the data received. Faulty packets merger manifests
itself by corrupting payload of the later packet.

Signed-off-by: Michal Luczaj <mhal@rbox.co>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Link: https://patch.msgid.link/20260113-vsock-recv-coalescence-v2-2-552b17837cf4@rbox.co
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
tools/testing/vsock/vsock_test.c
tools/testing/vsock/vsock_test_zerocopy.c
tools/testing/vsock/vsock_test_zerocopy.h

index bbe3723babdc4c784d6b39fd0fc031da304f2e06..27e39354499a06cb3eef0baeebbcc9daffacd19d 100644 (file)
@@ -2403,6 +2403,11 @@ static struct test_case test_cases[] = {
                .run_client = test_stream_accepted_setsockopt_client,
                .run_server = test_stream_accepted_setsockopt_server,
        },
+       {
+               .name = "SOCK_STREAM virtio MSG_ZEROCOPY coalescence corruption",
+               .run_client = test_stream_msgzcopy_mangle_client,
+               .run_server = test_stream_msgzcopy_mangle_server,
+       },
        {},
 };
 
index 9d9a6cb9614ad850d6508cda0cf9f5a806becc55..a31ddfc1cd0cfc605d0e16f6e96a01634b3d101e 100644 (file)
@@ -9,14 +9,18 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <unistd.h>
 #include <poll.h>
 #include <linux/errqueue.h>
 #include <linux/kernel.h>
+#include <linux/sockios.h>
+#include <linux/time64.h>
 #include <errno.h>
 
 #include "control.h"
+#include "timeout.h"
 #include "vsock_test_zerocopy.h"
 #include "msg_zerocopy_common.h"
 
@@ -356,3 +360,73 @@ void test_stream_msgzcopy_empty_errq_server(const struct test_opts *opts)
        control_expectln("DONE");
        close(fd);
 }
+
+#define GOOD_COPY_LEN  128     /* net/vmw_vsock/virtio_transport_common.c */
+
+void test_stream_msgzcopy_mangle_client(const struct test_opts *opts)
+{
+       char sbuf1[PAGE_SIZE + 1], sbuf2[GOOD_COPY_LEN];
+       unsigned long hash;
+       struct pollfd fds;
+       int fd, i;
+
+       fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
+       if (fd < 0) {
+               perror("connect");
+               exit(EXIT_FAILURE);
+       }
+
+       enable_so_zerocopy_check(fd);
+
+       memset(sbuf1, 'x', sizeof(sbuf1));
+       send_buf(fd, sbuf1, sizeof(sbuf1), 0, sizeof(sbuf1));
+
+       for (i = 0; i < sizeof(sbuf2); i++)
+               sbuf2[i] = rand() & 0xff;
+
+       send_buf(fd, sbuf2, sizeof(sbuf2), MSG_ZEROCOPY, sizeof(sbuf2));
+
+       hash = hash_djb2(sbuf2, sizeof(sbuf2));
+       control_writeulong(hash);
+
+       fds.fd = fd;
+       fds.events = 0;
+
+       if (poll(&fds, 1, TIMEOUT * MSEC_PER_SEC) != 1 ||
+           !(fds.revents & POLLERR)) {
+               perror("poll");
+               exit(EXIT_FAILURE);
+       }
+
+       close(fd);
+}
+
+void test_stream_msgzcopy_mangle_server(const struct test_opts *opts)
+{
+       unsigned long local_hash, remote_hash;
+       char rbuf[PAGE_SIZE + 1];
+       int fd;
+
+       fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);
+       if (fd < 0) {
+               perror("accept");
+               exit(EXIT_FAILURE);
+       }
+
+       /* Wait, don't race the (buggy) skbs coalescence. */
+       vsock_ioctl_int(fd, SIOCINQ, PAGE_SIZE + 1 + GOOD_COPY_LEN);
+
+       /* Discard the first packet. */
+       recv_buf(fd, rbuf, PAGE_SIZE + 1, 0, PAGE_SIZE + 1);
+
+       recv_buf(fd, rbuf, GOOD_COPY_LEN, 0, GOOD_COPY_LEN);
+       remote_hash = control_readulong();
+       local_hash = hash_djb2(rbuf, GOOD_COPY_LEN);
+
+       if (local_hash != remote_hash) {
+               fprintf(stderr, "Data received corrupted\n");
+               exit(EXIT_FAILURE);
+       }
+
+       close(fd);
+}
index 3ef2579e024d82fcb46a0810b3713dc078df6798..d46c91a69f16eac7f413dff637ea12a714cce25a 100644 (file)
@@ -12,4 +12,7 @@ void test_seqpacket_msgzcopy_server(const struct test_opts *opts);
 void test_stream_msgzcopy_empty_errq_client(const struct test_opts *opts);
 void test_stream_msgzcopy_empty_errq_server(const struct test_opts *opts);
 
+void test_stream_msgzcopy_mangle_client(const struct test_opts *opts);
+void test_stream_msgzcopy_mangle_server(const struct test_opts *opts);
+
 #endif /* VSOCK_TEST_ZEROCOPY_H */