Skip to content

Commit b8e3a87

Browse files
jordalgoanakryiko
authored andcommitted
bpf: Add crosstask check to __bpf_get_stack
Currently get_perf_callchain only supports user stack walking for the current task. Passing the correct *crosstask* param will return 0 frames if the task passed to __bpf_get_stack isn't the current one instead of a single incorrect frame/address. This change passes the correct *crosstask* param but also does a preemptive check in __bpf_get_stack if the task is current and returns -EOPNOTSUPP if it is not. This issue was found using bpf_get_task_stack inside a BPF iterator ("iter/task"), which iterates over all tasks. bpf_get_task_stack works fine for fetching kernel stacks but because get_perf_callchain relies on the caller to know if the requested *task* is the current one (via *crosstask*) it was failing in a confusing way. It might be possible to get user stacks for all tasks utilizing something like access_process_vm but that requires the bpf program calling bpf_get_task_stack to be sleepable and would therefore be a breaking change. Fixes: fa28dcb ("bpf: Introduce helper bpf_get_task_stack()") Signed-off-by: Jordan Rome <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 9241176 commit b8e3a87

File tree

3 files changed

+16
-1
lines changed

3 files changed

+16
-1
lines changed

include/uapi/linux/bpf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4517,6 +4517,8 @@ union bpf_attr {
45174517
* long bpf_get_task_stack(struct task_struct *task, void *buf, u32 size, u64 flags)
45184518
* Description
45194519
* Return a user or a kernel stack in bpf program provided buffer.
4520+
* Note: the user stack will only be populated if the *task* is
4521+
* the current task; all other tasks will return -EOPNOTSUPP.
45204522
* To achieve this, the helper needs *task*, which is a valid
45214523
* pointer to **struct task_struct**. To store the stacktrace, the
45224524
* bpf program provides *buf* with a nonnegative *size*.
@@ -4528,6 +4530,7 @@ union bpf_attr {
45284530
*
45294531
* **BPF_F_USER_STACK**
45304532
* Collect a user space stack instead of a kernel stack.
4533+
* The *task* must be the current task.
45314534
* **BPF_F_USER_BUILD_ID**
45324535
* Collect buildid+offset instead of ips for user stack,
45334536
* only valid if **BPF_F_USER_STACK** is also specified.

kernel/bpf/stackmap.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
388388
{
389389
u32 trace_nr, copy_len, elem_size, num_elem, max_depth;
390390
bool user_build_id = flags & BPF_F_USER_BUILD_ID;
391+
bool crosstask = task && task != current;
391392
u32 skip = flags & BPF_F_SKIP_FIELD_MASK;
392393
bool user = flags & BPF_F_USER_STACK;
393394
struct perf_callchain_entry *trace;
@@ -410,6 +411,14 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
410411
if (task && user && !user_mode(regs))
411412
goto err_fault;
412413

414+
/* get_perf_callchain does not support crosstask user stack walking
415+
* but returns an empty stack instead of NULL.
416+
*/
417+
if (crosstask && user) {
418+
err = -EOPNOTSUPP;
419+
goto clear;
420+
}
421+
413422
num_elem = size / elem_size;
414423
max_depth = num_elem + skip;
415424
if (sysctl_perf_event_max_stack < max_depth)
@@ -421,7 +430,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
421430
trace = get_callchain_entry_for_task(task, max_depth);
422431
else
423432
trace = get_perf_callchain(regs, 0, kernel, user, max_depth,
424-
false, false);
433+
crosstask, false);
425434
if (unlikely(!trace))
426435
goto err_fault;
427436

tools/include/uapi/linux/bpf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4517,6 +4517,8 @@ union bpf_attr {
45174517
* long bpf_get_task_stack(struct task_struct *task, void *buf, u32 size, u64 flags)
45184518
* Description
45194519
* Return a user or a kernel stack in bpf program provided buffer.
4520+
* Note: the user stack will only be populated if the *task* is
4521+
* the current task; all other tasks will return -EOPNOTSUPP.
45204522
* To achieve this, the helper needs *task*, which is a valid
45214523
* pointer to **struct task_struct**. To store the stacktrace, the
45224524
* bpf program provides *buf* with a nonnegative *size*.
@@ -4528,6 +4530,7 @@ union bpf_attr {
45284530
*
45294531
* **BPF_F_USER_STACK**
45304532
* Collect a user space stack instead of a kernel stack.
4533+
* The *task* must be the current task.
45314534
* **BPF_F_USER_BUILD_ID**
45324535
* Collect buildid+offset instead of ips for user stack,
45334536
* only valid if **BPF_F_USER_STACK** is also specified.

0 commit comments

Comments
 (0)