Vulnerability: readelf.c in file before 5.22, as used in the Fileinfo component in PHP before 5.4.37, 5.5.x before 5.5.21, and 5.6.x before 5.6.5, does not consider that pread calls sometimes read only a subset of the available data, which allows remote attackers to cause a denial of service (uninitialized memory access) or possibly have unspecified other impact via a crafted ELF file.
Vulnerability ID: CVE-2014-9653
CWE: CWE-20
Severity score: 6.4
Project: https://github.com/file/file
Published: 2015-03-30
File: src/readelf.c
Commit message: Bail out on partial reads, from Alexander Cherepanov
Commit author: Christos Zoulas

Vulnerable version (before fix):
 #include "file.h"  #ifndef lint FILE_RCSID("@(#)$File: readelf.c,v 1.113 2014/12/11 14:10:53 christos Exp $") #endif  #ifdef BUILTIN_ELF dophn_core(struct magic_set *ms, int clazz, int swap, int fd, off_t off, 	 * Loop through all the program headers. 	 */ 	for ( ; num; num--) { 		if (pread(fd, xph_addr, xph_sizeof, off) == -1) { 			file_badread(ms); 			return -1; 		} doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, 	uint64_t cap_hw1 = 0;	/* SunOS 5.x hardware capabilites */ 	uint64_t cap_sf1 = 0;	/* SunOS 5.x software capabilites */ 	char name[50];  	if (size != xsh_sizeof) { 		if (file_printf(ms, ", corrupted section header size") == -1) doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, 	}  	/* Read offset of name section to be able to read section names later */ 	if (pread(fd, xsh_addr, xsh_sizeof, off + size * strtab) == -1) { 		file_badread(ms); 		return -1; 	} 	name_off = xsh_offset;  	for ( ; num; num--) { 		/* Read the name of this section. */ 		if (pread(fd, name, sizeof(name), name_off + xsh_name) == -1) { 			file_badread(ms); 			return -1; 		} 		name[sizeof(name) - 1] = '\0'; 		if (strcmp(name, ".debug_info") == 0) 			stripped = 0;  		if (pread(fd, xsh_addr, xsh_sizeof, off) == -1) { 			file_badread(ms); 			return -1; 		} doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, 				    " for note"); 				return -1; 			} 			if (pread(fd, nbuf, xsh_size, xsh_offset) == -1) { 				file_badread(ms); 				free(nbuf); 				return -1; dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off, 	}    	for ( ; num; num--) { 		if (pread(fd, xph_addr, xph_sizeof, off) == -1) { 			file_badread(ms); 			return -1; 		}

Fixed version (after fix):
 #include "file.h"  #ifndef lint FILE_RCSID("@(#)$File: readelf.c,v 1.114 2014/12/11 14:19:36 christos Exp $") #endif  #ifdef BUILTIN_ELF dophn_core(struct magic_set *ms, int clazz, int swap, int fd, off_t off, 	 * Loop through all the program headers. 	 */ 	for ( ; num; num--) { 		if (pread(fd, xph_addr, xph_sizeof, off) < (ssize_t)xph_sizeof) { 			file_badread(ms); 			return -1; 		} doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, 	uint64_t cap_hw1 = 0;	/* SunOS 5.x hardware capabilites */ 	uint64_t cap_sf1 = 0;	/* SunOS 5.x software capabilites */ 	char name[50]; 	ssize_t namesize;  	if (size != xsh_sizeof) { 		if (file_printf(ms, ", corrupted section header size") == -1) doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, 	}  	/* Read offset of name section to be able to read section names later */ 	if (pread(fd, xsh_addr, xsh_sizeof, off + size * strtab) < (ssize_t)xsh_sizeof) { 		file_badread(ms); 		return -1; 	} 	name_off = xsh_offset;  	for ( ; num; num--) { 		/* Read the name of this section. */ 		if ((namesize = pread(fd, name, sizeof(name) - 1, name_off + xsh_name)) == -1) { 			file_badread(ms); 			return -1; 		} 		name[namesize] = '\0'; 		if (strcmp(name, ".debug_info") == 0) 			stripped = 0;  		if (pread(fd, xsh_addr, xsh_sizeof, off) < (ssize_t)xsh_sizeof) { 			file_badread(ms); 			return -1; 		} doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, 				    " for note"); 				return -1; 			} 			if (pread(fd, nbuf, xsh_size, xsh_offset) < (ssize_t)xsh_size) { 				file_badread(ms); 				free(nbuf); 				return -1; dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off, 	}    	for ( ; num; num--) { 		if (pread(fd, xph_addr, xph_sizeof, off) < (ssize_t)xph_sizeof) { 			file_badread(ms); 			return -1; 		}

