]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bpf: Fix helper writes to read-only maps
authorDaniel Borkmann <daniel@iogearbox.net>
Fri, 13 Sep 2024 19:17:48 +0000 (21:17 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 13 Sep 2024 20:17:55 +0000 (13:17 -0700)
Lonial found an issue that despite user- and BPF-side frozen BPF map
(like in case of .rodata), it was still possible to write into it from
a BPF program side through specific helpers having ARG_PTR_TO_{LONG,INT}
as arguments.

In check_func_arg() when the argument is as mentioned, the meta->raw_mode
is never set. Later, check_helper_mem_access(), under the case of
PTR_TO_MAP_VALUE as register base type, it assumes BPF_READ for the
subsequent call to check_map_access_type() and given the BPF map is
read-only it succeeds.

The helpers really need to be annotated as ARG_PTR_TO_{LONG,INT} | MEM_UNINIT
when results are written into them as opposed to read out of them. The
latter indicates that it's okay to pass a pointer to uninitialized memory
as the memory is written to anyway.

However, ARG_PTR_TO_{LONG,INT} is a special case of ARG_PTR_TO_FIXED_SIZE_MEM
just with additional alignment requirement. So it is better to just get
rid of the ARG_PTR_TO_{LONG,INT} special cases altogether and reuse the
fixed size memory types. For this, add MEM_ALIGNED to additionally ensure
alignment given these helpers write directly into the args via *<ptr> = val.
The .arg*_size has been initialized reflecting the actual sizeof(*<ptr>).

MEM_ALIGNED can only be used in combination with MEM_FIXED_SIZE annotated
argument types, since in !MEM_FIXED_SIZE cases the verifier does not know
the buffer size a priori and therefore cannot blindly write *<ptr> = val.

Fixes: 57c3bb725a3d ("bpf: Introduce ARG_PTR_TO_{INT,LONG} arg types")
Reported-by: Lonial Con <kongln9170@gmail.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
Link: https://lore.kernel.org/r/20240913191754.13290-3-daniel@iogearbox.net
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/linux/bpf.h
kernel/bpf/helpers.c
kernel/bpf/syscall.c
kernel/bpf/verifier.c
kernel/trace/bpf_trace.c
net/core/filter.c

index 7f3e8477d5e7f0ef84798579be431e484e7e309c..0c3893c4717110a4b9cd6ae3df39678b2f0e58c9 100644 (file)
@@ -695,6 +695,11 @@ enum bpf_type_flag {
        /* DYNPTR points to xdp_buff */
        DYNPTR_TYPE_XDP         = BIT(16 + BPF_BASE_TYPE_BITS),
 
+       /* Memory must be aligned on some architectures, used in combination with
+        * MEM_FIXED_SIZE.
+        */
+       MEM_ALIGNED             = BIT(17 + BPF_BASE_TYPE_BITS),
+
        __BPF_TYPE_FLAG_MAX,
        __BPF_TYPE_LAST_FLAG    = __BPF_TYPE_FLAG_MAX - 1,
 };
@@ -732,8 +737,6 @@ enum bpf_arg_type {
        ARG_ANYTHING,           /* any (initialized) argument is ok */
        ARG_PTR_TO_SPIN_LOCK,   /* pointer to bpf_spin_lock */
        ARG_PTR_TO_SOCK_COMMON, /* pointer to sock_common */
-       ARG_PTR_TO_INT,         /* pointer to int */
-       ARG_PTR_TO_LONG,        /* pointer to long */
        ARG_PTR_TO_SOCKET,      /* pointer to bpf_sock (fullsock) */
        ARG_PTR_TO_BTF_ID,      /* pointer to in-kernel struct */
        ARG_PTR_TO_RINGBUF_MEM, /* pointer to dynamically reserved ringbuf memory */
index 5404bb964d833b96a81534a536b3f486d626eb4d..5e97fdc6b20f96bf612ca4cf534545063605b960 100644 (file)
@@ -537,7 +537,8 @@ const struct bpf_func_proto bpf_strtol_proto = {
        .arg1_type      = ARG_PTR_TO_MEM | MEM_RDONLY,
        .arg2_type      = ARG_CONST_SIZE,
        .arg3_type      = ARG_ANYTHING,
-       .arg4_type      = ARG_PTR_TO_LONG,
+       .arg4_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
+       .arg4_size      = sizeof(s64),
 };
 
 BPF_CALL_4(bpf_strtoul, const char *, buf, size_t, buf_len, u64, flags,
@@ -563,7 +564,8 @@ const struct bpf_func_proto bpf_strtoul_proto = {
        .arg1_type      = ARG_PTR_TO_MEM | MEM_RDONLY,
        .arg2_type      = ARG_CONST_SIZE,
        .arg3_type      = ARG_ANYTHING,
-       .arg4_type      = ARG_PTR_TO_LONG,
+       .arg4_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
+       .arg4_size      = sizeof(u64),
 };
 
 BPF_CALL_3(bpf_strncmp, const char *, s1, u32, s1_sz, const char *, s2)
index 6988e432fc3dbbdfcb00b28c1b0dd4046a901385..a7cd3719e26a38c396202abb49a82a9a863dbbf3 100644 (file)
@@ -5954,7 +5954,8 @@ static const struct bpf_func_proto bpf_kallsyms_lookup_name_proto = {
        .arg1_type      = ARG_PTR_TO_MEM,
        .arg2_type      = ARG_CONST_SIZE_OR_ZERO,
        .arg3_type      = ARG_ANYTHING,
-       .arg4_type      = ARG_PTR_TO_LONG,
+       .arg4_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
+       .arg4_size      = sizeof(u64),
 };
 
 static const struct bpf_func_proto *
index 068f763dcefb9d013124509b16a8ba850b9f7a26..7d62fd576089bbc06d837cceb8a37e1964c16d93 100644 (file)
@@ -8301,16 +8301,6 @@ static bool arg_type_is_dynptr(enum bpf_arg_type type)
        return base_type(type) == ARG_PTR_TO_DYNPTR;
 }
 
-static int int_ptr_type_to_size(enum bpf_arg_type type)
-{
-       if (type == ARG_PTR_TO_INT)
-               return sizeof(u32);
-       else if (type == ARG_PTR_TO_LONG)
-               return sizeof(u64);
-
-       return -EINVAL;
-}
-
 static int resolve_map_arg_type(struct bpf_verifier_env *env,
                                 const struct bpf_call_arg_meta *meta,
                                 enum bpf_arg_type *arg_type)
@@ -8383,16 +8373,6 @@ static const struct bpf_reg_types mem_types = {
        },
 };
 
-static const struct bpf_reg_types int_ptr_types = {
-       .types = {
-               PTR_TO_STACK,
-               PTR_TO_PACKET,
-               PTR_TO_PACKET_META,
-               PTR_TO_MAP_KEY,
-               PTR_TO_MAP_VALUE,
-       },
-};
-
 static const struct bpf_reg_types spin_lock_types = {
        .types = {
                PTR_TO_MAP_VALUE,
@@ -8453,8 +8433,6 @@ static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = {
        [ARG_PTR_TO_SPIN_LOCK]          = &spin_lock_types,
        [ARG_PTR_TO_MEM]                = &mem_types,
        [ARG_PTR_TO_RINGBUF_MEM]        = &ringbuf_mem_types,
-       [ARG_PTR_TO_INT]                = &int_ptr_types,
-       [ARG_PTR_TO_LONG]               = &int_ptr_types,
        [ARG_PTR_TO_PERCPU_BTF_ID]      = &percpu_btf_ptr_types,
        [ARG_PTR_TO_FUNC]               = &func_ptr_types,
        [ARG_PTR_TO_STACK]              = &stack_ptr_types,
@@ -9017,9 +8995,11 @@ skip_type_check:
                 */
                meta->raw_mode = arg_type & MEM_UNINIT;
                if (arg_type & MEM_FIXED_SIZE) {
-                       err = check_helper_mem_access(env, regno,
-                                                     fn->arg_size[arg], false,
-                                                     meta);
+                       err = check_helper_mem_access(env, regno, fn->arg_size[arg], false, meta);
+                       if (err)
+                               return err;
+                       if (arg_type & MEM_ALIGNED)
+                               err = check_ptr_alignment(env, reg, 0, fn->arg_size[arg], true);
                }
                break;
        case ARG_CONST_SIZE:
@@ -9044,17 +9024,6 @@ skip_type_check:
                if (err)
                        return err;
                break;
-       case ARG_PTR_TO_INT:
-       case ARG_PTR_TO_LONG:
-       {
-               int size = int_ptr_type_to_size(arg_type);
-
-               err = check_helper_mem_access(env, regno, size, false, meta);
-               if (err)
-                       return err;
-               err = check_ptr_alignment(env, reg, 0, size, true);
-               break;
-       }
        case ARG_PTR_TO_CONST_STR:
        {
                err = check_reg_const_str(env, reg, regno);
index 68b5905c6eb524d07cd75ee67ee86dd5322bca05..b3283c250e273f1d6d5a15b4b667a73a4908ea44 100644 (file)
@@ -1202,7 +1202,8 @@ static const struct bpf_func_proto bpf_get_func_arg_proto = {
        .ret_type       = RET_INTEGER,
        .arg1_type      = ARG_PTR_TO_CTX,
        .arg2_type      = ARG_ANYTHING,
-       .arg3_type      = ARG_PTR_TO_LONG,
+       .arg3_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
+       .arg3_size      = sizeof(u64),
 };
 
 BPF_CALL_2(get_func_ret, void *, ctx, u64 *, value)
@@ -1218,7 +1219,8 @@ static const struct bpf_func_proto bpf_get_func_ret_proto = {
        .func           = get_func_ret,
        .ret_type       = RET_INTEGER,
        .arg1_type      = ARG_PTR_TO_CTX,
-       .arg2_type      = ARG_PTR_TO_LONG,
+       .arg2_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
+       .arg2_size      = sizeof(u64),
 };
 
 BPF_CALL_1(get_func_arg_cnt, void *, ctx)
index ecf2ddf633bfc5f19b63cb87af05da97ca79b896..d0550c87e5209f70bff055912d564610f34e31f9 100644 (file)
@@ -6346,7 +6346,8 @@ static const struct bpf_func_proto bpf_skb_check_mtu_proto = {
        .ret_type       = RET_INTEGER,
        .arg1_type      = ARG_PTR_TO_CTX,
        .arg2_type      = ARG_ANYTHING,
-       .arg3_type      = ARG_PTR_TO_INT,
+       .arg3_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
+       .arg3_size      = sizeof(u32),
        .arg4_type      = ARG_ANYTHING,
        .arg5_type      = ARG_ANYTHING,
 };
@@ -6357,7 +6358,8 @@ static const struct bpf_func_proto bpf_xdp_check_mtu_proto = {
        .ret_type       = RET_INTEGER,
        .arg1_type      = ARG_PTR_TO_CTX,
        .arg2_type      = ARG_ANYTHING,
-       .arg3_type      = ARG_PTR_TO_INT,
+       .arg3_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
+       .arg3_size      = sizeof(u32),
        .arg4_type      = ARG_ANYTHING,
        .arg5_type      = ARG_ANYTHING,
 };