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 'af_unix-correct-manage_oob-when-oob-follows-a-consumed-oob'

Kuniyuki Iwashima says:

====================
af_unix: Correct manage_oob() when OOB follows a consumed OOB.

Recently syzkaller reported UAF of OOB skb.

The bug was introduced by commit 93c99f21db36 ("af_unix: Don't stop
recv(MSG_DONTWAIT) if consumed OOB skb is at the head.") but uncovered
by another recent commit 8594d9b85c07 ("af_unix: Don't call skb_get()
for OOB skb.").

[0]: https://lore.kernel.org/netdev/00000000000083b05a06214c9ddc@google.com/
====================

Link: https://patch.msgid.link/20240905193240.17565-1-kuniyu@amazon.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+62 -34
+39 -34
net/unix/af_unix.c
··· 2654 2654 static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk, 2655 2655 int flags, int copied) 2656 2656 { 2657 + struct sk_buff *read_skb = NULL, *unread_skb = NULL; 2657 2658 struct unix_sock *u = unix_sk(sk); 2658 2659 2660 + if (likely(unix_skb_len(skb) && skb != READ_ONCE(u->oob_skb))) 2661 + return skb; 2662 + 2663 + spin_lock(&sk->sk_receive_queue.lock); 2664 + 2659 2665 if (!unix_skb_len(skb)) { 2660 - struct sk_buff *unlinked_skb = NULL; 2661 - 2662 - spin_lock(&sk->sk_receive_queue.lock); 2663 - 2664 2666 if (copied && (!u->oob_skb || skb == u->oob_skb)) { 2665 2667 skb = NULL; 2666 2668 } else if (flags & MSG_PEEK) { 2667 2669 skb = skb_peek_next(skb, &sk->sk_receive_queue); 2668 2670 } else { 2669 - unlinked_skb = skb; 2671 + read_skb = skb; 2670 2672 skb = skb_peek_next(skb, &sk->sk_receive_queue); 2671 - __skb_unlink(unlinked_skb, &sk->sk_receive_queue); 2673 + __skb_unlink(read_skb, &sk->sk_receive_queue); 2672 2674 } 2673 2675 2674 - spin_unlock(&sk->sk_receive_queue.lock); 2675 - 2676 - consume_skb(unlinked_skb); 2677 - } else { 2678 - struct sk_buff *unlinked_skb = NULL; 2679 - 2680 - spin_lock(&sk->sk_receive_queue.lock); 2681 - 2682 - if (skb == u->oob_skb) { 2683 - if (copied) { 2684 - skb = NULL; 2685 - } else if (!(flags & MSG_PEEK)) { 2686 - WRITE_ONCE(u->oob_skb, NULL); 2687 - 2688 - if (!sock_flag(sk, SOCK_URGINLINE)) { 2689 - __skb_unlink(skb, &sk->sk_receive_queue); 2690 - unlinked_skb = skb; 2691 - skb = skb_peek(&sk->sk_receive_queue); 2692 - } 2693 - } else if (!sock_flag(sk, SOCK_URGINLINE)) { 2694 - skb = skb_peek_next(skb, &sk->sk_receive_queue); 2695 - } 2696 - } 2697 - 2698 - spin_unlock(&sk->sk_receive_queue.lock); 2699 - 2700 - kfree_skb(unlinked_skb); 2676 + if (!skb) 2677 + goto unlock; 2701 2678 } 2679 + 2680 + if (skb != u->oob_skb) 2681 + goto unlock; 2682 + 2683 + if (copied) { 2684 + skb = NULL; 2685 + } else if (!(flags & MSG_PEEK)) { 2686 + WRITE_ONCE(u->oob_skb, NULL); 2687 + 2688 + if (!sock_flag(sk, SOCK_URGINLINE)) { 2689 + __skb_unlink(skb, &sk->sk_receive_queue); 2690 + unread_skb = skb; 2691 + skb = skb_peek(&sk->sk_receive_queue); 2692 + } 2693 + } else if (!sock_flag(sk, SOCK_URGINLINE)) { 2694 + skb = skb_peek_next(skb, &sk->sk_receive_queue); 2695 + } 2696 + 2697 + unlock: 2698 + spin_unlock(&sk->sk_receive_queue.lock); 2699 + 2700 + consume_skb(read_skb); 2701 + kfree_skb(unread_skb); 2702 + 2702 2703 return skb; 2703 2704 } 2704 2705 #endif ··· 3176 3175 skb = skb_peek(&sk->sk_receive_queue); 3177 3176 if (skb) { 3178 3177 struct sk_buff *oob_skb = READ_ONCE(u->oob_skb); 3178 + struct sk_buff *next_skb; 3179 + 3180 + next_skb = skb_peek_next(skb, &sk->sk_receive_queue); 3179 3181 3180 3182 if (skb == oob_skb || 3181 - (!oob_skb && !unix_skb_len(skb))) 3183 + (!unix_skb_len(skb) && 3184 + (!oob_skb || next_skb == oob_skb))) 3182 3185 answ = 1; 3183 3186 } 3184 3187
+23
tools/testing/selftests/net/af_unix/msg_oob.c
··· 525 525 } 526 526 } 527 527 528 + TEST_F(msg_oob, ex_oob_oob) 529 + { 530 + sendpair("x", 1, MSG_OOB); 531 + epollpair(true); 532 + siocatmarkpair(true); 533 + 534 + recvpair("x", 1, 1, MSG_OOB); 535 + epollpair(false); 536 + siocatmarkpair(true); 537 + 538 + sendpair("y", 1, MSG_OOB); 539 + epollpair(true); 540 + siocatmarkpair(true); 541 + 542 + recvpair("", -EAGAIN, 1, 0); 543 + epollpair(false); 544 + siocatmarkpair(false); 545 + 546 + recvpair("", -EINVAL, 1, MSG_OOB); 547 + epollpair(false); 548 + siocatmarkpair(false); 549 + } 550 + 528 551 TEST_F(msg_oob, ex_oob_ahead_break) 529 552 { 530 553 sendpair("hello", 5, MSG_OOB);