Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vsock/test: add stream TX credit bounds test

Add a regression test for the TX credit bounds fix. The test verifies
that a sender with a small local buffer size cannot queue excessive
data even when the peer advertises a large receive buffer.

The client:
- Sets a small buffer size (64 KiB)
- Connects to server (which advertises 2 MiB buffer)
- Sends in non-blocking mode until EAGAIN
- Verifies total queued data is bounded

This guards against the original vulnerability where a remote peer
could cause unbounded kernel memory allocation by advertising a large
buffer and reading slowly.

Suggested-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Melbin K Mathew <mlbnkm1@gmail.com>
[Stefano: use sock_buf_size to check the bytes sent + small fixes]
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
Link: https://patch.msgid.link/20260121093628.9941-5-sgarzare@redhat.com
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Melbin K Mathew and committed by
Paolo Abeni
2a689f76 8ee784fd

+101
+101
tools/testing/vsock/vsock_test.c
··· 347 347 } 348 348 349 349 #define SOCK_BUF_SIZE (2 * 1024 * 1024) 350 + #define SOCK_BUF_SIZE_SMALL (64 * 1024) 350 351 #define MAX_MSG_PAGES 4 351 352 352 353 static void test_seqpacket_msg_bounds_client(const struct test_opts *opts) ··· 2231 2230 close(fd); 2232 2231 } 2233 2232 2233 + static void test_stream_tx_credit_bounds_client(const struct test_opts *opts) 2234 + { 2235 + unsigned long long sock_buf_size; 2236 + size_t total = 0; 2237 + char buf[4096]; 2238 + int fd; 2239 + 2240 + memset(buf, 'A', sizeof(buf)); 2241 + 2242 + fd = vsock_stream_connect(opts->peer_cid, opts->peer_port); 2243 + if (fd < 0) { 2244 + perror("connect"); 2245 + exit(EXIT_FAILURE); 2246 + } 2247 + 2248 + sock_buf_size = SOCK_BUF_SIZE_SMALL; 2249 + 2250 + setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE, 2251 + sock_buf_size, 2252 + "setsockopt(SO_VM_SOCKETS_BUFFER_MAX_SIZE)"); 2253 + 2254 + setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE, 2255 + sock_buf_size, 2256 + "setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)"); 2257 + 2258 + if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) < 0) { 2259 + perror("fcntl(F_SETFL)"); 2260 + exit(EXIT_FAILURE); 2261 + } 2262 + 2263 + control_expectln("SRVREADY"); 2264 + 2265 + for (;;) { 2266 + ssize_t sent = send(fd, buf, sizeof(buf), 0); 2267 + 2268 + if (sent == 0) { 2269 + fprintf(stderr, "unexpected EOF while sending bytes\n"); 2270 + exit(EXIT_FAILURE); 2271 + } 2272 + 2273 + if (sent < 0) { 2274 + if (errno == EINTR) 2275 + continue; 2276 + 2277 + if (errno == EAGAIN || errno == EWOULDBLOCK) 2278 + break; 2279 + 2280 + perror("send"); 2281 + exit(EXIT_FAILURE); 2282 + } 2283 + 2284 + total += sent; 2285 + } 2286 + 2287 + control_writeln("CLIDONE"); 2288 + close(fd); 2289 + 2290 + /* We should not be able to send more bytes than the value set as 2291 + * local buffer size. 2292 + */ 2293 + if (total > sock_buf_size) { 2294 + fprintf(stderr, 2295 + "TX credit too large: queued %zu bytes (expected <= %llu)\n", 2296 + total, sock_buf_size); 2297 + exit(EXIT_FAILURE); 2298 + } 2299 + } 2300 + 2301 + static void test_stream_tx_credit_bounds_server(const struct test_opts *opts) 2302 + { 2303 + unsigned long long sock_buf_size; 2304 + int fd; 2305 + 2306 + fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL); 2307 + if (fd < 0) { 2308 + perror("accept"); 2309 + exit(EXIT_FAILURE); 2310 + } 2311 + 2312 + sock_buf_size = SOCK_BUF_SIZE; 2313 + 2314 + setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE, 2315 + sock_buf_size, 2316 + "setsockopt(SO_VM_SOCKETS_BUFFER_MAX_SIZE)"); 2317 + 2318 + setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE, 2319 + sock_buf_size, 2320 + "setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)"); 2321 + 2322 + control_writeln("SRVREADY"); 2323 + control_expectln("CLIDONE"); 2324 + 2325 + close(fd); 2326 + } 2327 + 2234 2328 static struct test_case test_cases[] = { 2235 2329 { 2236 2330 .name = "SOCK_STREAM connection reset", ··· 2514 2418 .name = "SOCK_STREAM virtio MSG_ZEROCOPY coalescence corruption", 2515 2419 .run_client = test_stream_msgzcopy_mangle_client, 2516 2420 .run_server = test_stream_msgzcopy_mangle_server, 2421 + }, 2422 + { 2423 + .name = "SOCK_STREAM TX credit bounds", 2424 + .run_client = test_stream_tx_credit_bounds_client, 2425 + .run_server = test_stream_tx_credit_bounds_server, 2517 2426 }, 2518 2427 {}, 2519 2428 };