Module Name:    src
Committed By:   maxv
Date:           Sat Jul  9 07:25:00 UTC 2016

Modified Files:
        src/sys/kern: subr_kobj.c subr_kobj_vfs.c
        src/sys/sys: kobj_impl.h

Log Message:
When loading a module from VFS and from the bootloader, the kernel packs
up the module segments into one big RWX chunk. Split this chunk into two
different text and data+bss+rodata chunks. The latter is made non-
executable. This also provides some kind of ASLR, since the chunks are
not necessarily contiguous.


To generate a diff of this commit:
cvs rdiff -u -r1.54 -r1.55 src/sys/kern/subr_kobj.c
cvs rdiff -u -r1.8 -r1.9 src/sys/kern/subr_kobj_vfs.c
cvs rdiff -u -r1.3 -r1.4 src/sys/sys/kobj_impl.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/kern/subr_kobj.c
diff -u src/sys/kern/subr_kobj.c:1.54 src/sys/kern/subr_kobj.c:1.55
--- src/sys/kern/subr_kobj.c:1.54	Fri Jul  8 08:55:48 2016
+++ src/sys/kern/subr_kobj.c	Sat Jul  9 07:25:00 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: subr_kobj.c,v 1.54 2016/07/08 08:55:48 maxv Exp $	*/
+/*	$NetBSD: subr_kobj.c,v 1.55 2016/07/09 07:25:00 maxv Exp $	*/
 
 /*-
  * Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -63,7 +63,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_kobj.c,v 1.54 2016/07/08 08:55:48 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_kobj.c,v 1.55 2016/07/09 07:25:00 maxv Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_modular.h"
@@ -160,8 +160,10 @@ kobj_load(kobj_t ko)
 	Elf_Ehdr *hdr;
 	Elf_Shdr *shdr;
 	Elf_Sym *es;
-	vaddr_t mapbase;
-	size_t mapsize;
+	vaddr_t map_text_base;
+	vaddr_t map_data_base;
+	size_t map_text_size;
+	size_t map_data_size;
 	int error;
 	int symtabindex;
 	int symstrindex;
@@ -392,40 +394,54 @@ kobj_load(kobj_t ko)
 	 * Size up code/data(progbits) and bss(nobits).
 	 */
 	alignmask = 0;
