Skip to content

Commit a5e8c07

Browse files
gianlucaborellodavem330
authored andcommitted
bpf: add bpf_probe_read_str helper
Provide a simple helper with the same semantics of strncpy_from_unsafe(): int bpf_probe_read_str(void *dst, int size, const void *unsafe_addr) This gives more flexibility to a bpf program. A typical use case is intercepting a file name during sys_open(). The current approach is: SEC("kprobe/sys_open") void bpf_sys_open(struct pt_regs *ctx) { char buf[PATHLEN]; // PATHLEN is defined to 256 bpf_probe_read(buf, sizeof(buf), ctx->di); /* consume buf */ } This is suboptimal because the size of the string needs to be estimated at compile time, causing more memory to be copied than often necessary, and can become more problematic if further processing on buf is done, for example by pushing it to userspace via bpf_perf_event_output(), since the real length of the string is unknown and the entire buffer must be copied (and defining an unrolled strnlen() inside the bpf program is a very inefficient and unfeasible approach). With the new helper, the code can easily operate on the actual string length rather than the buffer size: SEC("kprobe/sys_open") void bpf_sys_open(struct pt_regs *ctx) { char buf[PATHLEN]; // PATHLEN is defined to 256 int res = bpf_probe_read_str(buf, sizeof(buf), ctx->di); /* consume buf, for example push it to userspace via * bpf_perf_event_output(), but this time we can use * res (the string length) as event size, after checking * its boundaries. */ } Another useful use case is when parsing individual process arguments or individual environment variables navigating current->mm->arg_start and current->mm->env_start: using this helper and the return value, one can quickly iterate at the right offset of the memory area. The code changes simply leverage the already existent strncpy_from_unsafe() kernel function, which is safe to be called from a bpf program as it is used in bpf_trace_printk(). Signed-off-by: Gianluca Borello <[email protected]> Acked-by: Alexei Starovoitov <[email protected]> Acked-by: Daniel Borkmann <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 0760462 commit a5e8c07

File tree

2 files changed

+46
-1
lines changed

2 files changed

+46
-1
lines changed

include/uapi/linux/bpf.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,18 @@ union bpf_attr {
430430
* @xdp_md: pointer to xdp_md
431431
* @delta: An positive/negative integer to be added to xdp_md.data
432432
* Return: 0 on success or negative on error
433+
*
434+
* int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr)
435+
* Copy a NUL terminated string from unsafe address. In case the string
436+
* length is smaller than size, the target is not padded with further NUL
437+
* bytes. In case the string length is larger than size, just count-1
438+
* bytes are copied and the last byte is set to NUL.
439+
* @dst: destination address
440+
* @size: maximum number of bytes to copy, including the trailing NUL
441+
* @unsafe_ptr: unsafe address
442+
* Return:
443+
* > 0 length of the string including the trailing NUL on success
444+
* < 0 error
433445
*/
434446
#define __BPF_FUNC_MAPPER(FN) \
435447
FN(unspec), \
@@ -476,7 +488,8 @@ union bpf_attr {
476488
FN(set_hash_invalid), \
477489
FN(get_numa_node_id), \
478490
FN(skb_change_head), \
479-
FN(xdp_adjust_head),
491+
FN(xdp_adjust_head), \
492+
FN(probe_read_str),
480493

481494
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
482495
* function eBPF program intends to call

kernel/trace/bpf_trace.c

+32
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,36 @@ static const struct bpf_func_proto bpf_current_task_under_cgroup_proto = {
395395
.arg2_type = ARG_ANYTHING,
396396
};
397397

398+
BPF_CALL_3(bpf_probe_read_str, void *, dst, u32, size,
399+
const void *, unsafe_ptr)
400+
{
401+
int ret;
402+
403+
/*
404+
* The strncpy_from_unsafe() call will likely not fill the entire
405+
* buffer, but that's okay in this circumstance as we're probing
406+
* arbitrary memory anyway similar to bpf_probe_read() and might
407+
* as well probe the stack. Thus, memory is explicitly cleared
408+
* only in error case, so that improper users ignoring return
409+
* code altogether don't copy garbage; otherwise length of string
410+
* is returned that can be used for bpf_perf_event_output() et al.
411+
*/
412+
ret = strncpy_from_unsafe(dst, unsafe_ptr, size);
413+
if (unlikely(ret < 0))
414+
memset(dst, 0, size);
415+
416+
return ret;
417+
}
418+
419+
static const struct bpf_func_proto bpf_probe_read_str_proto = {
420+
.func = bpf_probe_read_str,
421+
.gpl_only = true,
422+
.ret_type = RET_INTEGER,
423+
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
424+
.arg2_type = ARG_CONST_SIZE,
425+
.arg3_type = ARG_ANYTHING,
426+
};
427+
398428
static const struct bpf_func_proto *tracing_func_proto(enum bpf_func_id func_id)
399429
{
400430
switch (func_id) {
@@ -432,6 +462,8 @@ static const struct bpf_func_proto *tracing_func_proto(enum bpf_func_id func_id)
432462
return &bpf_current_task_under_cgroup_proto;
433463
case BPF_FUNC_get_prandom_u32:
434464
return &bpf_get_prandom_u32_proto;
465+
case BPF_FUNC_probe_read_str:
466+
return &bpf_probe_read_str_proto;
435467
default:
436468
return NULL;
437469
}

0 commit comments

Comments
 (0)