Skip to content

Commit 988e55a

Browse files
borkmanngregkh
authored andcommitted
bpf: Fix helper writes to read-only maps
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: 57c3bb7 ("bpf: Introduce ARG_PTR_TO_{INT,LONG} arg types") Reported-by: Lonial Con <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Acked-by: Andrii Nakryiko <[email protected]> Acked-by: Shung-Hsi Yu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]> [ Resolve merge conflict in include/linux/bpf.h and merge conflict in kernel/bpf/verifier.c.] Signed-off-by: Bin Lan <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 0f910db commit 988e55a

File tree

6 files changed

+24
-45
lines changed

6 files changed

+24
-45
lines changed

include/linux/bpf.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,11 @@ enum bpf_type_flag {
475475
/* Size is known at compile time. */
476476
MEM_FIXED_SIZE = BIT(10 + BPF_BASE_TYPE_BITS),
477477

478+
/* Memory must be aligned on some architectures, used in combination with
479+
* MEM_FIXED_SIZE.
480+
*/
481+
MEM_ALIGNED = BIT(17 + BPF_BASE_TYPE_BITS),
482+
478483
__BPF_TYPE_FLAG_MAX,
479484
__BPF_TYPE_LAST_FLAG = __BPF_TYPE_FLAG_MAX - 1,
480485
};
@@ -510,8 +515,6 @@ enum bpf_arg_type {
510515
ARG_ANYTHING, /* any (initialized) argument is ok */
511516
ARG_PTR_TO_SPIN_LOCK, /* pointer to bpf_spin_lock */
512517
ARG_PTR_TO_SOCK_COMMON, /* pointer to sock_common */
513-
ARG_PTR_TO_INT, /* pointer to int */
514-
ARG_PTR_TO_LONG, /* pointer to long */
515518
ARG_PTR_TO_SOCKET, /* pointer to bpf_sock (fullsock) */
516519
ARG_PTR_TO_BTF_ID, /* pointer to in-kernel struct */
517520
ARG_PTR_TO_ALLOC_MEM, /* pointer to dynamically allocated memory */

kernel/bpf/helpers.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,8 @@ const struct bpf_func_proto bpf_strtol_proto = {
531531
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
532532
.arg2_type = ARG_CONST_SIZE,
533533
.arg3_type = ARG_ANYTHING,
534-
.arg4_type = ARG_PTR_TO_LONG,
534+
.arg4_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
535+
.arg4_size = sizeof(s64),
535536
};
536537

537538
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 = {
560561
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
561562
.arg2_type = ARG_CONST_SIZE,
562563
.arg3_type = ARG_ANYTHING,
563-
.arg4_type = ARG_PTR_TO_LONG,
564+
.arg4_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
565+
.arg4_size = sizeof(u64),
564566
};
565567

566568
BPF_CALL_3(bpf_strncmp, const char *, s1, u32, s1_sz, const char *, s2)

kernel/bpf/syscall.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5260,7 +5260,8 @@ static const struct bpf_func_proto bpf_kallsyms_lookup_name_proto = {
52605260
.arg1_type = ARG_PTR_TO_MEM,
52615261
.arg2_type = ARG_CONST_SIZE_OR_ZERO,
52625262
.arg3_type = ARG_ANYTHING,
5263-
.arg4_type = ARG_PTR_TO_LONG,
5263+
.arg4_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
5264+
.arg4_size = sizeof(u64),
52645265
};
52655266

52665267
static const struct bpf_func_proto *

kernel/bpf/verifier.c

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5818,16 +5818,6 @@ static bool arg_type_is_dynptr(enum bpf_arg_type type)
58185818
return base_type(type) == ARG_PTR_TO_DYNPTR;
58195819
}
58205820

5821-
static int int_ptr_type_to_size(enum bpf_arg_type type)
5822-
{
5823-
if (type == ARG_PTR_TO_INT)
5824-
return sizeof(u32);
5825-
else if (type == ARG_PTR_TO_LONG)
5826-
return sizeof(u64);
5827-
5828-
return -EINVAL;
5829-
}
5830-
58315821
static int resolve_map_arg_type(struct bpf_verifier_env *env,
58325822
const struct bpf_call_arg_meta *meta,
58335823
enum bpf_arg_type *arg_type)
@@ -5908,16 +5898,6 @@ static const struct bpf_reg_types mem_types = {
59085898
},
59095899
};
59105900

