Commit 0e1540d3 authored by Daniel Borkmann's avatar Daniel Borkmann Committed by Kamal Mostafa

bpf: try harder on clones when writing into skb

BugLink: http://bugs.launchpad.net/bugs/1601952

[ Upstream commit 3697649f ]

When we're dealing with clones and the area is not writeable, try
harder and get a copy via pskb_expand_head(). Replace also other
occurences in tc actions with the new skb_try_make_writable().
Reported-by: default avatarAshhad Sheikh <ashhadsheikh394@gmail.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarTim Gardner <tim.gardner@canonical.com>
Signed-off-by: default avatarKamal Mostafa <kamal@canonical.com>
parent 1f397d89
...@@ -2567,6 +2567,13 @@ static inline int skb_clone_writable(const struct sk_buff *skb, unsigned int len ...@@ -2567,6 +2567,13 @@ static inline int skb_clone_writable(const struct sk_buff *skb, unsigned int len
skb_headroom(skb) + len <= skb->hdr_len; skb_headroom(skb) + len <= skb->hdr_len;
} }
static inline int skb_try_make_writable(struct sk_buff *skb,
unsigned int write_len)
{
return skb_cloned(skb) && !skb_clone_writable(skb, write_len) &&
pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
}
static inline int __skb_cow(struct sk_buff *skb, unsigned int headroom, static inline int __skb_cow(struct sk_buff *skb, unsigned int headroom,
int cloned) int cloned)
{ {
......
...@@ -1275,9 +1275,7 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags) ...@@ -1275,9 +1275,7 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
*/ */
if (unlikely((u32) offset > 0xffff || len > sizeof(buf))) if (unlikely((u32) offset > 0xffff || len > sizeof(buf)))
return -EFAULT; return -EFAULT;
if (unlikely(skb_try_make_writable(skb, offset + len)))
if (unlikely(skb_cloned(skb) &&
!skb_clone_writable(skb, offset + len)))
return -EFAULT; return -EFAULT;
ptr = skb_header_pointer(skb, offset, len, buf); ptr = skb_header_pointer(skb, offset, len, buf);
...@@ -1321,8 +1319,7 @@ static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags) ...@@ -1321,8 +1319,7 @@ static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
if (unlikely((u32) offset > 0xffff)) if (unlikely((u32) offset > 0xffff))
return -EFAULT; return -EFAULT;
if (unlikely(skb_cloned(skb) && if (unlikely(skb_try_make_writable(skb, offset + sizeof(sum))))
!skb_clone_writable(skb, offset + sizeof(sum))))
return -EFAULT; return -EFAULT;
ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum); ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum);
...@@ -1367,9 +1364,7 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags) ...@@ -1367,9 +1364,7 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
if (unlikely((u32) offset > 0xffff)) if (unlikely((u32) offset > 0xffff))
return -EFAULT; return -EFAULT;
if (unlikely(skb_try_make_writable(skb, offset + sizeof(sum))))
if (unlikely(skb_cloned(skb) &&
!skb_clone_writable(skb, offset + sizeof(sum))))
return -EFAULT; return -EFAULT;
ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum); ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum);
...@@ -1554,6 +1549,13 @@ bool bpf_helper_changes_skb_data(void *func) ...@@ -1554,6 +1549,13 @@ bool bpf_helper_changes_skb_data(void *func)
return true; return true;
if (func == bpf_skb_vlan_pop) if (func == bpf_skb_vlan_pop)
return true; return true;
if (func == bpf_skb_store_bytes)
return true;
if (func == bpf_l3_csum_replace)
return true;
if (func == bpf_l4_csum_replace)
return true;
return false; return false;
} }
......
...@@ -105,9 +105,7 @@ static void *tcf_csum_skb_nextlayer(struct sk_buff *skb, ...@@ -105,9 +105,7 @@ static void *tcf_csum_skb_nextlayer(struct sk_buff *skb,
int hl = ihl + jhl; int hl = ihl + jhl;
if (!pskb_may_pull(skb, ipl + ntkoff) || (ipl < hl) || if (!pskb_may_pull(skb, ipl + ntkoff) || (ipl < hl) ||
(skb_cloned(skb) && skb_try_make_writable(skb, hl + ntkoff))
!skb_clone_writable(skb, hl + ntkoff) &&
pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
return NULL; return NULL;
else else
return (void *)(skb_network_header(skb) + ihl); return (void *)(skb_network_header(skb) + ihl);
...@@ -365,9 +363,7 @@ static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags) ...@@ -365,9 +363,7 @@ static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags)
} }
if (update_flags & TCA_CSUM_UPDATE_FLAG_IPV4HDR) { if (update_flags & TCA_CSUM_UPDATE_FLAG_IPV4HDR) {
if (skb_cloned(skb) && if (skb_try_make_writable(skb, sizeof(*iph) + ntkoff))
!skb_clone_writable(skb, sizeof(*iph) + ntkoff) &&
pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
goto fail; goto fail;
ip_send_check(ip_hdr(skb)); ip_send_check(ip_hdr(skb));
......
...@@ -126,9 +126,7 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a, ...@@ -126,9 +126,7 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a,
addr = iph->daddr; addr = iph->daddr;
if (!((old_addr ^ addr) & mask)) { if (!((old_addr ^ addr) & mask)) {
if (skb_cloned(skb) && if (skb_try_make_writable(skb, sizeof(*iph) + noff))
!skb_clone_writable(skb, sizeof(*iph) + noff) &&
pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
goto drop; goto drop;
new_addr &= mask; new_addr &= mask;
...@@ -156,9 +154,7 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a, ...@@ -156,9 +154,7 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a,
struct tcphdr *tcph; struct tcphdr *tcph;
if (!pskb_may_pull(skb, ihl + sizeof(*tcph) + noff) || if (!pskb_may_pull(skb, ihl + sizeof(*tcph) + noff) ||
(skb_cloned(skb) && skb_try_make_writable(skb, ihl + sizeof(*tcph) + noff))
!skb_clone_writable(skb, ihl + sizeof(*tcph) + noff) &&
pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
goto drop; goto drop;
tcph = (void *)(skb_network_header(skb) + ihl); tcph = (void *)(skb_network_header(skb) + ihl);
...@@ -171,9 +167,7 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a, ...@@ -171,9 +167,7 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a,
struct udphdr *udph; struct udphdr *udph;
if (!pskb_may_pull(skb, ihl + sizeof(*udph) + noff) || if (!pskb_may_pull(skb, ihl + sizeof(*udph) + noff) ||
(skb_cloned(skb) && skb_try_make_writable(skb, ihl + sizeof(*udph) + noff))
!skb_clone_writable(skb, ihl + sizeof(*udph) + noff) &&
pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
goto drop; goto drop;
udph = (void *)(skb_network_header(skb) + ihl); udph = (void *)(skb_network_header(skb) + ihl);
...@@ -213,10 +207,8 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a, ...@@ -213,10 +207,8 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a,
if ((old_addr ^ addr) & mask) if ((old_addr ^ addr) & mask)
break; break;
if (skb_cloned(skb) && if (skb_try_make_writable(skb, ihl + sizeof(*icmph) +
!skb_clone_writable(skb, ihl + sizeof(*icmph) + sizeof(*iph) + noff))
sizeof(*iph) + noff) &&
pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
goto drop; goto drop;
icmph = (void *)(skb_network_header(skb) + ihl); icmph = (void *)(skb_network_header(skb) + ihl);
......
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