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

xfrm: policy: avoid warning splat when merging nodes

syzbot reported a splat:
xfrm_policy_inexact_list_reinsert+0x625/0x6e0 net/xfrm/xfrm_policy.c:877
CPU: 1 PID: 6756 Comm: syz-executor.1 Not tainted 5.3.0-rc2+ #57
Call Trace:
xfrm_policy_inexact_node_reinsert net/xfrm/xfrm_policy.c:922 [inline]
xfrm_policy_inexact_node_merge net/xfrm/xfrm_policy.c:958 [inline]
xfrm_policy_inexact_insert_node+0x537/0xb50 net/xfrm/xfrm_policy.c:1023
xfrm_policy_inexact_alloc_chain+0x62b/0xbd0 net/xfrm/xfrm_policy.c:1139
xfrm_policy_inexact_insert+0xe8/0x1540 net/xfrm/xfrm_policy.c:1182
xfrm_policy_insert+0xdf/0xce0 net/xfrm/xfrm_policy.c:1574
xfrm_add_policy+0x4cf/0x9b0 net/xfrm/xfrm_user.c:1670
xfrm_user_rcv_msg+0x46b/0x720 net/xfrm/xfrm_user.c:2676
netlink_rcv_skb+0x1f0/0x460 net/netlink/af_netlink.c:2477
xfrm_netlink_rcv+0x74/0x90 net/xfrm/xfrm_user.c:2684
netlink_unicast_kernel net/netlink/af_netlink.c:1302 [inline]
netlink_unicast+0x809/0x9a0 net/netlink/af_netlink.c:1328
netlink_sendmsg+0xa70/0xd30 net/netlink/af_netlink.c:1917
sock_sendmsg_nosec net/socket.c:637 [inline]
sock_sendmsg net/socket.c:657 [inline]

There is no reproducer, however, the warning can be reproduced
by adding rules with ever smaller prefixes.

The sanity check ("does the policy match the node") uses the prefix value
of the node before its updated to the smaller value.

To fix this, update the prefix earlier. The bug has no impact on tree
correctness, this is only to prevent a false warning.

Reported-by: syzbot+8cc27ace5f6972910b31@syzkaller.appspotmail.com
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>

authored by

Florian Westphal and committed by
Steffen Klassert
769a807d 22d6552f

+11 -2
+4 -2
net/xfrm/xfrm_policy.c
··· 912 912 } else if (delta > 0) { 913 913 p = &parent->rb_right; 914 914 } else { 915 + bool same_prefixlen = node->prefixlen == n->prefixlen; 915 916 struct xfrm_policy *tmp; 916 917 917 918 hlist_for_each_entry(tmp, &n->hhead, bydst) { ··· 920 919 hlist_del_rcu(&tmp->bydst); 921 920 } 922 921 922 + node->prefixlen = prefixlen; 923 + 923 924 xfrm_policy_inexact_list_reinsert(net, node, family); 924 925 925 - if (node->prefixlen == n->prefixlen) { 926 + if (same_prefixlen) { 926 927 kfree_rcu(n, rcu); 927 928 return; 928 929 } ··· 932 929 rb_erase(*p, new); 933 930 kfree_rcu(n, rcu); 934 931 n = node; 935 - n->prefixlen = prefixlen; 936 932 goto restart; 937 933 } 938 934 }
+7
tools/testing/selftests/net/xfrm_policy.sh
··· 106 106 # 107 107 # 10.0.0.0/24 and 10.0.1.0/24 nodes have been merged as 10.0.0.0/23. 108 108 ip -net $ns xfrm policy add src 10.1.0.0/24 dst 10.0.0.0/23 dir fwd priority 200 action block 109 + 110 + # similar to above: add policies (with partially random address), with shrinking prefixes. 111 + for p in 29 28 27;do 112 + for k in $(seq 1 32); do 113 + ip -net $ns xfrm policy add src 10.253.1.$((RANDOM%255))/$p dst 10.254.1.$((RANDOM%255))/$p dir fwd priority $((200+k)) action block 2>/dev/null 114 + done 115 + done 109 116 } 110 117 111 118 do_esp_policy_get_check() {