Commit fcf35131 authored by Ilya Leoshkevich's avatar Ilya Leoshkevich Committed by Daniel Borkmann

s390/bpf: Make sure JIT passes do not increase code size

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: default avatarIlya Leoshkevich <iii@linux.ibm.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20191114151820.53222-1-iii@linux.ibm.com
parent b7b3fc8d
...@@ -304,6 +304,24 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1) ...@@ -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 * Fill whole space with illegal instructions
*/ */
...@@ -381,9 +399,18 @@ static int get_end(struct bpf_jit *jit, int start) ...@@ -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) 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; 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 { do {
rs = get_start(jit, re); rs = get_start(jit, re);
if (!rs) if (!rs)
...@@ -394,7 +421,7 @@ static void save_restore_regs(struct bpf_jit *jit, int op, u32 stack_depth) ...@@ -394,7 +421,7 @@ static void save_restore_regs(struct bpf_jit *jit, int op, u32 stack_depth)
else else
restore_regs(jit, rs, re, stack_depth); restore_regs(jit, rs, re, stack_depth);
re++; re++;
} while (re <= 15); } while (re <= last);
} }
/* /*
...@@ -418,21 +445,21 @@ static void bpf_jit_prologue(struct bpf_jit *jit, u32 stack_depth) ...@@ -418,21 +445,21 @@ static void bpf_jit_prologue(struct bpf_jit *jit, u32 stack_depth)
/* Save registers */ /* Save registers */
save_restore_regs(jit, REGS_SAVE, stack_depth); save_restore_regs(jit, REGS_SAVE, stack_depth);
/* Setup literal pool */ /* Setup literal pool */
if (jit->seen & SEEN_LITERAL) { if (is_first_pass(jit) || (jit->seen & SEEN_LITERAL)) {
/* basr %r13,0 */ /* basr %r13,0 */
EMIT2(0x0d00, REG_L, REG_0); EMIT2(0x0d00, REG_L, REG_0);
jit->base_ip = jit->prg; jit->base_ip = jit->prg;
} }
/* Setup stack and backchain */ /* Setup stack and backchain */
if (jit->seen & SEEN_STACK) { if (is_first_pass(jit) || (jit->seen & SEEN_STACK)) {
if (jit->seen & SEEN_FUNC) if (is_first_pass(jit) || (jit->seen & SEEN_FUNC))
/* lgr %w1,%r15 (backchain) */ /* lgr %w1,%r15 (backchain) */
EMIT4(0xb9040000, REG_W1, REG_15); EMIT4(0xb9040000, REG_W1, REG_15);
/* la %bfp,STK_160_UNUSED(%r15) (BPF frame pointer) */ /* la %bfp,STK_160_UNUSED(%r15) (BPF frame pointer) */
EMIT4_DISP(0x41000000, BPF_REG_FP, REG_15, STK_160_UNUSED); EMIT4_DISP(0x41000000, BPF_REG_FP, REG_15, STK_160_UNUSED);
/* aghi %r15,-STK_OFF */ /* aghi %r15,-STK_OFF */
EMIT4_IMM(0xa70b0000, REG_15, -(STK_OFF + stack_depth)); 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) */ /* stg %w1,152(%r15) (backchain) */
EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0, EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0,
REG_15, 152); REG_15, 152);
...@@ -468,7 +495,7 @@ static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth) ...@@ -468,7 +495,7 @@ static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth)
_EMIT2(0x07fe); _EMIT2(0x07fe);
if (__is_defined(CC_USING_EXPOLINE) && !nospec_disable && 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; jit->r1_thunk_ip = jit->prg;
/* Generate __s390_indirect_jump_r1 thunk */ /* Generate __s390_indirect_jump_r1 thunk */
if (test_facility(35)) { if (test_facility(35)) {
...@@ -1275,6 +1302,34 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, ...@@ -1275,6 +1302,34 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
return insn_count; 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 * Compile eBPF program into s390x code
*/ */
...@@ -1287,12 +1342,15 @@ static int bpf_jit_prog(struct bpf_jit *jit, struct bpf_prog *fp, ...@@ -1287,12 +1342,15 @@ static int bpf_jit_prog(struct bpf_jit *jit, struct bpf_prog *fp,
jit->prg = 0; jit->prg = 0;
bpf_jit_prologue(jit, fp->aux->stack_depth); 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) { for (i = 0; i < fp->len; i += insn_count) {
insn_count = bpf_jit_insn(jit, fp, i, extra_pass); insn_count = bpf_jit_insn(jit, fp, i, extra_pass);
if (insn_count < 0) if (insn_count < 0)
return -1; return -1;
/* Next instruction address */ /* 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); bpf_jit_epilogue(jit, fp->aux->stack_depth);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment