CVE-2014-8116: Resource Management Errors (CWE-399)
value: The ELF parser (readelf.c) in file before 5.21 allows remote attackers to cause a denial of service (CPU consumption or crash) via a large number of (1) program or (2) section headers or (3) invalid capabilities.
CWE description: Weaknesses in this category are related to improper management of system resources.
Published: 2014-12-17T19:59Z
Repository: https://github.com/file/file
Language: C
Files changed: src/elfclass.h, src/readelf.c
Commit date: 2014-11-22 16:04:29 +0000
Commit message: - limit the number of program and section header number of sections to be
  processed to avoid excessive processing time.
- if a bad note is found, return 0 to stop processing immediately.

Diff: diff --git a/src/elfclass.h b/src/elfclass.h
index 010958a42..0826ce321 100644
--- a/src/elfclass.h
+++ b/src/elfclass.h
@@ -33,33 +33,41 @@
 
 	type = elf_getu16(swap, elfhdr.e_type);
 	switch (type) {
 #ifdef ELFCORE
 	case ET_CORE:
+		phnum = elf_getu16(swap, elfhdr.e_phnum);
+		if (phnum > MAX_PHNUM)
+			return toomany(ms, "program", phnum);
 		flags |= FLAGS_IS_CORE;
 		if (dophn_core(ms, clazz, swap, fd,
-		    (off_t)elf_getu(swap, elfhdr.e_phoff),
-		    elf_getu16(swap, elfhdr.e_phnum), 
+		    (off_t)elf_getu(swap, elfhdr.e_phoff), phnum,
 		    (size_t)elf_getu16(swap, elfhdr.e_phentsize),
 		    fsize, &flags) == -1)
 			return -1;
 		break;
 #endif
 	case ET_EXEC:
 	case ET_DYN:
+		phnum = elf_getu16(swap, elfhdr.e_phnum);
+		if (phnum > MAX_PHNUM)
+			return toomany(ms, "program", phnum);
+		shnum = elf_getu16(swap, elfhdr.e_shnum);
+		if (shnum > MAX_SHNUM)
+			return toomany(ms, "section", shnum);
 		if (dophn_exec(ms, clazz, swap, fd,
-		    (off_t)elf_getu(swap, elfhdr.e_phoff),
-		    elf_getu16(swap, elfhdr.e_phnum), 
+		    (off_t)elf_getu(swap, elfhdr.e_phoff), phnum,
 		    (size_t)elf_getu16(swap, elfhdr.e_phentsize),
-		    fsize, &flags, elf_getu16(swap, elfhdr.e_shnum))
-		    == -1)
+		    fsize, &flags, shnum) == -1)
 			return -1;
 		/*FALLTHROUGH*/
 	case ET_REL:
+		shnum = elf_getu16(swap, elfhdr.e_shnum);
+		if (shnum > MAX_SHNUM)
+			return toomany(ms, "section", shnum);
 		if (doshn(ms, clazz, swap, fd,
-		    (off_t)elf_getu(swap, elfhdr.e_shoff),
-		    elf_getu16(swap, elfhdr.e_shnum),
+		    (off_t)elf_getu(swap, elfhdr.e_shoff), shnum,
 		    (size_t)elf_getu16(swap, elfhdr.e_shentsize),
 		    fsize, &flags, elf_getu16(swap, elfhdr.e_machine),
 		    (int)elf_getu16(swap, elfhdr.e_shstrndx)) == -1)
 			return -1;
 		break;
diff --git a/src/readelf.c b/src/readelf.c
index 9ebdebd61..dd4672b44 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -25,11 +25,11 @@
  * SUCH DAMAGE.
  */
 #include "file.h"
 
 #ifndef lint
-FILE_RCSID("@(#)$File: readelf.c,v 1.103 2014/05/02 02:25:10 christos Exp $")
+FILE_RCSID("@(#)$File: readelf.c,v 1.104 2014/10/17 15:49:00 christos Exp $")
 #endif
 
 #ifdef BUILTIN_ELF
 #include <string.h>
 #include <ctype.h>
