From: Florian Westphal fw@strlen.de
mainline inclusion from mainline-v5.5-rc1 commit 51210ad5a558dcc7511d0c083f5cd796077b4e4d category: bugfix bugzilla: NA CVE: CVE-2021-20177
--------------------------------
syzbot triggered struct net NULL deref in NF_HOOK_LIST: RIP: 0010:NF_HOOK_LIST include/linux/netfilter.h:331 [inline] RIP: 0010:ip6_sublist_rcv+0x5c9/0x930 net/ipv6/ip6_input.c:292 ipv6_list_rcv+0x373/0x4b0 net/ipv6/ip6_input.c:328 __netif_receive_skb_list_ptype net/core/dev.c:5274 [inline]
Reason: void ipv6_list_rcv(struct list_head *head, struct packet_type *pt, struct net_device *orig_dev) [..] list_for_each_entry_safe(skb, next, head, list) { /* iterates list */ skb = ip6_rcv_core(skb, dev, net); /* ip6_rcv_core drops skb -> NULL is returned */ if (skb == NULL) continue; [..] } /* sublist is empty -> curr_net is NULL */ ip6_sublist_rcv(&sublist, curr_dev, curr_net);
Before the recent change NF_HOOK_LIST did a list iteration before struct net deref, i.e. it was a no-op in the empty list case.
List iteration now happens after *net deref, causing crash.
Follow the same pattern as the ip(v6)_list_rcv loop and add a list_empty test for the final sublist dispatch too.
Cc: Edward Cree ecree@solarflare.com Reported-by: syzbot+c54f457cad330e57e967@syzkaller.appspotmail.com Fixes: ca58fbe06c54 ("netfilter: add and use nf_hook_slow_list()") Signed-off-by: Florian Westphal fw@strlen.de Tested-by: Leon Romanovsky leonro@mellanox.com Tested-by: Nikolay Aleksandrov nikolay@cumulusnetworks.com Signed-off-by: David S. Miller davem@davemloft.net Signed-off-by: Yang Yingliang yangyingliang@huawei.com Reviewed-by: Yue Haibing yuehaibing@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- net/ipv4/ip_input.c | 3 ++- net/ipv6/ip6_input.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index c3a0683e83df..d4ab0edb53b5 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -616,5 +616,6 @@ void ip_list_rcv(struct list_head *head, struct packet_type *pt, list_add_tail(&skb->list, &sublist); } /* dispatch final sublist */ - ip_sublist_rcv(&sublist, curr_dev, curr_net); + if (!list_empty(&sublist)) + ip_sublist_rcv(&sublist, curr_dev, curr_net); } diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index a8269ef01e60..ec06fd3e545d 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -324,7 +324,8 @@ void ipv6_list_rcv(struct list_head *head, struct packet_type *pt, list_add_tail(&skb->list, &sublist); } /* dispatch final sublist */ - ip6_sublist_rcv(&sublist, curr_dev, curr_net); + if (!list_empty(&sublist)) + ip6_sublist_rcv(&sublist, curr_dev, curr_net); }
/*