mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-05-24 10:39:52 +00:00
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: fa28dcb82a
("bpf: Introduce helper bpf_get_task_stack()")
Signed-off-by: Jordan Rome <jordalgo@meta.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20231108112334.3433136-1-jordalgo@meta.com
This commit is contained in:
parent
92411764e3
commit
b8e3a87a62
3 changed files with 16 additions and 1 deletions
|
@ -4517,6 +4517,8 @@ union bpf_attr {
|
||||||
* long bpf_get_task_stack(struct task_struct *task, void *buf, u32 size, u64 flags)
|
* long bpf_get_task_stack(struct task_struct *task, void *buf, u32 size, u64 flags)
|
||||||
* Description
|
* Description
|
||||||
* Return a user or a kernel stack in bpf program provided buffer.
|
* 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
|
* To achieve this, the helper needs *task*, which is a valid
|
||||||
* pointer to **struct task_struct**. To store the stacktrace, the
|
* pointer to **struct task_struct**. To store the stacktrace, the
|
||||||
* bpf program provides *buf* with a nonnegative *size*.
|
* bpf program provides *buf* with a nonnegative *size*.
|
||||||
|
@ -4528,6 +4530,7 @@ union bpf_attr {
|
||||||
*
|
*
|
||||||
* **BPF_F_USER_STACK**
|
* **BPF_F_USER_STACK**
|
||||||
* Collect a user space stack instead of a kernel stack.
|
* Collect a user space stack instead of a kernel stack.
|
||||||
|
* The *task* must be the current task.
|
||||||
* **BPF_F_USER_BUILD_ID**
|
* **BPF_F_USER_BUILD_ID**
|
||||||
* Collect buildid+offset instead of ips for user stack,
|
* Collect buildid+offset instead of ips for user stack,
|
||||||
* only valid if **BPF_F_USER_STACK** is also specified.
|
* only valid if **BPF_F_USER_STACK** is also specified.
|
||||||
|
|
|
@ -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;
|
u32 trace_nr, copy_len, elem_size, num_elem, max_depth;
|
||||||
bool user_build_id = flags & BPF_F_USER_BUILD_ID;
|
bool user_build_id = flags & BPF_F_USER_BUILD_ID;
|
||||||
|
bool crosstask = task && task != current;
|
||||||
u32 skip = flags & BPF_F_SKIP_FIELD_MASK;
|
u32 skip = flags & BPF_F_SKIP_FIELD_MASK;
|
||||||
bool user = flags & BPF_F_USER_STACK;
|
bool user = flags & BPF_F_USER_STACK;
|
||||||
struct perf_callchain_entry *trace;
|
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))
|
if (task && user && !user_mode(regs))
|
||||||
goto err_fault;
|
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;
|
num_elem = size / elem_size;
|
||||||
max_depth = num_elem + skip;
|
max_depth = num_elem + skip;
|
||||||
if (sysctl_perf_event_max_stack < max_depth)
|
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);
|
trace = get_callchain_entry_for_task(task, max_depth);
|
||||||
else
|
else
|
||||||
trace = get_perf_callchain(regs, 0, kernel, user, max_depth,
|
trace = get_perf_callchain(regs, 0, kernel, user, max_depth,
|
||||||
false, false);
|
crosstask, false);
|
||||||
if (unlikely(!trace))
|
if (unlikely(!trace))
|
||||||
goto err_fault;
|
goto err_fault;
|
||||||
|
|
||||||
|
|
|
@ -4517,6 +4517,8 @@ union bpf_attr {
|
||||||
* long bpf_get_task_stack(struct task_struct *task, void *buf, u32 size, u64 flags)
|
* long bpf_get_task_stack(struct task_struct *task, void *buf, u32 size, u64 flags)
|
||||||
* Description
|
* Description
|
||||||
* Return a user or a kernel stack in bpf program provided buffer.
|
* 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
|
* To achieve this, the helper needs *task*, which is a valid
|
||||||
* pointer to **struct task_struct**. To store the stacktrace, the
|
* pointer to **struct task_struct**. To store the stacktrace, the
|
||||||
* bpf program provides *buf* with a nonnegative *size*.
|
* bpf program provides *buf* with a nonnegative *size*.
|
||||||
|
@ -4528,6 +4530,7 @@ union bpf_attr {
|
||||||
*
|
*
|
||||||
* **BPF_F_USER_STACK**
|
* **BPF_F_USER_STACK**
|
||||||
* Collect a user space stack instead of a kernel stack.
|
* Collect a user space stack instead of a kernel stack.
|
||||||
|
* The *task* must be the current task.
|
||||||
* **BPF_F_USER_BUILD_ID**
|
* **BPF_F_USER_BUILD_ID**
|
||||||
* Collect buildid+offset instead of ips for user stack,
|
* Collect buildid+offset instead of ips for user stack,
|
||||||
* only valid if **BPF_F_USER_STACK** is also specified.
|
* only valid if **BPF_F_USER_STACK** is also specified.
|
||||||
|
|
Loading…
Add table
Reference in a new issue