Skip to content

Commit 34d4ef5

Browse files
joannekoonganakryiko
authored andcommitted
bpf: Add dynptr data slices
This patch adds a new helper function void *bpf_dynptr_data(struct bpf_dynptr *ptr, u32 offset, u32 len); which returns a pointer to the underlying data of a dynptr. *len* must be a statically known value. The bpf program may access the returned data slice as a normal buffer (eg can do direct reads and writes), since the verifier associates the length with the returned pointer, and enforces that no out of bounds accesses occur. Signed-off-by: Joanne Koong <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Acked-by: Yonghong Song <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 13bbbfb commit 34d4ef5

File tree

5 files changed

+76
-0
lines changed

5 files changed

+76
-0
lines changed

include/linux/bpf.h

+1
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@ enum bpf_return_type {
488488
RET_PTR_TO_TCP_SOCK_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_TCP_SOCK,
489489
RET_PTR_TO_SOCK_COMMON_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_SOCK_COMMON,
490490
RET_PTR_TO_ALLOC_MEM_OR_NULL = PTR_MAYBE_NULL | MEM_ALLOC | RET_PTR_TO_ALLOC_MEM,
491+
RET_PTR_TO_DYNPTR_MEM_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_ALLOC_MEM,
491492
RET_PTR_TO_BTF_ID_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_BTF_ID,
492493

493494
/* This must be the last entry. Its purpose is to ensure the enum is

include/uapi/linux/bpf.h

+12
Original file line numberDiff line numberDiff line change
@@ -5238,6 +5238,17 @@ union bpf_attr {
52385238
* 0 on success, -E2BIG if *offset* + *len* exceeds the length
52395239
* of *dst*'s data, -EINVAL if *dst* is an invalid dynptr or if *dst*
52405240
* is a read-only dynptr.
5241+
*
5242+
* void *bpf_dynptr_data(struct bpf_dynptr *ptr, u32 offset, u32 len)
5243+
* Description
5244+
* Get a pointer to the underlying dynptr data.
5245+
*
5246+
* *len* must be a statically known value. The returned data slice
5247+
* is invalidated whenever the dynptr is invalidated.
5248+
* Return
5249+
* Pointer to the underlying dynptr data, NULL if the dynptr is
5250+
* read-only, if the dynptr is invalid, or if the offset and length
5251+
* is out of bounds.
52415252
*/
52425253
#define __BPF_FUNC_MAPPER(FN) \
52435254
FN(unspec), \
@@ -5443,6 +5454,7 @@ union bpf_attr {
54435454
FN(ringbuf_discard_dynptr), \
54445455
FN(dynptr_read), \
54455456
FN(dynptr_write), \
5457+
FN(dynptr_data), \
54465458
/* */
54475459

54485460
/* integer value in 'imm' field of BPF_CALL instruction selects which helper

kernel/bpf/helpers.c

+28
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,32 @@ const struct bpf_func_proto bpf_dynptr_write_proto = {
15491549
.arg4_type = ARG_CONST_SIZE_OR_ZERO,
15501550
};
15511551

1552+
BPF_CALL_3(bpf_dynptr_data, struct bpf_dynptr_kern *, ptr, u32, offset, u32, len)
1553+
{
1554+
int err;
1555+
1556+
if (!ptr->data)
1557+
return 0;
1558+
1559+
err = bpf_dynptr_check_off_len(ptr, offset, len);
1560+
if (err)
1561+
return 0;
1562+
1563+
if (bpf_dynptr_is_rdonly(ptr))
1564+
return 0;
1565+
1566+
return (unsigned long)(ptr->data + ptr->offset + offset);
1567+
}
1568+
1569+
const struct bpf_func_proto bpf_dynptr_data_proto = {
1570+
.func = bpf_dynptr_data,
1571+
.gpl_only = false,
1572+
.ret_type = RET_PTR_TO_DYNPTR_MEM_OR_NULL,
1573+
.arg1_type = ARG_PTR_TO_DYNPTR,
1574+
.arg2_type = ARG_ANYTHING,
1575+
.arg3_type = ARG_CONST_ALLOC_SIZE_OR_ZERO,
1576+
};
1577+
15521578
const struct bpf_func_proto bpf_get_current_task_proto __weak;
15531579
const struct bpf_func_proto bpf_get_current_task_btf_proto __weak;
15541580
const struct bpf_func_proto bpf_probe_read_user_proto __weak;
@@ -1615,6 +1641,8 @@ bpf_base_func_proto(enum bpf_func_id func_id)
16151641
return &bpf_dynptr_read_proto;
16161642
case BPF_FUNC_dynptr_write:
16171643
return &bpf_dynptr_write_proto;
1644+
case BPF_FUNC_dynptr_data:
1645+
return &bpf_dynptr_data_proto;
16181646
default:
16191647
break;
16201648
}

kernel/bpf/verifier.c

+23
Original file line numberDiff line numberDiff line change
@@ -5832,6 +5832,14 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env,
58325832
return __check_ptr_off_reg(env, reg, regno, fixed_off_ok);
58335833
}
58345834

5835+
static u32 stack_slot_get_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg)
5836+
{
5837+
struct bpf_func_state *state = func(env, reg);
5838+
int spi = get_spi(reg->off);
5839+
5840+
return state->stack[spi].spilled_ptr.id;
5841+
}
5842+
58355843
static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
58365844
struct bpf_call_arg_meta *meta,
58375845
const struct bpf_func_proto *fn)
@@ -7384,6 +7392,21 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
73847392
regs[BPF_REG_0].id = id;
73857393
/* For release_reference() */
73867394
regs[BPF_REG_0].ref_obj_id = id;
7395+
} else if (func_id == BPF_FUNC_dynptr_data) {
7396+
int dynptr_id = 0, i;
7397+
7398+
/* Find the id of the dynptr we're acquiring a reference to */
7399+
for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) {
7400+
if (arg_type_is_dynptr(fn->arg_type[i])) {
7401+
if (dynptr_id) {
7402+
verbose(env, "verifier internal error: multiple dynptr args in func\n");
7403+
return -EFAULT;
7404+
}
7405+
dynptr_id = stack_slot_get_id(env, &regs[BPF_REG_1 + i]);
7406+
}
7407+
}
7408+
/* For release_reference() */
7409+
regs[BPF_REG_0].ref_obj_id = dynptr_id;
73877410
}
73887411

