All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net 0/2] Fix slab out-of-bounds on insufficient headroom for IPv6 packets
@ 2018-12-05  0:13 Stefano Brivio
  2018-12-05  0:13 ` [PATCH net 1/2] ipv6: Check available headroom in ip6_xmit() even without options Stefano Brivio
  2018-12-05  0:13 ` [PATCH net 2/2] neighbour: BUG_ON() writing before skb->head in neigh_hh_output() Stefano Brivio
  0 siblings, 2 replies; 4+ messages in thread
From: Stefano Brivio @ 2018-12-05  0:13 UTC (permalink / raw)
  To: David S. Miller
  Cc: Jianlin Shi, Hangbin Liu, Eric Dumazet, Stephen Hemminger, netdev

Patch 1/2 fixes a slab out-of-bounds occurring with short SCTP packets over
IPv4 over L2TP over IPv6 on a configuration with relatively low HEADER_MAX.

Patch 2/2 makes sure we panic in neigh_hh_output() instead of silently
writing before the allocated buffer in case the headroom is enough for the
unaligned hardware header size, but not enough for the aligned one.

Stefano Brivio (2):
  ipv6: Check available headroom in ip6_xmit() even without options
  neighbour: BUG_ON() writing before skb->head in neigh_hh_output()

 include/net/neighbour.h |  8 ++++++--
 net/ipv6/ip6_output.c   | 42 ++++++++++++++++++++---------------------
 2 files changed, 27 insertions(+), 23 deletions(-)

-- 
2.19.2

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH net 1/2] ipv6: Check available headroom in ip6_xmit() even without options
  2018-12-05  0:13 [PATCH net 0/2] Fix slab out-of-bounds on insufficient headroom for IPv6 packets Stefano Brivio
@ 2018-12-05  0:13 ` Stefano Brivio
  2018-12-05  0:13 ` [PATCH net 2/2] neighbour: BUG_ON() writing before skb->head in neigh_hh_output() Stefano Brivio
  1 sibling, 0 replies; 4+ messages in thread
From: Stefano Brivio @ 2018-12-05  0:13 UTC (permalink / raw)
  To: David S. Miller
  Cc: Jianlin Shi, Hangbin Liu, Eric Dumazet, Stephen Hemminger, netdev

Even if we send an IPv6 packet without options, MAX_HEADER might not be
enough to account for the additional headroom required by alignment of
hardware headers.

On a configuration without HYPERV_NET, WLAN, AX25, and with IPV6_TUNNEL,
sending short SCTP packets over IPv4 over L2TP over IPv6, we start with
100 bytes of allocated headroom in sctp_packet_transmit(), end up with 54
bytes after l2tp_xmit_skb(), and 14 bytes in ip6_finish_output2().

Those would be enough to append our 14 bytes header, but we're going to
align that to 16 bytes, and write 2 bytes out of the allocated slab in
neigh_hh_output().

KASan says:

