Message ID | 20220721153625.1282007-3-benjamin.tissoires@redhat.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | Introduce eBPF support for HID devices | expand |
On Thu, 21 Jul 2022 at 17:36, Benjamin Tissoires <benjamin.tissoires@redhat.com> wrote: > > When a kfunc was trying to access data from context in a syscall eBPF > program, the verifier was rejecting the call. > This is because the syscall context is not known at compile time, and > so we need to check this when actually accessing it. > > Check for the valid memory access and allow such situation to happen. > > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> > > --- > LGTM, with just a couple more nits. Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> > changes in v7: > - renamed access_t into atype > - allow zero-byte read > - check_mem_access() to the correct offset/size > > new in v6 > --- > kernel/bpf/verifier.c | 21 +++++++++++++++++++++ > 1 file changed, 21 insertions(+) > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index 7c1e056624f9..d5fe7e618c52 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -248,6 +248,7 @@ struct bpf_call_arg_meta { > struct bpf_map *map_ptr; > bool raw_mode; > bool pkt_access; > + bool is_kfunc; > u8 release_regno; > int regno; > int access_size; > @@ -5170,6 +5171,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, > struct bpf_call_arg_meta *meta) > { > struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; > + enum bpf_prog_type prog_type = resolve_prog_type(env->prog); > u32 *max_access; > > switch (base_type(reg->type)) { > @@ -5223,6 +5225,24 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, > env, > regno, reg->off, access_size, > zero_size_allowed, ACCESS_HELPER, meta); > + case PTR_TO_CTX: > + /* in case of a kfunc called in a program of type SYSCALL, the context is > + * user supplied, so not computed statically. > + * Dynamically check it now > + */ > + if (prog_type == BPF_PROG_TYPE_SYSCALL && meta && meta->is_kfunc) { > + enum bpf_access_type atype = meta->raw_mode ? BPF_WRITE : BPF_READ; > + int offset = access_size - 1; > + > + /* Allow zero-byte read from NULL or PTR_TO_CTX */ This will not be handling the case for NULL, only for kfunc(ptr_to_ctx, 0) A null pointer has its reg->type as scalar, so it will be handled by the default case. > + if (access_size == 0) > + return zero_size_allowed ? 0 : -EINVAL; We should use -EACCES, just to be consistent. > + > + return check_mem_access(env, env->insn_idx, regno, offset, BPF_B, > + atype, -1, false); > + } > + > + fallthrough; > default: /* scalar_value or invalid ptr */ > /* Allow zero-byte read from NULL, regardless of pointer type */ > if (zero_size_allowed && access_size == 0 && > @@ -5335,6 +5355,7 @@ int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state > WARN_ON_ONCE(regno < BPF_REG_2 || regno > BPF_REG_5); > > memset(&meta, 0, sizeof(meta)); > + meta.is_kfunc = true; > > if (may_be_null) { > saved_reg = *mem_reg; > -- > 2.36.1 >
On Thu, Jul 21, 2022 at 10:15 PM Kumar Kartikeya Dwivedi <memxor@gmail.com> wrote: > > On Thu, 21 Jul 2022 at 17:36, Benjamin Tissoires > <benjamin.tissoires@redhat.com> wrote: > > > > When a kfunc was trying to access data from context in a syscall eBPF > > program, the verifier was rejecting the call. > > This is because the syscall context is not known at compile time, and > > so we need to check this when actually accessing it. > > > > Check for the valid memory access and allow such situation to happen. > > > > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> > > > > --- > > > > LGTM, with just a couple more nits. > Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Thanks a lot for the quick review. Sending the new version of just this patch now. Cheers, Benjamin > > > changes in v7: > > - renamed access_t into atype > > - allow zero-byte read > > - check_mem_access() to the correct offset/size > > > > new in v6 > > --- > > kernel/bpf/verifier.c | 21 +++++++++++++++++++++ > > 1 file changed, 21 insertions(+) > > > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > > index 7c1e056624f9..d5fe7e618c52 100644 > > --- a/kernel/bpf/verifier.c > > +++ b/kernel/bpf/verifier.c > > @@ -248,6 +248,7 @@ struct bpf_call_arg_meta { > > struct bpf_map *map_ptr; > > bool raw_mode; > > bool pkt_access; > > + bool is_kfunc; > > u8 release_regno; > > int regno; > > int access_size; > > @@ -5170,6 +5171,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, > > struct bpf_call_arg_meta *meta) > > { > > struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; > > + enum bpf_prog_type prog_type = resolve_prog_type(env->prog); > > u32 *max_access; > > > > switch (base_type(reg->type)) { > > @@ -5223,6 +5225,24 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, > > env, > > regno, reg->off, access_size, > > zero_size_allowed, ACCESS_HELPER, meta); > > + case PTR_TO_CTX: > > + /* in case of a kfunc called in a program of type SYSCALL, the context is > > + * user supplied, so not computed statically. > > + * Dynamically check it now > > + */ > > + if (prog_type == BPF_PROG_TYPE_SYSCALL && meta && meta->is_kfunc) { > > + enum bpf_access_type atype = meta->raw_mode ? BPF_WRITE : BPF_READ; > > + int offset = access_size - 1; > > + > > + /* Allow zero-byte read from NULL or PTR_TO_CTX */ > > This will not be handling the case for NULL, only for kfunc(ptr_to_ctx, 0) > A null pointer has its reg->type as scalar, so it will be handled by > the default case. > > > + if (access_size == 0) > > + return zero_size_allowed ? 0 : -EINVAL; > > We should use -EACCES, just to be consistent. > > > + > > + return check_mem_access(env, env->insn_idx, regno, offset, BPF_B, > > + atype, -1, false); > > + } > > + > > + fallthrough; > > default: /* scalar_value or invalid ptr */ > > /* Allow zero-byte read from NULL, regardless of pointer type */ > > if (zero_size_allowed && access_size == 0 && > > @@ -5335,6 +5355,7 @@ int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state > > WARN_ON_ONCE(regno < BPF_REG_2 || regno > BPF_REG_5); > > > > memset(&meta, 0, sizeof(meta)); > > + meta.is_kfunc = true; > > > > if (may_be_null) { > > saved_reg = *mem_reg; > > -- > > 2.36.1 > > >
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 7c1e056624f9..d5fe7e618c52 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -248,6 +248,7 @@ struct bpf_call_arg_meta { struct bpf_map *map_ptr; bool raw_mode; bool pkt_access; + bool is_kfunc; u8 release_regno; int regno; int access_size; @@ -5170,6 +5171,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, struct bpf_call_arg_meta *meta) { struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; + enum bpf_prog_type prog_type = resolve_prog_type(env->prog); u32 *max_access; switch (base_type(reg->type)) { @@ -5223,6 +5225,24 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, env, regno, reg->off, access_size, zero_size_allowed, ACCESS_HELPER, meta); + case PTR_TO_CTX: + /* in case of a kfunc called in a program of type SYSCALL, the context is + * user supplied, so not computed statically. + * Dynamically check it now + */ + if (prog_type == BPF_PROG_TYPE_SYSCALL && meta && meta->is_kfunc) { + enum bpf_access_type atype = meta->raw_mode ? BPF_WRITE : BPF_READ; + int offset = access_size - 1; + + /* Allow zero-byte read from NULL or PTR_TO_CTX */ + if (access_size == 0) + return zero_size_allowed ? 0 : -EINVAL; + + return check_mem_access(env, env->insn_idx, regno, offset, BPF_B, + atype, -1, false); + } + + fallthrough; default: /* scalar_value or invalid ptr */ /* Allow zero-byte read from NULL, regardless of pointer type */ if (zero_size_allowed && access_size == 0 && @@ -5335,6 +5355,7 @@ int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state WARN_ON_ONCE(regno < BPF_REG_2 || regno > BPF_REG_5); memset(&meta, 0, sizeof(meta)); + meta.is_kfunc = true; if (may_be_null) { saved_reg = *mem_reg;
When a kfunc was trying to access data from context in a syscall eBPF program, the verifier was rejecting the call. This is because the syscall context is not known at compile time, and so we need to check this when actually accessing it. Check for the valid memory access and allow such situation to happen. Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> --- changes in v7: - renamed access_t into atype - allow zero-byte read - check_mem_access() to the correct offset/size new in v6 --- kernel/bpf/verifier.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)