Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 28 Sep 2019 01:42:59 +0000 (UTC)
From:      Mark Johnston <markj@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r352829 - head/sys/kern
Message-ID:  <201909280142.x8S1gxHB082132@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: markj
Date: Sat Sep 28 01:42:59 2019
New Revision: 352829
URL: https://svnweb.freebsd.org/changeset/base/352829

Log:
  Fix some problems with the SPARSE_MAPPING option in the kernel linker.
  
  - Ensure that the end of the mapping passed to vm_page_wire() is
    page-aligned.  vm_page_wire() expects this.
  - Wire pages before reading data into them.
  - Apply protections specified in the segment descriptor using
    vm_map_protect() once relocation processing is done.
  - On amd64, ensure that we load KLDs above KERNBASE, since they
    are compiled with the "kernel" memory model by default.
  
  Reviewed by:	kib
  MFC after:	2 weeks
  Sponsored by:	Netflix
  Differential Revision:	https://reviews.freebsd.org/D21756

Modified:
  head/sys/kern/link_elf.c

Modified: head/sys/kern/link_elf.c
==============================================================================
--- head/sys/kern/link_elf.c	Sat Sep 28 01:31:33 2019	(r352828)
+++ head/sys/kern/link_elf.c	Sat Sep 28 01:42:59 2019	(r352829)
@@ -40,6 +40,9 @@ __FBSDID("$FreeBSD$");
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/malloc.h>
+#ifdef SPARSE_MAPPING
+#include <sys/mman.h>
+#endif
 #include <sys/mutex.h>
 #include <sys/mount.h>
 #include <sys/pcpu.h>
@@ -419,7 +422,7 @@ link_elf_init(void* arg)
 	ef->address = 0;
 #endif
 #ifdef SPARSE_MAPPING
-	ef->object = 0;
+	ef->object = NULL;
 #endif
 	ef->dynamic = dp;
 
@@ -772,7 +775,7 @@ link_elf_link_preload(linker_class_t cls,
 	ef->modptr = modptr;
 	ef->address = *(caddr_t *)baseptr;
 #ifdef SPARSE_MAPPING
-	ef->object = 0;
+	ef->object = NULL;
 #endif
 	dp = (vm_offset_t)ef->address + *(vm_offset_t *)dynptr;
 	ef->dynamic = (Elf_Dyn *)dp;
@@ -826,7 +829,7 @@ link_elf_load_file(linker_class_t cls, const char* fil
 	struct nameidata nd;
 	struct thread* td = curthread;	/* XXX */
 	Elf_Ehdr *hdr;
-	caddr_t firstpage;
+	caddr_t firstpage, segbase;
 	int nbytes, i;
 	Elf_Phdr *phdr;
 	Elf_Phdr *phlimit;
@@ -993,47 +996,61 @@ link_elf_load_file(linker_class_t cls, const char* fil
 		error = ENOMEM;
 		goto out;
 	}
-	ef->address = (caddr_t) vm_map_min(kernel_map);
+#ifdef __amd64__
+	mapbase = (caddr_t)KERNBASE;
+#else
+	mapbase = (caddr_t)vm_map_min(kernel_map);
+#endif
+	/*
+	 * Mapping protections are downgraded after relocation processing.
+	 */
 	error = vm_map_find(kernel_map, ef->object, 0,
-	    (vm_offset_t *) &ef->address, mapsize, 0, VMFS_OPTIMAL_SPACE,
+	    (vm_offset_t *)&mapbase, mapsize, 0, VMFS_OPTIMAL_SPACE,
 	    VM_PROT_ALL, VM_PROT_ALL, 0);
 	if (error != 0) {
 		vm_object_deallocate(ef->object);
-		ef->object = 0;
+		ef->object = NULL;
 		goto out;
 	}
 #else
-	ef->address = malloc(mapsize, M_LINKER, M_EXEC | M_WAITOK);
+	mapbase = malloc(mapsize, M_LINKER, M_EXEC | M_WAITOK);
 #endif
-	mapbase = ef->address;
+	ef->address = mapbase;
 
 	/*
 	 * Read the text and data sections and zero the bss.
 	 */
 	for (i = 0; i < nsegs; i++) {
-		caddr_t segbase = mapbase + segs[i]->p_vaddr - base_vaddr;
-		error = vn_rdwr(UIO_READ, nd.ni_vp,
-		    segbase, segs[i]->p_filesz, segs[i]->p_offset,
-		    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
-		    &resid, td);
-		if (error != 0)
-			goto out;
-		bzero(segbase + segs[i]->p_filesz,
-		    segs[i]->p_memsz - segs[i]->p_filesz);
+		segbase = mapbase + segs[i]->p_vaddr - base_vaddr;
 
 #ifdef SPARSE_MAPPING
 		/*
-		 * Wire down the pages
+		 * Consecutive segments may have different mapping permissions,
+		 * so be strict and verify that their mappings do not overlap.
 		 */
+		if (((vm_offset_t)segbase & PAGE_MASK) != 0) {
+			error = EINVAL;
+			goto out;
+		}
+
 		error = vm_map_wire(kernel_map,
-		    (vm_offset_t) segbase,
-		    (vm_offset_t) segbase + segs[i]->p_memsz,
-		    VM_MAP_WIRE_SYSTEM|VM_MAP_WIRE_NOHOLES);
+		    (vm_offset_t)segbase,
+		    (vm_offset_t)segbase + round_page(segs[i]->p_memsz),
+		    VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES);
 		if (error != KERN_SUCCESS) {
 			error = ENOMEM;
 			goto out;
 		}
 #endif
+
+		error = vn_rdwr(UIO_READ, nd.ni_vp,
+		    segbase, segs[i]->p_filesz, segs[i]->p_offset,
+		    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
+		    &resid, td);
+		if (error != 0)
+			goto out;
+		bzero(segbase + segs[i]->p_filesz,
+		    segs[i]->p_memsz - segs[i]->p_filesz);
 	}
 
 #ifdef GPROF
@@ -1070,6 +1087,34 @@ link_elf_load_file(linker_class_t cls, const char* fil
 	error = relocate_file(ef);
 	if (error != 0)
 		goto out;
+
+#ifdef SPARSE_MAPPING
+	/*
+	 * Downgrade permissions on text segment mappings now that relocation
+	 * processing is complete.  Restrict permissions on read-only segments.
+	 */
+	for (i = 0; i < nsegs; i++) {
+		vm_prot_t prot;
+
+		if (segs[i]->p_type != PT_LOAD)
+			continue;
+
+		prot = VM_PROT_READ;
+		if ((segs[i]->p_flags & PF_W) != 0)
+			prot |= VM_PROT_WRITE;
+		if ((segs[i]->p_flags & PF_X) != 0)
+			prot |= VM_PROT_EXECUTE;
+		segbase = mapbase + segs[i]->p_vaddr - base_vaddr;
+		error = vm_map_protect(kernel_map,
+		    (vm_offset_t)segbase,
+		    (vm_offset_t)segbase + round_page(segs[i]->p_memsz),
+		    prot, FALSE);
+		if (error != KERN_SUCCESS) {
+			error = ENOMEM;
+			goto out;
+		}
+	}
+#endif
 
 	/*
 	 * Try and load the symbol table if it's present.  (you can



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201909280142.x8S1gxHB082132>