Patch:
patch: @@ -27,7 +27,7 @@
 #include "file.h"
 
 #ifndef lint
-FILE_RCSID("@(#)$File: readelf.c,v 1.113 2014/12/11 14:10:53 christos Exp $")
+FILE_RCSID("@(#)$File: readelf.c,v 1.114 2014/12/11 14:19:36 christos Exp $")
 #endif
 
 #ifdef BUILTIN_ELF
@@ -319,7 +319,7 @@ dophn_core(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
 	 * Loop through all the program headers.
 	 */
 	for ( ; num; num--) {
-		if (pread(fd, xph_addr, xph_sizeof, off) == -1) {
+		if (pread(fd, xph_addr, xph_sizeof, off) < (ssize_t)xph_sizeof) {
 			file_badread(ms);
 			return -1;
 		}
@@ -928,6 +928,7 @@ doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num,
 	uint64_t cap_hw1 = 0;	/* SunOS 5.x hardware capabilites */
 	uint64_t cap_sf1 = 0;	/* SunOS 5.x software capabilites */
 	char name[50];
+	ssize_t namesize;
 
 	if (size != xsh_sizeof) {
 		if (file_printf(ms, ", corrupted section header size") == -1)
@@ -936,23 +937,23 @@ doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num,
 	}
 
 	/* Read offset of name section to be able to read section names later */
-	if (pread(fd, xsh_addr, xsh_sizeof, off + size * strtab) == -1) {
+	if (pread(fd, xsh_addr, xsh_sizeof, off + size * strtab) < (ssize_t)xsh_sizeof) {
 		file_badread(ms);
 		return -1;
 	}
 	name_off = xsh_offset;
 
 	for ( ; num; num--) {
 		/* Read the name of this section. */
-		if (pread(fd, name, sizeof(name), name_off + xsh_name) == -1) {
+		if ((namesize = pread(fd, name, sizeof(name) - 1, name_off + xsh_name)) == -1) {
 			file_badread(ms);
 			return -1;
 		}
-		name[sizeof(name) - 1] = '\0';
+		name[namesize] = '\0';
 		if (strcmp(name, ".debug_info") == 0)
 			stripped = 0;
 
-		if (pread(fd, xsh_addr, xsh_sizeof, off) == -1) {
+		if (pread(fd, xsh_addr, xsh_sizeof, off) < (ssize_t)xsh_sizeof) {
 			file_badread(ms);
 			return -1;
 		}
@@ -982,7 +983,7 @@ doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num,
 				    " for note");
 				return -1;
 			}
-			if (pread(fd, nbuf, xsh_size, xsh_offset) == -1) {
+			if (pread(fd, nbuf, xsh_size, xsh_offset) < (ssize_t)xsh_size) {
 				file_badread(ms);
 				free(nbuf);
 				return -1;
@@ -1178,7 +1179,7 @@ dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
 	}
 
   	for ( ; num; num--) {
-		if (pread(fd, xph_addr, xph_sizeof, off) == -1) {
+		if (pread(fd, xph_addr, xph_sizeof, off) < (ssize_t)xph_sizeof) {
 			file_badread(ms);
 			return -1;
 		}
path: src/readelf.c