forked from luck/tmp_suning_uos_patched
bpf: Allow narrow loads with offset > 0
Currently BPF verifier allows narrow loads for a context field only with offset zero. E.g. if there is a __u32 field then only the following loads are permitted: * off=0, size=1 (narrow); * off=0, size=2 (narrow); * off=0, size=4 (full). On the other hand LLVM can generate a load with offset different than zero that make sense from program logic point of view, but verifier doesn't accept it. E.g. tools/testing/selftests/bpf/sendmsg4_prog.c has code: #define DST_IP4 0xC0A801FEU /* 192.168.1.254 */ ... if ((ctx->user_ip4 >> 24) == (bpf_htonl(DST_IP4) >> 24) && where ctx is struct bpf_sock_addr. Some versions of LLVM can produce the following byte code for it: 8: 71 12 07 00 00 00 00 00 r2 = *(u8 *)(r1 + 7) 9: 67 02 00 00 18 00 00 00 r2 <<= 24 10: 18 03 00 00 00 00 00 fe 00 00 00 00 00 00 00 00 r3 = 4261412864 ll 12: 5d 32 07 00 00 00 00 00 if r2 != r3 goto +7 <LBB0_6> where `*(u8 *)(r1 + 7)` means narrow load for ctx->user_ip4 with size=1 and offset=3 (7 - sizeof(ctx->user_family) = 3). This load is currently rejected by verifier. Verifier code that rejects such loads is in bpf_ctx_narrow_access_ok() what means any is_valid_access implementation, that uses the function, works this way, e.g. bpf_skb_is_valid_access() for __sk_buff or sock_addr_is_valid_access() for bpf_sock_addr. The patch makes such loads supported. Offset can be in [0; size_default) but has to be multiple of load size. E.g. for __u32 field the following loads are supported now: * off=0, size=1 (narrow); * off=1, size=1 (narrow); * off=2, size=1 (narrow); * off=3, size=1 (narrow); * off=0, size=2 (narrow); * off=2, size=2 (narrow); * off=0, size=4 (full). Reported-by: Yonghong Song <yhs@fb.com> Signed-off-by: Andrey Ignatov <rdna@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
f2cbf95826
commit
46f53a65d2
|
@ -668,24 +668,10 @@ static inline u32 bpf_ctx_off_adjust_machine(u32 size)
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool bpf_ctx_narrow_align_ok(u32 off, u32 size_access,
|
|
||||||
u32 size_default)
|
|
||||||
{
|
|
||||||
size_default = bpf_ctx_off_adjust_machine(size_default);
|
|
||||||
size_access = bpf_ctx_off_adjust_machine(size_access);
|
|
||||||
|
|
||||||
#ifdef __LITTLE_ENDIAN
|
|
||||||
return (off & (size_default - 1)) == 0;
|
|
||||||
#else
|
|
||||||
return (off & (size_default - 1)) + size_access == size_default;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
bpf_ctx_narrow_access_ok(u32 off, u32 size, u32 size_default)
|
bpf_ctx_narrow_access_ok(u32 off, u32 size, u32 size_default)
|
||||||
{
|
{
|
||||||
return bpf_ctx_narrow_align_ok(off, size, size_default) &&
|
return size <= size_default && (size & (size - 1)) == 0;
|
||||||
size <= size_default && (size & (size - 1)) == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define bpf_classic_proglen(fprog) (fprog->len * sizeof(fprog->filter[0]))
|
#define bpf_classic_proglen(fprog) (fprog->len * sizeof(fprog->filter[0]))
|
||||||
|
|
|
@ -5718,10 +5718,10 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
|
||||||
int i, cnt, size, ctx_field_size, delta = 0;
|
int i, cnt, size, ctx_field_size, delta = 0;
|
||||||
const int insn_cnt = env->prog->len;
|
const int insn_cnt = env->prog->len;
|
||||||
struct bpf_insn insn_buf[16], *insn;
|
struct bpf_insn insn_buf[16], *insn;
|
||||||
|
u32 target_size, size_default, off;
|
||||||
struct bpf_prog *new_prog;
|
struct bpf_prog *new_prog;
|
||||||
enum bpf_access_type type;
|
enum bpf_access_type type;
|
||||||
bool is_narrower_load;
|
bool is_narrower_load;
|
||||||
u32 target_size;
|
|
||||||
|
|
||||||
if (ops->gen_prologue || env->seen_direct_write) {
|
if (ops->gen_prologue || env->seen_direct_write) {
|
||||||
if (!ops->gen_prologue) {
|
if (!ops->gen_prologue) {
|
||||||
|
@ -5814,9 +5814,9 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
|
||||||
* we will apply proper mask to the result.
|
* we will apply proper mask to the result.
|
||||||
*/
|
*/
|
||||||
is_narrower_load = size < ctx_field_size;
|
is_narrower_load = size < ctx_field_size;
|
||||||
|
size_default = bpf_ctx_off_adjust_machine(ctx_field_size);
|
||||||
|
off = insn->off;
|
||||||
if (is_narrower_load) {
|
if (is_narrower_load) {
|
||||||
u32 size_default = bpf_ctx_off_adjust_machine(ctx_field_size);
|
|
||||||
u32 off = insn->off;
|
|
||||||
u8 size_code;
|
u8 size_code;
|
||||||
|
|
||||||
if (type == BPF_WRITE) {
|
if (type == BPF_WRITE) {
|
||||||
|
@ -5844,12 +5844,23 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_narrower_load && size < target_size) {
|
if (is_narrower_load && size < target_size) {
|
||||||
if (ctx_field_size <= 4)
|
u8 shift = (off & (size_default - 1)) * 8;
|
||||||
|
|
||||||
|
if (ctx_field_size <= 4) {
|
||||||
|
if (shift)
|
||||||
|
insn_buf[cnt++] = BPF_ALU32_IMM(BPF_RSH,
|
||||||
|
insn->dst_reg,
|
||||||
|
shift);
|
||||||
insn_buf[cnt++] = BPF_ALU32_IMM(BPF_AND, insn->dst_reg,
|
insn_buf[cnt++] = BPF_ALU32_IMM(BPF_AND, insn->dst_reg,
|
||||||
(1 << size * 8) - 1);
|
(1 << size * 8) - 1);
|
||||||
else
|
} else {
|
||||||
|
if (shift)
|
||||||
|
insn_buf[cnt++] = BPF_ALU64_IMM(BPF_RSH,
|
||||||
|
insn->dst_reg,
|
||||||
|
shift);
|
||||||
insn_buf[cnt++] = BPF_ALU64_IMM(BPF_AND, insn->dst_reg,
|
insn_buf[cnt++] = BPF_ALU64_IMM(BPF_AND, insn->dst_reg,
|
||||||
(1 << size * 8) - 1);
|
(1 << size * 8) - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
|
new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user