Skip to content

Commit f4efc73

Browse files
author
Alexei Starovoitov
committed
Merge branch 'introduce-kfuncs-for-memory-reads-into-dynptrs'
Mykyta Yatsenko says: ==================== Introduce kfuncs for memory reads into dynptrs From: Mykyta Yatsenko <[email protected]> This patch adds new kfuncs that enable reading variable-length user or kernel data directly into dynptrs. These kfuncs provide a way to perform dynamically-sized reads while maintaining memory safety. Unlike existing `bpf_probe_read_{user|kernel}` APIs, which are limited to constant-sized reads, these new kfuncs allow for more flexible data access. v4 -> v5 * Fix pointers annotations, use __user where necessary, cast where needed v3 -> v4 * Added pid filtering in selftests v2 -> v3 * Add KF_TRUSTED_ARGS for kfuncs that take pointer to task_struct as an argument * Remove checks for non-NULL task, where it was not necessary * Added comments on constants used in selftests, etc. v1 -> v2 * Renaming helper functions to use "user_str" instead of "user_data_str" suffix ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents fd5fd53 + c61bcd2 commit f4efc73

File tree

6 files changed

+462
-12
lines changed

6 files changed

+462
-12
lines changed

include/linux/bpf.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,6 +1349,20 @@ u32 __bpf_dynptr_size(const struct bpf_dynptr_kern *ptr);
13491349
const void *__bpf_dynptr_data(const struct bpf_dynptr_kern *ptr, u32 len);
13501350
void *__bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len);
13511351
bool __bpf_dynptr_is_rdonly(const struct bpf_dynptr_kern *ptr);
1352+
int __bpf_dynptr_write(const struct bpf_dynptr_kern *dst, u32 offset,
1353+
void *src, u32 len, u64 flags);
1354+
void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr *p, u32 offset,
1355+
void *buffer__opt, u32 buffer__szk);
1356+
1357+
static inline int bpf_dynptr_check_off_len(const struct bpf_dynptr_kern *ptr, u32 offset, u32 len)
1358+
{
1359+
u32 size = __bpf_dynptr_size(ptr);
1360+
1361+
if (len > size || offset > size - len)
1362+
return -E2BIG;
1363+
1364+
return 0;
1365+
}
13521366

13531367
#ifdef CONFIG_BPF_JIT
13541368
int bpf_trampoline_link_prog(struct bpf_tramp_link *link,

kernel/bpf/helpers.c

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,16 +1714,6 @@ void bpf_dynptr_set_null(struct bpf_dynptr_kern *ptr)
17141714
memset(ptr, 0, sizeof(*ptr));
17151715
}
17161716

