Vulnerability: softmagic.c in file before 5.17 and libmagic allows context-dependent attackers to cause a denial of service (out-of-bounds memory access and crash) via crafted offsets in the softmagic of a PE executable.
Vulnerability ID: CVE-2014-2270
CWE: CWE-119
Severity score: 2.9
Project: https://github.com/file/file
Published: 2014-03-14
File: src/softmagic.c
Commit message: PR/313: Aaron Reffett: Check properly for exceeding the offset.
Commit author: Christos Zoulas

Vulnerable version (before fix):
 #include "file.h"  #ifndef	lint FILE_RCSID("@(#)$File: softmagic.c,v 1.170 2014/01/06 02:25:32 christos Exp $") #endif	/* lint */  #include "magic.h" private void cvt_16(union VALUETYPE *, const struct magic *); private void cvt_32(union VALUETYPE *, const struct magic *); private void cvt_64(union VALUETYPE *, const struct magic *);  /*  * softmagic - lookup one file in parsed, in-memory copy of database  * Passed the name and FILE * of one file to be typed. mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 		} 		switch (in_type = cvt_flip(m->in_type, flip)) { 		case FILE_BYTE: 			if (nbytes < offset || nbytes < (offset + 1)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 				offset = ~offset; 			break; 		case FILE_BESHORT: 			if (nbytes < offset || nbytes < (offset + 2)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 				offset = ~offset; 			break; 		case FILE_LESHORT: 			if (nbytes < offset || nbytes < (offset + 2)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 				offset = ~offset; 			break; 		case FILE_SHORT: 			if (nbytes < offset || nbytes < (offset + 2)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 			break; 		case FILE_BELONG: 		case FILE_BEID3: 			if (nbytes < offset || nbytes < (offset + 4)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 			break; 		case FILE_LELONG: 		case FILE_LEID3: 			if (nbytes < offset || nbytes < (offset + 4)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 				offset = ~offset; 			break; 		case FILE_MELONG: 			if (nbytes < offset || nbytes < (offset + 4)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 				offset = ~offset; 			break; 		case FILE_LONG: 			if (nbytes < offset || nbytes < (offset + 4)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 	/* Verify we have enough data to match magic type */ 	switch (m->type) { 	case FILE_BYTE: 		if (nbytes < (offset + 1)) /* should alway be true */ 			return 0; 		break;  	case FILE_SHORT: 	case FILE_BESHORT: 	case FILE_LESHORT: 		if (nbytes < (offset + 2)) 			return 0; 		break;  mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 	case FILE_FLOAT: 	case FILE_BEFLOAT: 	case FILE_LEFLOAT: 		if (nbytes < (offset + 4)) 			return 0; 		break;  	case FILE_DOUBLE: 	case FILE_BEDOUBLE: 	case FILE_LEDOUBLE: 		if (nbytes < (offset + 8)) 			return 0; 		break;  	case FILE_STRING: 	case FILE_PSTRING: 	case FILE_SEARCH: 		if (nbytes < (offset + m->vallen)) 			return 0; 		break;  	case FILE_REGEX: 		if (nbytes < offset) 			return 0; 		break;  	case FILE_INDIRECT: 		if (nbytes < offset) 			return 0; 		sbuf = ms->o.buf; 		soffset = ms->offset; mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 		return rv;  	case FILE_USE: 		if (nbytes < offset) 			return 0; 		sbuf = m->value.s; 		if (*sbuf == '^') {

