mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-15 22:41:38 +00:00
net: filter: make JITs zero A for SKF_AD_ALU_XOR_X
[ Upstream commit55795ef546] The SKF_AD_ALU_XOR_X ancillary is not like the other ancillary data instructions since it XORs A with X while all the others replace A with some loaded value. All the BPF JITs fail to clear A if this is used as the first instruction in a filter. This was found using american fuzzy lop. Add a helper to determine if A needs to be cleared given the first instruction in a filter, and use this in the JITs. Except for ARM, the rest have only been compile-tested. Fixes:3480593131("net: filter: get rid of BPF_S_* enum") Signed-off-by: Rabin Vincent <rabin@rab.in> Acked-by: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
a6b1d24893
commit
5596242a62
@@ -162,19 +162,6 @@ static inline int mem_words_used(struct jit_ctx *ctx)
|
|||||||
return fls(ctx->seen & SEEN_MEM);
|
return fls(ctx->seen & SEEN_MEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool is_load_to_a(u16 inst)
|
|
||||||
{
|
|
||||||
switch (inst) {
|
|
||||||
case BPF_LD | BPF_W | BPF_LEN:
|
|
||||||
case BPF_LD | BPF_W | BPF_ABS:
|
|
||||||
case BPF_LD | BPF_H | BPF_ABS:
|
|
||||||
case BPF_LD | BPF_B | BPF_ABS:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jit_fill_hole(void *area, unsigned int size)
|
static void jit_fill_hole(void *area, unsigned int size)
|
||||||
{
|
{
|
||||||
u32 *ptr;
|
u32 *ptr;
|
||||||
@@ -186,7 +173,6 @@ static void jit_fill_hole(void *area, unsigned int size)
|
|||||||
static void build_prologue(struct jit_ctx *ctx)
|
static void build_prologue(struct jit_ctx *ctx)
|
||||||
{
|
{
|
||||||
u16 reg_set = saved_regs(ctx);
|
u16 reg_set = saved_regs(ctx);
|
||||||
u16 first_inst = ctx->skf->insns[0].code;
|
|
||||||
u16 off;
|
u16 off;
|
||||||
|
|
||||||
#ifdef CONFIG_FRAME_POINTER
|
#ifdef CONFIG_FRAME_POINTER
|
||||||
@@ -216,7 +202,7 @@ static void build_prologue(struct jit_ctx *ctx)
|
|||||||
emit(ARM_MOV_I(r_X, 0), ctx);
|
emit(ARM_MOV_I(r_X, 0), ctx);
|
||||||
|
|
||||||
/* do not leak kernel data to userspace */
|
/* do not leak kernel data to userspace */
|
||||||
if ((first_inst != (BPF_RET | BPF_K)) && !(is_load_to_a(first_inst)))
|
if (bpf_needs_clear_a(&ctx->skf->insns[0]))
|
||||||
emit(ARM_MOV_I(r_A, 0), ctx);
|
emit(ARM_MOV_I(r_A, 0), ctx);
|
||||||
|
|
||||||
/* stack space for the BPF_MEM words */
|
/* stack space for the BPF_MEM words */
|
||||||
|
|||||||
@@ -556,19 +556,6 @@ static inline u16 align_sp(unsigned int num)
|
|||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_load_to_a(u16 inst)
|
|
||||||
{
|
|
||||||
switch (inst) {
|
|
||||||
case BPF_LD | BPF_W | BPF_LEN:
|
|
||||||
case BPF_LD | BPF_W | BPF_ABS:
|
|
||||||
case BPF_LD | BPF_H | BPF_ABS:
|
|
||||||
case BPF_LD | BPF_B | BPF_ABS:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void save_bpf_jit_regs(struct jit_ctx *ctx, unsigned offset)
|
static void save_bpf_jit_regs(struct jit_ctx *ctx, unsigned offset)
|
||||||
{
|
{
|
||||||
int i = 0, real_off = 0;
|
int i = 0, real_off = 0;
|
||||||
@@ -686,7 +673,6 @@ static unsigned int get_stack_depth(struct jit_ctx *ctx)
|
|||||||
|
|
||||||
static void build_prologue(struct jit_ctx *ctx)
|
static void build_prologue(struct jit_ctx *ctx)
|
||||||
{
|
{
|
||||||
u16 first_inst = ctx->skf->insns[0].code;
|
|
||||||
int sp_off;
|
int sp_off;
|
||||||
|
|
||||||
/* Calculate the total offset for the stack pointer */
|
/* Calculate the total offset for the stack pointer */
|
||||||
@@ -700,7 +686,7 @@ static void build_prologue(struct jit_ctx *ctx)
|
|||||||
emit_jit_reg_move(r_X, r_zero, ctx);
|
emit_jit_reg_move(r_X, r_zero, ctx);
|
||||||
|
|
||||||
/* Do not leak kernel data to userspace */
|
/* Do not leak kernel data to userspace */
|
||||||
if ((first_inst != (BPF_RET | BPF_K)) && !(is_load_to_a(first_inst)))
|
if (bpf_needs_clear_a(&ctx->skf->insns[0]))
|
||||||
emit_jit_reg_move(r_A, r_zero, ctx);
|
emit_jit_reg_move(r_A, r_zero, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,18 +78,9 @@ static void bpf_jit_build_prologue(struct bpf_prog *fp, u32 *image,
|
|||||||
PPC_LI(r_X, 0);
|
PPC_LI(r_X, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (filter[0].code) {
|
/* make sure we dont leak kernel information to user */
|
||||||
case BPF_RET | BPF_K:
|
if (bpf_needs_clear_a(&filter[0]))
|
||||||
case BPF_LD | BPF_W | BPF_LEN:
|
|
||||||
case BPF_LD | BPF_W | BPF_ABS:
|
|
||||||
case BPF_LD | BPF_H | BPF_ABS:
|
|
||||||
case BPF_LD | BPF_B | BPF_ABS:
|
|
||||||
/* first instruction sets A register (or is RET 'constant') */
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* make sure we dont leak kernel information to user */
|
|
||||||
PPC_LI(r_A, 0);
|
PPC_LI(r_A, 0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
|
static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
|
||||||
|
|||||||
@@ -420,22 +420,9 @@ void bpf_jit_compile(struct bpf_prog *fp)
|
|||||||
}
|
}
|
||||||
emit_reg_move(O7, r_saved_O7);
|
emit_reg_move(O7, r_saved_O7);
|
||||||
|
|
||||||
switch (filter[0].code) {
|
/* Make sure we dont leak kernel information to the user. */
|
||||||
case BPF_RET | BPF_K:
|
if (bpf_needs_clear_a(&filter[0]))
|
||||||
case BPF_LD | BPF_W | BPF_LEN:
|
|
||||||
case BPF_LD | BPF_W | BPF_ABS:
|
|
||||||
case BPF_LD | BPF_H | BPF_ABS:
|
|
||||||
case BPF_LD | BPF_B | BPF_ABS:
|
|
||||||
/* The first instruction sets the A register (or is
|
|
||||||
* a "RET 'constant'")
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* Make sure we dont leak kernel information to the
|
|
||||||
* user.
|
|
||||||
*/
|
|
||||||
emit_clear(r_A); /* A = 0 */
|
emit_clear(r_A); /* A = 0 */
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < flen; i++) {
|
for (i = 0; i < flen; i++) {
|
||||||
unsigned int K = filter[i].k;
|
unsigned int K = filter[i].k;
|
||||||
|
|||||||
@@ -428,6 +428,25 @@ static inline void bpf_jit_free(struct bpf_prog *fp)
|
|||||||
|
|
||||||
#define BPF_ANC BIT(15)
|
#define BPF_ANC BIT(15)
|
||||||
|
|
||||||
|
static inline bool bpf_needs_clear_a(const struct sock_filter *first)
|
||||||
|
{
|
||||||
|
switch (first->code) {
|
||||||
|
case BPF_RET | BPF_K:
|
||||||
|
case BPF_LD | BPF_W | BPF_LEN:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case BPF_LD | BPF_W | BPF_ABS:
|
||||||
|
case BPF_LD | BPF_H | BPF_ABS:
|
||||||
|
case BPF_LD | BPF_B | BPF_ABS:
|
||||||
|
if (first->k == SKF_AD_OFF + SKF_AD_ALU_XOR_X)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline u16 bpf_anc_helper(const struct sock_filter *ftest)
|
static inline u16 bpf_anc_helper(const struct sock_filter *ftest)
|
||||||
{
|
{
|
||||||
BUG_ON(ftest->code & BPF_ANC);
|
BUG_ON(ftest->code & BPF_ANC);
|
||||||
|
|||||||
Reference in New Issue
Block a user