73897412
do_refine_retval_range(regs, fn->ret_type, func_id, &meta);

tools/include/uapi/linux/bpf.h

+12
Original file line numberDiff line numberDiff line change
@@ -5238,6 +5238,17 @@ union bpf_attr {
52385238
* 0 on success, -E2BIG if *offset* + *len* exceeds the length
52395239
* of *dst*'s data, -EINVAL if *dst* is an invalid dynptr or if *dst*
52405240
* is a read-only dynptr.
5241+
*
5242+
* void *bpf_dynptr_data(struct bpf_dynptr *ptr, u32 offset, u32 len)
5243+
* Description
5244+
* Get a pointer to the underlying dynptr data.
5245+
*
5246+
* *len* must be a statically known value. The returned data slice
5247+
* is invalidated whenever the dynptr is invalidated.
5248+
* Return
5249+
* Pointer to the underlying dynptr data, NULL if the dynptr is
5250+
* read-only, if the dynptr is invalid, or if the offset and length
5251+
* is out of bounds.
52415252
*/
52425253
#define __BPF_FUNC_MAPPER(FN) \
52435254
FN(unspec), \
@@ -5443,6 +5454,7 @@ union bpf_attr {
54435454
FN(ringbuf_discard_dynptr), \
54445455
FN(dynptr_read), \
54455456
FN(dynptr_write), \
5457+
FN(dynptr_data), \
54465458
/* */
54475459

54485460
/* integer value in 'imm' field of BPF_CALL instruction selects which helper

0 commit comments

Comments
 (0)