Fixed version (after fix):
 #include "file.h"  #ifndef	lint FILE_RCSID("@(#)$File: softmagic.c,v 1.171 2014/01/08 22:02:06 christos Exp $") #endif	/* lint */  #include "magic.h" private void cvt_16(union VALUETYPE *, const struct magic *); private void cvt_32(union VALUETYPE *, const struct magic *); private void cvt_64(union VALUETYPE *, const struct magic *);  #define OFFSET_OOB(n, o, i)	((n) < (o) || (i) >= ((n) - (o))) /*  * softmagic - lookup one file in parsed, in-memory copy of database  * Passed the name and FILE * of one file to be typed. mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 		} 		switch (in_type = cvt_flip(m->in_type, flip)) { 		case FILE_BYTE: 			if (OFFSET_OOB(nbytes, offset, 1)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 				offset = ~offset; 			break; 		case FILE_BESHORT: 			if (OFFSET_OOB(nbytes, offset, 2)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 				offset = ~offset; 			break; 		case FILE_LESHORT: 			if (OFFSET_OOB(nbytes, offset, 2)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 				offset = ~offset; 			break; 		case FILE_SHORT: 			if (OFFSET_OOB(nbytes, offset, 2)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 			break; 		case FILE_BELONG: 		case FILE_BEID3: 			if (OFFSET_OOB(nbytes, offset, 4)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 			break; 		case FILE_LELONG: 		case FILE_LEID3: 			if (OFFSET_OOB(nbytes, offset, 4)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 				offset = ~offset; 			break; 		case FILE_MELONG: 			if (OFFSET_OOB(nbytes, offset, 4)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 				offset = ~offset; 			break; 		case FILE_LONG: 			if (OFFSET_OOB(nbytes, offset, 4)) 				return 0; 			if (off) { 				switch (m->in_op & FILE_OPS_MASK) { mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 	/* Verify we have enough data to match magic type */ 	switch (m->type) { 	case FILE_BYTE: 		if (OFFSET_OOB(nbytes, offset, 1)) 			return 0; 		break;  	case FILE_SHORT: 	case FILE_BESHORT: 	case FILE_LESHORT: 		if (OFFSET_OOB(nbytes, offset, 2)) 			return 0; 		break;  mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 	case FILE_FLOAT: 	case FILE_BEFLOAT: 	case FILE_LEFLOAT: 		if (OFFSET_OOB(nbytes, offset, 4)) 			return 0; 		break;  	case FILE_DOUBLE: 	case FILE_BEDOUBLE: 	case FILE_LEDOUBLE: 		if (OFFSET_OOB(nbytes, offset, 8)) 			return 0; 		break;  	case FILE_STRING: 	case FILE_PSTRING: 	case FILE_SEARCH: 		if (OFFSET_OOB(nbytes, offset, m->vallen)) 			return 0; 		break;  	case FILE_REGEX: 		if (OFFSET_OOB(nbytes, offset, 0)) 			return 0; 		break;  	case FILE_INDIRECT: 		if (OFFSET_OOB(nbytes, offset, 0)) 			return 0; 		sbuf = ms->o.buf; 		soffset = ms->offset; mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 		return rv;  	case FILE_USE: 		if (OFFSET_OOB(nbytes, offset, 0)) 			return 0; 		sbuf = m->value.s; 		if (*sbuf == '^') {

Patch:
patch: @@ -32,7 +32,7 @@
 #include "file.h"
 
 #ifndef	lint
-FILE_RCSID("@(#)$File: softmagic.c,v 1.170 2014/01/06 02:25:32 christos Exp $")
+FILE_RCSID("@(#)$File: softmagic.c,v 1.171 2014/01/08 22:02:06 christos Exp $")
 #endif	/* lint */
 
 #include "magic.h"
@@ -71,6 +71,7 @@ private void cvt_16(union VALUETYPE *, const struct magic *);
 private void cvt_32(union VALUETYPE *, const struct magic *);
 private void cvt_64(union VALUETYPE *, const struct magic *);
 
