From: Maciej Żenczykowski <hidden> Date: 2018-10-16 12:01:49
I believe that:
commit ad8b1ffc3efae2f65080bdb11145c87d299b8f9a
Author: Florian Westphal [off-list ref]
netfilter: ipv6: nf_defrag: drop skb dst before queueing
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -618,6 +618,8 @@ int nf_ct_frag6_gather(struct net *net, struct
sk_buff *skb, u32 user)
fq->q.meat == fq->q.len &&
nf_ct_frag6_reasm(fq, skb, dev))
ret = 0;
+ else
+ skb_dst_drop(skb);
out_unlock:
spin_unlock_bh(&fq->q.lock);
Is causing a crash on android after upgrading from 4.9.96 to 4.9.119
This is because clatd ipv4 to ipv6 translation user space daemon is
functionally equivalent to the syzkaller reproducer.
It will convert ipv4 frags it receives via tap into ipv6 frags which
it will write out via rawv6 sendmsg.
However we are also using xt_policy, after stripping cruft this is basically:
ip6tables -A OUTPUT -m policy --dir out --pol ipsec
Crash is:
match_policy_out()
const struct dst_entry *dst = skb_dst(skb); // returns NULL
if (dst->xfrm == NULL) <-- dst == NULL -> panic
[ 1136.606948] c1 2675 [<ffffff9ec38b4098>] policy_mt+0x34/0x18c
[ 1136.606954] c1 2675 [<ffffff9ec39e6af8>] ip6t_do_table+0x280/0x684
[ 1136.606961] c1 2675 [<ffffff9ec39e7250>] ip6table_filter_hook+0x20/0x28
[ 1136.606969] c1 2675 [<ffffff9ec386ecc8>] nf_hook_slow+0x98/0x154
[ 1136.606977] c1 2675 [<ffffff9ec39b9b10>] rawv6_sendmsg+0xd14/0x1520
[ 1136.606985] c1 2675 [<ffffff9ec39191fc>] inet_sendmsg+0x100/0x1b0
[ 1136.606993] c1 2675 [<ffffff9ec37d3720>] ___sys_sendmsg+0x2a0/0x414
[ 1136.606999] c1 2675 [<ffffff9ec37d3d48>] SyS_sendmsg+0x94/0xe4
Just checking for NULL in xt_policy.c:match_policy_out() and returning
0 or 1 unconditionally seems to be the wrong thing to do,
since after all prior to skb_dst_drop() the skb->dst->xfrm might not
have been NULL.
Maciej Żenczykowski, Kernel Networking Developer @ Google
This is only supposed to drop dst of skbs that are enqueued,
i.e. frag6_gather returns NF_STOLEN.
In case skb completes the queue, then that skbs dst_entry
is supposed to be kept, so skb_dst() does NOT return NULL.
Its not supposed to be any different than ipv4 defrag.
From: Maciej Żenczykowski <hidden> Date: 2018-10-16 17:38:59
4.19-rc8 - pass
4.14.76 - pass
4.9.133 - fail
4.9.133 + revert of ad8b1ffc3efae2f65080bdb11145c87d299b8f9a - pass
On Tue, Oct 16, 2018 at 2:41 AM Maciej Żenczykowski
[off-list ref] wrote:
(and v4.9.133 latest 4.9 LTS fails the same way, but curiously 4.19-rc8 doesn't)
From: Eric Dumazet <edumazet@google.com> Date: 2018-10-23 23:17:59
On Tue, Oct 23, 2018 at 7:48 AM Florian Westphal [off-list ref] wrote:
Unlike ipv4 and normal ipv6 defrag, netfilter ipv6 defragmentation did
not save/restore skb->dst.
This causes oops when handling locally generated ipv6 fragments, as
output path needs a valid dst.
Reported-by: Maciej Żenczykowski <redacted>
Fixes: 84379c9afe01 ("netfilter: ipv6: nf_defrag: drop skb dst before queueing")
Signed-off-by: Florian Westphal <fw@strlen.de>
@@ -587,11 +587,16 @@ int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)*/ret=-EINPROGRESS;if(fq->q.flags==(INET_FRAG_FIRST_IN|INET_FRAG_LAST_IN)&&-fq->q.meat==fq->q.len&&-nf_ct_frag6_reasm(fq,skb,dev))-ret=0;-else+fq->q.meat==fq->q.len){+unsignedlongorefdst=skb->_skb_refdst;++skb->_skb_refdst=0UL;+if(nf_ct_frag6_reasm(fq,skb,dev))+ret=0;+skb->_skb_refdst=orefdst;+}else{skb_dst_drop(skb);+}out_unlock:spin_unlock_bh(&fq->q.lock);--
2.18.1
I don't quite follow how this fixes things, but I'll trust you on it.
(nor do I understand why only 4.9 LTS appears to crash with a null ptr deref)
Thanks for the fix.
From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2018-10-25 16:50:36
On Tue, Oct 23, 2018 at 04:47:16PM +0200, Florian Westphal wrote:
Unlike ipv4 and normal ipv6 defrag, netfilter ipv6 defragmentation did
not save/restore skb->dst.
This causes oops when handling locally generated ipv6 fragments, as
output path needs a valid dst.