Module Name:    src
Committed By:   maxv
Date:           Tue Apr 15 17:29:00 UTC 2014

Modified Files:
        src/sys/compat/linux/common: linux_exec_elf32.c

Log Message:
A specially-crafted binary could easily control a kernel array index.
Add some checks to ensure that nothing will be read outside the allocated
area. Rewrite the code so that we don't need to allocate the whole section.

Spotted by several developers, patch from chs@/enami@


To generate a diff of this commit:
cvs rdiff -u -r1.90 -r1.91 src/sys/compat/linux/common/linux_exec_elf32.c

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

Modified files:

Index: src/sys/compat/linux/common/linux_exec_elf32.c
diff -u src/sys/compat/linux/common/linux_exec_elf32.c:1.90 src/sys/compat/linux/common/linux_exec_elf32.c:1.91
--- src/sys/compat/linux/common/linux_exec_elf32.c:1.90	Sun Feb 23 11:09:42 2014
+++ src/sys/compat/linux/common/linux_exec_elf32.c	Tue Apr 15 17:29:00 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: linux_exec_elf32.c,v 1.90 2014/02/23 11:09:42 njoly Exp $	*/
+/*	$NetBSD: linux_exec_elf32.c,v 1.91 2014/04/15 17:29:00 maxv Exp $	*/
 
 /*-
  * Copyright (c) 1995, 1998, 2000, 2001 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: linux_exec_elf32.c,v 1.90 2014/02/23 11:09:42 njoly Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux_exec_elf32.c,v 1.91 2014/04/15 17:29:00 maxv Exp $");
 
 #ifndef ELFSIZE
 /* XXX should die */
