s390/bpf: Make sure JIT passes do not increase code size
authorIlya Leoshkevich <iii@linux.ibm.com>
Thu, 14 Nov 2019 15:18:20 +0000 (16:18 +0100)
committerDaniel Borkmann <daniel@iogearbox.net>
Fri, 15 Nov 2019 21:25:54 +0000 (22:25 +0100)
The upcoming s390 branch length extension patches rely on "passes do
not increase code size" property in order to consistently choose between
short and long branches. Currently this property does not hold between
the first and the second passes for register save/restore sequences, as
well as various code fragments that depend on SEEN_* flags.

Generate the code during the first pass conservatively: assume register
save/restore sequences have the maximum possible length, and that all
SEEN_* flags are set.

Also refuse to JIT if this happens anyway (e.g. due to a bug), as this
might lead to verifier bypass once long branches are introduced.

Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20191114151820.53222-1-iii@linux.ibm.com
arch/s390/net/bpf_jit_comp.c

index 1115071..7bddb27 100644 (file)
@@ -304,6 +304,24 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
        }                                                       \
 })
 
+/*
+ * Return whether this is the first pass. The first pass is special, since we
+ * don't know any sizes yet, and thus must be conservative.
+ */
+static bool is_first_pass(struct bpf_jit *jit)
+{
+       return jit->size == 0;
+}
+
+/*
+ * Return whether this is the code generation pass. The code generation pass is
+ * special, since we should change as little as possible.
+ */
+static bool is_codegen_pass(struct bpf_jit *jit)
+{
+       return jit->prg_buf;
+}
+
 /*
  * Fill whole space with illegal instructions
  */
@@ -381,9 +399,18 @@ static int get_end(struct bpf_jit *jit, int start)
  */
 static void save_restore_regs(struct bpf_jit *jit, int op, u32 stack_depth)
 {
-
+       const int last = 15, save_restore_size = 6;
        int re = 6, rs;
 
+       if (is_first_pass(jit)) {
+               /*
+                * We don't know yet which registers are used. Reserve space
+                * conservatively.
+                */
+               jit->prg += (last - re + 1) * save_restore_size;
+               return;
+       }
+
        do {
                rs = get_start(jit, re);
                if (!rs)
@@ -394,7 +421,7 @@ static void save_restore_regs(struct bpf_jit *jit, int op, u32 stack_depth)
                else
                        restore_regs(jit, rs, re, stack_depth);
                re++;
-       } while (re <= 15);
+       } while (re <= last);
 }
 
 /*
@@ -418,21 +445,21 @@ static void bpf_jit_prologue(struct bpf_jit *jit, u32 stack_depth)
        /* Save registers */
        save_restore_regs(jit, REGS_SAVE, stack_depth);
        /* Setup literal pool */
-       if (jit->seen & SEEN_LITERAL) {
+       if (is_first_pass(jit) || (jit->seen & SEEN_LITERAL)) {
                /* basr %r13,0 */
                EMIT2(0x0d00, REG_L, REG_0);
                jit->base_ip = jit->prg;
        }
        /* Setup stack and backchain */
-       if (jit->seen & SEEN_STACK) {
-               if (jit->seen & SEEN_FUNC)
+       if (is_first_pass(jit) || (jit->seen & SEEN_STACK)) {
+               if (is_first_pass(jit) || (jit->seen & SEEN_FUNC))
                        /* lgr %w1,%r15 (backchain) */
                        EMIT4(0xb9040000, REG_W1, REG_15);
                /* la %bfp,STK_160_UNUSED(%r15) (BPF frame pointer) */
                EMIT4_DISP(0x41000000, BPF_REG_FP, REG_15, STK_160_UNUSED);
                /* aghi %r15,-STK_OFF */
                EMIT4_IMM(0xa70b0000, REG_15, -(STK_OFF + stack_depth));
-               if (jit->seen & SEEN_FUNC)
+               if (is_first_pass(jit) || (jit->seen & SEEN_FUNC))
                        /* stg %w1,152(%r15) (backchain) */
                        EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0,
                                      REG_15, 152);
@@ -468,7 +495,7 @@ static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth)
        _EMIT2(0x07fe);
 
        if (__is_defined(CC_USING_EXPOLINE) && !nospec_disable &&
-           (jit->seen & SEEN_FUNC)) {
+           (is_first_pass(jit) || (jit->seen & SEEN_FUNC))) {
                jit->r1_thunk_ip = jit->prg;
                /* Generate __s390_indirect_jump_r1 thunk */
                if (test_facility(35)) {
@@ -1275,6 +1302,34 @@ branch_oc:
        return insn_count;
 }
 
+/*
+ * Return whether new i-th instruction address does not violate any invariant
+ */
+static bool bpf_is_new_addr_sane(struct bpf_jit *jit, int i)
+{
+       /* On the first pass anything goes */
+       if (is_first_pass(jit))
+               return true;
+
+       /* The codegen pass must not change anything */
+       if (is_codegen_pass(jit))
+               return jit->addrs[i] == jit->prg;
+
+       /* Passes in between must not increase code size */
+       return jit->addrs[i] >= jit->prg;
+}
+
+/*
+ * Update the address of i-th instruction
+ */
+static int bpf_set_addr(struct bpf_jit *jit, int i)
+{
+       if (!bpf_is_new_addr_sane(jit, i))
+               return -1;
+       jit->addrs[i] = jit->prg;
+       return 0;
+}
+
 /*
  * Compile eBPF program into s390x code
  */
@@ -1287,12 +1342,15 @@ static int bpf_jit_prog(struct bpf_jit *jit, struct bpf_prog *fp,
        jit->prg = 0;
 
        bpf_jit_prologue(jit, fp->aux->stack_depth);
+       if (bpf_set_addr(jit, 0) < 0)
+               return -1;
        for (i = 0; i < fp->len; i += insn_count) {
                insn_count = bpf_jit_insn(jit, fp, i, extra_pass);
                if (insn_count < 0)
                        return -1;
                /* Next instruction address */
-               jit->addrs[i + insn_count] = jit->prg;
+               if (bpf_set_addr(jit, i + insn_count) < 0)
+                       return -1;
        }
        bpf_jit_epilogue(jit, fp->aux->stack_depth);