5911-
static const struct bpf_reg_types int_ptr_types = {
5912-
.types = {
5913-
PTR_TO_STACK,
5914-
PTR_TO_PACKET,
5915-
PTR_TO_PACKET_META,
5916-
PTR_TO_MAP_KEY,
5917-
PTR_TO_MAP_VALUE,
5918-
},
5919-
};
5920-
59215901
static const struct bpf_reg_types fullsock_types = { .types = { PTR_TO_SOCKET } };
59225902
static const struct bpf_reg_types scalar_types = { .types = { SCALAR_VALUE } };
59235903
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] = {
59555935
[ARG_PTR_TO_SPIN_LOCK] = &spin_lock_types,
59565936
[ARG_PTR_TO_MEM] = &mem_types,
59575937
[ARG_PTR_TO_ALLOC_MEM] = &alloc_mem_types,
5958-
[ARG_PTR_TO_INT] = &int_ptr_types,
5959-
[ARG_PTR_TO_LONG] = &int_ptr_types,
59605938
[ARG_PTR_TO_PERCPU_BTF_ID] = &percpu_btf_ptr_types,
59615939
[ARG_PTR_TO_FUNC] = &func_ptr_types,
59625940
[ARG_PTR_TO_STACK] = &stack_ptr_types,
@@ -6303,9 +6281,11 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
63036281
*/
63046282
meta->raw_mode = arg_type & MEM_UNINIT;
63056283
if (arg_type & MEM_FIXED_SIZE) {
6306-
err = check_helper_mem_access(env, regno,
6307-
fn->arg_size[arg], false,
6308-
meta);
6284+
err = check_helper_mem_access(env, regno, fn->arg_size[arg], false, meta);
6285+
if (err)
6286+
return err;
6287+
if (arg_type & MEM_ALIGNED)
6288+
err = check_ptr_alignment(env, reg, 0, fn->arg_size[arg], true);
63096289
}
63106290
break;
63116291
case ARG_CONST_SIZE:
@@ -6373,17 +6353,6 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
63736353
if (err)
63746354
return err;
63756355
break;
6376-
case ARG_PTR_TO_INT:
6377-
case ARG_PTR_TO_LONG:
6378-
{
6379-
int size = int_ptr_type_to_size(arg_type);
6380-
6381-
err = check_helper_mem_access(env, regno, size, false, meta);
6382-
if (err)
6383-
return err;
6384-
err = check_ptr_alignment(env, reg, 0, size, true);
6385-
break;
6386-
}
63876356
case ARG_PTR_TO_CONST_STR:
63886357
{
63896358
struct bpf_map *map = reg->map_ptr;

kernel/trace/bpf_trace.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,7 +1192,8 @@ static const struct bpf_func_proto bpf_get_func_arg_proto = {
11921192
.ret_type = RET_INTEGER,
11931193
.arg1_type = ARG_PTR_TO_CTX,
11941194
.arg2_type = ARG_ANYTHING,
1195-
.arg3_type = ARG_PTR_TO_LONG,
1195+
.arg3_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
1196+
.arg3_size = sizeof(u64),
11961197
};
11971198

11981199
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 = {
12081209
.func = get_func_ret,
12091210
.ret_type = RET_INTEGER,
12101211
.arg1_type = ARG_PTR_TO_CTX,
1211-
.arg2_type = ARG_PTR_TO_LONG,
1212+
.arg2_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
1213+
.arg2_size = sizeof(u64),
12121214
};
12131215

12141216
BPF_CALL_1(get_func_arg_cnt, void *, ctx)

net/core/filter.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6233,7 +6233,8 @@ static const struct bpf_func_proto bpf_skb_check_mtu_proto = {
62336233
.ret_type = RET_INTEGER,
62346234
.arg1_type = ARG_PTR_TO_CTX,
62356235
.arg2_type = ARG_ANYTHING,
6236-
.arg3_type = ARG_PTR_TO_INT,
6236+
.arg3_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
6237+
.arg3_size = sizeof(u32),
62376238
.arg4_type = ARG_ANYTHING,
62386239
.arg5_type = ARG_ANYTHING,
62396240
};
@@ -6244,7 +6245,8 @@ static const struct bpf_func_proto bpf_xdp_check_mtu_proto = {
62446245
.ret_type = RET_INTEGER,
62456246
.arg1_type = ARG_PTR_TO_CTX,
62466247
.arg2_type = ARG_ANYTHING,
6247-
.arg3_type = ARG_PTR_TO_INT,
6248+
.arg3_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
6249+
.arg3_size = sizeof(u32),
62486250
.arg4_type = ARG_ANYTHING,
62496251
.arg5_type = ARG_ANYTHING,
62506252
};

0 commit comments

Comments
 (0)