mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Jiri Slaby <jslaby@suse.cz>
To: stable@vger.kernel.org
Cc: linux-kernel@vger.kernel.org,
	Rusty Russell <rusty@rustcorp.com.au>,
	Jiri Slaby <jslaby@suse.cz>
Subject: [PATCH 3.12 52/58] modules: fix longstanding /proc/kallsyms vs module insertion race.
Date: Wed, 16 Mar 2016 11:59:37 +0100	[thread overview]
Message-ID: <a801fe8935d78c2d900b0d8a422ee3bef2ea7bf2.1458125909.git.jslaby@suse.cz> (raw)
In-Reply-To: <377b71e18f20d69b0df301ce7040554f40ba9651.1458125909.git.jslaby@suse.cz>
In-Reply-To: <cover.1458121137.git.jslaby@suse.cz>

From: Rusty Russell <rusty@rustcorp.com.au>

3.12-stable review patch.  If anyone has any objections, please let me know.

===============

commit 8244062ef1e54502ef55f54cced659913f244c3e upstream.

For CONFIG_KALLSYMS, we keep two symbol tables and two string tables.
There's one full copy, marked SHF_ALLOC and laid out at the end of the
module's init section.  There's also a cut-down version that only
contains core symbols and strings, and lives in the module's core
section.

After module init (and before we free the module memory), we switch
the mod->symtab, mod->num_symtab and mod->strtab to point to the core
versions.  We do this under the module_mutex.

However, kallsyms doesn't take the module_mutex: it uses
preempt_disable() and rcu tricks to walk through the modules, because
it's used in the oops path.  It's also used in /proc/kallsyms.
There's nothing atomic about the change of these variables, so we can
get the old (larger!) num_symtab and the new symtab pointer; in fact
this is what I saw when trying to reproduce.

