Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ia64: module: fix symbolizer crash on fdescr

Noticed failure as a crash on ia64 when tried to symbolize all backtraces
collected by page_owner=on:

$ cat /sys/kernel/debug/page_owner
<oops>

CPU: 1 PID: 2074 Comm: cat Not tainted 5.12.0-rc4 #226
Hardware name: hp server rx3600, BIOS 04.03 04/08/2008
ip is at dereference_module_function_descriptor+0x41/0x100

Crash happens at dereference_module_function_descriptor() due to
use-after-free when dereferencing ".opd" section header.

All section headers are already freed after module is laoded successfully.

To keep symbolizer working the change stores ".opd" address and size after
module is relocated to a new place and before section headers are
discarded.

To make similar errors less obscure module_finalize() now zeroes out all
variables relevant to module loading only.

Link: https://lkml.kernel.org/r/20210403074803.3309096-1-slyfox@gentoo.org
Signed-off-by: Sergei Trofimovich <slyfox@gentoo.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Sergei Trofimovich and committed by
Linus Torvalds
99e729bd 9187592b

+30 -5
+5 -1
arch/ia64/include/asm/module.h
··· 14 14 struct elf64_shdr; /* forward declration */ 15 15 16 16 struct mod_arch_specific { 17 + /* Used only at module load time. */ 17 18 struct elf64_shdr *core_plt; /* core PLT section */ 18 19 struct elf64_shdr *init_plt; /* init PLT section */ 19 20 struct elf64_shdr *got; /* global offset table */ 20 21 struct elf64_shdr *opd; /* official procedure descriptors */ 21 22 struct elf64_shdr *unwind; /* unwind-table section */ 22 23 unsigned long gp; /* global-pointer for module */ 24 + unsigned int next_got_entry; /* index of next available got entry */ 23 25 26 + /* Used at module run and cleanup time. */ 24 27 void *core_unw_table; /* core unwind-table cookie returned by unwinder */ 25 28 void *init_unw_table; /* init unwind-table cookie returned by unwinder */ 26 - unsigned int next_got_entry; /* index of next available got entry */ 29 + void *opd_addr; /* symbolize uses .opd to get to actual function */ 30 + unsigned long opd_size; 27 31 }; 28 32 29 33 #define ARCH_SHF_SMALL SHF_IA_64_SHORT
+25 -4
arch/ia64/kernel/module.c
··· 905 905 int 906 906 module_finalize (const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *mod) 907 907 { 908 + struct mod_arch_specific *mas = &mod->arch; 909 + 908 910 DEBUGP("%s: init: entry=%p\n", __func__, mod->init); 909 - if (mod->arch.unwind) 911 + if (mas->unwind) 910 912 register_unwind_table(mod); 913 + 914 + /* 915 + * ".opd" was already relocated to the final destination. Store 916 + * it's address for use in symbolizer. 917 + */ 918 + mas->opd_addr = (void *)mas->opd->sh_addr; 919 + mas->opd_size = mas->opd->sh_size; 920 + 921 + /* 922 + * Module relocation was already done at this point. Section 923 + * headers are about to be deleted. Wipe out load-time context. 924 + */ 925 + mas->core_plt = NULL; 926 + mas->init_plt = NULL; 927 + mas->got = NULL; 928 + mas->opd = NULL; 929 + mas->unwind = NULL; 930 + mas->gp = 0; 931 + mas->next_got_entry = 0; 932 + 911 933 return 0; 912 934 } 913 935 ··· 948 926 949 927 void *dereference_module_function_descriptor(struct module *mod, void *ptr) 950 928 { 951 - Elf64_Shdr *opd = mod->arch.opd; 929 + struct mod_arch_specific *mas = &mod->arch; 952 930 953 - if (ptr < (void *)opd->sh_addr || 954 - ptr >= (void *)(opd->sh_addr + opd->sh_size)) 931 + if (ptr < mas->opd_addr || ptr >= mas->opd_addr + mas->opd_size) 955 932 return ptr; 956 933 957 934 return dereference_function_descriptor(ptr);