@@ -92,50 +92,43 @@ ELFNAME2(linux,atexit_signature)(
 	struct exec_package *epp,
 	Elf_Ehdr *eh)
 {
+	Elf_Shdr *sh;
 	size_t shsize;
-	int strndx;
+	u_int shstrndx;
 	size_t i;
 	static const char signature[] = "__libc_atexit";
-	char *strtable = NULL;
-	Elf_Shdr *sh;
-
+	const size_t sigsz = sizeof(signature);
+	char tbuf[sizeof(signature)];
 	int error;
 
-	/*
-	 * load the section header table
-	 */
+	/* Load the section header table. */
 	shsize = eh->e_shnum * sizeof(Elf_Shdr);
 	sh = (Elf_Shdr *) malloc(shsize, M_TEMP, M_WAITOK);
 	error = exec_read_from(l, epp->ep_vp, eh->e_shoff, sh, shsize);
 	if (error)
 		goto out;
 
-	/*
-	 * Now let's find the string table. If it does not exists, give up.
-	 */
-	strndx = (int)(eh->e_shstrndx);
-	if (strndx == SHN_UNDEF) {
+	/* Now let's find the string table. If it does not exist, give up. */
+	shstrndx = eh->e_shstrndx;
+	if (shstrndx == SHN_UNDEF || shstrndx >= eh->e_shnum) {
 		error = ENOEXEC;
 		goto out;
 	}
 
-	/*
-	 * strndx is the index in section header table of the string table
-	 * section get the whole string table in strtable, and then we get access to the names
-	 * s->sh_name is the offset of the section name in strtable.
-	 */
-	strtable = malloc(sh[strndx].sh_size, M_TEMP, M_WAITOK);
-	error = exec_read_from(l, epp->ep_vp, sh[strndx].sh_offset, strtable,
-	    sh[strndx].sh_size);
-	if (error)
-		goto out;
-
+	/* Check if any section has the name we're looking for. */
+	const off_t stroff = sh[shstrndx].sh_offset;
 	for (i = 0; i < eh->e_shnum; i++) {
 		Elf_Shdr *s = &sh[i];
-		if (!memcmp((void*)(&(strtable[s->sh_name])), signature,
-				sizeof(signature))) {
-			DPRINTF(("linux_atexit_sig=%s\n",
-			    &(strtable[s->sh_name])));
+
+		if (s->sh_name + sigsz > sh[shstrndx].sh_size)
+			continue;
+
+		error = exec_read_from(l, epp->ep_vp, stroff + s->sh_name, tbuf,
+		    sigsz);
+		if (error)
+			goto out;
+		if (!memcmp(tbuf, signature, sigsz)) {
+			DPRINTF(("linux_atexit_sig=%s\n", tbuf));
 			error = 0;
 			goto out;
 		}
@@ -144,8 +137,6 @@ ELFNAME2(linux,atexit_signature)(
 
 out:
 	free(sh, M_TEMP);
-	if (strtable)
-		free(strtable, M_TEMP);
 	return (error);
 }
 #endif
@@ -219,56 +210,48 @@ out:
 
 #ifdef LINUX_DEBUGLINK_SIGNATURE
 /*
- * Look for a .gnu_debuglink, specific to x86_64 interpeter
+ * Look for a .gnu_debuglink, specific to x86_64 interpreter
  */
 int
 ELFNAME2(linux,debuglink_signature)(struct lwp *l, struct exec_package *epp, Elf_Ehdr *eh)
 {
+	Elf_Shdr *sh;
 	size_t shsize;
-	int strndx;
+	u_int shstrndx;
 	size_t i;
 	static const char signature[] = ".gnu_debuglink";
-	char *strtable = NULL;
-	Elf_Shdr *sh;
-
+	const size_t sigsz = sizeof(signature);
+	char tbuf[sizeof(signature)];
 	int error;
 
-	/*
-	 * load the section header table
-	 */
+	/* Load the section header table. */
 	shsize = eh->e_shnum * sizeof(Elf_Shdr);
 	sh = (Elf_Shdr *) malloc(shsize, M_TEMP, M_WAITOK);
 	error = exec_read_from(l, epp->ep_vp, eh->e_shoff, sh, shsize);
 	if (error)
 		goto out;
 
-	/*
-	 * Now let's find the string table. If it does not exists, give up.
-	 */
-	strndx = (int)(eh->e_shstrndx);
-	if (strndx == SHN_UNDEF) {
+	/* Now let's find the string table. If it does not exist, give up. */
+	shstrndx = eh->e_shstrndx;
+	if (shstrndx == SHN_UNDEF || shstrndx >= eh->e_shnum) {
 		error = ENOEXEC;
 		goto out;
 	}
 
-	/*
-	 * strndx is the index in section header table of the string table
-	 * section get the whole string table in strtable, and then we get access to the names
-	 * s->sh_name is the offset of the section name in strtable.
-	 */
-	strtable = malloc(sh[strndx].sh_size, M_TEMP, M_WAITOK);
-	error = exec_read_from(l, epp->ep_vp, sh[strndx].sh_offset, strtable,
-	    sh[strndx].sh_size);
-	if (error)
-		goto out;
-
+	/* Check if any section has the name we're looking for. */
+	const off_t stroff = sh[shstrndx].sh_offset;
 	for (i = 0; i < eh->e_shnum; i++) {
 		Elf_Shdr *s = &sh[i];
 
-		if (!memcmp((void*)(&(strtable[s->sh_name])), signature,
-				sizeof(signature))) {
-			DPRINTF(("linux_debuglink_sig=%s\n",
-			    &(strtable[s->sh_name])));
+		if (s->sh_name + sigsz > sh[shstrndx].sh_size)
+			continue;
+
+		error = exec_read_from(l, epp->ep_vp, stroff + s->sh_name, tbuf,
+		    sigsz);
+		if (error)
+			goto out;
+		if (!memcmp(tbuf, signature, sigsz)) {
+			DPRINTF(("linux_debuglink_sig=%s\n", tbuf));
 			error = 0;
 			goto out;
 		}
@@ -277,8 +260,6 @@ ELFNAME2(linux,debuglink_signature)(stru
 
 out:
 	free(sh, M_TEMP);
-	if (strtable)
-		free(strtable, M_TEMP);
 	return (error);
 }
 #endif
@@ -340,7 +321,7 @@ ELFNAME2(linux,signature)(struct lwp *l,
 		continue;
 	}
 
-	/* Check for certain intepreter names. */
+	/* Check for certain interpreter names. */
 	if (itp) {
 		if (!strncmp(itp, "/lib/ld-linux", 13) ||
 #if (ELFSIZE == 64)

Reply via email to