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 */