-	mapsize = 0;
+	map_text_size = 0;
+	map_data_size = 0;
 	for (i = 0; i < hdr->e_shnum; i++) {
-		switch (shdr[i].sh_type) {
-		case SHT_PROGBITS:
-		case SHT_NOBITS:
-			alignmask = shdr[i].sh_addralign - 1;
-			mapsize += alignmask;
-			mapsize &= ~alignmask;
-			mapsize += shdr[i].sh_size;
-			break;
+		if (shdr[i].sh_type != SHT_PROGBITS &&
+		    shdr[i].sh_type != SHT_NOBITS)
+			continue;
+		alignmask = shdr[i].sh_addralign - 1;
+		if ((shdr[i].sh_flags & SHF_EXECINSTR)) {
+			map_text_size += alignmask;
+			map_text_size &= ~alignmask;
+			map_text_size += shdr[i].sh_size;
+		} else {
+			map_data_size += alignmask;
+			map_data_size &= ~alignmask;
+			map_data_size += shdr[i].sh_size;
 		}
 	}
 
-	/*
-	 * We know how much space we need for the text/data/bss/etc.
-	 * This stuff needs to be in a single chunk so that profiling etc
-	 * can get the bounds and gdb can associate offsets with modules.
-	 */
-	if (mapsize == 0) {
-		kobj_error(ko, "no text/data/bss");
+	if (map_text_size == 0) {
+		kobj_error(ko, "no text");
+		error = ENOEXEC;
+ 		goto out;
+ 	}
+	if (map_data_size == 0) {
+		kobj_error(ko, "no data/bss");
 		error = ENOEXEC;
  		goto out;
  	}
 
-	mapbase = uvm_km_alloc(module_map, round_page(mapsize),
+	map_text_base = uvm_km_alloc(module_map, round_page(map_text_size),
 	    0, UVM_KMF_WIRED | UVM_KMF_EXEC);
-	if (mapbase == 0) {
+	if (map_text_base == 0) {
 		kobj_error(ko, "out of memory");
 		error = ENOMEM;
 		goto out;
 	}
+	ko->ko_text_address = map_text_base;
+	ko->ko_text_size = map_text_size;
 
-	ko->ko_address = mapbase;
-	ko->ko_size = mapsize;
+	map_data_base = uvm_km_alloc(module_map, round_page(map_data_size),
+	    0, UVM_KMF_WIRED);
+	if (map_data_base == 0) {
+		kobj_error(ko, "out of memory");
+		error = ENOMEM;
+		goto out;
+	}
+	ko->ko_data_address = map_data_base;
+	ko->ko_data_size = map_data_size;
 
 	/*
 	 * Now load code/data(progbits), zero bss(nobits), allocate space
@@ -440,10 +456,17 @@ kobj_load(kobj_t ko)
 		case SHT_PROGBITS:
 		case SHT_NOBITS:
 			alignmask = shdr[i].sh_addralign - 1;
-			mapbase += alignmask;
-			mapbase &= ~alignmask;
-			addr = (void *)mapbase;
-			mapbase += shdr[i].sh_size;
+			if ((shdr[i].sh_flags & SHF_EXECINSTR)) {
+				map_text_base += alignmask;
+				map_text_base &= ~alignmask;
+				addr = (void *)map_text_base;
+				map_text_base += shdr[i].sh_size;
+ 			} else {
+				map_data_base += alignmask;
+				map_data_base &= ~alignmask;
+				addr = (void *)map_data_base;
+				map_data_base += shdr[i].sh_size;
+ 			}
 
 			ko->ko_progtab[pb].addr = addr;
 			if (shdr[i].sh_type == SHT_PROGBITS) {
@@ -535,12 +558,19 @@ kobj_load(kobj_t ko)
 		panic("%s:%d: %s: lost rela", __func__, __LINE__,
 		   ko->ko_name);
 	}
-	if (mapbase != ko->ko_address + mapsize) {
-		panic("%s:%d: %s: "
-		    "mapbase 0x%lx != address %lx + mapsize %ld (0x%lx)\n",
-		    __func__, __LINE__, ko->ko_name,
-		    (long)mapbase, (long)ko->ko_address, (long)mapsize,
-		    (long)ko->ko_address + mapsize);
+	if (map_text_base != ko->ko_text_address + map_text_size) {
+		panic("%s:%d: %s: map_text_base 0x%lx != address %lx "
+		    "+ map_text_size %ld (0x%lx)\n",
+		    __func__, __LINE__, ko->ko_name, (long)map_text_base,
+		    (long)ko->ko_text_address, (long)map_text_size,
+		    (long)ko->ko_text_address + map_text_size);
+	}
+	if (map_data_base != ko->ko_data_address + map_data_size) {
+		panic("%s:%d: %s: map_data_base 0x%lx != address %lx "
+		    "+ map_data_size %ld (0x%lx)\n",
+		    __func__, __LINE__, ko->ko_name, (long)map_data_base,
+		    (long)ko->ko_data_address, (long)map_data_size,
+		    (long)ko->ko_data_address + map_data_size);
 	}
 
 	/*
@@ -580,16 +610,25 @@ kobj_unload(kobj_t ko)
 	 * Notify MD code that a module has been unloaded.
 	 */
 	if (ko->ko_loaded) {
-		error = kobj_machdep(ko, (void *)ko->ko_address, ko->ko_size,
-		    false);
+		error = kobj_machdep(ko, (void *)ko->ko_text_address,
+		    ko->ko_text_size, false);
 		if (error != 0)
-			kobj_error(ko, "machine dependent deinit failed %d",
+			kobj_error(ko, "machine dependent deinit failed (text) %d",
 			    error);
-	}
-	if (ko->ko_address != 0) {
-		uvm_km_free(module_map, ko->ko_address, round_page(ko->ko_size),
-		    UVM_KMF_WIRED);
-	}
+		error = kobj_machdep(ko, (void *)ko->ko_data_address,
+		    ko->ko_data_size, false);
+ 		if (error != 0)
+			kobj_error(ko, "machine dependent deinit failed (data) %d",
+ 			    error);
+	}
+	if (ko->ko_text_address != 0) {
+		uvm_km_free(module_map, ko->ko_text_address,
+		    round_page(ko->ko_text_size), UVM_KMF_WIRED);
+	}
+	if (ko->ko_data_address != 0) {
+		uvm_km_free(module_map, ko->ko_data_address,
+		    round_page(ko->ko_data_size), UVM_KMF_WIRED);
+ 	}
 	if (ko->ko_ksyms == true) {
 		ksyms_modunload(ko->ko_name);
 	}
@@ -622,10 +661,10 @@ kobj_stat(kobj_t ko, vaddr_t *address, s
 {
 
 	if (address != NULL) {
-		*address = ko->ko_address;
+		*address = ko->ko_text_address;
 	}
 	if (size != NULL) {
-		*size = ko->ko_size;
+		*size = ko->ko_text_size;
 	}
 	return 0;
 }
@@ -673,10 +712,15 @@ kobj_affix(kobj_t ko, const char *name)
 	 * Most architectures use this opportunity to flush their caches.
 	 */
 	if (error == 0) {
-		error = kobj_machdep(ko, (void *)ko->ko_address, ko->ko_size,
-		    true);
+		error = kobj_machdep(ko, (void *)ko->ko_text_address,
+		    ko->ko_text_size, true);
+		if (error != 0)
+			kobj_error(ko, "machine dependent init failed (text) %d",
+			    error);
+		error = kobj_machdep(ko, (void *)ko->ko_data_address,
+		    ko->ko_data_size, true);
 		if (error != 0)
-			kobj_error(ko, "machine dependent init failed %d",
+			kobj_error(ko, "machine dependent init failed (data) %d",
 			    error);
 		ko->ko_loaded = true;
 	}

Index: src/sys/kern/subr_kobj_vfs.c
diff -u src/sys/kern/subr_kobj_vfs.c:1.8 src/sys/kern/subr_kobj_vfs.c:1.9
--- src/sys/kern/subr_kobj_vfs.c:1.8	Mon Aug 24 22:50:32 2015
+++ src/sys/kern/subr_kobj_vfs.c	Sat Jul  9 07:25:00 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: subr_kobj_vfs.c,v 1.8 2015/08/24 22:50:32 pooka Exp $	*/
+/*	$NetBSD: subr_kobj_vfs.c,v 1.9 2016/07/09 07:25:00 maxv Exp $	*/
 
 /*-
  * Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -75,7 +75,7 @@
 #include <sys/vnode.h>
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_kobj_vfs.c,v 1.8 2015/08/24 22:50:32 pooka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_kobj_vfs.c,v 1.9 2016/07/09 07:25:00 maxv Exp $");
 
 static void
 kobj_close_vfs(kobj_t ko)
@@ -104,9 +104,19 @@ kobj_read_vfs(kobj_t ko, void **basep, s
 		base = kmem_alloc(size, KM_SLEEP);
 	} else {
 		base = *basep;
-		KASSERT((uintptr_t)base >= (uintptr_t)ko->ko_address);
-		KASSERT((uintptr_t)base + size <=
-		    (uintptr_t)ko->ko_address + ko->ko_size);
+#ifdef DIAGNOSTIC
+		bool ok = false;
+		if ((uintptr_t)base >= (uintptr_t)ko->ko_text_address &&
+		    (uintptr_t)base + size <=
+		    (uintptr_t)ko->ko_text_address + ko->ko_text_size)
+			ok = true;
+		if ((uintptr_t)base >= (uintptr_t)ko->ko_data_address &&
+		    (uintptr_t)base + size <=
+		    (uintptr_t)ko->ko_data_address + ko->ko_data_size)
+			ok = true;
+		if (!ok)
+			panic("kobj_read_vfs: not in a dedicated segment");
+#endif
 	}
 
 	error = vn_rdwr(UIO_READ, ko->ko_source, base, size, off,

Index: src/sys/sys/kobj_impl.h
diff -u src/sys/sys/kobj_impl.h:1.3 src/sys/sys/kobj_impl.h:1.4
--- src/sys/sys/kobj_impl.h:1.3	Sat Aug 13 21:04:07 2011
+++ src/sys/sys/kobj_impl.h	Sat Jul  9 07:25:00 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: kobj_impl.h,v 1.3 2011/08/13 21:04:07 christos Exp $	*/
+/*	$NetBSD: kobj_impl.h,v 1.4 2016/07/09 07:25:00 maxv Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -107,7 +107,8 @@ struct kobj {
 	kobjtype_t	ko_type;
 	void		*ko_source;
 	ssize_t		ko_memsize;
-	vaddr_t		ko_address;	/* Relocation address */
+	vaddr_t		ko_text_address;	/* Address of text segment */
+	vaddr_t		ko_data_address;	/* Address of data segment */
 	Elf_Shdr	*ko_shdr;
 	progent_t	*ko_progtab;
 	relaent_t	*ko_relatab;
@@ -115,7 +116,8 @@ struct kobj {
 	Elf_Sym		*ko_symtab;	/* Symbol table */
 	char		*ko_strtab;	/* String table */
 	char		*ko_shstrtab;	/* Section name string table */
-	size_t		ko_size;	/* Size of text/data/bss */
+	size_t		ko_text_size;	/* Size of text segment */
+	size_t		ko_data_size;	/* Size of data/bss/rodata segment */
 	size_t		ko_symcnt;	/* Number of symbols */
 	size_t		ko_strtabsz;	/* Number of bytes in string table */
 	size_t		ko_shstrtabsz;	/* Number of bytes in scn str table */

Reply via email to