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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'vsock-virtio-fix-msg_peek-calculation-on-bytes-to-copy'

Luigi Leonardi says:

====================
vsock/virtio: fix MSG_PEEK calculation on bytes to copy

`virtio_transport_stream_do_peek`, when calculating the number of bytes to
copy, didn't consider the `offset`, caused by partial reads that happened
before.
This might cause out-of-bounds read that lead to an EFAULT.
More details in the commits.

Commit 1 introduces the fix
Commit 2 introduces some preliminary work for adding a test and fixes a
problem in existing tests.
Commit 3 introduces a test that checks for this bug to avoid future
regressions.

For disclosure: this bug was found initially by claude opus 4.6, I then analyzed
it and worked on the fix and the test.
====================

Link: https://patch.msgid.link/20260415-fix_peek-v4-0-8207e872759e@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+55 -15
+2 -3
net/vmw_vsock/virtio_transport_common.c
··· 545 545 skb_queue_walk(&vvs->rx_queue, skb) { 546 546 size_t bytes; 547 547 548 - bytes = len - total; 549 - if (bytes > skb->len) 550 - bytes = skb->len; 548 + bytes = min_t(size_t, len - total, 549 + skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset); 551 550 552 551 spin_unlock_bh(&vvs->rx_lock); 553 552
+15
tools/testing/vsock/util.c
··· 381 381 } 382 382 } 383 383 384 + #define RECV_PEEK_RETRY_USEC (10 * 1000) 385 + 384 386 /* Receive bytes in a buffer and check the return value. 387 + * 388 + * When MSG_PEEK is set, recv() is retried until it returns at least 389 + * expected_ret bytes. The function returns on error, EOF, or timeout 390 + * as usual. 385 391 * 386 392 * expected_ret: 387 393 * <0 Negative errno (for testing errors) ··· 408 402 continue; 409 403 if (ret <= 0) 410 404 break; 405 + 406 + if (flags & MSG_PEEK) { 407 + if (ret >= expected_ret) { 408 + nread = ret; 409 + break; 410 + } 411 + timeout_usleep(RECV_PEEK_RETRY_USEC); 412 + continue; 413 + } 411 414 412 415 nread += ret; 413 416 } while (nread < len);
+38 -12
tools/testing/vsock/vsock_test.c
··· 346 346 return test_msg_peek_server(opts, false); 347 347 } 348 348 349 + static void test_stream_peek_after_recv_server(const struct test_opts *opts) 350 + { 351 + unsigned char buf_normal[MSG_PEEK_BUF_LEN]; 352 + unsigned char buf_peek[MSG_PEEK_BUF_LEN]; 353 + int fd; 354 + 355 + fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL); 356 + if (fd < 0) { 357 + perror("accept"); 358 + exit(EXIT_FAILURE); 359 + } 360 + 361 + control_writeln("SRVREADY"); 362 + 363 + /* Partial recv to advance offset within the skb */ 364 + recv_buf(fd, buf_normal, 1, 0, 1); 365 + 366 + /* Peek with a buffer larger than the remaining data */ 367 + recv_buf(fd, buf_peek, sizeof(buf_peek), MSG_PEEK, sizeof(buf_peek) - 1); 368 + 369 + /* Consume the remaining data */ 370 + recv_buf(fd, buf_normal, sizeof(buf_normal) - 1, 0, sizeof(buf_normal) - 1); 371 + 372 + /* Compare full peek and normal read. */ 373 + if (memcmp(buf_peek, buf_normal, sizeof(buf_peek) - 1)) { 374 + fprintf(stderr, "Full peek data mismatch\n"); 375 + exit(EXIT_FAILURE); 376 + } 377 + 378 + close(fd); 379 + } 380 + 349 381 #define SOCK_BUF_SIZE (2 * 1024 * 1024) 350 382 #define SOCK_BUF_SIZE_SMALL (64 * 1024) 351 383 #define MAX_MSG_PAGES 4 ··· 1532 1500 } 1533 1501 1534 1502 /* Wait until there will be 128KB of data in rx queue. */ 1535 - while (1) { 1536 - ssize_t res; 1537 - 1538 - res = recv(fd, buf, buf_size, MSG_PEEK); 1539 - if (res == buf_size) 1540 - break; 1541 - 1542 - if (res <= 0) { 1543 - fprintf(stderr, "unexpected 'recv()' return: %zi\n", res); 1544 - exit(EXIT_FAILURE); 1545 - } 1546 - } 1503 + recv_buf(fd, buf, buf_size, MSG_PEEK, buf_size); 1547 1504 1548 1505 /* There is 128KB of data in the socket's rx queue, dequeue first 1549 1506 * 64KB, credit update is sent if 'low_rx_bytes_test' == true. ··· 2540 2519 .name = "SOCK_STREAM TX credit bounds", 2541 2520 .run_client = test_stream_tx_credit_bounds_client, 2542 2521 .run_server = test_stream_tx_credit_bounds_server, 2522 + }, 2523 + { 2524 + .name = "SOCK_STREAM MSG_PEEK after partial recv", 2525 + .run_client = test_stream_msg_peek_client, 2526 + .run_server = test_stream_peek_after_recv_server, 2543 2527 }, 2544 2528 {}, 2545 2529 };