[  264.967848] ==================================================================
[  264.967861] BUG: KASAN: slab-out-of-bounds in ip6_finish_output2+0x1aec/0x1c70
[  264.967866] Write of size 16 at addr 000000006af1c7fe by task netperf/6201
[  264.967870]
[  264.967876] CPU: 0 PID: 6201 Comm: netperf Not tainted 4.20.0-rc4+ #1
[  264.967881] Hardware name: IBM 2827 H43 400 (z/VM 6.4.0)
[  264.967887] Call Trace:
[  264.967896] ([<00000000001347d6>] show_stack+0x56/0xa0)
[  264.967903]  [<00000000017e379c>] dump_stack+0x23c/0x290
[  264.967912]  [<00000000007bc594>] print_address_description+0xf4/0x290
[  264.967919]  [<00000000007bc8fc>] kasan_report+0x13c/0x240
[  264.967927]  [<000000000162f5e4>] ip6_finish_output2+0x1aec/0x1c70
[  264.967935]  [<000000000163f890>] ip6_finish_output+0x430/0x7f0
[  264.967943]  [<000000000163fe44>] ip6_output+0x1f4/0x580
[  264.967953]  [<000000000163882a>] ip6_xmit+0xfea/0x1ce8
[  264.967963]  [<00000000017396e2>] inet6_csk_xmit+0x282/0x3f8
[  264.968033]  [<000003ff805fb0ba>] l2tp_xmit_skb+0xe02/0x13e0 [l2tp_core]
[  264.968037]  [<000003ff80631192>] l2tp_eth_dev_xmit+0xda/0x150 [l2tp_eth]
[  264.968041]  [<0000000001220020>] dev_hard_start_xmit+0x268/0x928
[  264.968069]  [<0000000001330e8e>] sch_direct_xmit+0x7ae/0x1350
[  264.968071]  [<000000000122359c>] __dev_queue_xmit+0x2b7c/0x3478
[  264.968075]  [<00000000013d2862>] ip_finish_output2+0xce2/0x11a0
[  264.968078]  [<00000000013d9b14>] ip_finish_output+0x56c/0x8c8
[  264.968081]  [<00000000013ddd1e>] ip_output+0x226/0x4c0
[  264.968083]  [<00000000013dbd6c>] __ip_queue_xmit+0x894/0x1938
[  264.968100]  [<000003ff80bc3a5c>] sctp_packet_transmit+0x29d4/0x3648 [sctp]
[  264.968116]  [<000003ff80b7bf68>] sctp_outq_flush_ctrl.constprop.5+0x8d0/0xe50 [sctp]
[  264.968131]  [<000003ff80b7c716>] sctp_outq_flush+0x22e/0x7d8 [sctp]
[  264.968146]  [<000003ff80b35c68>] sctp_cmd_interpreter.isra.16+0x530/0x6800 [sctp]
[  264.968161]  [<000003ff80b3410a>] sctp_do_sm+0x222/0x648 [sctp]
[  264.968177]  [<000003ff80bbddac>] sctp_primitive_ASSOCIATE+0xbc/0xf8 [sctp]
[  264.968192]  [<000003ff80b93328>] __sctp_connect+0x830/0xc20 [sctp]
[  264.968208]  [<000003ff80bb11ce>] sctp_inet_connect+0x2e6/0x378 [sctp]
[  264.968212]  [<0000000001197942>] __sys_connect+0x21a/0x450
[  264.968215]  [<000000000119aff8>] sys_socketcall+0x3d0/0xb08
[  264.968218]  [<000000000184ea7a>] system_call+0x2a2/0x2c0

[...]

Just like ip_finish_output2() does for IPv4, check that we have enough
headroom in ip6_xmit(), and reallocate it if we don't.

This issue is older than git history.

Reported-by: Jianlin Shin <jishi@redhat.com>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
---
 net/ipv6/ip6_output.c | 42 +++++++++++++++++++++---------------------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 827a3f5ff3bb..fcd3c66ded16 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -195,37 +195,37 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
 	const struct ipv6_pinfo *np = inet6_sk(sk);
 	struct in6_addr *first_hop = &fl6->daddr;
 	struct dst_entry *dst = skb_dst(skb);
+	unsigned int head_room;
 	struct ipv6hdr *hdr;
 	u8  proto = fl6->flowi6_proto;
 	int seg_len = skb->len;
 	int hlimit = -1;
 	u32 mtu;
 
-	if (opt) {
-		unsigned int head_room;
+	head_room = sizeof(struct ipv6hdr) + LL_RESERVED_SPACE(dst->dev);
+	if (opt)
+		head_room += opt->opt_nflen + opt->opt_flen;
 
-		/* First: exthdrs may take lots of space (~8K for now)
-		   MAX_HEADER is not enough.
-		 */
-		head_room = opt->opt_nflen + opt->opt_flen;
-		seg_len += head_room;
-		head_room += sizeof(struct ipv6hdr) + LL_RESERVED_SPACE(dst->dev);
-
-		if (skb_headroom(skb) < head_room) {
-			struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
-			if (!skb2) {
-				IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
-					      IPSTATS_MIB_OUTDISCARDS);
-				kfree_skb(skb);
-				return -ENOBUFS;
-			}
-			if (skb->sk)
-				skb_set_owner_w(skb2, skb->sk);
-			consume_skb(skb);
-			skb = skb2;
+	if (unlikely(skb_headroom(skb) < head_room)) {
+		struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
+		if (!skb2) {
+			IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
+				      IPSTATS_MIB_OUTDISCARDS);
+			kfree_skb(skb);
+			return -ENOBUFS;
 		}
+		if (skb->sk)
+			skb_set_owner_w(skb2, skb->sk);
+		consume_skb(skb);
+		skb = skb2;
+	}
+
+	if (opt) {
+		seg_len += opt->opt_nflen + opt->opt_flen;
+
 		if (opt->opt_flen)
 			ipv6_push_frag_opts(skb, opt, &proto);
+
 		if (opt->opt_nflen)
 			ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop,
 					     &fl6->saddr);
