[PATCH v14 net-next 08/11] bpf: handle pseudo BPF_LD_IMM64 insn

2014-09-21 Thread Alexei Starovoitov
eBPF programs passed from userspace are using pseudo BPF_LD_IMM64 instructions
to refer to process-local map_fd. Scan the program for such instructions and
if FDs are valid, convert them to 'struct bpf_map' pointers which will be used
by verifier to check access to maps in bpf_map_lookup/update() calls.
If program passes verifier, convert pseudo BPF_LD_IMM64 into generic by dropping
BPF_PSEUDO_MAP_FD flag.

Note that eBPF interpreter is generic and knows nothing about pseudo insns.

Signed-off-by: Alexei Starovoitov 
---
 include/linux/filter.h |6 ++
 kernel/bpf/verifier.c  |  147 
 2 files changed, 153 insertions(+)

diff --git a/include/linux/filter.h b/include/linux/filter.h
index 4ffc0958d85e..ca95abd2bed1 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -145,6 +145,12 @@ struct bpf_prog_aux;
.off   = 0, \
.imm   = ((__u64) (IMM)) >> 32 })
 
+#define BPF_PSEUDO_MAP_FD  1
+
+/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */
+#define BPF_LD_MAP_FD(DST, MAP_FD) \
+   BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
+
 /* Short form of mov based on type, BPF_X: dst_reg = src_reg, BPF_K: dst_reg = 
imm32 */
 
 #define BPF_MOV64_RAW(TYPE, DST, SRC, IMM) \
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 871edc1f2e1f..7227543e474b 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -125,10 +125,15 @@
  * are set to NOT_INIT to indicate that they are no longer readable.
  */
 
