]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
binfmt_elf: preserve original ELF e_flags for core dumps
authorSvetlana Parfenova <svetlana.parfenova@syntacore.com>
Mon, 1 Sep 2025 13:53:50 +0000 (20:53 +0700)
committerKees Cook <kees@kernel.org>
Thu, 4 Sep 2025 03:49:32 +0000 (20:49 -0700)
Some architectures, such as RISC-V, use the ELF e_flags field to encode
ABI-specific information (e.g., ISA extensions, fpu support). Debuggers
like GDB rely on these flags in core dumps to correctly interpret
optional register sets. If the flags are missing or incorrect, GDB may
warn and ignore valid data, for example:

    warning: Unexpected size of section '.reg2/213' in core file.

This can prevent access to fpu or other architecture-specific registers
even when they were dumped.

Save the e_flags field during ELF binary loading (in load_elf_binary())
into the mm_struct, and later retrieve it during core dump generation
(in fill_note_info()). Kconfig option CONFIG_ARCH_HAS_ELF_CORE_EFLAGS
is introduced for architectures that require this behaviour.

Signed-off-by: Svetlana Parfenova <svetlana.parfenova@syntacore.com>
Link: https://lore.kernel.org/r/20250901135350.619485-1-svetlana.parfenova@syntacore.com
Signed-off-by: Kees Cook <kees@kernel.org>
arch/riscv/Kconfig
fs/Kconfig.binfmt
fs/binfmt_elf.c
include/linux/mm_types.h

index a4b233a0659ed80c0eb6b118ea8c8db81ed3fdba..b06a758abb63b0701765a4de4cac6b17f06b3298 100644 (file)
@@ -28,6 +28,7 @@ config RISCV
        select ARCH_HAS_DEBUG_VIRTUAL if MMU
        select ARCH_HAS_DEBUG_VM_PGTABLE
        select ARCH_HAS_DEBUG_WX
+       select ARCH_HAS_ELF_CORE_EFLAGS
        select ARCH_HAS_FAST_MULTIPLIER
        select ARCH_HAS_FORTIFY_SOURCE
        select ARCH_HAS_GCOV_PROFILE_ALL
index bd2f530e574086ff5b6d67fd0b906b5196271f58..1949e25c7741b198c3c429260c90b519f8d85244 100644 (file)
@@ -184,4 +184,13 @@ config EXEC_KUNIT_TEST
          This builds the exec KUnit tests, which tests boundary conditions
          of various aspects of the exec internals.
 
+config ARCH_HAS_ELF_CORE_EFLAGS
+       bool
+       depends on BINFMT_ELF && ELF_CORE
+       default n
+       help
+         Select this option if the architecture makes use of the e_flags
+         field in the ELF header to store ABI or other architecture-specific
+         information that should be preserved in core dumps.
+
 endmenu
index 4aacf9c9cc2dfeeec740acebe70dbb479688022b..e4653bb99946b1d9be67ff625d4344ca41d6e7ca 100644 (file)
@@ -103,6 +103,21 @@ static struct linux_binfmt elf_format = {
 
 #define BAD_ADDR(x) (unlikely((unsigned long)(x) >= TASK_SIZE))
 
+static inline void elf_coredump_set_mm_eflags(struct mm_struct *mm, u32 flags)
+{
+#ifdef CONFIG_ARCH_HAS_ELF_CORE_EFLAGS
+       mm->saved_e_flags = flags;
+#endif
+}
+
+static inline u32 elf_coredump_get_mm_eflags(struct mm_struct *mm, u32 flags)
+{
+#ifdef CONFIG_ARCH_HAS_ELF_CORE_EFLAGS
+       flags = mm->saved_e_flags;
+#endif
+       return flags;
+}
+
 /*
  * We need to explicitly zero any trailing portion of the page that follows
  * p_filesz when it ends before the page ends (e.g. bss), otherwise this
@@ -1290,6 +1305,8 @@ out_free_interp:
        mm->end_data = end_data;
        mm->start_stack = bprm->p;
 
+       elf_coredump_set_mm_eflags(mm, elf_ex->e_flags);
+
        /**
         * DOC: "brk" handling
         *
@@ -1804,6 +1821,8 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
        struct elf_thread_core_info *t;
        struct elf_prpsinfo *psinfo;
        struct core_thread *ct;
+       u16 machine;
+       u32 flags;
 
        psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL);
        if (!psinfo)
@@ -1831,17 +1850,26 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
                return 0;
        }
 
-       /*
-        * Initialize the ELF file header.
-        */
-       fill_elf_header(elf, phdrs,
-                       view->e_machine, view->e_flags);
+       machine = view->e_machine;
+       flags = view->e_flags;
 #else
        view = NULL;
        info->thread_notes = 2;
-       fill_elf_header(elf, phdrs, ELF_ARCH, ELF_CORE_EFLAGS);
+       machine = ELF_ARCH;
+       flags = ELF_CORE_EFLAGS;
 #endif
 
+       /*
+        * Override ELF e_flags with value taken from process,
+        * if arch needs that.
+        */
+       flags = elf_coredump_get_mm_eflags(dump_task->mm, flags);
+
+       /*
+        * Initialize the ELF file header.
+        */
+       fill_elf_header(elf, phdrs, machine, flags);
+
        /*
         * Allocate a structure for each thread.
         */
index 08bc2442db93484123f0402607cfc6f6e2abcf0b..04a2857f12f26041a7e55e02ae33285aa87728f3 100644 (file)
@@ -1102,6 +1102,11 @@ struct mm_struct {
 
                unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
 
+#ifdef CONFIG_ARCH_HAS_ELF_CORE_EFLAGS
+               /* the ABI-related flags from the ELF header. Used for core dump */
+               unsigned long saved_e_flags;
+#endif
+
                struct percpu_counter rss_stat[NR_MM_COUNTERS];
 
                struct linux_binfmt *binfmt;