+#define OFFSET_OOB(n, o, i)	((n) < (o) || (i) >= ((n) - (o)))
 /*
  * softmagic - lookup one file in parsed, in-memory copy of database
  * Passed the name and FILE * of one file to be typed.
@@ -1223,7 +1224,7 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m,
 		}
 		switch (in_type = cvt_flip(m->in_type, flip)) {
 		case FILE_BYTE:
-			if (nbytes < offset || nbytes < (offset + 1))
+			if (OFFSET_OOB(nbytes, offset, 1))
 				return 0;
 			if (off) {
 				switch (m->in_op & FILE_OPS_MASK) {
@@ -1258,7 +1259,7 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m,
 				offset = ~offset;
 			break;
 		case FILE_BESHORT:
-			if (nbytes < offset || nbytes < (offset + 2))
+			if (OFFSET_OOB(nbytes, offset, 2))
 				return 0;
 			if (off) {
 				switch (m->in_op & FILE_OPS_MASK) {
@@ -1310,7 +1311,7 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m,
 				offset = ~offset;
 			break;
 		case FILE_LESHORT:
-			if (nbytes < offset || nbytes < (offset + 2))
+			if (OFFSET_OOB(nbytes, offset, 2))
 				return 0;
 			if (off) {
 				switch (m->in_op & FILE_OPS_MASK) {
@@ -1362,7 +1363,7 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m,
 				offset = ~offset;
 			break;
 		case FILE_SHORT:
-			if (nbytes < offset || nbytes < (offset + 2))
+			if (OFFSET_OOB(nbytes, offset, 2))
 				return 0;
 			if (off) {
 				switch (m->in_op & FILE_OPS_MASK) {
@@ -1399,7 +1400,7 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m,
 			break;
 		case FILE_BELONG:
 		case FILE_BEID3:
-			if (nbytes < offset || nbytes < (offset + 4))
+			if (OFFSET_OOB(nbytes, offset, 4))
 				return 0;
 			if (off) {
 				switch (m->in_op & FILE_OPS_MASK) {
@@ -1470,7 +1471,7 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m,
 			break;
 		case FILE_LELONG:
 		case FILE_LEID3:
-			if (nbytes < offset || nbytes < (offset + 4))
+			if (OFFSET_OOB(nbytes, offset, 4))
 				return 0;
 			if (off) {
 				switch (m->in_op & FILE_OPS_MASK) {
@@ -1540,7 +1541,7 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m,
 				offset = ~offset;
 			break;
 		case FILE_MELONG:
-			if (nbytes < offset || nbytes < (offset + 4))
+			if (OFFSET_OOB(nbytes, offset, 4))
 				return 0;
 			if (off) {
 				switch (m->in_op & FILE_OPS_MASK) {
@@ -1610,7 +1611,7 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m,
 				offset = ~offset;
 			break;
 		case FILE_LONG:
-			if (nbytes < offset || nbytes < (offset + 4))
+			if (OFFSET_OOB(nbytes, offset, 4))
 				return 0;
 			if (off) {
 				switch (m->in_op & FILE_OPS_MASK) {
@@ -1687,14 +1688,14 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m,
 	/* Verify we have enough data to match magic type */
 	switch (m->type) {
 	case FILE_BYTE:
-		if (nbytes < (offset + 1)) /* should alway be true */
+		if (OFFSET_OOB(nbytes, offset, 1))
 			return 0;
 		break;
 
 	case FILE_SHORT:
 	case FILE_BESHORT:
 	case FILE_LESHORT:
-		if (nbytes < (offset + 2))
+		if (OFFSET_OOB(nbytes, offset, 2))
 			return 0;
 		break;
 
@@ -1713,31 +1714,31 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m,
 	case FILE_FLOAT:
 	case FILE_BEFLOAT:
 	case FILE_LEFLOAT:
-		if (nbytes < (offset + 4))
+		if (OFFSET_OOB(nbytes, offset, 4))
 			return 0;
 		break;
 
 	case FILE_DOUBLE:
 	case FILE_BEDOUBLE:
 	case FILE_LEDOUBLE:
-		if (nbytes < (offset + 8))
+		if (OFFSET_OOB(nbytes, offset, 8))
 			return 0;
 		break;
 
 	case FILE_STRING:
 	case FILE_PSTRING:
 	case FILE_SEARCH:
-		if (nbytes < (offset + m->vallen))
+		if (OFFSET_OOB(nbytes, offset, m->vallen))
 			return 0;
 		break;
 
 	case FILE_REGEX:
-		if (nbytes < offset)
+		if (OFFSET_OOB(nbytes, offset, 0))
 			return 0;
 		break;
 
 	case FILE_INDIRECT:
-		if (nbytes < offset)
+		if (OFFSET_OOB(nbytes, offset, 0))
 			return 0;
 		sbuf = ms->o.buf;
 		soffset = ms->offset;
@@ -1761,7 +1762,7 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m,
 		return rv;
 
 	case FILE_USE:
-		if (nbytes < offset)
+		if (OFFSET_OOB(nbytes, offset, 0))
 			return 0;
 		sbuf = m->value.s;
 		if (*sbuf == '^') {
path: src/softmagic.c