unwind_deferred: Implement sframe handling
Subsystems (9)
v10 cover unwind_deferred: Implement sframe handling
[
This version is simply a rebase of v9 on top of the v6.17-rc3. It needs to be updated to work with the latest SFrame specification. Indu said she'll be able to make those changes, but I needed to forward port the latest code.
You can test this code with the x86 and perf changes applied at:
git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace.git
unwind/sframe-test
]
This is the implementation of parsing the SFrame section in an ELF file. It's a continuation of Josh's last work that can be found here:
https://lore.kernel.org/all/[email protected]/
Currently the only way to get a user space stack trace from a stack walk (and not just copying large amount of user stack into the kernel ring buffer) is to use frame pointers. This has a few issues. The biggest one is that compiling frame pointers into every application and library has been shown to cause performance overhead.
Another issue is that the format of the frames may not always be consistent between different compilers and some architectures (s390) has no defined format to do a reliable stack walk. The only way to perform user space profiling on these architectures is to copy the user stack into the kernel buffer.
SFrames[1] is now supported in gcc binutils and soon will also be supported by LLVM. SFrames acts more like ORC, and lives in the ELF executable file as its own section. Like ORC it has two tables where the first table is sorted by instruction pointers (IP) and using the current IP and finding it's entry in the first table, it will take you to the second table which will tell you where the return address of the current function is located and then you can use that address to look it up in the first table to find the return address of that function, and so on. This performs a user space stack walk.
Now because the SFrame section lives in the ELF file it needs to be faulted into memory when it is used. This means that walking the user space stack requires being in a faultable context. As profilers like perf request a stack trace in interrupt or NMI context, it cannot do the walking when it is requested. Instead it must be deferred until it is safe to fault in user space. One place this is known to be safe is when the task is about to return back to user space.
This series makes the deferred unwind code implement SFrames.
[1] https://sourceware.org/binutils/wiki/sframe
Changes since v9: https://lore.kernel.org/linux-trace-kernel/[email protected]/
- Rebased on v6.17-rc3
- Update the changes to unwind/user.c to handle passing a const unwind_user_frame pointer.
Josh Poimboeuf (11):
unwind_user/sframe: Add support for reading .sframe headers unwind_user/sframe: Store sframe section data in per-mm maple tree x86/uaccess: Add unsafe_copy_from_user() implementation unwind_user/sframe: Add support for reading .sframe contents unwind_user/sframe: Detect .sframe sections in executables unwind_user/sframe: Wire up unwind_user to sframe unwind_user/sframe/x86: Enable sframe unwinding on x86 unwind_user/sframe: Remove .sframe section on detected corruption unwind_user/sframe: Show file name in debug output unwind_user/sframe: Add .sframe validation option unwind_user/sframe: Add prctl() interface for registering .sframe sections
----
MAINTAINERS | 1 + arch/Kconfig | 23 ++ arch/x86/Kconfig | 1 + arch/x86/include/asm/mmu.h | 2 +- arch/x86/include/asm/uaccess.h | 39 ++- fs/binfmt_elf.c | 49 +++- include/linux/mm_types.h | 3 + include/linux/sframe.h | 60 ++++ include/linux/unwind_user_types.h | 4 +- include/uapi/linux/elf.h | 1 + include/uapi/linux/prctl.h | 6 +- kernel/fork.c | 10 + kernel/sys.c | 9 + kernel/unwind/Makefile | 3 +- kernel/unwind/sframe.c | 593 ++++++++++++++++++++++++++++++++++++++ kernel/unwind/sframe.h | 71 +++++ kernel/unwind/sframe_debug.h | 68 +++++ kernel/unwind/user.c | 41 ++- mm/init-mm.c | 2 + 19 files changed, 967 insertions(+), 19 deletions(-) create mode 100644 include/linux/sframe.h create mode 100644 kernel/unwind/sframe.c create mode 100644 kernel/unwind/sframe.h create mode 100644 kernel/unwind/sframe_debug.h
PATCH v10 1/11 unwind_user/sframe: Add support for reading .sframe headers
From: Josh Poimboeuf <[email protected]>
In preparation for unwinding user space stacks with sframe, add basic sframe compile infrastructure and support for reading the .sframe section header.
sframe_add_section() reads the header and unconditionally returns an error, so it's not very useful yet. A subsequent patch will improve that.
--- MAINTAINERS | 1 + arch/Kconfig | 3 + include/linux/sframe.h | 40 ++++++++++++ kernel/unwind/Makefile | 3 +- kernel/unwind/sframe.c | 136 +++++++++++++++++++++++++++++++++++++++++ kernel/unwind/sframe.h | 71 +++++++++++++++++++++ 6 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 include/linux/sframe.h create mode 100644 kernel/unwind/sframe.c create mode 100644 kernel/unwind/sframe.h
MAINTAINERS +1 -0
@@ -26333,6 +26333,7 @@ USERSPACE STACK UNWINDING M: Josh Poimboeuf <[email protected]> M: Steven Rostedt <[email protected]> S: Maintained +F: include/linux/sframe.h F: include/linux/unwind*.h F: kernel/unwind/
arch/Kconfig +3 -0
@@ -451,6 +451,9 @@ config HAVE_UNWIND_USER_FP bool select UNWIND_USER +config HAVE_UNWIND_USER_SFRAME + bool + config HAVE_PERF_REGS bool help
include/linux/sframe.h new file +40 -0
@@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_SFRAME_H +#define _LINUX_SFRAME_H + +#include <linux/mm_types.h> +#include <linux/unwind_user_types.h> + +#ifdef CONFIG_HAVE_UNWIND_USER_SFRAME + +struct sframe_section { +unsigned long sframe_start; +unsigned long sframe_end; +unsigned long text_start; +unsigned long text_end; + +unsigned long fdes_start; +unsigned long fres_start; +unsigned long fres_end; +unsigned int num_fdes; + +signed char ra_off; +signed char fp_off; +}; + +extern int sframe_add_section (unsigned long sframe_start,unsigned long sframe_end, +unsigned long text_start,unsigned long text_end); +extern int sframe_remove_section (unsigned long sframe_addr); + +#else /* !CONFIG_HAVE_UNWIND_USER_SFRAME */ + +static inline int sframe_add_section (unsigned long sframe_start,unsigned long sframe_end, +unsigned long text_start,unsigned long text_end) +{ +return -ENOSYS ; +} +static inline int sframe_remove_section (unsigned long sframe_addr) {return -ENOSYS ; } + +#endif /* CONFIG_HAVE_UNWIND_USER_SFRAME */ + +#endif /* _LINUX_SFRAME_H */
kernel/unwind/Makefile +2 -1
@@ -1 +1,2 @@ - obj-$(CONFIG_UNWIND_USER) += user.o deferred.o + obj-$(CONFIG_UNWIND_USER) += user.o deferred.o + obj-$(CONFIG_HAVE_UNWIND_USER_SFRAME) += sframe.o
kernel/unwind/sframe.c new file +136 -0
136 lines not on this page · raw message
kernel/unwind/sframe.h new file +71 -0
71 lines not on this page · raw message
Signature · 1 line
2.50.1
PATCH v10 2/11 unwind_user/sframe: Store sframe section data in per-mm maple tree
In reply to Steven RostedtFrom: Josh Poimboeuf <[email protected]>
Associate an sframe section with its mm by adding it to a per-mm maple tree which is indexed by the corresponding text address range. A single sframe section can be associated with multiple text ranges.
--- arch/x86/include/asm/mmu.h | 2 +- include/linux/mm_types.h | 3 +++ include/linux/sframe.h | 13 +++++++++ kernel/fork.c | 10 +++++++ kernel/unwind/sframe.c | 55 +++++++++++++++++++++++++++++++++++--- mm/init-mm.c | 2 ++ 6 files changed, 81 insertions(+), 4 deletions(-)
arch/x86/include/asm/mmu.h +1 -1
@@ -87,7 +87,7 @@ typedef struct { .context = { \ .ctx_id = 1, \ .lock = __MUTEX_INITIALIZER(mm.context.lock), \ - } + },void leave_mm (void );#define leave_mm leave_mm
include/linux/mm_types.h +3 -0
@@ -1210,6 +1210,9 @@ struct mm_struct {#ifdef CONFIG_MM_ID mm_id_t mm_id;#endif /* CONFIG_MM_ID */ +#ifdef CONFIG_HAVE_UNWIND_USER_SFRAME +struct maple_tree sframe_mt; +#endif } __randomize_layout;/*
include/linux/sframe.h +13 -0
@@ -22,18 +22,31 @@ struct sframe_section {signed char fp_off; }; +#define INIT_MM_SFRAME .sframe_mt = MTREE_INIT(sframe_mt, 0), +extern void sframe_free_mm (struct mm_struct *mm); +extern int sframe_add_section (unsigned long sframe_start,unsigned long sframe_end,unsigned long text_start,unsigned long text_end);extern int sframe_remove_section (unsigned long sframe_addr); +static inline bool current_has_sframe (void ) +{ +struct mm_struct *mm = current->mm; + +return mm && !mtree_empty (&mm->sframe_mt); +} +#else /* !CONFIG_HAVE_UNWIND_USER_SFRAME */ +#define INIT_MM_SFRAME +static inline void sframe_free_mm (struct mm_struct *mm) {}static inline int sframe_add_section (unsigned long sframe_start,unsigned long sframe_end,unsigned long text_start,unsigned long text_end) {return -ENOSYS ; }static inline int sframe_remove_section (unsigned long sframe_addr) {return -ENOSYS ; } +static inline bool current_has_sframe (void ) {return false; }#endif /* CONFIG_HAVE_UNWIND_USER_SFRAME */
kernel/fork.c +10 -0
@@ -106,6 +106,7 @@#include <linux/pidfs.h> #include <linux/tick.h> #include <linux/unwind_deferred.h> +#include <linux/sframe.h> #include <asm/pgalloc.h> #include <linux/uaccess.h>
@@ -690,6 +691,7 @@ void __mmdrop(struct mm_struct *mm)mm_destroy_cid (mm);percpu_counter_destroy_many (mm->rss_stat,NR_MM_COUNTERS );futex_hash_free (mm); +sframe_free_mm (mm);free_mm (mm); }
@@ -1027,6 +1029,13 @@ static void mmap_init_lock(struct mm_struct *mm)#endif } +static void mm_init_sframe (struct mm_struct *mm) +{ +#ifdef CONFIG_HAVE_UNWIND_USER_SFRAME +mt_init (&mm->sframe_mt); +#endif +} +static struct mm_struct *mm_init (struct mm_struct *mm,struct task_struct *p,struct user_namespace *user_ns) {
@@ -1055,6 +1064,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, mm->pmd_huge_pte =NULL ;#endif mm_init_uprobes_state (mm); +mm_init_sframe (mm);hugetlb_count_init (mm);if (current->mm) {
kernel/unwind/sframe.c +52 -3
67 lines not on this page · raw message
mm/init-mm.c +2 -0
@@ -11,6 +11,7 @@#include <linux/atomic.h> #include <linux/user_namespace.h> #include <linux/iommu.h> +#include <linux/sframe.h> #include <asm/mmu.h> #ifndef INIT_MM_CONTEXT
@@ -46,6 +47,7 @@ struct mm_struct init_mm = { .user_ns = &init_user_ns, .cpu_bitmap =CPU_BITS_NONE ,INIT_MM_CONTEXT (init_mm) + INIT_MM_SFRAME }; void setup_initial_init_mm(void *start_code, void *end_code,
Signature · 1 line
2.50.1
Re: [PATCH v10 02/11] unwind_user/sframe: Store sframe section data in per-mm maple tree
Quoted 179 lines · Steven Rostedt
From: Josh Poimboeuf <[email protected]>
Associate an sframe section with its mm by adding it to a per-mm maple tree which is indexed by the corresponding text address range. A single sframe section can be associated with multiple text ranges.
Cc Thomas Gleixner <[email protected]>Cc Ingo Molnar <[email protected]>Cc Borislav Petkov <[email protected]>Cc Dave Hansen <[email protected]>Cc "H. Peter Anvin" <[email protected]>Cc Andrew Morton <[email protected]>Cc David Hildenbrand <[email protected]>Cc Lorenzo Stoakes <[email protected]>Cc "Liam R. Howlett" <[email protected]>Cc Vlastimil Babka <[email protected]>Cc Mike Rapoport <[email protected]>Cc Suren Baghdasaryan <[email protected]>Cc Michal Hocko <[email protected]>Signed-off-by Josh Poimboeuf <[email protected]>Signed-off-by Steven Rostedt (Google) <[email protected]>--- arch/x86/include/asm/mmu.h | 2 +- include/linux/mm_types.h | 3 +++ include/linux/sframe.h | 13 +++++++++ kernel/fork.c | 10 +++++++ kernel/unwind/sframe.c | 55 +++++++++++++++++++++++++++++++++++--- mm/init-mm.c | 2 ++ 6 files changed, 81 insertions(+), 4 deletions(-)arch/x86/include/asm/mmu.h +1
-1@@ -87,7 +87,7 @@ typedef struct { .context = { \ .ctx_id = 1, \ .lock = __MUTEX_INITIALIZER(mm.context.lock), \ - } + },void leave_mm (void );#define leave_mm leave_mminclude/linux/mm_types.h +3
-0@@ -1210,6 +1210,9 @@ struct mm_struct {#ifdef CONFIG_MM_ID mm_id_t mm_id;#endif /* CONFIG_MM_ID */ +#ifdef CONFIG_HAVE_UNWIND_USER_SFRAME +struct maple_tree sframe_mt; +#endif } __randomize_layout;/* include/linux/sframe.h +13
-0@@ -22,18 +22,31 @@ struct sframe_section {signed char fp_off; }; +#define INIT_MM_SFRAME .sframe_mt = MTREE_INIT(sframe_mt, 0), +extern void sframe_free_mm (struct mm_struct *mm); +extern int sframe_add_section (unsigned long sframe_start,unsigned long sframe_end,unsigned long text_start,unsigned long text_end);extern int sframe_remove_section (unsigned long sframe_addr); +static inline bool current_has_sframe (void ) +{ +struct mm_struct *mm = current->mm; + +return mm && !mtree_empty (&mm->sframe_mt); +} +#else /* !CONFIG_HAVE_UNWIND_USER_SFRAME */ +#define INIT_MM_SFRAME +static inline void sframe_free_mm (struct mm_struct *mm) {}static inline int sframe_add_section (unsigned long sframe_start,unsigned long sframe_end,unsigned long text_start,unsigned long text_end) {return -ENOSYS ; }static inline int sframe_remove_section (unsigned long sframe_addr) {return -ENOSYS ; } +static inline bool current_has_sframe (void ) {return false; }#endif /* CONFIG_HAVE_UNWIND_USER_SFRAME */ kernel/fork.c +10
-0@@ -106,6 +106,7 @@#include <linux/pidfs.h> #include <linux/tick.h> #include <linux/unwind_deferred.h> +#include <linux/sframe.h> #include <asm/pgalloc.h> #include <linux/uaccess.h> @@ -690,6 +691,7 @@ void __mmdrop(struct mm_struct *mm)mm_destroy_cid (mm);percpu_counter_destroy_many (mm->rss_stat,NR_MM_COUNTERS );futex_hash_free (mm); +sframe_free_mm (mm);free_mm (mm); }@@ -1027,6 +1029,13 @@ static void mmap_init_lock(struct mm_struct *mm)#endif } +static void mm_init_sframe (struct mm_struct *mm) +{ +#ifdef CONFIG_HAVE_UNWIND_USER_SFRAME +mt_init (&mm->sframe_mt); +#endif +} +static struct mm_struct *mm_init (struct mm_struct *mm,struct task_struct *p,struct user_namespace *user_ns) {@@ -1055,6 +1064,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, mm->pmd_huge_pte =NULL ;#endif mm_init_uprobes_state (mm); +mm_init_sframe (mm);hugetlb_count_init (mm);if (current->mm) {kernel/unwind/sframe.c +23
-3@@ -122,15 +122,64 @@ int sframe_add_section(unsigned long sframe_start, unsigned long sframe_end,if (ret) goto err_free; -/* TODO nowhere to store it yet - just free it and return an error */ - ret = -ENOSYS ; + ret =mtree_insert_range (sframe_mt, sec->text_start, sec->text_end, sec,GFP_KERNEL ); +if (ret) { +dbg ("mtree_insert_range failed: text=%lx-%lx\n" , + sec->text_start, sec->text_end); + goto err_free; + } + +return 0 ; err_free:free_section (sec);return ret; } +static int __sframe_remove_section (struct mm_struct *mm, +struct sframe_section *sec) +{ +if (!mtree_erase (&mm->sframe_mt, sec->text_start)) { +dbg ("mtree_erase failed: text=%lx\n" , sec->text_start); +return -EINVAL ; + } + +free_section (sec); + +return 0 ; +} +int sframe_remove_section (unsigned long sframe_start) { -return -ENOSYS ; +struct mm_struct *mm = current->mm; +struct sframe_section *sec;
kernel/unwind/sframe.c int sframe_add_section(unsigned long sframe_start, unsigned long sframe_end, · line 165
Quoted 10 lines
kernel/unwind/sframe.c +10
-0+unsigned long index =0 ; +bool found = false; +int ret =0 ; + +mt_for_each (&mm->sframe_mt, sec, index,ULONG_MAX ) { +if (sec->sframe_start == sframe_start) { + found = true; + ret |=__sframe_remove_section (mm, sec); + } + }
If you use the advanced interface you have to handle the locking, but it will be faster. I'm not sure how frequent you loop across many entries, but you can do something like:
MA_SATE(mas, &mm->sframe_mt, index, index);
mas_lock(&mas);mas_for_each(&mas, sec, ULONG_MAX) {
...
}mas_unlock(&mas);
The maple state contains memory addresses of internal nodes, so you cannot just edit the tree without it being either unlocked (which negates the gains you would have) or by using it in the modification.
This seems like a good choice considering the __sframe_remove_section() is called from only one place. You can pass the struct ma_state through to the remove function and use it with mas_erase().
Actually, reading it again, why are you starting a search at 0? And why are you deleting everything after the sframe_start to ULONG_MAX? This seems incorrect. Can you explain your plan a bit here?
Quoted 9 lines
kernel/unwind/sframe.c +9
-0+ +if (!found || ret) +return -EINVAL ; + +return 0 ; +} + +void sframe_free_mm (struct mm_struct *mm) +{
kernel/unwind/sframe.c int sframe_add_section(unsigned long sframe_start, unsigned long sframe_end, · line 184
Quoted 10 lines
kernel/unwind/sframe.c +10
-0+struct sframe_section *sec; +unsigned long index =0 ; + +if (!mm) +return ; + +mt_for_each (&mm->sframe_mt, sec, index,ULONG_MAX ) +free_section (sec); + +mtree_destroy (&mm->sframe_mt);
The same goes for this function. mt_for_each will start at the top of the tree, lock, find your result, unlock. Each search starts from the top of tree because it was unlocked. In the mas_ functions, the tree is iterated in place which can be significantly faster depending on the tree size.
Since you are not going to edit the tree you can use a maple state:
struct sframe_section *sec;MA_STATE(mas, &mm->sframe_mt, 0, 0);
mas_lock(&mas);mas_for_each(&mas, sec, ULONG_MAX)
free_section(sec);
mas_unlock(&mas);mtree_destroy(&mm->sframe_mt);
Quoted 25 lines
kernel/unwind/sframe.c +0
-0}mm/init-mm.c +2
-0@@ -11,6 +11,7 @@#include <linux/atomic.h> #include <linux/user_namespace.h> #include <linux/iommu.h> +#include <linux/sframe.h> #include <asm/mmu.h> #ifndef INIT_MM_CONTEXT @@ -46,6 +47,7 @@ struct mm_struct init_mm = { .user_ns = &init_user_ns, .cpu_bitmap =CPU_BITS_NONE ,INIT_MM_CONTEXT (init_mm) + INIT_MM_SFRAME }; void setup_initial_init_mm(void *start_code, void *end_code,Signature · 1 line
2.50.1
Steven Rostedt
maintainerRe: [PATCH v10 02/11] unwind_user/sframe: Store sframe section data in per-mm maple tree
Quoted 7 lines · On Wed, 27 Aug 2025 21:46:01 -0400 "Liam R. Howlett" <[email protected]>…
Quoted 7 lines
int
sframe_remove_section(unsigned long sframe_start){kernel/unwind/sframe.c +4
-1-return -ENOSYS ; +struct mm_struct *mm = current->mm; +struct sframe_section *sec; +unsigned long index =0 ; +bool found = false;
Quoted 9 lines
kernel/unwind/sframe.c int sframe_add_section(unsigned long sframe_start, unsigned long sframe_end, · line 165
Quoted 8 lines
kernel/unwind/sframe.c +8
-0+int ret =0 ; + +mt_for_each (&mm->sframe_mt, sec, index,ULONG_MAX ) { +if (sec->sframe_start == sframe_start) { + found = true; + ret |=__sframe_remove_section (mm, sec); + } + }
Josh should be able to answer this better than I can, as he wrote it, and I'm not too familiar with how to use maple tree (reading the documentation now).
Quoted 16 lines
If you use the advanced interface you have to handle the locking, but it will be faster. I'm not sure how frequent you loop across many entries, but you can do something like:
MA_SATE(mas, &mm->sframe_mt, index, index);
mas_lock(&mas);mas_for_each(&mas, sec, ULONG_MAX){
...
}mas_unlock(&mas);The maple state contains memory addresses of internal nodes, so you cannot just edit the tree without it being either unlocked (which negates the gains you would have) or by using it in the modification.
Quoted 7 lines
This seems like a good choice considering the
__sframe_remove_section()is called from only one place. You can pass the structma_statethrough to the remove function and use it withmas_erase().Actually, reading it again, why are you starting a search at 0? And why are you deleting everything after the
sframe_starttoULONG_MAX? This seems incorrect. Can you explain your plan a bit here?
Let me give a brief overview of how and why maple trees are used for sframes:
The sframe section is mapped to the user space address from the elf file when the application starts. The dynamic library loader could also do a system call to tell the kernel where the sframe is for some dynamically loaded code. Since there can be more than one text section that has an sframe associated to it, the mm->sframe_mt is used to hold the range of text to find its corresponding sframe section. That is, there's one sframe section for the code that was loaded during exec(), and then there may be a separate sframe section for every library that is loaded. Note, it is possible that the same sframe section may cover more than one range of text.
When doing stack walking, the instruction pointer is used as the key in the maple tree to find its corresponding sframe section.
Now, if the sframe is determined to be corrupted, it must be removed from the current->mm->sframe_mt. It also gets removed when the dynamic loader removes some text from the application that has the code.
I'm guessing that the 0 to ULONG_MAX is to simply find and remove all the associated sframe sections, as there may be more than one text range that a single sframe section covers.
Does this make sense?
Thanks for reviewing!
-- Steve
Quoted 21 lines
Quoted 20 lines
kernel/unwind/sframe.c +19
-0+ +if (!found || ret) +return -EINVAL ; + +return 0 ; +} + +void sframe_free_mm (struct mm_struct *mm) +{ +struct sframe_section *sec; +unsigned long index =0 ; + +if (!mm) +return ; + +mt_for_each (&mm->sframe_mt, sec, index,ULONG_MAX ) +free_section (sec); + +mtree_destroy (&mm->sframe_mt);
Re: [PATCH v10 02/11] unwind_user/sframe: Store sframe section data in per-mm maple tree
Quoted 68 lines · Steven Rostedt
Quoted 7 lines · On Wed, 27 Aug 2025 21:46:01 -0400 "Liam R. Howlett" <[email protected]>…
Quoted 7 lines
int
sframe_remove_section(unsigned long sframe_start){kernel/unwind/sframe.c +4
-1-return -ENOSYS ; +struct mm_struct *mm = current->mm; +struct sframe_section *sec; +unsigned long index =0 ; +bool found = false;Quoted 9 lines
kernel/unwind/sframe.c int sframe_add_section(unsigned long sframe_start, unsigned long sframe_end, · line 165
Quoted 8 lines
kernel/unwind/sframe.c +8
-0+int ret =0 ; + +mt_for_each (&mm->sframe_mt, sec, index,ULONG_MAX ) { +if (sec->sframe_start == sframe_start) { + found = true; + ret |=__sframe_remove_section (mm, sec); + } + }Josh should be able to answer this better than I can, as he wrote it, and I'm not too familiar with how to use maple tree (reading the documentation now).
Quoted 16 lines
If you use the advanced interface you have to handle the locking, but it will be faster. I'm not sure how frequent you loop across many entries, but you can do something like:
MA_SATE(mas, &mm->sframe_mt, index, index);
mas_lock(&mas);mas_for_each(&mas, sec, ULONG_MAX){
...
}mas_unlock(&mas);The maple state contains memory addresses of internal nodes, so you cannot just edit the tree without it being either unlocked (which negates the gains you would have) or by using it in the modification.
Quoted 7 lines
This seems like a good choice considering the
__sframe_remove_section()is called from only one place. You can pass the structma_statethrough to the remove function and use it withmas_erase().Actually, reading it again, why are you starting a search at 0? And why are you deleting everything after the
sframe_starttoULONG_MAX? This seems incorrect. Can you explain your plan a bit here?Let me give a brief overview of how and why maple trees are used for sframes:
The sframe section is mapped to the user space address from the elf file when the application starts. The dynamic library loader could also do a system call to tell the kernel where the sframe is for some dynamically loaded code. Since there can be more than one text section that has an sframe associated to it, the
mm->sframe_mtis used to hold the range of text to find its corresponding sframe section. That is, there's one sframe section for the code that was loaded duringexec(), and then there may be a separate sframe section for every library that is loaded. Note, it is possible that the same sframe section may cover more than one range of text.When doing stack walking, the instruction pointer is used as the key in the maple tree to find its corresponding sframe section.
Now, if the sframe is determined to be corrupted, it must be removed from the
current->mm->sframe_mt. It also gets removed when the dynamic loader removes some text from the application that has the code.
Quoted 6 lines
I'm guessing that the 0 to
ULONG_MAXis to simply find and remove all the associated sframe sections, as there may be more than one text range that a single sframe section covers.Does this make sense?
Perhaps it's the corruption part that I'm missing here. If the sframe is corrupt, you are iterating over all elements and checking the start address passed in against the section start.
So if the section is corrupted then how can we depend on the sec->sframe_start?
And is the maple tree corrupted? I mean, the mappings to sframe_start -> sec is still reliable, right?
Looking at the storing code, you store text_start - text_end to sec, presumably the text_start cannot be smaller than the sframe_start?
Thanks,
Liam
Steven Rostedt
maintainerRe: [PATCH v10 02/11] unwind_user/sframe: Store sframe section data in per-mm maple tree
Quoted 7 lines · On Thu, 28 Aug 2025 11:27:00 -0400 "Liam R. Howlett" <[email protected]>…
Quoted 2 lines
Does this make sense?
Perhaps it's the corruption part that I'm missing here. If the sframe is corrupt, you are iterating over all elements and checking the start address passed in against the section start.
Quoted 8 lines
So if the section is corrupted then how can we depend on the
sec->sframe_start?And is the maple tree corrupted? I mean, the mappings to
sframe_start-> sec is still reliable, right?Looking at the storing code, you store
text_start-text_endto sec, presumably thetext_startcannot be smaller than thesframe_start?
Sorry, that's not what gets corrupted. I should have expanded on it.
The sframe section is two tables that describe how to get the return address from text locations, much like how ORC works in the kernel. We get a start and end address of where the sframe exists (that has the two tables) and a start and end section of the text it represents.
When I said "corrupted", I meant that the sframe tables are totally created by user space and can not be trusted. While reading the sframe tables, if there's any anomaly that is found, it is considered "corrupted". So no, the start and end of where the sframes are and where the text should be validated at the start (I need to check that we do ;-).
But once we start reading the sframe tables, they could hold garbage, or have something in there that the kernel doesn't support. As soon as that is detected, it gets removed so that it isn't looked at again.
-- Steve
PATCH v10 3/11 x86/uaccess: Add unsafe_copy_from_user() implementation
In reply to Steven RostedtFrom: Josh Poimboeuf <[email protected]>
Add an x86 implementation of unsafe_copy_from_user() similar to the existing unsafe_copy_to_user().
--- arch/x86/include/asm/uaccess.h | 39 +++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 10 deletions(-)
arch/x86/include/asm/uaccess.h +29 -10
@@ -599,7 +599,7 @@ _label: \* We want the unsafe accessors to always be inlined and use * the error labels - thus the macro games. */ -#define unsafe_copy_loop (dst, src, len, type, label) \ +#define unsafe_copy_to_user_loop (dst, src, len, type, label) \ while (len >= sizeof(type)) { \ unsafe_put_user(*(type *)(src),(type __user *)(dst),label); \ dst += sizeof(type); \
@@ -607,15 +607,34 @@ _label: \ len -=sizeof (type); \ } -#define unsafe_copy_to_user (_dst,_src,_len,label) \ -do { \ - char __user *__ucu_dst = (_dst); \ - const char *__ucu_src = (_src); \ - size_t __ucu_len = (_len); \ - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \ - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \ - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \ - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \ +#define unsafe_copy_to_user (_dst, _src, _len, label) \ +do { \ + void __user *__dst = (_dst); \ + const void *__src = (_src); \ + size_t __len = (_len); \ + unsafe_copy_to_user_loop(__dst, __src, __len, u64, label); \ + unsafe_copy_to_user_loop(__dst, __src, __len, u32, label); \ + unsafe_copy_to_user_loop(__dst, __src, __len, u16, label); \ + unsafe_copy_to_user_loop(__dst, __src, __len, u8, label); \ +} while (0) + +#define unsafe_copy_from_user_loop (dst, src, len, type, label) \ + while (len >= sizeof(type)) { \ + unsafe_get_user(*(type *)(dst), (type __user *)(src), label); \ + dst += sizeof(type); \ + src += sizeof(type); \ + len -= sizeof(type); \ + } + +#define unsafe_copy_from_user (_dst, _src, _len, label) \ +do { \ + void *__dst = (_dst); \ + void __user *__src = (_src); \ + size_t __len = (_len); \ + unsafe_copy_from_user_loop(__dst, __src, __len, u64, label); \ + unsafe_copy_from_user_loop(__dst, __src, __len, u32, label); \ + unsafe_copy_from_user_loop(__dst, __src, __len, u16, label); \ + unsafe_copy_from_user_loop(__dst, __src, __len, u8, label); \ } while (0)#ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT
Signature · 1 line
2.50.1
PATCH v10 4/11 unwind_user/sframe: Add support for reading .sframe contents
In reply to Steven RostedtFrom: Josh Poimboeuf <[email protected]>
In preparation for using sframe to unwind user space stacks, add an sframe_find() interface for finding the sframe information associated with a given text address.
For performance, use user_read_access_begin() and the corresponding unsafe_*() accessors. Note that use of pr_debug() in uaccess-enabled regions would break noinstr validation, so there aren't any debug messages yet. That will be added in a subsequent commit.
--- include/linux/sframe.h | 5 + kernel/unwind/sframe.c | 311 ++++++++++++++++++++++++++++++++++- kernel/unwind/sframe_debug.h | 35 ++++ 3 files changed, 347 insertions(+), 4 deletions(-) create mode 100644 kernel/unwind/sframe_debug.h
include/linux/sframe.h +5 -0
@@ -3,11 +3,14 @@#define _LINUX_SFRAME_H#include <linux/mm_types.h> +#include <linux/srcu.h> #include <linux/unwind_user_types.h> #ifdef CONFIG_HAVE_UNWIND_USER_SFRAME struct sframe_section { + struct rcu_head rcu; + unsigned long sframe_start; unsigned long sframe_end; unsigned long text_start;
@@ -28,6 +31,7 @@ extern void sframe_free_mm(struct mm_struct *mm);extern int sframe_add_section (unsigned long sframe_start,unsigned long sframe_end,unsigned long text_start,unsigned long text_end);extern int sframe_remove_section (unsigned long sframe_addr); +extern int sframe_find (unsigned long ip,struct unwind_user_frame *frame);static inline bool current_has_sframe (void ) {
@@ -46,6 +50,7 @@ static inline int sframe_add_section(unsigned long sframe_start, unsigned long s return -ENOSYS; }static inline int sframe_remove_section (unsigned long sframe_addr) {return -ENOSYS ; } +static inline int sframe_find (unsigned long ip,struct unwind_user_frame *frame) {return -ENOSYS ; }static inline bool current_has_sframe (void ) {return false; }#endif /* CONFIG_HAVE_UNWIND_USER_SFRAME */
kernel/unwind/sframe.c +307 -4
337 lines not on this page · raw message
kernel/unwind/sframe_debug.h new file +35 -0
@@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _SFRAME_DEBUG_H +#define _SFRAME_DEBUG_H + +#include <linux/sframe.h> +#include "sframe.h" + +#ifdef CONFIG_DYNAMIC_DEBUG + +#define dbg (fmt, ...) \ + pr_debug("%s (%d): " fmt, current->comm, current->pid, ##__VA_ARGS__) + +static __always_inline void dbg_print_header(struct sframe_section *sec) +{ +unsigned long fdes_end; + + fdes_end = sec->fdes_start + (sec->num_fdes *sizeof (struct sframe_fde )); + +dbg ("SEC: sframe:0x%lx-0x%lx text:0x%lx-0x%lx " +"fdes:0x%lx-0x%lx fres:0x%lx-0x%lx " +"ra_off:%d fp_off:%d\n" , + sec->sframe_start, sec->sframe_end, sec->text_start, sec->text_end, + sec->fdes_start, fdes_end, sec->fres_start, sec->fres_end, + sec->ra_off, sec->fp_off); +} + +#else /* !CONFIG_DYNAMIC_DEBUG */ + +#define dbg(args...) no_printk(args) + +static inline void dbg_print_header (struct sframe_section *sec) {} + +#endif /* !CONFIG_DYNAMIC_DEBUG */ + +#endif /* _SFRAME_DEBUG_H */
Signature · 1 line
2.50.1
PATCH v10 5/11 unwind_user/sframe: Detect .sframe sections in executables
In reply to Steven RostedtFrom: Josh Poimboeuf <[email protected]>
When loading an ELF executable, automatically detect an .sframe section and associate it with the mm_struct.
--- fs/binfmt_elf.c | 49 +++++++++++++++++++++++++++++++++++++--- include/uapi/linux/elf.h | 1 + 2 files changed, 47 insertions(+), 3 deletions(-)
fs/binfmt_elf.c +46 -3
@@ -47,6 +47,7 @@#include <linux/dax.h> #include <linux/uaccess.h> #include <linux/rseq.h> +#include <linux/sframe.h> #include <asm/param.h> #include <asm/page.h>
@@ -622,6 +623,21 @@ static inline int make_prot(u32 p_flags, struct arch_elf_state *arch_state,return arch_elf_adjust_prot (prot, arch_state, has_interp, is_interp); } +static void elf_add_sframe (struct elf_phdr *text,struct elf_phdr *sframe, +unsigned long base_addr) +{ +unsigned long sframe_start, sframe_end, text_start, text_end; + + sframe_start = base_addr + sframe->p_vaddr; + sframe_end = sframe_start + sframe->p_memsz; + + text_start = base_addr + text->p_vaddr; + text_end = text_start + text->p_memsz; + +/* Ignore return value, sframe section isn't critical */ +sframe_add_section (sframe_start, sframe_end, text_start, text_end); +} +/* This is much more generalized than the library routine read function, so we keep this separate. Technically the library read function is only provided so that we can read a.out libraries that have
@@ -632,7 +648,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, unsigned long no_base, struct elf_phdr *interp_elf_phdata, struct arch_elf_state *arch_state) { - struct elf_phdr *eppnt; + struct elf_phdr *eppnt, *sframe_phdr = NULL;unsigned long load_addr =0 ;int load_addr_set =0 ;unsigned long error = ~0UL ;
@@ -658,7 +674,8 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, eppnt = interp_elf_phdata;for (i =0 ; i < interp_elf_ex->e_phnum; i++, eppnt++) { -if (eppnt->p_type ==PT_LOAD ) { +switch (eppnt->p_type) { +case PT_LOAD : {int elf_type =MAP_PRIVATE ;int elf_prot =make_prot (eppnt->p_flags, arch_state, true, true);
@@ -697,6 +714,20 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, error = -ENOMEM ; goto out; } +break ; + } +case PT_GNU_SFRAME : + sframe_phdr = eppnt; +break ; + } + } + +if (sframe_phdr) { + eppnt = interp_elf_phdata; +for (i =0 ; i < interp_elf_ex->e_phnum; i++, eppnt++) { +if (eppnt->p_flags &PF_X ) { +elf_add_sframe (eppnt, sframe_phdr, load_addr); + } } }
@@ -821,7 +852,7 @@ static int load_elf_binary(struct linux_binprm *bprm)int first_pt_load =1 ;unsigned long error;struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata =NULL ; -struct elf_phdr *elf_property_phdata =NULL ; +struct elf_phdr *elf_property_phdata =NULL , *sframe_phdr =NULL ;unsigned long elf_brk;bool brk_moved = false;int retval, i;
@@ -930,6 +961,10 @@ static int load_elf_binary(struct linux_binprm *bprm) executable_stack =EXSTACK_DISABLE_X ;break ; +case PT_GNU_SFRAME : + sframe_phdr = elf_ppnt; +break ; + case PT_LOPROC ... PT_HIPROC: retval = arch_elf_pt_proc(elf_ex, elf_ppnt, bprm->file, false,
@@ -1227,6 +1262,14 @@ static int load_elf_binary(struct linux_binprm *bprm) elf_brk = k; } +if (sframe_phdr) { +for (i =0 , elf_ppnt = elf_phdata; + i < elf_ex->e_phnum; i++, elf_ppnt++) { +if ((elf_ppnt->p_flags &PF_X )) +elf_add_sframe (elf_ppnt, sframe_phdr, load_bias); + } + } + e_entry = elf_ex->e_entry + load_bias; phdr_addr += load_bias; elf_brk += load_bias;
include/uapi/linux/elf.h +1 -0
@@ -41,6 +41,7 @@ typedef __u16 Elf64_Versym;#define PT_GNU_STACK (PT_LOOS + 0x474e551)#define PT_GNU_RELRO (PT_LOOS + 0x474e552)#define PT_GNU_PROPERTY (PT_LOOS + 0x474e553) +#define PT_GNU_SFRAME (PT_LOOS + 0x474e554)/* ARM MTE memory tag segment type */
Signature · 1 line
2.50.1
PATCH v10 6/11 unwind_user/sframe: Wire up unwind_user to sframe
In reply to Steven RostedtFrom: Josh Poimboeuf <[email protected]>
Now that the sframe infrastructure is fully in place, make it work by hooking it up to the unwind_user interface.
---
Changes since v9: https://lore.kernel.org/[email protected]
- Update the changes to unwind/user.c to handle passing a const unwind_user_frame pointer.
arch/Kconfig | 1 + include/linux/unwind_user_types.h | 4 ++- kernel/unwind/user.c | 41 +++++++++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 3 deletions(-)
arch/Kconfig +1 -0
@@ -453,6 +453,7 @@ config HAVE_UNWIND_USER_FP config HAVE_UNWIND_USER_SFRAME bool + select UNWIND_USER config HAVE_PERF_REGS bool
include/linux/unwind_user_types.h +3 -1
@@ -9,7 +9,8 @@* available. */ enum unwind_user_type_bits { -UNWIND_USER_TYPE_FP_BIT =0 , +UNWIND_USER_TYPE_SFRAME_BIT =0 , +UNWIND_USER_TYPE_FP_BIT =1 ,NR_UNWIND_USER_TYPE_BITS , };
@@ -17,6 +18,7 @@ enum unwind_user_type_bits {enum unwind_user_type {/* Type "none" for the start of stack walk iteration. */ UNWIND_USER_TYPE_NONE =0 , +UNWIND_USER_TYPE_SFRAME =BIT (UNWIND_USER_TYPE_SFRAME_BIT ),UNWIND_USER_TYPE_FP =BIT (UNWIND_USER_TYPE_FP_BIT ), };
kernel/unwind/user.c +39 -2
74 lines not on this page · raw message
Signature · 1 line
2.50.1
PATCH v10 7/11 unwind_user/sframe/x86: Enable sframe unwinding on x86
In reply to Steven RostedtFrom: Josh Poimboeuf <[email protected]>
The x86 sframe 2.0 implementation works fairly well, starting with binutils 2.41 (though some bugs are getting fixed in later versions). Enable it.
--- arch/x86/Kconfig | 1 + 1 file changed, 1 insertion(+)
arch/x86/Kconfig +1 -0
@@ -300,6 +300,7 @@ config X86 select HAVE_UACCESS_VALIDATION if HAVE_OBJTOOL select HAVE_UNSTABLE_SCHED_CLOCK select HAVE_UNWIND_USER_FP if X86_64 + select HAVE_UNWIND_USER_SFRAME if X86_64 select HAVE_USER_RETURN_NOTIFIER select HAVE_GENERIC_VDSO select VDSO_GETRANDOM if X86_64
Signature · 1 line
2.50.1
PATCH v10 8/11 unwind_user/sframe: Remove .sframe section on detected corruption
In reply to Steven RostedtFrom: Josh Poimboeuf <[email protected]>
To avoid continued attempted use of a bad .sframe section, remove it on demand when the first sign of corruption is detected.
--- kernel/unwind/sframe.c | 4 ++++ 1 file changed, 4 insertions(+)
kernel/unwind/sframe.c +4 -0
@@ -310,6 +310,10 @@ int sframe_find(unsigned long ip, struct unwind_user_frame *frame) ret =__find_fre (sec, &fde, ip, frame); end:user_read_access_end (); + +if (ret == -EFAULT ) +WARN_ON_ONCE (sframe_remove_section (sec->sframe_start)); +return ret; }
Signature · 1 line
2.50.1
PATCH v10 9/11 unwind_user/sframe: Show file name in debug output
In reply to Steven RostedtFrom: Josh Poimboeuf <[email protected]>
When debugging sframe issues, the error messages aren't all that helpful without knowing what file a corresponding .sframe section belongs to. Prefix debug output strings with the file name.
--- include/linux/sframe.h | 4 +++- kernel/unwind/sframe.c | 23 ++++++++++-------- kernel/unwind/sframe_debug.h | 45 +++++++++++++++++++++++++++++++----- 3 files changed, 56 insertions(+), 16 deletions(-)
include/linux/sframe.h +3 -1
@@ -10,7 +10,9 @@struct sframe_section {struct rcu_head rcu; - +#ifdef CONFIG_DYNAMIC_DEBUG +const char *filename; +#endif unsigned long sframe_start;unsigned long sframe_end;unsigned long text_start;
kernel/unwind/sframe.c +14 -9
@@ -311,14 +311,17 @@ int sframe_find(unsigned long ip, struct unwind_user_frame *frame) end:user_read_access_end (); -if (ret == -EFAULT ) +if (ret == -EFAULT ) { +dbg_sec ("removing bad .sframe section\n" );WARN_ON_ONCE (sframe_remove_section (sec->sframe_start)); + }return ret; }static void free_section (struct sframe_section *sec) { +dbg_free (sec);kfree (sec); }
@@ -329,7 +332,7 @@ static int sframe_read_header(struct sframe_section *sec)unsigned int num_fdes;if (copy_from_user (&shdr, (void __user *)sec->sframe_start,sizeof (shdr))) { -dbg ("header usercopy failed\n" ); +dbg_sec ("header usercopy failed\n" );return -EFAULT ; }
@@ -337,18 +340,18 @@ static int sframe_read_header(struct sframe_section *sec) shdr.preamble.version != SFRAME_VERSION_2 || !(shdr.preamble.flags & SFRAME_F_FDE_SORTED) || shdr.auxhdr_len) { -dbg ("bad/unsupported sframe header\n" ); +dbg_sec ("bad/unsupported sframe header\n" );return -EINVAL ; }if (!shdr.num_fdes || !shdr.num_fres) { -dbg ("no fde/fre entries\n" ); +dbg_sec ("no fde/fre entries\n" );return -EINVAL ; } header_end = sec->sframe_start +SFRAME_HEADER_SIZE (shdr);if (header_end >= sec->sframe_end) { -dbg ("header doesn't fit in section\n" ); +dbg_sec ("header doesn't fit in section\n" );return -EINVAL ; }
@@ -360,7 +363,7 @@ static int sframe_read_header(struct sframe_section *sec) fres_end = fres_start + shdr.fre_len;if (fres_start < fdes_end || fres_end > sec->sframe_end) { -dbg ("inconsistent fde/fre offsets\n" ); +dbg_sec ("inconsistent fde/fre offsets\n" );return -EINVAL ; }
@@ -416,6 +419,8 @@ int sframe_add_section(unsigned long sframe_start, unsigned long sframe_end, sec->text_start = text_start; sec->text_end = text_end; +dbg_init (sec); + ret =sframe_read_header (sec);if (ret) {dbg_print_header (sec);
@@ -424,8 +429,8 @@ int sframe_add_section(unsigned long sframe_start, unsigned long sframe_end, ret =mtree_insert_range (sframe_mt, sec->text_start, sec->text_end, sec,GFP_KERNEL );if (ret) { -dbg ("mtree_insert_range failed: text=%lx-%lx\n" , - sec->text_start, sec->text_end); +dbg_sec ("mtree_insert_range failed: text=%lx-%lx\n" , + sec->text_start, sec->text_end); goto err_free; }
@@ -447,7 +452,7 @@ static int __sframe_remove_section(struct mm_struct *mm, struct sframe_section *sec) {if (!mtree_erase (&mm->sframe_mt, sec->text_start)) { -dbg ("mtree_erase failed: text=%lx\n" , sec->text_start); +dbg_sec ("mtree_erase failed: text=%lx\n" , sec->text_start);return -EINVAL ; }
kernel/unwind/sframe_debug.h +39 -6
65 lines not on this page · raw message
Signature · 1 line
2.50.1
PATCH v10 10/11 unwind_user/sframe: Add .sframe validation option
In reply to Steven RostedtFrom: Josh Poimboeuf <[email protected]>
Add a debug feature to validate all .sframe sections when first loading the file rather than on demand.
--- arch/Kconfig | 19 +++++++++ kernel/unwind/sframe.c | 96 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+)
arch/Kconfig +19 -0
@@ -455,6 +455,25 @@ config HAVE_UNWIND_USER_SFRAME bool select UNWIND_USER +config SFRAME_VALIDATION + bool "Enable .sframe section debugging" + depends on HAVE_UNWIND_USER_SFRAME + depends on DYNAMIC_DEBUG + help + When adding an .sframe section for a task, validate the entire + section immediately rather than on demand. + + This is a debug feature which is helpful for rooting out .sframe + section issues. If the .sframe section is corrupt, it will fail to + load immediately, with more information provided in dynamic printks. + + This has a significant page cache footprint due to its reading of the + entire .sframe section for every loaded executable and shared + library. Also, it's done for all processes, even those which don't + get stack traced by the kernel. Not recommended for general use. + + If unsure, say N. + config HAVE_PERF_REGS bool help
kernel/unwind/sframe.c +96 -0
108 lines not on this page · raw message
Signature · 1 line
2.50.1
PATCH v10 11/11 unwind_user/sframe: Add prctl() interface for registering .sframe sections
In reply to Steven RostedtFrom: Josh Poimboeuf <[email protected]>
The kernel doesn't have direct visibility to the ELF contents of shared libraries. Add some prctl() interfaces which allow glibc to tell the kernel where to find .sframe sections.
[
This adds an interface for prctl() for testing loading of sframes for libraries. But this interface should really be a system call. This patch is for testing purposes only and should not be applied to mainline.
]
--- include/uapi/linux/prctl.h | 6 +++++- kernel/sys.c | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-)
include/uapi/linux/prctl.h +5 -1
@@ -358,7 +358,7 @@ struct prctl_mm_map {* configuration. All bits may be locked via this call, including * undefined bits. */ -#define PR_LOCK_SHADOW_STACK_STATUS 76 +#define PR_LOCK_SHADOW_STACK_STATUS 76/* * Controls the mode of timer_create() for CRIU restore operations.
@@ -376,4 +376,8 @@ struct prctl_mm_map {# define PR_FUTEX_HASH_SET_SLOTS 1# define PR_FUTEX_HASH_GET_SLOTS 2 +/* SFRAME management */ +#define PR_ADD_SFRAME 79 +#define PR_REMOVE_SFRAME 80 +#endif /* _LINUX_PRCTL_H */
kernel/sys.c +9 -0
@@ -65,6 +65,7 @@#include <linux/rcupdate.h> #include <linux/uidgid.h> #include <linux/cred.h> +#include <linux/sframe.h> #include <linux/nospec.h>
@@ -2805,6 +2806,14 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,case PR_FUTEX_HASH : error =futex_hash_prctl (arg2, arg3, arg4);break ; +case PR_ADD_SFRAME : + error =sframe_add_section (arg2, arg3, arg4, arg5); +break ; +case PR_REMOVE_SFRAME : +if (arg3 || arg4 || arg5) +return -EINVAL ; + error =sframe_remove_section (arg2); +break ;default :trace_task_prctl_unknown (option, arg2, arg3, arg4, arg5); error = -EINVAL ;
Signature · 1 line
2.50.1