]> git.ipfire.org Git - thirdparty/kernel/stable.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)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 14 Dec 2024 18:54:53 +0000 (19:54 +0100)
commit 32556ce93bc45c730829083cb60f95a2728ea48b upstream.

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>
[ Resolve merge conflict in include/linux/bpf.h and merge conflict in
  kernel/bpf/verifier.c.]
Signed-off-by: Bin Lan <bin.lan.cn@windriver.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.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 6b18b8da025f90f2002915f46de8acd8d1c4d514..7f4ce183dcb02c6fa6c693421a716a4571a05531 100644 (file)
@@ -475,6 +475,11 @@ enum bpf_type_flag {
        /* Size is known at compile time. */
        MEM_FIXED_SIZE          = BIT(10 + 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,
 };
@@ -510,8 +515,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_ALLOC_MEM,   /* pointer to dynamically allocated memory */
index a3fc4e2e8256add78438d159e43e28b654b9223b..14ad6856257c25078718342e3f4aa4cc77a9f07c 100644 (file)
@@ -531,7 +531,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,
@@ -560,7 +561,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 42f5b37a74c6f78a00a2d956397ecf685b9144e4..f9906e5ad2e574b3338b2cee50c72331761c8d3e 100644 (file)
@@ -5260,7 +5260,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 da90f565317d4c7687c5befffec46781ea109a04..b68572c41e96409359f9b174ef691dfa97f80c06 100644 (file)
@@ -5818,16 +5818,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)
@@ -5908,16 +5898,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 fullsock_types = { .types = { PTR_TO_SOCKET } };
 static const struct bpf_reg_types scalar_types = { .types = { SCALAR_VALUE } };
 static const struct bpf_reg_types context_types = { .types = { PTR_TO_CTX } };
@@ -5955,8 +5935,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_ALLOC_MEM]          = &alloc_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,
@@ -6303,9 +6281,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:
@@ -6373,17 +6353,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:
        {
                struct bpf_map *map = reg->map_ptr;
index 583961a9e539a0a2662cf1b1b0d0463579cc4f86..d8212fea1e99d936f6ba18276bd0364c9f6b5e84 100644 (file)
@@ -1192,7 +1192,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)
@@ -1208,7 +1209,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 4a97c89c9da9a357f435f9bbe9c2aaee78c87ce2..540124096aaa337413dad0fa3315284386a5e200 100644 (file)
@@ -6233,7 +6233,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,
 };
@@ -6244,7 +6245,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,
 };