@@ -58,10 +58,22 @@ private size_t donote(struct magic_set *, void *, size_t, size_t, int,
 
 private uint16_t getu16(int, uint16_t);
 private uint32_t getu32(int, uint32_t);
 private uint64_t getu64(int, uint64_t);
 
+#define MAX_PHNUM	256
+#define	MAX_SHNUM	1024
+
+private int
+toomany(struct magic_set *ms, const char *name, uint16_t num)
+{
+	if (file_printf(ms, ", too many %s header sections (%u)", name, num
+	    ) == -1)
+		return -1;
+	return 0;
+}
+
 private uint16_t
 getu16(int swap, uint16_t value)
 {
 	union {
 		uint16_t ui;
@@ -497,17 +509,17 @@ donote(struct magic_set *ms, void *vbuf, size_t offset, size_t size,
 	}
 
 	if (namesz & 0x80000000) {
 	    (void)file_printf(ms, ", bad note name size 0x%lx",
 		(unsigned long)namesz);
-	    return offset;
+	    return 0;
 	}
 
 	if (descsz & 0x80000000) {
 	    (void)file_printf(ms, ", bad note description size 0x%lx",
 		(unsigned long)descsz);
-	    return offset;
+	    return 0;
 	}
 
 
 	noff = offset;
 	doff = ELF_ALIGN(offset + namesz);
@@ -1238,11 +1250,11 @@ file_tryelf(struct magic_set *ms, int fd, const unsigned char *buf,
 	struct stat st;
 	off_t fsize;
 	int flags = 0;
 	Elf32_Ehdr elf32hdr;
 	Elf64_Ehdr elf64hdr;
-	uint16_t type;
+	uint16_t type, phnum, shnum;
 
 	if (ms->flags & (MAGIC_MIME|MAGIC_APPLE))
 		return 0;
 	/*
 	 * ELF executables have multiple section headers in arbitrary

Vulnerable code: 		    (off_t)elf_getu(swap, elfhdr.e_phoff),
		    elf_getu16(swap, elfhdr.e_phnum), 
		    (off_t)elf_getu(swap, elfhdr.e_phoff),
		    elf_getu16(swap, elfhdr.e_phnum), 
		    fsize, &flags, elf_getu16(swap, elfhdr.e_shnum))
		    == -1)
		    (off_t)elf_getu(swap, elfhdr.e_shoff),
		    elf_getu16(swap, elfhdr.e_shnum),
FILE_RCSID("@(#)$File: readelf.c,v 1.103 2014/05/02 02:25:10 christos Exp $")
	    return offset;
	    return offset;
	uint16_t type;
Fixed code: 		phnum = elf_getu16(swap, elfhdr.e_phnum);
		if (phnum > MAX_PHNUM)
			return toomany(ms, "program", phnum);
		    (off_t)elf_getu(swap, elfhdr.e_phoff), phnum,
		phnum = elf_getu16(swap, elfhdr.e_phnum);
		if (phnum > MAX_PHNUM)
			return toomany(ms, "program", phnum);
		shnum = elf_getu16(swap, elfhdr.e_shnum);
		if (shnum > MAX_SHNUM)
			return toomany(ms, "section", shnum);
		    (off_t)elf_getu(swap, elfhdr.e_phoff), phnum,
		    fsize, &flags, shnum) == -1)
		shnum = elf_getu16(swap, elfhdr.e_shnum);
		if (shnum > MAX_SHNUM)
			return toomany(ms, "section", shnum);
		    (off_t)elf_getu(swap, elfhdr.e_shoff), shnum,
FILE_RCSID("@(#)$File: readelf.c,v 1.104 2014/10/17 15:49:00 christos Exp $")
#define MAX_PHNUM	256
#define	MAX_SHNUM	1024

private int
toomany(struct magic_set *ms, const char *name, uint16_t num)
{
	if (file_printf(ms, ", too many %s header sections (%u)", name, num
	    ) == -1)
		return -1;
	return 0;
}

	    return 0;
	    return 0;
	uint16_t type, phnum, shnum;