1717-
static int bpf_dynptr_check_off_len(const struct bpf_dynptr_kern *ptr, u32 offset, u32 len)
1718-
{
1719-
u32 size = __bpf_dynptr_size(ptr);
1720-
1721-
if (len > size || offset > size - len)
1722-
return -E2BIG;
1723-
1724-
return 0;
1725-
}
1726-
17271717
BPF_CALL_4(bpf_dynptr_from_mem, void *, data, u32, size, u64, flags, struct bpf_dynptr_kern *, ptr)
17281718
{
17291719
int err;
@@ -1810,8 +1800,8 @@ static const struct bpf_func_proto bpf_dynptr_read_proto = {
18101800
.arg5_type = ARG_ANYTHING,
18111801
};
18121802

1813-
static int __bpf_dynptr_write(const struct bpf_dynptr_kern *dst, u32 offset, void *src,
1814-
u32 len, u64 flags)
1803+
int __bpf_dynptr_write(const struct bpf_dynptr_kern *dst, u32 offset, void *src,
1804+
u32 len, u64 flags)
18151805
{
18161806
enum bpf_dynptr_type type;
18171807
int err;
@@ -3388,6 +3378,14 @@ BTF_ID_FLAGS(func, bpf_iter_kmem_cache_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLE
33883378
BTF_ID_FLAGS(func, bpf_iter_kmem_cache_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
33893379
BTF_ID_FLAGS(func, bpf_local_irq_save)
33903380
BTF_ID_FLAGS(func, bpf_local_irq_restore)
3381+
BTF_ID_FLAGS(func, bpf_probe_read_user_dynptr)
3382+
BTF_ID_FLAGS(func, bpf_probe_read_kernel_dynptr)
3383+
BTF_ID_FLAGS(func, bpf_probe_read_user_str_dynptr)
3384+
BTF_ID_FLAGS(func, bpf_probe_read_kernel_str_dynptr)
3385+
BTF_ID_FLAGS(func, bpf_copy_from_user_dynptr, KF_SLEEPABLE)
3386+
BTF_ID_FLAGS(func, bpf_copy_from_user_str_dynptr, KF_SLEEPABLE)
3387+
BTF_ID_FLAGS(func, bpf_copy_from_user_task_dynptr, KF_SLEEPABLE | KF_TRUSTED_ARGS)
3388+
BTF_ID_FLAGS(func, bpf_copy_from_user_task_str_dynptr, KF_SLEEPABLE | KF_TRUSTED_ARGS)
33913389
BTF_KFUNCS_END(common_btf_ids)
33923390

33933391
static const struct btf_kfunc_id_set common_kfunc_set = {

kernel/trace/bpf_trace.c

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3466,6 +3466,142 @@ static int __init bpf_kprobe_multi_kfuncs_init(void)
34663466

34673467
late_initcall(bpf_kprobe_multi_kfuncs_init);
34683468

3469+
typedef int (*copy_fn_t)(void *dst, const void *src, u32 size, struct task_struct *tsk);
3470+
3471+
/*
3472+
* The __always_inline is to make sure the compiler doesn't
3473+
* generate indirect calls into callbacks, which is expensive,
3474+
* on some kernel configurations. This allows compiler to put
3475+
* direct calls into all the specific callback implementations
3476+
* (copy_user_data_sleepable, copy_user_data_nofault, and so on)
3477+
*/
3478+
static __always_inline int __bpf_dynptr_copy_str(struct bpf_dynptr *dptr, u32 doff, u32 size,
3479+
const void *unsafe_src,
3480+
copy_fn_t str_copy_fn,
3481+
struct task_struct *tsk)
3482+
{
3483+
struct bpf_dynptr_kern *dst;
3484+
u32 chunk_sz, off;
3485+
void *dst_slice;
3486+
int cnt, err;
3487+
char buf[256];
3488+
3489+
dst_slice = bpf_dynptr_slice_rdwr(dptr, doff, NULL, size);
3490+
if (likely(dst_slice))
3491+
return str_copy_fn(dst_slice, unsafe_src, size, tsk);
3492+
3493+
dst = (struct bpf_dynptr_kern *)dptr;
3494+
if (bpf_dynptr_check_off_len(dst, doff, size))
3495+
return -E2BIG;
3496+
3497+
for (off = 0; off < size; off += chunk_sz - 1) {
3498+
chunk_sz = min_t(u32, sizeof(buf), size - off);
3499+
/* Expect str_copy_fn to return count of copied bytes, including
3500+
* zero terminator. Next iteration increment off by chunk_sz - 1 to
3501+
* overwrite NUL.
3502+
*/
3503+
cnt = str_copy_fn(buf, unsafe_src + off, chunk_sz, tsk);
3504+
if (cnt < 0)
3505+
return cnt;
3506+
err = __bpf_dynptr_write(dst, doff + off, buf, cnt, 0);
3507+
if (err)
3508+
return err;
3509+
if (cnt < chunk_sz || chunk_sz == 1) /* we are done */
3510+
return off + cnt;
3511+
}
3512+
return off;
3513+
}
3514+
3515+
static __always_inline int __bpf_dynptr_copy(const struct bpf_dynptr *dptr, u32 doff,
3516+
u32 size, const void *unsafe_src,
3517+
copy_fn_t copy_fn, struct task_struct *tsk)
3518+
{
3519+
struct bpf_dynptr_kern *dst;
3520+
void *dst_slice;
3521+
char buf[256];
3522+
u32 off, chunk_sz;
3523+
int err;
3524+
3525+
dst_slice = bpf_dynptr_slice_rdwr(dptr, doff, NULL, size);
3526+
if (likely(dst_slice))
3527+
return copy_fn(dst_slice, unsafe_src, size, tsk);
3528+
3529+
dst = (struct bpf_dynptr_kern *)dptr;
3530+
if (bpf_dynptr_check_off_len(dst, doff, size))
3531+
return -E2BIG;
3532+
3533+
for (off = 0; off < size; off += chunk_sz) {
3534+
chunk_sz = min_t(u32, sizeof(buf), size - off);
3535+
err = copy_fn(buf, unsafe_src + off, chunk_sz, tsk);
3536+
if (err)
3537+
return err;
3538+
err = __bpf_dynptr_write(dst, doff + off, buf, chunk_sz, 0);
3539+
if (err)
3540+
return err;
3541+
}
3542+
return 0;
3543+
}
3544+
3545+
static __always_inline int copy_user_data_nofault(void *dst, const void *unsafe_src,
3546+
u32 size, struct task_struct *tsk)
3547+
{
3548+
return copy_from_user_nofault(dst, (const void __user *)unsafe_src, size);
3549+
}
3550+
3551+
static __always_inline int copy_user_data_sleepable(void *dst, const void *unsafe_src,
3552+
u32 size, struct task_struct *tsk)
3553+
{
3554+
int ret;
3555+
3556+
if (!tsk) /* Read from the current task */
3557+
return copy_from_user(dst, (const void __user *)unsafe_src, size);
3558+
3559+
ret = access_process_vm(tsk, (unsigned long)unsafe_src, dst, size, 0);
3560+
if (ret != size)
3561+
return -EFAULT;
3562+
return 0;
3563+
}
3564+
3565+
static __always_inline int copy_kernel_data_nofault(void *dst, const void *unsafe_src,
3566+
u32 size, struct task_struct *tsk)
3567+
{
3568+
return copy_from_kernel_nofault(dst, unsafe_src, size);
3569+
}
3570+
3571+
static __always_inline int copy_user_str_nofault(void *dst, const void *unsafe_src,
3572+
u32 size, struct task_struct *tsk)
3573+
{
3574+
return strncpy_from_user_nofault(dst, (const void __user *)unsafe_src, size);
3575+
}
3576+
3577+
static __always_inline int copy_user_str_sleepable(void *dst, const void *unsafe_src,
3578+
u32 size, struct task_struct *tsk)
3579+
{
3580+
int ret;
3581+
3582+
if (unlikely(size == 0))
3583+
return 0;
3584+
3585+
if (tsk) {
3586+
ret = copy_remote_vm_str(tsk, (unsigned long)unsafe_src, dst, size, 0);
3587+
} else {
3588+
ret = strncpy_from_user(dst, (const void __user *)unsafe_src, size - 1);
3589+
/* strncpy_from_user does not guarantee NUL termination */
3590+
if (ret >= 0)
3591+
((char *)dst)[ret] = '\0';
3592+
}
3593+
3594+
if (ret < 0)
3595+
return ret;
3596+
return ret + 1;
3597+
}
3598+
3599+
static __always_inline int copy_kernel_str_nofault(void *dst, const void *unsafe_src,
3600+
u32 size, struct task_struct *tsk)
3601+
{
3602+
return strncpy_from_kernel_nofault(dst, unsafe_src, size);
3603+
}
3604+
34693605
__bpf_kfunc_start_defs();
34703606

34713607
__bpf_kfunc int bpf_send_signal_task(struct task_struct *task, int sig, enum pid_type type,
@@ -3477,4 +3613,62 @@ __bpf_kfunc int bpf_send_signal_task(struct task_struct *task, int sig, enum pid
34773613
return bpf_send_signal_common(sig, type, task, value);
34783614
}
34793615

3616+
__bpf_kfunc int bpf_probe_read_user_dynptr(struct bpf_dynptr *dptr, u32 off,
3617+
u32 size, const void __user *unsafe_ptr__ign)
3618+
{
3619+
return __bpf_dynptr_copy(dptr, off, size, (const void *)unsafe_ptr__ign,
3620+
copy_user_data_nofault, NULL);
3621+
}
3622+
3623+
__bpf_kfunc int bpf_probe_read_kernel_dynptr(struct bpf_dynptr *dptr, u32 off,
3624+
u32 size, const void *unsafe_ptr__ign)
3625+
{
3626+
return __bpf_dynptr_copy(dptr, off, size, unsafe_ptr__ign,
3627+
copy_kernel_data_nofault, NULL);
3628+
}
3629+
3630+
__bpf_kfunc int bpf_probe_read_user_str_dynptr(struct bpf_dynptr *dptr, u32 off,
3631+
u32 size, const void __user *unsafe_ptr__ign)
3632+
{
3633+
return __bpf_dynptr_copy_str(dptr, off, size, (const void *)unsafe_ptr__ign,
3634+
copy_user_str_nofault, NULL);
3635+
}
3636+
3637+
__bpf_kfunc int bpf_probe_read_kernel_str_dynptr(struct bpf_dynptr *dptr, u32 off,
3638+
u32 size, const void *unsafe_ptr__ign)
3639+
{
3640+
return __bpf_dynptr_copy_str(dptr, off, size, unsafe_ptr__ign,
3641+
copy_kernel_str_nofault, NULL);
3642+
}
3643+
3644+
__bpf_kfunc int bpf_copy_from_user_dynptr(struct bpf_dynptr *dptr, u32 off,
3645+
u32 size, const void __user *unsafe_ptr__ign)
3646+
{
3647+
return __bpf_dynptr_copy(dptr, off, size, (const void *)unsafe_ptr__ign,
3648+
copy_user_data_sleepable, NULL);
3649+
}
3650+
3651+
__bpf_kfunc int bpf_copy_from_user_str_dynptr(struct bpf_dynptr *dptr, u32 off,
3652+
u32 size, const void __user *unsafe_ptr__ign)
3653+
{
3654+
return __bpf_dynptr_copy_str(dptr, off, size, (const void *)unsafe_ptr__ign,
3655+
copy_user_str_sleepable, NULL);
3656+
}
3657+
3658+
__bpf_kfunc int bpf_copy_from_user_task_dynptr(struct bpf_dynptr *dptr, u32 off,
3659+
u32 size, const void __user *unsafe_ptr__ign,
3660+
struct task_struct *tsk)
3661+
{
3662+
return __bpf_dynptr_copy(dptr, off, size, (const void *)unsafe_ptr__ign,
3663+
copy_user_data_sleepable, tsk);
3664+
}
3665+
3666+
__bpf_kfunc int bpf_copy_from_user_task_str_dynptr(struct bpf_dynptr *dptr, u32 off,
3667+
u32 size, const void __user *unsafe_ptr__ign,
3668+
struct task_struct *tsk)
3669+
{
3670+
return __bpf_dynptr_copy_str(dptr, off, size, (const void *)unsafe_ptr__ign,
3671+
copy_user_str_sleepable, tsk);
3672+
}
3673+
34803674
__bpf_kfunc_end_defs();

tools/testing/selftests/bpf/DENYLIST

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# TEMPORARY
22
# Alphabetical order
3+
dynptr/test_probe_read_user_str_dynptr # disabled until https://patchwork.kernel.org/project/linux-mm/patch/[email protected]/ makes it into the bpf-next
34
get_stack_raw_tp # spams with kernel warnings until next bpf -> bpf-next merge
45
stacktrace_build_id
56
stacktrace_build_id_nmi

tools/testing/selftests/bpf/prog_tests/dynptr.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,19 @@ static struct {
3333
{"test_dynptr_skb_no_buff", SETUP_SKB_PROG},
3434
{"test_dynptr_skb_strcmp", SETUP_SKB_PROG},
3535
{"test_dynptr_skb_tp_btf", SETUP_SKB_PROG_TP},
36+
{"test_probe_read_user_dynptr", SETUP_XDP_PROG},
37+
{"test_probe_read_kernel_dynptr", SETUP_XDP_PROG},
38+
{"test_probe_read_user_str_dynptr", SETUP_XDP_PROG},
39+
{"test_probe_read_kernel_str_dynptr", SETUP_XDP_PROG},
40+
{"test_copy_from_user_dynptr", SETUP_SYSCALL_SLEEP},
41+
{"test_copy_from_user_str_dynptr", SETUP_SYSCALL_SLEEP},
42+
{"test_copy_from_user_task_dynptr", SETUP_SYSCALL_SLEEP},
43+
{"test_copy_from_user_task_str_dynptr", SETUP_SYSCALL_SLEEP},
3644
};
3745

3846
static void verify_success(const char *prog_name, enum test_setup_type setup_type)
3947
{
48+
char user_data[384] = {[0 ... 382] = 'a', '\0'};
4049
struct dynptr_success *skel;
4150
struct bpf_program *prog;
4251
struct bpf_link *link;
@@ -58,6 +67,10 @@ static void verify_success(const char *prog_name, enum test_setup_type setup_typ
5867
if (!ASSERT_OK(err, "dynptr_success__load"))
5968
goto cleanup;
6069

70+
skel->bss->user_ptr = user_data;
71+
skel->data->test_len[0] = sizeof(user_data);
72+
memcpy(skel->bss->expected_str, user_data, sizeof(user_data));
73+
6174
switch (setup_type) {
6275
case SETUP_SYSCALL_SLEEP:
6376
link = bpf_program__attach(prog);

0 commit comments

Comments
 (0)