Thread (67 messages) flat view 67 messages, 3 authors, 2026-08-21

Re: [RFC PATCH v1 08/25] unwind_user/eh_frame: Store .eh_frame_hdr section data in per-mm maple tree

From: sashiko-bot@kernel.org
Date: 2026-08-18 15:08:49
Also in: linux-s390

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Critical] The `eh_frame_mt` maple tree is initialized without `MT_FLAGS_USE_RCU`, causing internal nodes to be synchronously freed, which crashes concurrent lockless readers.
- [High] fork() fails to inherit .eh_frame_hdr registrations, breaking unwinding for child processes.
- [High] A TOCTOU race condition in `eh_frame_remove_section` allows a double-free of one section and a memory leak of another.
- [High] An integer overflow in `table_end` calculation allows maliciously crafted `.eh_frame_hdr` sections to bypass bounds checking.
- [Low] Appending INIT_MM_EH_FRAME in init_mm causes a missing comma syntax error on multiple architectures.
--
--- Patch [8]: [RFC PATCH v1 08/25] unwind_user/eh_frame: Store .eh_frame_hdr section data in per-mm maple tree ---
commit bd68e8b201de54350859df41e271e507dd497773
Author: Jens Remus [off-list ref]
Subject: unwind_user/eh_frame: Store .eh_frame_hdr section data in per-mm maple tree

This commit associates .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.
quoted hunk ↗ jump to hunk
diff --git a/include/linux/eh_frame.h b/include/linux/eh_frame.h
--- a/include/linux/eh_frame.h
+++ b/include/linux/eh_frame.h
[ ... ]
quoted hunk ↗ jump to hunk
@@ -19,14 +24,27 @@ struct eh_frame_section {
 	u8		binary_search_table_enc;
 };
 
+#define INIT_MM_EH_FRAME .eh_frame_mt = MTREE_INIT(eh_frame_mt, 0),
[Severity: Critical]
Is the MT_FLAGS_USE_RCU flag required here?

Since readers are locklessly iterating the tree using mt_for_each() (which
relies on rcu_read_lock()), omitting MT_FLAGS_USE_RCU causes concurrent
mtree_erase() or mtree_insert_range() to synchronously free internal nodes via
kfree() instead of kfree_rcu(). Can this cause a use-after-free for the
concurrent readers?
+extern void eh_frame_free_mm(struct mm_struct *mm);
+
 extern int eh_frame_add_section(unsigned long eh_frame_hdr_start,
[ ... ]
quoted hunk ↗ jump to hunk
diff --git a/kernel/fork.c b/kernel/fork.c
--- a/kernel/fork.c
+++ b/kernel/fork.c
[ ... ]
quoted hunk ↗ jump to hunk
@@ -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(&mm->eh_frame_mt);
[Severity: Critical]
Should this use mt_init_flags() with MT_FLAGS_USE_RCU?

Similar to the INIT_MM_EH_FRAME macro, initializing without RCU could lead to
a use-after-free if internal maple tree nodes are freed while lockless readers
are iterating.

[Severity: High]
Does fork() fail to inherit .eh_frame_hdr registrations?

A process that registers unwinding information and then calls fork() will have
its VMAs copied in dup_mmap(), but the child receives this empty maple tree
without any copied eh_frame_mt entries. Will this break stack unwinding for
the child process?
+#endif
+}
+
 static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
[ ... ]
quoted hunk ↗ jump to hunk
diff --git a/kernel/unwind/eh_frame.c b/kernel/unwind/eh_frame.c
--- a/kernel/unwind/eh_frame.c
+++ b/kernel/unwind/eh_frame.c
[ ... ]
+static int __eh_frame_remove_section(struct mm_struct *mm,
+				     struct eh_frame_section *sec)
+{
+	if (!mtree_erase(&mm->eh_frame_mt, sec->text_start)) {
+		dbg("mtree_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;
+	unsigned long index = 0;
+	bool found = false;
+	int ret = 0;
+
+	guard(srcu)(&eh_frame_srcu);
+
+	mt_for_each(&mm->eh_frame_mt, sec, index, ULONG_MAX) {
+		if (sec->eh_frame_hdr_start == eh_frame_hdr_start) {
+			found = true;
+			ret |= __eh_frame_remove_section(mm, sec);
+		}
+	}
[Severity: High]
Can this mtree_erase() and call_srcu() sequence race with another thread and
cause a double-free?

If Thread A locklessly iterates and finds sec1 in mt_for_each(), and Thread B
concurrently removes sec1, Thread C could map a new section sec2 at the same
text_start address.

When Thread A calls __eh_frame_remove_section(mm, sec1), mtree_erase() would
successfully remove and return sec2. However, Thread A then passes the
original sec1 pointer to call_srcu(), double-freeing sec1 and leaking sec2.

[ ... ]
quoted hunk ↗ jump to hunk
diff --git a/mm/init-mm.c b/mm/init-mm.c
--- a/mm/init-mm.c
+++ b/mm/init-mm.c
@@ -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
 };
[Severity: Low]
Does appending INIT_MM_EH_FRAME here cause a missing comma syntax error on
some architectures?

While x86's INIT_MM_CONTEXT was updated to include a trailing comma in this
patch, other architectures (like UML or PowerPC Book3s 32-bit) may lack this
trailing comma in their INIT_MM_CONTEXT definitions, breaking the build.


In a related function, eh_frame_read_header() contains a potential overflow:

kernel/unwind/eh_frame.c:eh_frame_read_header() {
    ...
		table_end = table_start + fde_count * entry_size;
		if (table_end > sec->eh_frame_hdr_end)
			return -EINVAL;
    ...
}

[Severity: High]
Can this arithmetic multiplication overflow and bypass the bounds check?

If a malicious .eh_frame_hdr provides an artificially large fde_count,
fde_count * entry_size could overflow the unsigned long capacity, wrapping
around to a small value. This would bypass the bounds check and allow
out-of-bounds reads during later unwinding binary searches.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260818144954.2320378-1-jremus@linux.ibm.com?part=8
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help