Commit 8b78903b authored by David S. Miller's avatar David S. Miller

Merge branch 'skb-headroom-slab-out-of-bounds'

Stefano Brivio says:

====================
Fix slab out-of-bounds on insufficient headroom for IPv6 packets

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 avoid writing before the allocated buffer in
neigh_hh_output() in case the headroom is enough for the unaligned hardware
header size, but not enough for the aligned one, and that we warn if we hit
this condition.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents f9bfe4e6 e6ac64d4
...@@ -454,6 +454,7 @@ static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb) ...@@ -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) 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 seq;
unsigned int hh_len; unsigned int hh_len;
...@@ -461,16 +462,33 @@ static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb ...@@ -461,16 +462,33 @@ static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb
seq = read_seqbegin(&hh->hh_lock); seq = read_seqbegin(&hh->hh_lock);
hh_len = hh->hh_len; hh_len = hh->hh_len;
if (likely(hh_len <= HH_DATA_MOD)) { if (likely(hh_len <= HH_DATA_MOD)) {
/* this is inlined by gcc */ hh_alen = HH_DATA_MOD;
memcpy(skb->data - HH_DATA_MOD, hh->hh_data, HH_DATA_MOD);
/* skb_push() would proceed silently if we have room for
* the unaligned size but not for the aligned size:
* check headroom explicitly.
*/
if (likely(skb_headroom(skb) >= HH_DATA_MOD)) {
/* this is inlined by gcc */
memcpy(skb->data - HH_DATA_MOD, hh->hh_data,
HH_DATA_MOD);
}
} else { } 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); if (likely(skb_headroom(skb) >= hh_alen)) {
memcpy(skb->data - hh_alen, hh->hh_data,
hh_alen);
}
} }
} while (read_seqretry(&hh->hh_lock, seq)); } while (read_seqretry(&hh->hh_lock, seq));
skb_push(skb, hh_len); if (WARN_ON_ONCE(skb_headroom(skb) < hh_alen)) {
kfree_skb(skb);
return NET_XMIT_DROP;
}
__skb_push(skb, hh_len);
return dev_queue_xmit(skb); return dev_queue_xmit(skb);
} }
......
...@@ -195,37 +195,37 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, ...@@ -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); const struct ipv6_pinfo *np = inet6_sk(sk);
struct in6_addr *first_hop = &fl6->daddr; struct in6_addr *first_hop = &fl6->daddr;
struct dst_entry *dst = skb_dst(skb); struct dst_entry *dst = skb_dst(skb);
unsigned int head_room;
struct ipv6hdr *hdr; struct ipv6hdr *hdr;
u8 proto = fl6->flowi6_proto; u8 proto = fl6->flowi6_proto;
int seg_len = skb->len; int seg_len = skb->len;
int hlimit = -1; int hlimit = -1;
u32 mtu; u32 mtu;
if (opt) { head_room = sizeof(struct ipv6hdr) + LL_RESERVED_SPACE(dst->dev);
unsigned int head_room; if (opt)
head_room += opt->opt_nflen + opt->opt_flen;
/* First: exthdrs may take lots of space (~8K for now) if (unlikely(skb_headroom(skb) < head_room)) {
MAX_HEADER is not enough. struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
*/ if (!skb2) {
head_room = opt->opt_nflen + opt->opt_flen; IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
seg_len += head_room; IPSTATS_MIB_OUTDISCARDS);
head_room += sizeof(struct ipv6hdr) + LL_RESERVED_SPACE(dst->dev); kfree_skb(skb);
return -ENOBUFS;
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 (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) if (opt->opt_flen)
ipv6_push_frag_opts(skb, opt, &proto); ipv6_push_frag_opts(skb, opt, &proto);
if (opt->opt_nflen) if (opt->opt_nflen)
ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop, ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop,
&fl6->saddr); &fl6->saddr);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment