On Mon, Dec 6, 2021 at 3:22 PM Hao Luo <haoluo@xxxxxxxxxx> wrote: > > Some helper functions may modify its arguments, for example, > bpf_d_path, bpf_get_stack etc. Previously, their argument types > were marked as ARG_PTR_TO_MEM, which is compatible with read-only > mem types, such as PTR_TO_RDONLY_BUF. Therefore it's legitimate > to modify a read-only memory by passing it into one of such helper > functions. > > This patch tags the bpf_args compatible with immutable memory with > MEM_RDONLY flag. The arguments that don't have this flag will be > only compatible with mutable memory types, preventing the helper > from modifying a read-only memory. The bpf_args that have > MEM_RDONLY are compatible with both mutable memory and immutable > memory. > > Signed-off-by: Hao Luo <haoluo@xxxxxxxxxx> > --- > include/linux/bpf.h | 4 ++- > kernel/bpf/btf.c | 2 +- > kernel/bpf/cgroup.c | 2 +- > kernel/bpf/helpers.c | 8 ++--- > kernel/bpf/ringbuf.c | 2 +- > kernel/bpf/syscall.c | 2 +- > kernel/bpf/verifier.c | 14 +++++++-- > kernel/trace/bpf_trace.c | 26 ++++++++-------- > net/core/filter.c | 64 ++++++++++++++++++++-------------------- > 9 files changed, 67 insertions(+), 57 deletions(-) > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h > index 03418ab3f416..5151d6b1f392 100644 > --- a/include/linux/bpf.h > +++ b/include/linux/bpf.h > @@ -311,7 +311,9 @@ enum bpf_type_flag { > /* PTR may be NULL. */ > PTR_MAYBE_NULL = BIT(0 + BPF_BASE_TYPE_BITS), > > - /* MEM is read-only. */ > + /* MEM is read-only. When applied on bpf_arg, it indicates the arg is > + * compatible with both mutable and immutable memory. > + */ > MEM_RDONLY = BIT(1 + BPF_BASE_TYPE_BITS), > > __BPF_TYPE_LAST_FLAG = MEM_RDONLY, > diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c > index 7adc099bb24b..e09b5a7bfdc3 100644 > --- a/kernel/bpf/btf.c > +++ b/kernel/bpf/btf.c > @@ -6350,7 +6350,7 @@ const struct bpf_func_proto bpf_btf_find_by_name_kind_proto = { > .func = bpf_btf_find_by_name_kind, > .gpl_only = false, > .ret_type = RET_INTEGER, > - .arg1_type = ARG_PTR_TO_MEM, > + .arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY, > .arg2_type = ARG_CONST_SIZE, > .arg3_type = ARG_ANYTHING, > .arg4_type = ARG_ANYTHING, > diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c > index 2ca643af9a54..b8fe671af13c 100644 > --- a/kernel/bpf/cgroup.c > +++ b/kernel/bpf/cgroup.c > @@ -1789,7 +1789,7 @@ static const struct bpf_func_proto bpf_sysctl_set_new_value_proto = { > .gpl_only = false, > .ret_type = RET_INTEGER, > .arg1_type = ARG_PTR_TO_CTX, > - .arg2_type = ARG_PTR_TO_MEM, > + .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, > .arg3_type = ARG_CONST_SIZE, > }; > > diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c > index a5e349c9d3e3..66b466903a4e 100644 > --- a/kernel/bpf/helpers.c > +++ b/kernel/bpf/helpers.c > @@ -530,7 +530,7 @@ const struct bpf_func_proto bpf_strtol_proto = { > .func = bpf_strtol, > .gpl_only = false, > .ret_type = RET_INTEGER, > - .arg1_type = ARG_PTR_TO_MEM, > + .arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY, > .arg2_type = ARG_CONST_SIZE, > .arg3_type = ARG_ANYTHING, > .arg4_type = ARG_PTR_TO_LONG, > @@ -558,7 +558,7 @@ const struct bpf_func_proto bpf_strtoul_proto = { > .func = bpf_strtoul, > .gpl_only = false, > .ret_type = RET_INTEGER, > - .arg1_type = ARG_PTR_TO_MEM, > + .arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY, > .arg2_type = ARG_CONST_SIZE, > .arg3_type = ARG_ANYTHING, > .arg4_type = ARG_PTR_TO_LONG, > @@ -630,7 +630,7 @@ const struct bpf_func_proto bpf_event_output_data_proto = { > .arg1_type = ARG_PTR_TO_CTX, > .arg2_type = ARG_CONST_MAP_PTR, > .arg3_type = ARG_ANYTHING, > - .arg4_type = ARG_PTR_TO_MEM, > + .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, > .arg5_type = ARG_CONST_SIZE_OR_ZERO, > }; > > @@ -1011,7 +1011,7 @@ const struct bpf_func_proto bpf_snprintf_proto = { > .arg1_type = ARG_PTR_TO_MEM_OR_NULL, > .arg2_type = ARG_CONST_SIZE_OR_ZERO, > .arg3_type = ARG_PTR_TO_CONST_STR, > - .arg4_type = ARG_PTR_TO_MEM_OR_NULL, > + .arg4_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY, > .arg5_type = ARG_CONST_SIZE_OR_ZERO, > }; > > diff --git a/kernel/bpf/ringbuf.c b/kernel/bpf/ringbuf.c > index 9e0c10c6892a..638d7fd7b375 100644 > --- a/kernel/bpf/ringbuf.c > +++ b/kernel/bpf/ringbuf.c > @@ -444,7 +444,7 @@ const struct bpf_func_proto bpf_ringbuf_output_proto = { > .func = bpf_ringbuf_output, > .ret_type = RET_INTEGER, > .arg1_type = ARG_CONST_MAP_PTR, > - .arg2_type = ARG_PTR_TO_MEM, > + .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, > .arg3_type = ARG_CONST_SIZE_OR_ZERO, > .arg4_type = ARG_ANYTHING, > }; > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c > index 50f96ea4452a..301afb44e47f 100644 > --- a/kernel/bpf/syscall.c > +++ b/kernel/bpf/syscall.c > @@ -4757,7 +4757,7 @@ static const struct bpf_func_proto bpf_sys_bpf_proto = { > .gpl_only = false, > .ret_type = RET_INTEGER, > .arg1_type = ARG_ANYTHING, > - .arg2_type = ARG_PTR_TO_MEM, > + .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, > .arg3_type = ARG_CONST_SIZE, > }; > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index 44af65f07a82..a7c015a7b52d 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -4966,6 +4966,7 @@ static int resolve_map_arg_type(struct bpf_verifier_env *env, > return 0; > } > > + nit: unnecessary extra empty line? > struct bpf_reg_types { > const enum bpf_reg_type types[10]; > u32 *btf_id; > @@ -5012,7 +5013,6 @@ static const struct bpf_reg_types mem_types = { > PTR_TO_MAP_VALUE, > PTR_TO_MEM, > PTR_TO_BUF, > - PTR_TO_BUF | MEM_RDONLY, > }, > }; > > @@ -5074,6 +5074,7 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno, > struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; > enum bpf_reg_type expected, type = reg->type; > const struct bpf_reg_types *compatible; > + u32 compatible_flags; > int i, j; > > compatible = compatible_reg_types[base_type(arg_type)]; > @@ -5082,6 +5083,13 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno, > return -EFAULT; > } > > + /* If arg_type is tagged with MEM_RDONLY, it's compatible with both > + * RDONLY and non-RDONLY reg values. Therefore fold this flag before > + * comparison. PTR_MAYBE_NULL is similar. > + */ > + compatible_flags = arg_type & (MEM_RDONLY | PTR_MAYBE_NULL); > + type &= ~compatible_flags; > + wouldn't type &= ~MEM_RDONLY; /* clear read-only flag, if any */ type &= ~PTR_MAYBE_NULL; /* clear nullable flag, if any */ be cleaner and more straightforward? > for (i = 0; i < ARRAY_SIZE(compatible->types); i++) { > expected = compatible->types[i]; > if (expected == NOT_INIT) [...]