bpf: try harder on clones when writing into skb
authorDaniel Borkmann <daniel@iogearbox.net>
Fri, 19 Feb 2016 22:05:25 +0000 (23:05 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 22 Feb 2016 03:07:10 +0000 (22:07 -0500)
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: Ashhad Sheikh <ashhadsheikh394@gmail.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/skbuff.h
net/core/filter.c
net/sched/act_csum.c
net/sched/act_nat.c

index 89b536796e5350c769641824323caad7cae17a42..6a57757a86cf962e2b45fc8bf82a8b0e36f2546d 100644 (file)
@@ -2630,6 +2630,13 @@ static inline int skb_clone_writable(const struct sk_buff *skb, unsigned int 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,
                            int cloned)
 {
index ea391e6be7fa3f9d667bc422b234ca9e5b8a056f..f031b82128f3f276810402fee2b1fd3dbe1c6c5e 100644 (file)
@@ -1364,9 +1364,7 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
         */
        if (unlikely((u32) offset > 0xffff || len > sizeof(sp->buff)))
                return -EFAULT;
-
-       if (unlikely(skb_cloned(skb) &&
-                    !skb_clone_writable(skb, offset + len)))
+       if (unlikely(skb_try_make_writable(skb, offset + len)))
                return -EFAULT;
 
        ptr = skb_header_pointer(skb, offset, len, sp->buff);
@@ -1439,9 +1437,7 @@ static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
                return -EINVAL;
        if (unlikely((u32) offset > 0xffff))
                return -EFAULT;
-
-       if (unlikely(skb_cloned(skb) &&
-                    !skb_clone_writable(skb, offset + sizeof(sum))))
+       if (unlikely(skb_try_make_writable(skb, offset + sizeof(sum))))
                return -EFAULT;
 
        ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum);
@@ -1488,9 +1484,7 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
                return -EINVAL;
        if (unlikely((u32) offset > 0xffff))
                return -EFAULT;
-
-       if (unlikely(skb_cloned(skb) &&
-                    !skb_clone_writable(skb, offset + sizeof(sum))))
+       if (unlikely(skb_try_make_writable(skb, offset + sizeof(sum))))
                return -EFAULT;
 
        ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum);
@@ -1734,6 +1728,13 @@ bool bpf_helper_changes_skb_data(void *func)
                return true;
        if (func == bpf_skb_vlan_pop)
                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;
 }
 
index b07c535ba8e7c6f8dcbc52f4eb69cf4a1ab3d0c2..eeb3eb3ea9ebac1ab55ca9b34e3e6eeb72c1421e 100644 (file)
@@ -105,9 +105,7 @@ static void *tcf_csum_skb_nextlayer(struct sk_buff *skb,
        int hl = ihl + jhl;
 
        if (!pskb_may_pull(skb, ipl + ntkoff) || (ipl < hl) ||
-           (skb_cloned(skb) &&
-            !skb_clone_writable(skb, hl + ntkoff) &&
-            pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
+           skb_try_make_writable(skb, hl + ntkoff))
                return NULL;
        else
                return (void *)(skb_network_header(skb) + ihl);
@@ -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 (skb_cloned(skb) &&
-                   !skb_clone_writable(skb, sizeof(*iph) + ntkoff) &&
-                   pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+               if (skb_try_make_writable(skb, sizeof(*iph) + ntkoff))
                        goto fail;
 
                ip_send_check(ip_hdr(skb));
index b7c4ead8b5a8e863d87f8c1c4c1e37840fe50577..27607b863aba39c8fa1a567e2834d432eeb5a7c7 100644 (file)
@@ -126,9 +126,7 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a,
                addr = iph->daddr;
 
        if (!((old_addr ^ addr) & mask)) {
-               if (skb_cloned(skb) &&
-                   !skb_clone_writable(skb, sizeof(*iph) + noff) &&
-                   pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+               if (skb_try_make_writable(skb, sizeof(*iph) + noff))
                        goto drop;
 
                new_addr &= mask;
@@ -156,9 +154,7 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a,
                struct tcphdr *tcph;
 
                if (!pskb_may_pull(skb, ihl + sizeof(*tcph) + noff) ||
-                   (skb_cloned(skb) &&
-                    !skb_clone_writable(skb, ihl + sizeof(*tcph) + noff) &&
-                    pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
+                   skb_try_make_writable(skb, ihl + sizeof(*tcph) + noff))
                        goto drop;
 
                tcph = (void *)(skb_network_header(skb) + ihl);
@@ -171,9 +167,7 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a,
                struct udphdr *udph;
 
                if (!pskb_may_pull(skb, ihl + sizeof(*udph) + noff) ||
-                   (skb_cloned(skb) &&
-                    !skb_clone_writable(skb, ihl + sizeof(*udph) + noff) &&
-                    pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
+                   skb_try_make_writable(skb, ihl + sizeof(*udph) + noff))
                        goto drop;
 
                udph = (void *)(skb_network_header(skb) + ihl);
@@ -213,10 +207,8 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a,
                if ((old_addr ^ addr) & mask)
                        break;
 
-               if (skb_cloned(skb) &&
-                   !skb_clone_writable(skb, ihl + sizeof(*icmph) +
-                                            sizeof(*iph) + noff) &&
-                   pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+               if (skb_try_make_writable(skb, ihl + sizeof(*icmph) +
+                                         sizeof(*iph) + noff))
                        goto drop;
 
                icmph = (void *)(skb_network_header(skb) + ihl);