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

net, bpf: Fix ip6ip6 crash with collect_md populated skbs

I ran into a crash where setting up a ip6ip6 tunnel device which was /not/
set to collect_md mode was receiving collect_md populated skbs for xmit.

The BPF prog was populating the skb via bpf_skb_set_tunnel_key() which is
assigning special metadata dst entry and then redirecting the skb to the
device, taking ip6_tnl_start_xmit() -> ipxip6_tnl_xmit() -> ip6_tnl_xmit()
and in the latter it performs a neigh lookup based on skb_dst(skb) where
we trigger a NULL pointer dereference on dst->ops->neigh_lookup() since
the md_dst_ops do not populate neigh_lookup callback with a fake handler.

Transform the md_dst_ops into generic dst_blackhole_ops that can also be
reused elsewhere when needed, and use them for the metadata dst entries as
callback ops.

Also, remove the dst_md_discard{,_out}() ops and rely on dst_discard{,_out}()
from dst_init() which free the skb the same way modulo the splat. Given we
will be able to recover just fine from there, avoid any potential splats
iff this gets ever triggered in future (or worse, panic on warns when set).

Fixes: f38a9eb1f77b ("dst: Metadata destinations")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Daniel Borkmann and committed by
David S. Miller
a188bb56 c4c877b2

+9 -22
+9 -22
net/core/dst.c
··· 275 275 } 276 276 EXPORT_SYMBOL_GPL(dst_blackhole_mtu); 277 277 278 - static struct dst_ops md_dst_ops = { 279 - .family = AF_UNSPEC, 278 + static struct dst_ops dst_blackhole_ops = { 279 + .family = AF_UNSPEC, 280 + .neigh_lookup = dst_blackhole_neigh_lookup, 281 + .check = dst_blackhole_check, 282 + .cow_metrics = dst_blackhole_cow_metrics, 283 + .update_pmtu = dst_blackhole_update_pmtu, 284 + .redirect = dst_blackhole_redirect, 285 + .mtu = dst_blackhole_mtu, 280 286 }; 281 - 282 - static int dst_md_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb) 283 - { 284 - WARN_ONCE(1, "Attempting to call output on metadata dst\n"); 285 - kfree_skb(skb); 286 - return 0; 287 - } 288 - 289 - static int dst_md_discard(struct sk_buff *skb) 290 - { 291 - WARN_ONCE(1, "Attempting to call input on metadata dst\n"); 292 - kfree_skb(skb); 293 - return 0; 294 - } 295 287 296 288 static void __metadata_dst_init(struct metadata_dst *md_dst, 297 289 enum metadata_type type, u8 optslen) 298 - 299 290 { 300 291 struct dst_entry *dst; 301 292 302 293 dst = &md_dst->dst; 303 - dst_init(dst, &md_dst_ops, NULL, 1, DST_OBSOLETE_NONE, 294 + dst_init(dst, &dst_blackhole_ops, NULL, 1, DST_OBSOLETE_NONE, 304 295 DST_METADATA | DST_NOCOUNT); 305 - 306 - dst->input = dst_md_discard; 307 - dst->output = dst_md_discard_out; 308 - 309 296 memset(dst + 1, 0, sizeof(*md_dst) + optslen - sizeof(*dst)); 310 297 md_dst->type = type; 311 298 }