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

Merge branch 'af_unix-fix-two-oob-issues'

Kuniyuki Iwashima says:

====================
af_unix: Fix two OOB issues.

From: Kuniyuki Iwashima <kuniyu@google.com>

Recently, two issues are reported regarding MSG_OOB.

Patch 1 fixes issues that happen when multiple consumed OOB
skbs are placed consecutively in the recv queue.

Patch 2 fixes an inconsistent behaviour that close()ing a socket
with a consumed OOB skb at the head of the recv queue triggers
-ECONNRESET on the peer's recv().

v1: https://lore.kernel.org/netdev/20250618043453.281247-1-kuni1840@gmail.com/
====================

Link: https://patch.msgid.link/20250619041457.1132791-1-kuni1840@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

+161 -12
+23 -8
net/unix/af_unix.c
··· 660 660 #endif 661 661 } 662 662 663 + static unsigned int unix_skb_len(const struct sk_buff *skb) 664 + { 665 + return skb->len - UNIXCB(skb).consumed; 666 + } 667 + 663 668 static void unix_release_sock(struct sock *sk, int embrion) 664 669 { 665 670 struct unix_sock *u = unix_sk(sk); ··· 699 694 700 695 if (skpair != NULL) { 701 696 if (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) { 697 + struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); 698 + 699 + #if IS_ENABLED(CONFIG_AF_UNIX_OOB) 700 + if (skb && !unix_skb_len(skb)) 701 + skb = skb_peek_next(skb, &sk->sk_receive_queue); 702 + #endif 702 703 unix_state_lock(skpair); 703 704 /* No more writes */ 704 705 WRITE_ONCE(skpair->sk_shutdown, SHUTDOWN_MASK); 705 - if (!skb_queue_empty_lockless(&sk->sk_receive_queue) || embrion) 706 + if (skb || embrion) 706 707 WRITE_ONCE(skpair->sk_err, ECONNRESET); 707 708 unix_state_unlock(skpair); 708 709 skpair->sk_state_change(skpair); ··· 2672 2661 return timeo; 2673 2662 } 2674 2663 2675 - static unsigned int unix_skb_len(const struct sk_buff *skb) 2676 - { 2677 - return skb->len - UNIXCB(skb).consumed; 2678 - } 2679 - 2680 2664 struct unix_stream_read_state { 2681 2665 int (*recv_actor)(struct sk_buff *, int, int, 2682 2666 struct unix_stream_read_state *); ··· 2686 2680 #if IS_ENABLED(CONFIG_AF_UNIX_OOB) 2687 2681 static int unix_stream_recv_urg(struct unix_stream_read_state *state) 2688 2682 { 2683 + struct sk_buff *oob_skb, *read_skb = NULL; 2689 2684 struct socket *sock = state->socket; 2690 2685 struct sock *sk = sock->sk; 2691 2686 struct unix_sock *u = unix_sk(sk); 2692 2687 int chunk = 1; 2693 - struct sk_buff *oob_skb; 2694 2688 2695 2689 mutex_lock(&u->iolock); 2696 2690 unix_state_lock(sk); ··· 2705 2699 2706 2700 oob_skb = u->oob_skb; 2707 2701 2708 - if (!(state->flags & MSG_PEEK)) 2702 + if (!(state->flags & MSG_PEEK)) { 2709 2703 WRITE_ONCE(u->oob_skb, NULL); 2704 + 2705 + if (oob_skb->prev != (struct sk_buff *)&sk->sk_receive_queue && 2706 + !unix_skb_len(oob_skb->prev)) { 2707 + read_skb = oob_skb->prev; 2708 + __skb_unlink(read_skb, &sk->sk_receive_queue); 2709 + } 2710 + } 2710 2711 2711 2712 spin_unlock(&sk->sk_receive_queue.lock); 2712 2713 unix_state_unlock(sk); ··· 2724 2711 UNIXCB(oob_skb).consumed += 1; 2725 2712 2726 2713 mutex_unlock(&u->iolock); 2714 + 2715 + consume_skb(read_skb); 2727 2716 2728 2717 if (chunk < 0) 2729 2718 return -EFAULT;
+138 -4
tools/testing/selftests/net/af_unix/msg_oob.c
··· 210 210 static void __recvpair(struct __test_metadata *_metadata, 211 211 FIXTURE_DATA(msg_oob) *self, 212 212 const char *expected_buf, int expected_len, 213 - int buf_len, int flags) 213 + int buf_len, int flags, bool is_sender) 214 214 { 215 215 int i, ret[2], recv_errno[2], expected_errno = 0; 216 216 char recv_buf[2][BUF_SZ] = {}; ··· 221 221 errno = 0; 222 222 223 223 for (i = 0; i < 2; i++) { 224 - ret[i] = recv(self->fd[i * 2 + 1], recv_buf[i], buf_len, flags); 224 + int index = is_sender ? i * 2 : i * 2 + 1; 225 + 226 + ret[i] = recv(self->fd[index], recv_buf[i], buf_len, flags); 225 227 recv_errno[i] = errno; 226 228 } 227 229 ··· 310 308 ASSERT_EQ(answ[0], answ[1]); 311 309 } 312 310 311 + static void __resetpair(struct __test_metadata *_metadata, 312 + FIXTURE_DATA(msg_oob) *self, 313 + const FIXTURE_VARIANT(msg_oob) *variant, 314 + bool reset) 315 + { 316 + int i; 317 + 318 + for (i = 0; i < 2; i++) 319 + close(self->fd[i * 2 + 1]); 320 + 321 + __recvpair(_metadata, self, "", reset ? -ECONNRESET : 0, 1, 322 + variant->peek ? MSG_PEEK : 0, true); 323 + } 324 + 313 325 #define sendpair(buf, len, flags) \ 314 326 __sendpair(_metadata, self, buf, len, flags) 315 327 ··· 332 316 if (variant->peek) \ 333 317 __recvpair(_metadata, self, \ 334 318 expected_buf, expected_len, \ 335 - buf_len, (flags) | MSG_PEEK); \ 319 + buf_len, (flags) | MSG_PEEK, false); \ 336 320 __recvpair(_metadata, self, \ 337 - expected_buf, expected_len, buf_len, flags); \ 321 + expected_buf, expected_len, \ 322 + buf_len, flags, false); \ 338 323 } while (0) 339 324 340 325 #define epollpair(oob_remaining) \ ··· 346 329 347 330 #define setinlinepair() \ 348 331 __setinlinepair(_metadata, self) 332 + 333 + #define resetpair(reset) \ 334 + __resetpair(_metadata, self, variant, reset) 349 335 350 336 #define tcp_incompliant \ 351 337 for (self->tcp_compliant = false; \ ··· 364 344 recvpair("", -EINVAL, 1, MSG_OOB); 365 345 epollpair(false); 366 346 siocatmarkpair(false); 347 + 348 + resetpair(true); 349 + } 350 + 351 + TEST_F(msg_oob, non_oob_no_reset) 352 + { 353 + sendpair("x", 1, 0); 354 + epollpair(false); 355 + siocatmarkpair(false); 356 + 357 + recvpair("x", 1, 1, 0); 358 + epollpair(false); 359 + siocatmarkpair(false); 360 + 361 + resetpair(false); 367 362 } 368 363 369 364 TEST_F(msg_oob, oob) ··· 390 355 recvpair("x", 1, 1, MSG_OOB); 391 356 epollpair(false); 392 357 siocatmarkpair(true); 358 + 359 + tcp_incompliant { 360 + resetpair(false); /* TCP sets -ECONNRESET for ex-OOB. */ 361 + } 362 + } 363 + 364 + TEST_F(msg_oob, oob_reset) 365 + { 366 + sendpair("x", 1, MSG_OOB); 367 + epollpair(true); 368 + siocatmarkpair(true); 369 + 370 + resetpair(true); 393 371 } 394 372 395 373 TEST_F(msg_oob, oob_drop) ··· 418 370 recvpair("", -EINVAL, 1, MSG_OOB); 419 371 epollpair(false); 420 372 siocatmarkpair(false); 373 + 374 + resetpair(false); 421 375 } 422 376 423 377 TEST_F(msg_oob, oob_ahead) ··· 435 385 recvpair("hell", 4, 4, 0); 436 386 epollpair(false); 437 387 siocatmarkpair(true); 388 + 389 + tcp_incompliant { 390 + resetpair(false); /* TCP sets -ECONNRESET for ex-OOB. */ 391 + } 438 392 } 439 393 440 394 TEST_F(msg_oob, oob_break) ··· 457 403 458 404 recvpair("", -EAGAIN, 1, 0); 459 405 siocatmarkpair(false); 406 + 407 + resetpair(false); 460 408 } 461 409 462 410 TEST_F(msg_oob, oob_ahead_break) ··· 482 426 recvpair("world", 5, 5, 0); 483 427 epollpair(false); 484 428 siocatmarkpair(false); 429 + 430 + resetpair(false); 485 431 } 486 432 487 433 TEST_F(msg_oob, oob_break_drop) ··· 507 449 recvpair("", -EINVAL, 1, MSG_OOB); 508 450 epollpair(false); 509 451 siocatmarkpair(false); 452 + 453 + resetpair(false); 510 454 } 511 455 512 456 TEST_F(msg_oob, ex_oob_break) ··· 536 476 recvpair("ld", 2, 2, 0); 537 477 epollpair(false); 538 478 siocatmarkpair(false); 479 + 480 + resetpair(false); 539 481 } 540 482 541 483 TEST_F(msg_oob, ex_oob_drop) ··· 560 498 epollpair(false); 561 499 siocatmarkpair(true); 562 500 } 501 + 502 + resetpair(false); 563 503 } 564 504 565 505 TEST_F(msg_oob, ex_oob_drop_2) ··· 587 523 epollpair(false); 588 524 siocatmarkpair(true); 589 525 } 526 + 527 + resetpair(false); 590 528 } 591 529 592 530 TEST_F(msg_oob, ex_oob_oob) ··· 612 546 recvpair("", -EINVAL, 1, MSG_OOB); 613 547 epollpair(false); 614 548 siocatmarkpair(false); 549 + 550 + resetpair(false); 551 + } 552 + 553 + TEST_F(msg_oob, ex_oob_ex_oob) 554 + { 555 + sendpair("x", 1, MSG_OOB); 556 + epollpair(true); 557 + siocatmarkpair(true); 558 + 559 + recvpair("x", 1, 1, MSG_OOB); 560 + epollpair(false); 561 + siocatmarkpair(true); 562 + 563 + sendpair("y", 1, MSG_OOB); 564 + epollpair(true); 565 + siocatmarkpair(true); 566 + 567 + recvpair("y", 1, 1, MSG_OOB); 568 + epollpair(false); 569 + siocatmarkpair(true); 570 + 571 + tcp_incompliant { 572 + resetpair(false); /* TCP sets -ECONNRESET for ex-OOB. */ 573 + } 574 + } 575 + 576 + TEST_F(msg_oob, ex_oob_ex_oob_oob) 577 + { 578 + sendpair("x", 1, MSG_OOB); 579 + epollpair(true); 580 + siocatmarkpair(true); 581 + 582 + recvpair("x", 1, 1, MSG_OOB); 583 + epollpair(false); 584 + siocatmarkpair(true); 585 + 586 + sendpair("y", 1, MSG_OOB); 587 + epollpair(true); 588 + siocatmarkpair(true); 589 + 590 + recvpair("y", 1, 1, MSG_OOB); 591 + epollpair(false); 592 + siocatmarkpair(true); 593 + 594 + sendpair("z", 1, MSG_OOB); 595 + epollpair(true); 596 + siocatmarkpair(true); 615 597 } 616 598 617 599 TEST_F(msg_oob, ex_oob_ahead_break) ··· 690 576 recvpair("d", 1, 1, MSG_OOB); 691 577 epollpair(false); 692 578 siocatmarkpair(true); 579 + 580 + tcp_incompliant { 581 + resetpair(false); /* TCP sets -ECONNRESET for ex-OOB. */ 582 + } 693 583 } 694 584 695 585 TEST_F(msg_oob, ex_oob_siocatmark) ··· 713 595 recvpair("hell", 4, 4, 0); /* Intentionally stop at ex-OOB. */ 714 596 epollpair(true); 715 597 siocatmarkpair(false); 598 + 599 + resetpair(true); 716 600 } 717 601 718 602 TEST_F(msg_oob, inline_oob) ··· 732 612 recvpair("x", 1, 1, 0); 733 613 epollpair(false); 734 614 siocatmarkpair(false); 615 + 616 + resetpair(false); 735 617 } 736 618 737 619 TEST_F(msg_oob, inline_oob_break) ··· 755 633 recvpair("o", 1, 1, 0); 756 634 epollpair(false); 757 635 siocatmarkpair(false); 636 + 637 + resetpair(false); 758 638 } 759 639 760 640 TEST_F(msg_oob, inline_oob_ahead_break) ··· 785 661 786 662 epollpair(false); 787 663 siocatmarkpair(false); 664 + 665 + resetpair(false); 788 666 } 789 667 790 668 TEST_F(msg_oob, inline_ex_oob_break) ··· 812 686 recvpair("rld", 3, 3, 0); 813 687 epollpair(false); 814 688 siocatmarkpair(false); 689 + 690 + resetpair(false); 815 691 } 816 692 817 693 TEST_F(msg_oob, inline_ex_oob_no_drop) ··· 835 707 recvpair("y", 1, 1, 0); 836 708 epollpair(false); 837 709 siocatmarkpair(false); 710 + 711 + resetpair(false); 838 712 } 839 713 840 714 TEST_F(msg_oob, inline_ex_oob_drop) ··· 861 731 epollpair(false); 862 732 siocatmarkpair(false); 863 733 } 734 + 735 + resetpair(false); 864 736 } 865 737 866 738 TEST_F(msg_oob, inline_ex_oob_siocatmark) ··· 884 752 recvpair("hell", 4, 4, 0); /* Intentionally stop at ex-OOB. */ 885 753 epollpair(true); 886 754 siocatmarkpair(false); 755 + 756 + resetpair(true); 887 757 } 888 758 889 759 TEST_HARNESS_MAIN