Skip to content

Commit 5e32d0f

Browse files
committed
unwind_user/deferred: Add unwind_user_faultable()
Add a new API to retrieve a user space callstack called unwind_user_faultable(). The difference between this user space stack tracer from the current user space stack tracer is that this must be called from faultable context as it may use routines to access user space data that needs to be faulted in. It can be safely called from entering or exiting a system call as the code can still be faulted in there. This code is based on work by Josh Poimboeuf's deferred unwinding code: Link: https://lore.kernel.org/all/6052e8487746603bdb29b65f4033e739092d9925.1737511963.git.jpoimboe@kernel.org/ Cc: Masami Hiramatsu <[email protected]> Cc: Mathieu Desnoyers <[email protected]> Cc: Josh Poimboeuf <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Arnaldo Carvalho de Melo <[email protected]> Cc: Namhyung Kim <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Andrii Nakryiko <[email protected]> Cc: Indu Bhagat <[email protected]> Cc: "Jose E. Marchesi" <[email protected]> Cc: Beau Belgrave <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Jens Axboe <[email protected]> Cc: Florian Weimer <[email protected]> Cc: Sam James <[email protected]> Link: https://lore.kernel.org/[email protected] Reviewed-by: Jens Remus <[email protected]> Signed-off-by: Steven Rostedt (Google) <[email protected]>
1 parent 71753c6 commit 5e32d0f

File tree

6 files changed

+103
-1
lines changed

6 files changed

+103
-1
lines changed

include/linux/sched.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include <linux/rv.h>
4747
#include <linux/uidgid_types.h>
4848
#include <linux/tracepoint-defs.h>
49+
#include <linux/unwind_deferred_types.h>
4950
#include <asm/kmap_size.h>
5051

5152
/* task_struct member predeclarations (sorted alphabetically): */
@@ -1654,6 +1655,10 @@ struct task_struct {
16541655
struct user_event_mm *user_event_mm;
16551656
#endif
16561657

1658+
#ifdef CONFIG_UNWIND_USER
1659+
struct unwind_task_info unwind_info;
1660+
#endif
1661+
16571662
/* CPU-specific state of this task: */
16581663
struct thread_struct thread;
16591664

include/linux/unwind_deferred.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _LINUX_UNWIND_USER_DEFERRED_H
3+
#define _LINUX_UNWIND_USER_DEFERRED_H
4+
5+
#include <linux/unwind_user.h>
6+
#include <linux/unwind_deferred_types.h>
7+
8+
#ifdef CONFIG_UNWIND_USER
9+
10+
void unwind_task_init(struct task_struct *task);
11+
void unwind_task_free(struct task_struct *task);
12+
13+
int unwind_user_faultable(struct unwind_stacktrace *trace);
14+
15+
#else /* !CONFIG_UNWIND_USER */
16+
17+
static inline void unwind_task_init(struct task_struct *task) {}
18+
static inline void unwind_task_free(struct task_struct *task) {}
19+
20+
static inline int unwind_user_faultable(struct unwind_stacktrace *trace) { return -ENOSYS; }
21+
22+
#endif /* !CONFIG_UNWIND_USER */
23+
24+
#endif /* _LINUX_UNWIND_USER_DEFERRED_H */

include/linux/unwind_deferred_types.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _LINUX_UNWIND_USER_DEFERRED_TYPES_H
3+
#define _LINUX_UNWIND_USER_DEFERRED_TYPES_H
4+
5+
struct unwind_task_info {
6+
unsigned long *entries;
7+
};
8+
9+
#endif /* _LINUX_UNWIND_USER_DEFERRED_TYPES_H */

kernel/fork.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
#include <uapi/linux/pidfd.h>
106106
#include <linux/pidfs.h>
107107
#include <linux/tick.h>
108+
#include <linux/unwind_deferred.h>
108109

109110
#include <asm/pgalloc.h>
110111
#include <linux/uaccess.h>
@@ -732,6 +733,7 @@ void __put_task_struct(struct task_struct *tsk)
732733
WARN_ON(refcount_read(&tsk->usage));
733734
WARN_ON(tsk == current);
734735

736+
unwind_task_free(tsk);
735737
sched_ext_free(tsk);
736738
io_uring_free(tsk);
737739
cgroup_free(tsk);
@@ -2135,6 +2137,8 @@ __latent_entropy struct task_struct *copy_process(
21352137
p->bpf_ctx = NULL;
21362138
#endif
21372139

2140+
unwind_task_init(p);
2141+
21382142
/* Perform scheduler related setup. Assign this task to a CPU. */
21392143
retval = sched_fork(clone_flags, p);
21402144
if (retval)

kernel/unwind/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
obj-$(CONFIG_UNWIND_USER) += user.o
1+
obj-$(CONFIG_UNWIND_USER) += user.o deferred.o

kernel/unwind/deferred.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Deferred user space unwinding
4+
*/
5+
#include <linux/kernel.h>
6+
#include <linux/sched.h>
7+
#include <linux/slab.h>
8+
#include <linux/unwind_deferred.h>
9+
10+
#define UNWIND_MAX_ENTRIES 512
11+
12+
/**
13+
* unwind_user_faultable - Produce a user stacktrace in faultable context
14+
* @trace: The descriptor that will store the user stacktrace
15+
*
16+
* This must be called in a known faultable context (usually when entering
17+
* or exiting user space). Depending on the available implementations
18+
* the @trace will be loaded with the addresses of the user space stacktrace
19+
* if it can be found.
20+
*
21+
* Return: 0 on success and negative on error
22+
* On success @trace will contain the user space stacktrace
23+
*/
24+
int unwind_user_faultable(struct unwind_stacktrace *trace)
25+
{
26+
struct unwind_task_info *info = &current->unwind_info;
27+
28+
/* Should always be called from faultable context */
29+
might_fault();
30+
31+
if (current->flags & PF_EXITING)
32+
return -EINVAL;
33+
34+
if (!info->entries) {
35+
info->entries = kmalloc_array(UNWIND_MAX_ENTRIES, sizeof(long),
36+
GFP_KERNEL);
37+
if (!info->entries)
38+
return -ENOMEM;
39+
}
40+
41+
trace->nr = 0;
42+
trace->entries = info->entries;
43+
unwind_user(trace, UNWIND_MAX_ENTRIES);
44+
45+
return 0;
46+
}
47+
48+
void unwind_task_init(struct task_struct *task)
49+
{
50+
struct unwind_task_info *info = &task->unwind_info;
51+
52+
memset(info, 0, sizeof(*info));
53+
}
54+
55+
void unwind_task_free(struct task_struct *task)
56+
{
57+
struct unwind_task_info *info = &task->unwind_info;
58+
59+
kfree(info->entries);
60+
}

0 commit comments

Comments
 (0)