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>
Subject: [RFC PATCH v1 14/25] unwind_user/eh_frame: Add .eh_frame[_hdr] validation option
Date: Tue, 18 Aug 2026 16:49:43 +0200 [thread overview]
Message-ID: <20260818144954.2320378-15-jremus@linux.ibm.com> (raw)
In-Reply-To: <20260818144954.2320378-1-jremus@linux.ibm.com>
Add a debug feature to validate all .eh_frame[_hdr] sections when first
loading the file rather than on demand.
Signed-off-by: Jens Remus <jremus@linux.ibm.com>
---
Notes (jremus):
FIXME: dbg*() with UACCESS enabled.
arch/Kconfig | 22 ++++++++
kernel/unwind/eh_frame.c | 93 ++++++++++++++++++++++++++++++++++
kernel/unwind/eh_frame_debug.h | 4 ++
3 files changed, 119 insertions(+)
diff --git a/arch/Kconfig b/arch/Kconfig
index ea969811f798..30d9e876f28a 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -490,6 +490,28 @@ config HAVE_UNWIND_USER_EH_FRAME
bool
select UNWIND_USER
+config EH_FRAME_VALIDATION
+ bool "Enable .eh_frame[_hdr] section debugging"
+ depends on HAVE_UNWIND_USER_EH_FRAME
+ depends on DYNAMIC_DEBUG
+ help
+ When adding an .eh_frame_hdr section for a test, validate the
+ entire section and its referenced entrire .eh_frame section
+ immediately rather than on demand.
+
+ This is a debug feature which is helpful for rooting out
+ .eh_frame[_hdr] section issues. If the .eh_frame[_hdr]
+ 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 .eh_frame[_hdr] sections 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_UNWIND_USER_FP
bool
select UNWIND_USER
diff --git a/kernel/unwind/eh_frame.c b/kernel/unwind/eh_frame.c
index 46ffb535ca53..c9161229196c 100644
--- a/kernel/unwind/eh_frame.c
+++ b/kernel/unwind/eh_frame.c
@@ -1163,6 +1163,95 @@ int eh_frame_find(unsigned long ip, struct unwind_user_frame *frame)
return ret;
}
+#ifdef CONFIG_EH_FRAME_VALIDATION
+
+static int eh_frame_validate_section(struct eh_frame_section *sec)
+{
+ void __user *table_start_ptr;
+ unsigned long table_size;
+ u8 table_enc;
+ int entry_size;
+ unsigned long prev_func_addr;
+ unsigned int i;
+
+ if (!sec->has_binary_search_table)
+ return 0;
+
+ if (!sec->fde_count) {
+ dbg_sec(".eh_frame_hdr: invalid FDE count\n");
+ return -EINVAL;
+ }
+
+ table_enc = sec->binary_search_table_enc;
+ entry_size = 2 * encoded_pointer_size(table_enc);
+ if (!entry_size) {
+ dbg_sec(".eh_frame_hdr: invalid binary search table entry size\n");
+ return -EINVAL;
+ }
+ table_start_ptr = (void __user *)sec->binary_search_table_start;
+ table_size = sec->binary_search_table_end - sec->binary_search_table_start;
+
+ for (i = 0; i < sec->fde_count; i++) {
+ struct eh_frame_fde fde;
+ unsigned long cur;
+ unsigned long func_addr, fde_addr;
+ int ret;
+
+ cur = sec->binary_search_table_start + i * entry_size;
+
+ scoped_user_read_access_size(table_start_ptr, table_size, Efault) {
+ /* Read function start address from table */
+ ret = read_encoded_pointer(sec, NULL, &cur,
+ sec->binary_search_table_end,
+ table_enc, &func_addr);
+ if (ret) {
+ dbg_sec_ehfh(cur, "table[%u]: failed to read function start address\n", i);
+ return ret;
+ }
+ if (i && func_addr <= prev_func_addr) {
+ dbg_sec(".eh_frame_hdr: table[%u]: not sorted\n", i);
+ return -EINVAL;
+ }
+ prev_func_addr = func_addr;
+
+ /* Read FDE address from table */
+ ret = read_encoded_pointer(sec, NULL, &cur,
+ sec->binary_search_table_end,
+ table_enc, &fde_addr);
+ if (ret) {
+ dbg_sec_ehfh(cur, "table[%u]: failed to read FDE pointer\n", i);
+ return ret;
+ }
+ if (fde_addr < sec->eh_frame_start) {
+ dbg_sec(".eh_frame_hdr: table[%u]: invalid FDE address\n", i);
+ return -EINVAL;
+ }
+ }
+
+ ret = __read_fde(sec, fde_addr, &fde);
+ if (ret) {
+ dbg_sec(".eh_frame_hdr: table[%u]: failed to read FDE at .eh_frame+%#lx\n",
+ i, fde_addr - sec->eh_frame_start);
+ return ret;
+ }
+ if (func_addr != fde.func_addr) {
+ dbg_sec(".eh_frame_hdr: table[%u]: function start address mismatch\n", i);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+
+Efault:
+ return -EFAULT;
+}
+
+#else /* !CONFIG_EH_FRAME_VALIDATION */
+
+static int eh_frame_validate_section(struct eh_frame_section *sec) { return 0; }
+
+#endif /* !CONFIG_EH_FRAME_VALIDATION */
+
static void free_section(struct eh_frame_section *sec)
{
dbg_free(sec);
@@ -1299,6 +1388,10 @@ int eh_frame_add_section(unsigned long eh_frame_hdr_start,
if (ret)
goto err_free;
+ ret = eh_frame_validate_section(sec);
+ if (ret)
+ goto err_free;
+
ret = mtree_insert_range(eh_frame_mt, sec->text_start, sec->text_end - 1,
sec, GFP_KERNEL_ACCOUNT);
if (ret) {
diff --git a/kernel/unwind/eh_frame_debug.h b/kernel/unwind/eh_frame_debug.h
index 40a80861d4d4..bcb2d03ab9ab 100644
--- a/kernel/unwind/eh_frame_debug.h
+++ b/kernel/unwind/eh_frame_debug.h
@@ -14,6 +14,9 @@
#define dbg_sec(fmt, ...) \
dbg("%s: " fmt, sec->filename, ##__VA_ARGS__)
+#define dbg_sec_ehfh(addr, fmt, ...) \
+ dbg_sec(".eh_frame_hdr+%#lx: " fmt, ((addr) - sec->eh_frame_hdr_start), ##__VA_ARGS__)
+
static inline void dbg_init(struct eh_frame_section *sec)
{
struct mm_struct *mm = current->mm;
@@ -47,6 +50,7 @@ static inline void dbg_free(struct eh_frame_section *sec)
#define dbg(args...) no_printk(args)
#define dbg_sec(args...) no_printk(args)
+#define dbg_sec_ehfh(args...) no_printk(args)
static inline void dbg_init(struct eh_frame_section *sec) {}
static inline void dbg_free(struct eh_frame_section *sec) {}
--
2.53.0
next prev parent reply other threads:[~2026-08-18 14:50 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-18 14:49 [RFC PATCH v1 00/25] unwind_user: Implement .eh_frame handling Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 01/25] unwind_user: Add generic and arch-specific headers to MAINTAINERS Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 02/25] unwind_user: Stop when reaching an outermost frame Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 03/25] unwind_user: Enable archs that pass RA in a register Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 04/25] unwind_user: Flexible FP/RA recovery rules Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 05/25] unwind_user: Flexible CFA " Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 06/25] unwind_user: Enable archs that define CFA = SP_callsite + offset Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 07/25] unwind_user/eh_frame: Add support for reading .eh_frame_hdr section Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 08/25] unwind_user/eh_frame: Store .eh_frame_hdr section data in per-mm maple tree Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 09/25] unwind_user/eh_frame: Add support for reading .eh_frame section Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 10/25] unwind_user/eh_frame: Detect .eh_frame_hdr sections in executables Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 11/25] unwind_user/eh_frame: Wire up unwind_user to eh_frame Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 12/25] unwind_user/eh_frame: Remove .eh_frame[_hdr] section on detected corruption Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 13/25] unwind_user/eh_frame: Show file name in debug output Jens Remus
2026-08-18 14:49 ` Jens Remus [this message]
2026-08-18 14:49 ` [RFC PATCH v1 15/25] unwind_user/eh_frame: Duplicate registered .eh_frame[_hdr] section data on clone/fork Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 16/25] unwind_user/eh_frame: Add linear .eh_frame search fallback Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 17/25] unwind_user/eh_frame: Ignore DW_CFA_GNU_args_size Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 18/25] unwind_user/eh_frame: Add support for DWARF expressions Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 19/25] x86/uaccess: Add unsafe_copy_from_user() implementation Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 20/25] unwind_user/eh_frame/x86: Enable eh_frame unwinding on x86 Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 21/25] unwind_user/eh_frame/x86: Handle PLT expressions Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 22/25] unwind_user/eh_frame/x86: Handle DRAP expressions Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 23/25] s390/ptrace: Provide frame_pointer() Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 24/25] unwind_user/eh_frame/s390: Enable eh_frame unwinding on s390 Jens Remus
2026-08-18 14:49 ` [RFC PATCH v1 25/25] unwind_user/eh_frame: Add prctl() interface for (un)registering .eh_frame_hdr sections Jens Remus
2026-08-18 17:21 ` [RFC PATCH v1 00/25] unwind_user: Implement .eh_frame handling Steven Rostedt
2026-08-21 19:51 ` Jens Remus
2026-08-19 8:35 ` Jens Remus
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=20260818144954.2320378-15-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=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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox