bpf: support decreasing order in direct packet access
authorAlexei Starovoitov <ast@fb.com>
Fri, 20 May 2016 01:17:13 +0000 (18:17 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 20 May 2016 23:53:03 +0000 (19:53 -0400)
when packet headers are accessed in 'decreasing' order (like TCP port
may be fetched before the program reads IP src) the llvm may generate
the following code:
[...]                // R7=pkt(id=0,off=22,r=70)
r2 = *(u32 *)(r7 +0) // good access
[...]
r7 += 40             // R7=pkt(id=0,off=62,r=70)
r8 = *(u32 *)(r7 +0) // good access
[...]
r1 = *(u32 *)(r7 -20) // this one will fail though it's within a safe range
                      // it's doing *(u32*)(skb->data + 42)
Fix verifier to recognize such code pattern

Alos turned out that 'off > range' condition is not a verifier bug.
It's a buggy program that may do something like:
if (ptr + 50 > data_end)
  return 0;
ptr += 60;
*(u32*)ptr;
in such case emit
"invalid access to packet, off=0 size=4, R1(id=0,off=60,r=50)" error message,
so all information is available for the program author to fix the program.

Fixes: 969bf05eb3ce ("bpf: direct packet access")
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
kernel/bpf/verifier.c

index a08d662..d54e348 100644 (file)
@@ -683,15 +683,11 @@ static int check_packet_access(struct verifier_env *env, u32 regno, int off,
 {
        struct reg_state *regs = env->cur_state.regs;
        struct reg_state *reg = &regs[regno];
-       int linear_size = (int) reg->range - (int) reg->off;
 
-       if (linear_size < 0 || linear_size >= MAX_PACKET_OFF) {
-               verbose("verifier bug\n");
-               return -EFAULT;
-       }
-       if (off < 0 || off + size > linear_size) {
-               verbose("invalid access to packet, off=%d size=%d, allowed=%d\n",
-                       off, size, linear_size);
+       off += reg->off;
+       if (off < 0 || off + size > reg->range) {
+               verbose("invalid access to packet, off=%d size=%d, R%d(id=%d,off=%d,r=%d)\n",
+                       off, size, regno, reg->id, reg->off, reg->range);
                return -EACCES;
        }
        return 0;