+#define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */
+
 /* single container for all structs
  * one verifier_env per bpf_check() call
  */
 struct verifier_env {
+   struct bpf_prog *prog;  /* eBPF program being verified */
+   struct bpf_map *used_maps[MAX_USED_MAPS]; /* array of map's used by 
eBPF program */
+   u32 used_map_cnt;   /* number of used maps */
 };
 
 /* verbose verifier prints what it's seeing
@@ -300,6 +305,115 @@ static void print_bpf_insn(struct bpf_insn *insn)
}
 }
 
+/* return the map pointer stored inside BPF_LD_IMM64 instruction */
+static struct bpf_map *ld_imm64_to_map_ptr(struct bpf_insn *insn)
+{
+   u64 imm64 = ((u64) (u32) insn[0].imm) | ((u64) (u32) insn[1].imm) << 32;
+
+   return (struct bpf_map *) (unsigned long) imm64;
+}
+
+/* look for pseudo eBPF instructions that access map FDs and
+ * replace them with actual map pointers
+ */
+static int replace_map_fd_with_map_ptr(struct verifier_env *env)
+{
+   struct bpf_insn *insn = env->prog->insnsi;
+   int insn_cnt = env->prog->len;
+   int i, j;
+
+   for (i = 0; i < insn_cnt; i++, insn++) {
+   if (insn[0].code == (BPF_LD | BPF_IMM | BPF_DW)) {
+   struct bpf_map *map;
+   struct fd f;
+
+   if (i == insn_cnt - 1 || insn[1].code != 0 ||
+   insn[1].dst_reg != 0 || insn[1].src_reg != 0 ||
+   insn[1].off != 0) {
+   verbose("invalid bpf_ld_imm64 insn\n");
+   return -EINVAL;
+   }
+
+   if (insn->src_reg == 0)
+   /* valid generic load 64-bit imm */
+   goto next_insn;
+
+   if (insn->src_reg != BPF_PSEUDO_MAP_FD) {
+   verbose("unrecognized bpf_ld_imm64 insn\n");
+   return -EINVAL;
+   }
+
+   f = fdget(insn->imm);
+
+   map = bpf_map_get(f);
+   if (IS_ERR(map)) {
+   verbose("fd %d is not pointing to valid 
bpf_map\n",
+   insn->imm);
+   fdput(f);
+   return PTR_ERR(map);
+   }
+
+   /* store map pointer inside BPF_LD_IMM64 instruction */
+   insn[0].imm = (u32) (unsigned long) map;
+   insn[1].imm = ((u64) (unsigned long) map) >> 32;
+
+   /* check whether we recorded this map already */
+   for (j = 0; j < env->used_map_cnt; j++)
+   if (env->used_maps[j] == map) {
+   fdput(f);
+   goto next_insn;
+   }
+
+   if (env->used_map_cnt >= MAX_USED_MAPS) {
+   fdput(f);
+   return -E2BIG;
+   }
+
+   /* remember this map */
+   env->used_maps[env->used_map_cnt++] = map;
+
+   /* hold the 

[PATCH v14 net-next 08/11] bpf: handle pseudo BPF_LD_IMM64 insn

2014-09-21 Thread Alexei Starovoitov
eBPF programs passed from userspace are using pseudo BPF_LD_IMM64 instructions
to refer to process-local map_fd. Scan the program for such instructions and
if FDs are valid, convert them to 'struct bpf_map' pointers which will be used
by verifier to check access to maps in bpf_map_lookup/update() calls.
If program passes verifier, convert pseudo BPF_LD_IMM64 into generic by dropping
BPF_PSEUDO_MAP_FD flag.

Note that eBPF interpreter is generic and knows nothing about pseudo insns.

Signed-off-by: Alexei Starovoitov a...@plumgrid.com
---
 include/linux/filter.h |6 ++
 kernel/bpf/verifier.c  |  147 
 2 files changed, 153 insertions(+)

diff --git a/include/linux/filter.h b/include/linux/filter.h
index 4ffc0958d85e..ca95abd2bed1 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -145,6 +145,12 @@ struct bpf_prog_aux;
.off   = 0, \
.imm   = ((__u64) (IMM))  32 })
 
+#define BPF_PSEUDO_MAP_FD  1
+
+/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */
+#define BPF_LD_MAP_FD(DST, MAP_FD) \
+   BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
+
 /* Short form of mov based on type, BPF_X: dst_reg = src_reg, BPF_K: dst_reg = 
imm32 */
 
 #define BPF_MOV64_RAW(TYPE, DST, SRC, IMM) \
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 871edc1f2e1f..7227543e474b 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -125,10 +125,15 @@
  * are set to NOT_INIT to indicate that they are no longer readable.
  */
 
+#define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */
+
 /* single container for all structs
  * one verifier_env per bpf_check() call
  */
 struct verifier_env {
+   struct bpf_prog *prog;  /* eBPF program being verified */
+   struct bpf_map *used_maps[MAX_USED_MAPS]; /* array of map's used by 
eBPF program */
+   u32 used_map_cnt;   /* number of used maps */
 };
 
 /* verbose verifier prints what it's seeing
@@ -300,6 +305,115 @@ static void print_bpf_insn(struct bpf_insn *insn)
}
 }
 
+/* return the map pointer stored inside BPF_LD_IMM64 instruction */
+static struct bpf_map *ld_imm64_to_map_ptr(struct bpf_insn *insn)
+{
+   u64 imm64 = ((u64) (u32) insn[0].imm) | ((u64) (u32) insn[1].imm)  32;
+
+   return (struct bpf_map *) (unsigned long) imm64;
+}
+
+/* look for pseudo eBPF instructions that access map FDs and
+ * replace them with actual map pointers
+ */
+static int replace_map_fd_with_map_ptr(struct verifier_env *env)
+{
+   struct bpf_insn *insn = env-prog-insnsi;
+   int insn_cnt = env-prog-len;
+   int i, j;
+
+   for (i = 0; i  insn_cnt; i++, insn++) {
+   if (insn[0].code == (BPF_LD | BPF_IMM | BPF_DW)) {
+   struct bpf_map *map;
+   struct fd f;
+
+   if (i == insn_cnt - 1 || insn[1].code != 0 ||
+   insn[1].dst_reg != 0 || insn[1].src_reg != 0 ||
+   insn[1].off != 0) {
+   verbose(invalid bpf_ld_imm64 insn\n);
+   return -EINVAL;
+   }
+
+   if (insn-src_reg == 0)
+   /* valid generic load 64-bit imm */
+   goto next_insn;
+
+   if (insn-src_reg != BPF_PSEUDO_MAP_FD) {
+   verbose(unrecognized bpf_ld_imm64 insn\n);
+   return -EINVAL;
+   }
+
+   f = fdget(insn-imm);
+
+   map = bpf_map_get(f);
+   if (IS_ERR(map)) {
+   verbose(fd %d is not pointing to valid 
bpf_map\n,
+   insn-imm);
+   fdput(f);
+   return PTR_ERR(map);
+   }
+
+   /* store map pointer inside BPF_LD_IMM64 instruction */
+   insn[0].imm = (u32) (unsigned long) map;
+   insn[1].imm = ((u64) (unsigned long) map)  32;
+
+   /* check whether we recorded this map already */
+   for (j = 0; j  env-used_map_cnt; j++)
+   if (env-used_maps[j] == map) {
+   fdput(f);
+   goto next_insn;
+   }
+
+   if (env-used_map_cnt = MAX_USED_MAPS) {
+   fdput(f);
+   return -E2BIG;
+   }
+
+   /* remember this map */
+   env-used_maps[env-used_map_cnt++] = map;
+
+   /* hold the map. If the