]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - kernel/bpf/verifier.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
[linux.git] / kernel / bpf / verifier.c
index ffc3e53f53009fe358e5670893d587bb5f02c533..d3446f018b9a8eb5fd1ab24ec2de541fca4f5f3d 100644 (file)
@@ -2739,6 +2739,41 @@ static void coerce_reg_to_size(struct bpf_reg_state *reg, int size)
        reg->smax_value = reg->umax_value;
 }
 
+static bool bpf_map_is_rdonly(const struct bpf_map *map)
+{
+       return (map->map_flags & BPF_F_RDONLY_PROG) && map->frozen;
+}
+
+static int bpf_map_direct_read(struct bpf_map *map, int off, int size, u64 *val)
+{
+       void *ptr;
+       u64 addr;
+       int err;
+
+       err = map->ops->map_direct_value_addr(map, &addr, off);
+       if (err)
+               return err;
+       ptr = (void *)(long)addr + off;
+
+       switch (size) {
+       case sizeof(u8):
+               *val = (u64)*(u8 *)ptr;
+               break;
+       case sizeof(u16):
+               *val = (u64)*(u16 *)ptr;
+               break;
+       case sizeof(u32):
+               *val = (u64)*(u32 *)ptr;
+               break;
+       case sizeof(u64):
+               *val = *(u64 *)ptr;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
 /* check whether memory at (regno + off) is accessible for t = (read | write)
  * if t==write, value_regno is a register which value is stored into memory
  * if t==read, value_regno is a register which will receive the value from memory
@@ -2776,9 +2811,27 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
                if (err)
                        return err;
                err = check_map_access(env, regno, off, size, false);
-               if (!err && t == BPF_READ && value_regno >= 0)
-                       mark_reg_unknown(env, regs, value_regno);
+               if (!err && t == BPF_READ && value_regno >= 0) {
+                       struct bpf_map *map = reg->map_ptr;
+
+                       /* if map is read-only, track its contents as scalars */
+                       if (tnum_is_const(reg->var_off) &&
+                           bpf_map_is_rdonly(map) &&
+                           map->ops->map_direct_value_addr) {
+                               int map_off = off + reg->var_off.value;
+                               u64 val = 0;
 
+                               err = bpf_map_direct_read(map, map_off, size,
+                                                         &val);
+                               if (err)
+                                       return err;
+
+                               regs[value_regno].type = SCALAR_VALUE;
+                               __mark_reg_known(&regs[value_regno], val);
+                       } else {
+                               mark_reg_unknown(env, regs, value_regno);
+                       }
+               }
        } else if (reg->type == PTR_TO_CTX) {
                enum bpf_reg_type reg_type = SCALAR_VALUE;