Commit b8e3a87a authored by Jordan Rome's avatar Jordan Rome Committed by Andrii Nakryiko
Browse files

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: fa28dcb8 ("bpf: Introduce helper bpf_get_task_stack()")
Signed-off-by: default avatarJordan Rome <jordalgo@meta.com>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20231108112334.3433136-1-jordalgo@meta.com
parent 92411764
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -4517,6 +4517,8 @@ union bpf_attr {
 * long bpf_get_task_stack(struct task_struct *task, void *buf, u32 size, u64 flags)
 *	Description
 *		Return a user or a kernel stack in bpf program provided buffer.
 *		Note: the user stack will only be populated if the *task* is
 *		the current task; all other tasks will return -EOPNOTSUPP.
 *		To achieve this, the helper needs *task*, which is a valid
 *		pointer to **struct task_struct**. To store the stacktrace, the
 *		bpf program provides *buf* with a nonnegative *size*.
@@ -4528,6 +4530,7 @@ union bpf_attr {
 *
 *		**BPF_F_USER_STACK**
 *			Collect a user space stack instead of a kernel stack.
 *			The *task* must be the current task.
 *		**BPF_F_USER_BUILD_ID**
 *			Collect buildid+offset instead of ips for user stack,
 *			only valid if **BPF_F_USER_STACK** is also specified.
+10 −1
Original line number Diff line number Diff line
@@ -388,6 +388,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
{
	u32 trace_nr, copy_len, elem_size, num_elem, max_depth;
	bool user_build_id = flags & BPF_F_USER_BUILD_ID;
	bool crosstask = task && task != current;
	u32 skip = flags & BPF_F_SKIP_FIELD_MASK;
	bool user = flags & BPF_F_USER_STACK;
	struct perf_callchain_entry *trace;
@@ -410,6 +411,14 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
	if (task && user && !user_mode(regs))
		goto err_fault;

	/* get_perf_callchain does not support crosstask user stack walking
	 * but returns an empty stack instead of NULL.
	 */
	if (crosstask && user) {
		err = -EOPNOTSUPP;
		goto clear;
	}

	num_elem = size / elem_size;
	max_depth = num_elem + skip;
	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,
		trace = get_callchain_entry_for_task(task, max_depth);
	else
		trace = get_perf_callchain(regs, 0, kernel, user, max_depth,
					   false, false);
					   crosstask, false);
	if (unlikely(!trace))
		goto err_fault;

+3 −0
Original line number Diff line number Diff line
@@ -4517,6 +4517,8 @@ union bpf_attr {
 * long bpf_get_task_stack(struct task_struct *task, void *buf, u32 size, u64 flags)
 *	Description
 *		Return a user or a kernel stack in bpf program provided buffer.
 *		Note: the user stack will only be populated if the *task* is
 *		the current task; all other tasks will return -EOPNOTSUPP.
 *		To achieve this, the helper needs *task*, which is a valid
 *		pointer to **struct task_struct**. To store the stacktrace, the
 *		bpf program provides *buf* with a nonnegative *size*.
@@ -4528,6 +4530,7 @@ union bpf_attr {
 *
 *		**BPF_F_USER_STACK**
 *			Collect a user space stack instead of a kernel stack.
 *			The *task* must be the current task.
 *		**BPF_F_USER_BUILD_ID**
 *			Collect buildid+offset instead of ips for user stack,
 *			only valid if **BPF_F_USER_STACK** is also specified.