From: Jens Remus <jremus@linux.ibm.com>
To: linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org,
linux-s390@vger.kernel.org, x86@kernel.org,
Steven Rostedt <rostedt@kernel.org>,
Josh Poimboeuf <jpoimboe@kernel.org>,
Peter Zijlstra <peterz@infradead.org>,
Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Jens Remus <jremus@linux.ibm.com>,
Heiko Carstens <hca@linux.ibm.com>,
Vasily Gorbik <gor@linux.ibm.com>,
Alexander Gordeev <agordeev@linux.ibm.com>,
Ilya Leoshkevich <iii@linux.ibm.com>,
Indu Bhagat <ibhagatgnu@gmail.com>,
Thomas Gleixner <tglx@kernel.org>, Ingo Molnar <mingo@redhat.com>,
Borislav Petkov <bp@alien8.de>,
Dave Hansen <dave.hansen@linux.intel.com>,
"H. Peter Anvin" <hpa@zytor.com>,
Namhyung Kim <namhyung@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>, Kees Cook <kees@kernel.org>,
Sam James <sam@gentoo.org>, Fangrui Song <i@maskray.me>
Subject: [RFC PATCH v2 08/24] unwind_user/eh_frame: Store .eh_frame_hdr section data in per-mm maple tree
Date: Fri, 21 Aug 2026 21:52:43 +0200 [thread overview]
Message-ID: <20260821195259.2688377-9-jremus@linux.ibm.com> (raw)
In-Reply-To: <20260821195259.2688377-1-jremus@linux.ibm.com>
Associate .eh_frame_hdr sections with their mm by adding them to a
per-mm maple tree which is indexed by the corresponding text address
range. A single .eh_frame_hdr section can be associated with multiple
text ranges.
Based on Josh Poimboeuf's, Steven Rostedt's, and my unwind user sframe
implementation.
Signed-off-by: Jens Remus <jremus@linux.ibm.com>
---
Notes (jremus):
Changes in RFC v2:
- INIT_MM_EH_FRAME and mm_init_eh_frame(): Use MT_FLAGS_USE_RCU to
enable concurrent eh_frame_mt read and write access. (Sashiko AI)
- eh_frame_remove_section(): Fix ABA problem by using the advanced
maple tree API to hold the internal spinlock while iterating over
the eh_frame_mt and removing the eh_frame section(s). (Sashiko AI)
arch/x86/include/asm/mmu.h | 2 +-
include/linux/eh_frame.h | 22 ++++++++++++
include/linux/mm_types.h | 3 ++
kernel/fork.c | 10 ++++++
kernel/unwind/eh_frame.c | 70 ++++++++++++++++++++++++++++++++++++--
mm/init-mm.c | 2 ++
6 files changed, 105 insertions(+), 4 deletions(-)
diff --git a/arch/x86/include/asm/mmu.h b/arch/x86/include/asm/mmu.h
index 0fe9c569d171..227a32899a59 100644
--- a/arch/x86/include/asm/mmu.h
+++ b/arch/x86/include/asm/mmu.h
@@ -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
diff --git a/include/linux/eh_frame.h b/include/linux/eh_frame.h
index aaac2dd663d5..c4279ee77c13 100644
--- a/include/linux/eh_frame.h
+++ b/include/linux/eh_frame.h
@@ -2,9 +2,14 @@
#ifndef _LINUX_EH_FRAME_H
#define _LINUX_EH_FRAME_H
+#include <linux/mm_types.h>
+#include <linux/srcu.h>
+
#ifdef CONFIG_HAVE_UNWIND_USER_EH_FRAME
struct eh_frame_section {
+ struct rcu_head rcu;
+
unsigned long eh_frame_hdr_start;
unsigned long eh_frame_hdr_end;
unsigned long text_start;
@@ -19,14 +24,29 @@ struct eh_frame_section {
u8 binary_search_table_enc;
};
+#define EH_FRAME_MT_FLAGS (MT_FLAGS_USE_RCU)
+
+#define INIT_MM_EH_FRAME .eh_frame_mt = MTREE_INIT(eh_frame_mt, EH_FRAME_MT_FLAGS),
+extern void eh_frame_free_mm(struct mm_struct *mm);
+
extern int eh_frame_add_section(unsigned long eh_frame_hdr_start,
unsigned long eh_frame_hdr_end,
unsigned long text_start,
unsigned long text_end);
extern int eh_frame_remove_section(unsigned long eh_frame_hdr_start);
+static inline bool current_has_eh_frame(void)
+{
+ struct mm_struct *mm = current->mm;
+
+ return mm && !mtree_empty(&mm->eh_frame_mt);
+}
+
#else /* !CONFIG_HAVE_UNWIND_USER_EH_FRAME */
+#define INIT_MM_EH_FRAME
+static inline void eh_frame_free_mm(struct mm_struct *mm) {}
+
static inline int eh_frame_add_section(unsigned long eh_frame_hdr_start,
unsigned long eh_frame_hdr_end,
unsigned long text_start,
@@ -40,6 +60,8 @@ static inline int eh_frame_remove_section(unsigned long eh_frame_hdr_start)
return -ENOSYS;
}
+static inline bool current_has_eh_frame(void) { return false; }
+
#endif /* CONFIG_HAVE_UNWIND_USER_EH_FRAME */
#endif /* _LINUX_EH_FRAME_H */
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index b18c2b2e7d2c..ac51d6212e86 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -1404,6 +1404,9 @@ struct mm_struct {
#ifdef CONFIG_MM_ID
mm_id_t mm_id;
#endif /* CONFIG_MM_ID */
+#ifdef CONFIG_HAVE_UNWIND_USER_EH_FRAME
+ struct maple_tree eh_frame_mt;
+#endif
} __randomize_layout;
/*
diff --git a/kernel/fork.c b/kernel/fork.c
index f0e2e131a9a5..dab67d2eb096 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -111,6 +111,7 @@
#include <linux/tick.h>
#include <linux/unwind_deferred.h>
#include <linux/pgalloc.h>
+#include <linux/eh_frame.h>
#include <linux/uaccess.h>
#include <asm/mmu_context.h>
@@ -738,6 +739,7 @@ void __mmdrop(struct mm_struct *mm)
mm_pasid_drop(mm);
mm_destroy_cid(mm);
percpu_counter_destroy_many(mm->rss_stat, NR_MM_COUNTERS);
+ eh_frame_free_mm(mm);
free_mm(mm);
}
@@ -1082,6 +1084,13 @@ static void mmap_init_lock(struct mm_struct *mm)
#endif
}
+static void mm_init_eh_frame(struct mm_struct *mm)
+{
+#ifdef CONFIG_HAVE_UNWIND_USER_EH_FRAME
+ mt_init_flags(&mm->eh_frame_mt, EH_FRAME_MT_FLAGS);
+#endif
+}
+
static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
{
mt_init_flags(&mm->mm_mt, MM_MT_FLAGS);
@@ -1109,6 +1118,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_eh_frame(mm);
hugetlb_count_init(mm);
futex_mm_init(mm);
diff --git a/kernel/unwind/eh_frame.c b/kernel/unwind/eh_frame.c
index f25a0923e315..8cf19c5298fd 100644
--- a/kernel/unwind/eh_frame.c
+++ b/kernel/unwind/eh_frame.c
@@ -19,6 +19,8 @@
#define dbg(fmt, ...) \
pr_debug("%s (%d): " fmt, current->comm, current->pid, ##__VA_ARGS__)
+DEFINE_STATIC_SRCU(eh_frame_srcu);
+
#define UNSAFE_GET_USER_INC(to, from, end, label) \
({ \
typeof(to) __to; \
@@ -310,6 +312,7 @@ int eh_frame_add_section(unsigned long eh_frame_hdr_start,
unsigned long text_start,
unsigned long text_end)
{
+ struct maple_tree *eh_frame_mt = ¤t->mm->eh_frame_mt;
struct mm_struct *mm = current->mm;
struct eh_frame_section *sec;
int ret;
@@ -352,15 +355,76 @@ int eh_frame_add_section(unsigned long eh_frame_hdr_start,
if (ret)
goto err_free;
- /* TODO nowhere to store it yet - just free it and return an error */
- ret = -ENOSYS;
+ ret = mtree_insert_range(eh_frame_mt, sec->text_start, sec->text_end - 1,
+ sec, GFP_KERNEL_ACCOUNT);
+ 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 void eh_frame_free_srcu(struct rcu_head *rcu)
+{
+ struct eh_frame_section *sec = container_of(rcu, struct eh_frame_section, rcu);
+
+ free_section(sec);
+}
+
+static int __eh_frame_remove_section(struct ma_state *mas,
+ struct eh_frame_section *sec)
+{
+ if (mas_erase(mas) != sec) {
+ dbg("mas_erase failed: text=%lx\n", sec->text_start);
+ return -EINVAL;
+ }
+
+ call_srcu(&eh_frame_srcu, &sec->rcu, eh_frame_free_srcu);
+
+ return 0;
+}
+
int eh_frame_remove_section(unsigned long eh_frame_hdr_start)
{
- return -ENOSYS;
+ struct mm_struct *mm = current->mm;
+ struct eh_frame_section *sec;
+ MA_STATE(mas, &mm->eh_frame_mt, 0, 0);
+ bool found = false;
+ int ret = 0;
+
+ guard(srcu)(&eh_frame_srcu);
+
+ mtree_lock(&mm->eh_frame_mt);
+ mas_for_each(&mas, sec, ULONG_MAX) {
+ if (sec->eh_frame_hdr_start == eh_frame_hdr_start) {
+ found = true;
+ ret |= __eh_frame_remove_section(&mas, sec);
+ }
+ }
+ mtree_unlock(&mm->eh_frame_mt);
+
+ if (!found || ret)
+ return -EINVAL;
+
+ return 0;
+}
+
+void eh_frame_free_mm(struct mm_struct *mm)
+{
+ struct eh_frame_section *sec;
+ unsigned long index = 0;
+
+ if (!mm)
+ return;
+
+ mt_for_each(&mm->eh_frame_mt, sec, index, ULONG_MAX)
+ free_section(sec);
+
+ mtree_destroy(&mm->eh_frame_mt);
}
diff --git a/mm/init-mm.c b/mm/init-mm.c
index 3e792aad7626..25c26341fc4f 100644
--- a/mm/init-mm.c
+++ b/mm/init-mm.c
@@ -11,6 +11,7 @@
#include <linux/atomic.h>
#include <linux/user_namespace.h>
#include <linux/iommu.h>
+#include <linux/eh_frame.h>
#include <asm/mmu.h>
#ifndef INIT_MM_CONTEXT
@@ -48,6 +49,7 @@ struct mm_struct init_mm = {
#endif
.flexible_array = MM_STRUCT_FLEXIBLE_ARRAY_INIT,
INIT_MM_CONTEXT(init_mm)
+ INIT_MM_EH_FRAME
};
void setup_initial_init_mm(void *start_code, void *end_code,
--
2.53.0
next prev parent reply other threads:[~2026-08-21 19:54 UTC|newest]
Thread overview: 48+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-21 19:52 [RFC PATCH v2 00/24] unwind_user: Implement .eh_frame handling Jens Remus
2026-08-21 19:52 ` [RFC PATCH v2 01/24] unwind_user: Add generic and arch-specific headers to MAINTAINERS Jens Remus
2026-08-21 19:52 ` [RFC PATCH v2 02/24] unwind_user: Stop when reaching an outermost frame Jens Remus
2026-08-21 20:00 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 03/24] unwind_user: Enable archs that pass RA in a register Jens Remus
2026-08-21 20:02 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 04/24] unwind_user: Flexible FP/RA recovery rules Jens Remus
2026-08-21 20:03 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 05/24] unwind_user: Flexible CFA " Jens Remus
2026-08-21 20:03 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 06/24] unwind_user: Enable archs that define CFA = SP_callsite + offset Jens Remus
2026-08-21 20:03 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 07/24] unwind_user/eh_frame: Add support for reading .eh_frame_hdr section Jens Remus
2026-08-21 20:06 ` sashiko-bot
2026-08-21 19:52 ` Jens Remus [this message]
2026-08-21 20:13 ` [RFC PATCH v2 08/24] unwind_user/eh_frame: Store .eh_frame_hdr section data in per-mm maple tree sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 09/24] unwind_user/eh_frame: Add support for reading .eh_frame section Jens Remus
2026-08-21 20:16 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 10/24] unwind_user/eh_frame: Detect .eh_frame_hdr sections in executables Jens Remus
2026-08-21 20:10 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 11/24] unwind_user/eh_frame: Wire up unwind_user to eh_frame Jens Remus
2026-08-21 20:07 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 12/24] unwind_user/eh_frame: Remove .eh_frame[_hdr] section on detected corruption Jens Remus
2026-08-21 20:18 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 13/24] unwind_user/eh_frame: Show file name in debug output Jens Remus
2026-08-21 20:06 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 14/24] unwind_user/eh_frame: Add .eh_frame[_hdr] validation option Jens Remus
2026-08-21 20:10 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 15/24] unwind_user/eh_frame: Duplicate registered .eh_frame[_hdr] section data on clone/fork Jens Remus
2026-08-21 20:09 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 16/24] unwind_user/eh_frame: Ignore DW_CFA_GNU_args_size Jens Remus
2026-08-21 20:03 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 17/24] unwind_user/eh_frame: Add support for DWARF expressions Jens Remus
2026-08-21 20:18 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 18/24] x86/uaccess: Add unsafe_copy_from_user() implementation Jens Remus
2026-08-21 20:11 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 19/24] unwind_user/eh_frame/x86: Enable eh_frame unwinding on x86 Jens Remus
2026-08-21 20:16 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 20/24] unwind_user/eh_frame/x86: Handle PLT expressions Jens Remus
2026-08-21 20:17 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 21/24] unwind_user/eh_frame/x86: Handle DRAP expressions Jens Remus
2026-08-21 20:15 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 22/24] s390/ptrace: Provide frame_pointer() Jens Remus
2026-08-21 20:07 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 23/24] unwind_user/eh_frame/s390: Enable eh_frame unwinding on s390 Jens Remus
2026-08-21 20:15 ` sashiko-bot
2026-08-21 19:52 ` [RFC PATCH v2 24/24] unwind_user/eh_frame: Add prctl() interface for (un)registering .eh_frame_hdr sections Jens Remus
2026-08-21 20:22 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260821195259.2688377-9-jremus@linux.ibm.com \
--to=jremus@linux.ibm.com \
--cc=agordeev@linux.ibm.com \
--cc=andrii@kernel.org \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=gor@linux.ibm.com \
--cc=hca@linux.ibm.com \
--cc=hpa@zytor.com \
--cc=i@maskray.me \
--cc=ibhagatgnu@gmail.com \
--cc=iii@linux.ibm.com \
--cc=jpoimboe@kernel.org \
--cc=kees@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-s390@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=mathieu.desnoyers@efficios.com \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.org \
--cc=rostedt@kernel.org \
--cc=sam@gentoo.org \
--cc=tglx@kernel.org \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.