-- 
2.19.2

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH net 2/2] neighbour: BUG_ON() writing before skb->head in neigh_hh_output()
  2018-12-05  0:13 [PATCH net 0/2] Fix slab out-of-bounds on insufficient headroom for IPv6 packets Stefano Brivio
  2018-12-05  0:13 ` [PATCH net 1/2] ipv6: Check available headroom in ip6_xmit() even without options Stefano Brivio
@ 2018-12-05  0:13 ` Stefano Brivio
       [not found]   ` <CANn89i+VoX7LNq4uGztJvH3EVzbE=Rhab=erkCOzHo3xyWBExg@mail.gmail.com>
  1 sibling, 1 reply; 4+ messages in thread
From: Stefano Brivio @ 2018-12-05  0:13 UTC (permalink / raw)
  To: David S. Miller
  Cc: Jianlin Shi, Hangbin Liu, Eric Dumazet, Stephen Hemminger, netdev

While skb_push() makes the kernel panic if the skb headroom is less than
the unaligned hardware header size in neigh_hh_output(), it will proceed
silently in case we copy more than that because of alignment.

In the case fixed by the previous patch,
"ipv6: Check available headroom in ip6_xmit() even without options", we
end up in neigh_hh_output() with 14 bytes headroom, 14 bytes hardware
header and write 16 bytes, starting 2 bytes before the allocated buffer.

Panic, instead of silently corrupting adjacent slabs.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
---
 include/net/neighbour.h | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index f58b384aa6c9..95dcba741fd5 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -454,6 +454,7 @@ static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb)
 
 static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb)
 {
+	unsigned int hh_alen = 0;
 	unsigned int seq;
 	unsigned int hh_len;
 
@@ -461,15 +462,18 @@ static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb
 		seq = read_seqbegin(&hh->hh_lock);
 		hh_len = hh->hh_len;
 		if (likely(hh_len <= HH_DATA_MOD)) {
+			hh_alen = HH_DATA_MOD;
 			/* this is inlined by gcc */
 			memcpy(skb->data - HH_DATA_MOD, hh->hh_data, HH_DATA_MOD);
 		} else {
-			unsigned int hh_alen = HH_DATA_ALIGN(hh_len);
-
+			hh_alen = HH_DATA_ALIGN(hh_len);
 			memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);
 		}
 	} while (read_seqretry(&hh->hh_lock, seq));
 
+	/* skb_push() won't panic if we have room for the unaligned size only */
+	BUG_ON(skb_headroom(skb) < hh_alen);
+
 	skb_push(skb, hh_len);
 	return dev_queue_xmit(skb);
 }
-- 
2.19.2

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH net 2/2] neighbour: BUG_ON() writing before skb->head in neigh_hh_output()
       [not found]   ` <CANn89i+VoX7LNq4uGztJvH3EVzbE=Rhab=erkCOzHo3xyWBExg@mail.gmail.com>
@ 2018-12-05  0:38     ` Stefano Brivio
  0 siblings, 0 replies; 4+ messages in thread
From: Stefano Brivio @ 2018-12-05  0:38 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: David Miller, Jianlin Shi, Hangbin Liu, Stephen Hemminger, netdev

On Tue, 4 Dec 2018 16:26:05 -0800
Eric Dumazet <edumazet@google.com> wrote:

> > +       /* skb_push() won't panic if we have room for the unaligned size
> > only */
> > +       BUG_ON(skb_headroom(skb) < hh_alen);
> >  
> 
> What about avoiding the panic and instead call kfree_skb() ?
> 
>          if (WARN_ON_ONCE(skb_headroom(skb) < hh_alen)) {
>               kfree_skb(skb);
>              return NET_XMIT_DROP;
>         }

Okay, I guess it won't go unnoticed anyway, and it's probably better
than the alternative.

> > +
> >         skb_push(skb, hh_len);
> >  
> 
> Maybe we can use __skb_push() here, since prior safety check should be
> enough ?

Indeed, I'll change that in v2. Thanks!

-- 
Stefano

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2018-12-05  0:38 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-05  0:13 [PATCH net 0/2] Fix slab out-of-bounds on insufficient headroom for IPv6 packets Stefano Brivio
2018-12-05  0:13 ` [PATCH net 1/2] ipv6: Check available headroom in ip6_xmit() even without options Stefano Brivio
2018-12-05  0:13 ` [PATCH net 2/2] neighbour: BUG_ON() writing before skb->head in neigh_hh_output() Stefano Brivio
     [not found]   ` <CANn89i+VoX7LNq4uGztJvH3EVzbE=Rhab=erkCOzHo3xyWBExg@mail.gmail.com>
2018-12-05  0:38     ` Stefano Brivio

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.