By grouping these variables together, we can use a
carefully-dereferenced pointer to ensure we always get one or the
other (the free of the module init section is already done in an RCU
callback, so that's safe).  We allocate the init one at the end of the
module init section, and keep the core one inside the struct module
itself (it could also have been allocated at the end of the module
core, but that's probably overkill).

[ Rebased for 4.4-stable and older, because the following changes aren't
  in the older trees:
  - e0224418516b4d8a6c2160574bac18447c354ef0: adds arg to is_core_symbol
  - 7523e4dc5057e157212b4741abd6256e03404cf1: module_init/module_core/init_size/core_size
    become init_layout.base/core_layout.base/init_layout.size/core_layout.size.

  Original commit: 8244062ef1e54502ef55f54cced659913f244c3e
]

Reported-by: Weilong Chen <chenweilong@huawei.com>
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=111541
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
---
 include/linux/module.h |  17 ++++----
 kernel/module.c        | 111 ++++++++++++++++++++++++++++++-------------------
 2 files changed, 78 insertions(+), 50 deletions(-)

diff --git a/include/linux/module.h b/include/linux/module.h
index 73c8c06c25bf..842ef3877d5b 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -223,6 +223,12 @@ struct module_ref {
 	unsigned long decs;
 } __attribute((aligned(2 * sizeof(unsigned long))));
 
+struct mod_kallsyms {
+	Elf_Sym *symtab;
+	unsigned int num_symtab;
+	char *strtab;
+};
+
 struct module
 {
 	enum module_state state;
@@ -311,14 +317,9 @@ struct module
 #endif
 
 #ifdef CONFIG_KALLSYMS
-	/*
-	 * We keep the symbol and string tables for kallsyms.
-	 * The core_* fields below are temporary, loader-only (they
-	 * could really be discarded after module init).
-	 */
-	Elf_Sym *symtab, *core_symtab;
-	unsigned int num_symtab, core_num_syms;
-	char *strtab, *core_strtab;
+	/* Protected by RCU and/or module_mutex: use rcu_dereference() */
+	struct mod_kallsyms *kallsyms;
+	struct mod_kallsyms core_kallsyms;
 
 	/* Section attributes */
 	struct module_sect_attrs *sect_attrs;
diff --git a/kernel/module.c b/kernel/module.c
index cb56e581062d..ec40f03aa473 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -180,6 +180,9 @@ struct load_info {
 	struct _ddebug *debug;
 	unsigned int num_debug;
 	bool sig_ok;
+#ifdef CONFIG_KALLSYMS
+	unsigned long mod_kallsyms_init_off;
+#endif
 	struct {
 		unsigned int sym, str, mod, vers, info, pcpu;
 	} index;
@@ -2362,8 +2365,20 @@ static void layout_symtab(struct module *mod, struct load_info *info)
 	strsect->sh_entsize = get_offset(mod, &mod->init_size, strsect,
 					 info->index.str) | INIT_OFFSET_MASK;
 	pr_debug("\t%s\n", info->secstrings + strsect->sh_name);
+
+	/* We'll tack temporary mod_kallsyms on the end. */
+	mod->init_size = ALIGN(mod->init_size,
+			       __alignof__(struct mod_kallsyms));
+	info->mod_kallsyms_init_off = mod->init_size;
+	mod->init_size += sizeof(struct mod_kallsyms);
+	mod->init_size = debug_align(mod->init_size);
 }
 
+/*
+ * We use the full symtab and strtab which layout_symtab arranged to
+ * be appended to the init section.  Later we switch to the cut-down
+ * core-only ones.
+ */
 static void add_kallsyms(struct module *mod, const struct load_info *info)
 {
 	unsigned int i, ndst;
@@ -2372,28 +2387,33 @@ static void add_kallsyms(struct module *mod, const struct load_info *info)
 	char *s;
 	Elf_Shdr *symsec = &info->sechdrs[info->index.sym];
 
-	mod->symtab = (void *)symsec->sh_addr;
-	mod->num_symtab = symsec->sh_size / sizeof(Elf_Sym);
+	/* Set up to point into init section. */
+	mod->kallsyms = mod->module_init + info->mod_kallsyms_init_off;
+
+	mod->kallsyms->symtab = (void *)symsec->sh_addr;
+	mod->kallsyms->num_symtab = symsec->sh_size / sizeof(Elf_Sym);
 	/* Make sure we get permanent strtab: don't use info->strtab. */
-	mod->strtab = (void *)info->sechdrs[info->index.str].sh_addr;
+	mod->kallsyms->strtab = (void *)info->sechdrs[info->index.str].sh_addr;
 
 	/* Set types up while we still have access to sections. */
-	for (i = 0; i < mod->num_symtab; i++)
-		mod->symtab[i].st_info = elf_type(&mod->symtab[i], info);
-
-	mod->core_symtab = dst = mod->module_core + info->symoffs;
-	mod->core_strtab = s = mod->module_core + info->stroffs;
-	src = mod->symtab;
-	for (ndst = i = 0; i < mod->num_symtab; i++) {
+	for (i = 0; i < mod->kallsyms->num_symtab; i++)
+		mod->kallsyms->symtab[i].st_info
+			= elf_type(&mod->kallsyms->symtab[i], info);
+
+	/* Now populate the cut down core kallsyms for after init. */
+	mod->core_kallsyms.symtab = dst = mod->module_core + info->symoffs;
+	mod->core_kallsyms.strtab = s = mod->module_core + info->stroffs;
+	src = mod->kallsyms->symtab;
+	for (ndst = i = 0; i < mod->kallsyms->num_symtab; i++) {
 		if (i == 0 ||
 		    is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum)) {
 			dst[ndst] = src[i];
-			dst[ndst++].st_name = s - mod->core_strtab;
-			s += strlcpy(s, &mod->strtab[src[i].st_name],
+			dst[ndst++].st_name = s - mod->core_kallsyms.strtab;
+			s += strlcpy(s, &mod->kallsyms->strtab[src[i].st_name],
 				     KSYM_NAME_LEN) + 1;
 		}
 	}
-	mod->core_num_syms = ndst;
+	mod->core_kallsyms.num_symtab = ndst;
 }
 #else
 static inline void layout_symtab(struct module *mod, struct load_info *info)
@@ -3107,9 +3127,8 @@ static int do_init_module(struct module *mod)
 	module_put(mod);
 	trim_init_extable(mod);
 #ifdef CONFIG_KALLSYMS
-	mod->num_symtab = mod->core_num_syms;
-	mod->symtab = mod->core_symtab;
-	mod->strtab = mod->core_strtab;
+	/* Switch to core kallsyms now init is done: kallsyms may be walking! */
+	rcu_assign_pointer(mod->kallsyms, &mod->core_kallsyms);
 #endif
 	unset_module_init_ro_nx(mod);
 	module_free(mod, mod->module_init);
@@ -3425,9 +3444,9 @@ static inline int is_arm_mapping_symbol(const char *str)
 	       && (str[2] == '\0' || str[2] == '.');
 }
 
-static const char *symname(struct module *mod, unsigned int symnum)
+static const char *symname(struct mod_kallsyms *kallsyms, unsigned int symnum)
 {
-	return mod->strtab + mod->symtab[symnum].st_name;
+	return kallsyms->strtab + kallsyms->symtab[symnum].st_name;
 }
 
 static const char *get_ksymbol(struct module *mod,
@@ -3437,6 +3456,7 @@ static const char *get_ksymbol(struct module *mod,
 {
 	unsigned int i, best = 0;
 	unsigned long nextval;
+	struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms);
 
 	/* At worse, next value is at end of module */
 	if (within_module_init(addr, mod))
@@ -3446,32 +3466,32 @@ static const char *get_ksymbol(struct module *mod,
 
 	/* Scan for closest preceding symbol, and next symbol. (ELF
 	   starts real symbols at 1). */
-	for (i = 1; i < mod->num_symtab; i++) {
-		if (mod->symtab[i].st_shndx == SHN_UNDEF)
+	for (i = 1; i < kallsyms->num_symtab; i++) {
+		if (kallsyms->symtab[i].st_shndx == SHN_UNDEF)
 			continue;
 
 		/* We ignore unnamed symbols: they're uninformative
 		 * and inserted at a whim. */
-		if (*symname(mod, i) == '\0'
-		    || is_arm_mapping_symbol(symname(mod, i)))
+		if (*symname(kallsyms, i) == '\0'
+		    || is_arm_mapping_symbol(symname(kallsyms, i)))
 			continue;
 
-		if (mod->symtab[i].st_value <= addr
-		    && mod->symtab[i].st_value > mod->symtab[best].st_value)
+		if (kallsyms->symtab[i].st_value <= addr
+		    && kallsyms->symtab[i].st_value > kallsyms->symtab[best].st_value)
 			best = i;
-		if (mod->symtab[i].st_value > addr
-		    && mod->symtab[i].st_value < nextval)
-			nextval = mod->symtab[i].st_value;
+		if (kallsyms->symtab[i].st_value > addr
+		    && kallsyms->symtab[i].st_value < nextval)
+			nextval = kallsyms->symtab[i].st_value;
 	}
 
 	if (!best)
 		return NULL;
 
 	if (size)
-		*size = nextval - mod->symtab[best].st_value;
+		*size = nextval - kallsyms->symtab[best].st_value;
 	if (offset)
-		*offset = addr - mod->symtab[best].st_value;
-	return symname(mod, best);
+		*offset = addr - kallsyms->symtab[best].st_value;
+	return symname(kallsyms, best);
 }
 
 /* For kallsyms to ask for address resolution.  NULL means not found.  Careful
@@ -3567,18 +3587,21 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
 
 	preempt_disable();
 	list_for_each_entry_rcu(mod, &modules, list) {
+		struct mod_kallsyms *kallsyms;
+
 		if (mod->state == MODULE_STATE_UNFORMED)
 			continue;
-		if (symnum < mod->num_symtab) {
-			*value = mod->symtab[symnum].st_value;
-			*type = mod->symtab[symnum].st_info;
-			strlcpy(name, symname(mod, symnum), KSYM_NAME_LEN);
+		kallsyms = rcu_dereference_sched(mod->kallsyms);
+		if (symnum < kallsyms->num_symtab) {
+			*value = kallsyms->symtab[symnum].st_value;
+			*type = kallsyms->symtab[symnum].st_info;
+			strlcpy(name, symname(kallsyms, symnum), KSYM_NAME_LEN);
 			strlcpy(module_name, mod->name, MODULE_NAME_LEN);
 			*exported = is_exported(name, *value, mod);
 			preempt_enable();
 			return 0;
 		}
-		symnum -= mod->num_symtab;
+		symnum -= kallsyms->num_symtab;
 	}
 	preempt_enable();
 	return -ERANGE;
@@ -3587,11 +3610,12 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
 static unsigned long mod_find_symname(struct module *mod, const char *name)
 {
 	unsigned int i;
+	struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms);
 
-	for (i = 0; i < mod->num_symtab; i++)
-		if (strcmp(name, symname(mod, i)) == 0 &&
-		    mod->symtab[i].st_info != 'U')
-			return mod->symtab[i].st_value;
+	for (i = 0; i < kallsyms->num_symtab; i++)
+		if (strcmp(name, symname(kallsyms, i)) == 0 &&
+		    kallsyms->symtab[i].st_info != 'U')
+			return kallsyms->symtab[i].st_value;
 	return 0;
 }
 
@@ -3628,11 +3652,14 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
 	int ret;
 
 	list_for_each_entry(mod, &modules, list) {
+		/* We hold module_mutex: no need for rcu_dereference_sched */
+		struct mod_kallsyms *kallsyms = mod->kallsyms;
+
 		if (mod->state == MODULE_STATE_UNFORMED)
 			continue;
-		for (i = 0; i < mod->num_symtab; i++) {
-			ret = fn(data, symname(mod, i),
-				 mod, mod->symtab[i].st_value);
+		for (i = 0; i < kallsyms->num_symtab; i++) {
+			ret = fn(data, symname(kallsyms, i),
+				 mod, kallsyms->symtab[i].st_value);
 			if (ret != 0)
 				return ret;
 		}
-- 
2.7.3

  parent reply	other threads:[~2016-03-16 11:01 UTC|newest]

Thread overview: 68+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-03-16 10:58 [PATCH 3.12 00/58] 3.12.57-stable review Jiri Slaby
2016-03-16 10:58 ` [PATCH 3.12 01/58] nfsd: fix problem with setting ACL on directories Jiri Slaby
2016-03-16 12:24   ` Sergio Gelato
2016-03-18  6:29     ` NeilBrown
2016-03-16 10:58 ` [PATCH 3.12 02/58] unix: properly account for FDs passed over unix sockets Jiri Slaby
2016-03-16 10:58 ` [PATCH 3.12 03/58] unix: correctly track in-flight fds in sending process user_struct Jiri Slaby
2016-03-16 10:58 ` [PATCH 3.12 04/58] USB: cp210x: flush device queues at close Jiri Slaby
2016-03-16 10:58 ` [PATCH 3.12 05/58] USB: cp210x: relocate private data from USB interface to port Jiri Slaby
2016-03-16 10:58 ` [PATCH 3.12 06/58] USB: cp210x: work around cp2108 GET_LINE_CTL bug Jiri Slaby
2016-03-16 10:58 ` [PATCH 3.12 07/58] [media] usbvision fix overflow of interfaces array Jiri Slaby
2016-03-16 10:58 ` [PATCH 3.12 08/58] usb: Add connected retry on resume for non SS devices Jiri Slaby
2016-03-16 10:58 ` [PATCH 3.12 09/58] ixgbe: use correct FCoE DDP max check Jiri Slaby
2016-03-16 10:58 ` [PATCH 3.12 10/58] ixgbe: fix broken PFC with X550 Jiri Slaby
2016-03-16 10:58 ` [PATCH 3.12 11/58] genksyms: Handle string literals with spaces in reference files Jiri Slaby
2016-03-16 10:58 ` [PATCH 3.12 12/58] cifs: fix out-of-bounds access in lease parsing Jiri Slaby
2016-03-16 10:58 ` [PATCH 3.12 13/58] CIFS: Fix SMB2+ interim response processing for read requests Jiri Slaby
2016-03-16 10:58 ` [PATCH 3.12 14/58] iommu/amd: Fix boot warning when device 00:00.0 is not iommu covered Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 15/58] x86/entry/compat: Add missing CLAC to entry_INT80_32 Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 16/58] drm/ast: Fix incorrect register check for DRAM width Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 17/58] libata: fix HDIO_GET_32BIT ioctl Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 18/58] libata: Align ata_device's id on a cacheline Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 19/58] PM / sleep / x86: Fix crash on graph trace through x86 suspend Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 20/58] Revert "jffs2: Fix lock acquisition order bug in jffs2_write_begin" Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 21/58] jffs2: Fix page lock / f->sem deadlock Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 22/58] Fix directory hardlinks from deleted directories Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 23/58] ALSA: ctl: Fix ioctls for X32 ABI Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 24/58] ALSA: rawmidi: Fix ioctls " Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 25/58] ALSA: timer: Fix ioctls for " Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 26/58] ALSA: seq: oss: Don't drain at closing a client Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 27/58] ALSA: hdspm: Fix wrong boolean ctl value accesses Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 28/58] ALSA: hdsp: " Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 29/58] ALSA: hdspm: Fix zero-division Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 30/58] ALSA: timer: Fix broken compat timer user status ioctl Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 31/58] USB: cp210x: Add ID for Parrot NMEA GPS Flight Recorder Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 32/58] USB: serial: option: add support for Telit LE922 PID 0x1045 Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 33/58] USB: serial: option: add support for Quectel UC20 Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 34/58] MIPS: traps: Fix SIGFPE information leak from `do_ov' and `do_trap_or_bp' Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 35/58] ubi: Fix out of bounds write in volume update code Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 36/58] Revert "drm/radeon: hold reference to fences in radeon_sa_bo_new" Jiri Slaby
2016-03-16 19:41   ` Nicolai Hähnle
2016-03-16 10:59 ` [PATCH 3.12 37/58] KVM: VMX: disable PEBS before a guest entry Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 38/58] tracing: Fix check for cpu online when event is disabled Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 39/58] ASoC: wm8994: Fix enum ctl accesses in a wrong type Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 40/58] ASoC: wm8958: " Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 41/58] wext: fix message delay/ordering Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 42/58] mac80211: minstrel_ht: set default tx aggregation timeout to 0 Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 43/58] powerpc: Fix dedotify for binutils >= 2.26 Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 44/58] KVM: x86: move steal time initialization to vcpu entry time Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 45/58] lib/ucs2_string: Add ucs2 -> utf8 helper functions Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 46/58] efi: Use ucs2_as_utf8 in efivarfs instead of open coding a bad version Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 47/58] efi: Do variable name validation tests in utf8 Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 48/58] efi: Make our variable validation list include the guid Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 49/58] efi: Make efivarfs entries immutable by default Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 50/58] efi: Add pstore variables to the deletion whitelist Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 51/58] lib/ucs2_string: Correct ucs2 -> utf8 conversion Jiri Slaby
2016-03-16 10:59 ` Jiri Slaby [this message]
2016-03-16 10:59 ` [PATCH 3.12 53/58] xen/pciback: Return error on XEN_PCI_OP_enable_msi when device has MSI or MSI-X enabled Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 54/58] xen/pciback: Return error on XEN_PCI_OP_enable_msix " Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 55/58] xen/pciback: Do not install an IRQ handler for MSI interrupts Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 56/58] xen/pciback: For XEN_PCI_OP_disable_msi[|x] only disable if device has MSI(X) enabled Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 57/58] xen/pciback: Don't allow MSI-X ops if PCI_COMMAND_MEMORY is not set Jiri Slaby
2016-03-16 10:59 ` [PATCH 3.12 58/58] xen/pciback: Check PF instead of VF for PCI_COMMAND_MEMORY Jiri Slaby
2016-03-16 15:41 ` [PATCH 3.12 00/58] 3.12.57-stable review Shuah Khan
2016-03-18  8:21   ` Jiri Slaby
2016-03-16 17:58 ` Guenter Roeck
2016-03-16 18:16   ` Jiri Slaby
2016-03-17  3:23     ` Guenter Roeck
2016-03-18  8:21       ` Jiri Slaby

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=a801fe8935d78c2d900b0d8a422ee3bef2ea7bf2.1458125909.git.jslaby@suse.cz \
    --to=jslaby@suse.cz \
    --cc=linux-kernel@vger.kernel.org \
    --cc=rusty@rustcorp.com.au \
    --cc=stable@vger.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

all inboxes | Powered by JetHome®