Vulnerability: The consume_init_expr function in wasm.c in radare2 1.3.0 allows remote attackers to cause a denial of service (heap-based buffer over-read and application crash) via a crafted Web Assembly file.
Vulnerability ID: CVE-2017-7854
CWE: CWE-125
Severity score: 2.9
Project: https://github.com/radare/radare2
Published: 2017-04-13
File: libr/bin/format/wasm/wasm.c
Commit message: Fix crash in fuzzed wasm r2_hoobr_consume_init_expr
Commit author: pancake

Vulnerable version (before fix):
 static size_t consume_str (ut8 *buf, ut8 *max, size_t sz, char *out, ut32 *offse 	if (offset) *offset += sz; 	return sz; } static size_t consume_init_expr (ut8 *buf, ut8 *max, ut8 eoc, void *out, ut32 *offset) { 	ut32 i = 0; 	while (buf + i < max && buf[i] != eoc) { 		// TODO: calc the expresion with the bytcode (ESIL?) 		i += 1; 	} 	if (buf[i] != eoc) { 		return 0; static RList *r_bin_wasm_get_code_entries (RBinWasmObj *bin, RBinWasmSection *se }  static RList *r_bin_wasm_get_data_entries (RBinWasmObj *bin, RBinWasmSection *sec) {  	RList *ret = NULL; 	RBinWasmDataEntry *ptr = NULL;  	if (!(ret = r_list_newf ((RListFree)free))) { 		return NULL; 	}  	ut8* buf = bin->buf->buf + (ut32)sec->payload_data; 	ut32 len =  sec->payload_len; 	ut32 count = sec->count; 	ut32 i = 0, r = 0; 	size_t n = 0;  	while (i < len && r < count) {  		if (!(ptr = R_NEW0 (RBinWasmDataEntry))) { 			return ret; 		}  		if (!(consume_u32 (buf + i, buf + len, &ptr->index, &i))) { 			free (ptr); 			return ret; 		}  		if (!(n = consume_init_expr (buf + i, buf + len, R_BIN_WASM_END_OF_CODE, NULL, &i))) { 			free (ptr); 			return ret; 		}  		ptr->offset.len = n;  		if (!(consume_u32 (buf + i, buf + len, &ptr->size, &i))) {	 			free (ptr); 			return ret; 		}  		ptr->data = sec->payload_data + i;  		r_list_append (ret, ptr);  		r += 1;  	}  	return ret; }  static RList *r_bin_wasm_get_table_entries (RBinWasmObj *bin, RBinWasmSection *s static RList *r_bin_wasm_get_global_entries (RBinWasmObj *bin, RBinWasmSection *sec) { 	RList *ret = NULL; 	RBinWasmGlobalEntry *ptr = NULL; 	int buflen = bin->buf->length; 	if (sec->payload_data + 32 > buflen) { 		return NULL; 	}  	if (!(ret = r_list_newf ((RListFree)free))) { 		return NULL; 	}  	ut8* buf = bin->buf->buf + (ut32)sec->payload_data; 	ut32 len =  sec->payload_len; 	ut32 count = sec->count; 	ut32 i = 0, r = 0; static RList *r_bin_wasm_get_global_entries (RBinWasmObj *bin, RBinWasmSection * }  static RList *r_bin_wasm_get_element_entries (RBinWasmObj *bin, RBinWasmSection *sec) {  	RList *ret = NULL; 	RBinWasmElementEntry *ptr = NULL;  static RList *r_bin_wasm_get_element_entries (RBinWasmObj *bin, RBinWasmSection 	}  	ut8* buf = bin->buf->buf + (ut32)sec->payload_data; 	ut32 len =  sec->payload_len; 	ut32 count = sec->count; 	ut32 i = 0, r = 0;  	while (i < len && r < count) {  		if (!(ptr = R_NEW0 (RBinWasmElementEntry))) { 			return ret; 		}  		if (!(consume_u32 (buf + i, buf + len, &ptr->index, &i))) { 			free (ptr); 			return ret; 		}  		if (!(consume_init_expr (buf + i, buf + len, R_BIN_WASM_END_OF_CODE, NULL, &i))) { 			free (ptr); 			return ret; 		}  		if (!(consume_u32 (buf + i, buf + len, &ptr->num_elem, &i))) { 			free (ptr); 			return ret; 		}  		ut32 j = 0; 		while (i < len && j < ptr->num_elem	) { 			// TODO: allocate space and fill entry 			ut32 e; 			if (!(consume_u32 (buf + i, buf + len, &e, &i))) { 				free (ptr); 				return ret; 			} 		}  		r_list_append (ret, ptr);  		r += 1;  	}  	return ret; } 

Fixed version (after fix):
 static size_t consume_str (ut8 *buf, ut8 *max, size_t sz, char *out, ut32 *offse 	if (offset) *offset += sz; 	return sz; }  static size_t consume_init_expr (ut8 *buf, ut8 *max, ut8 eoc, void *out, ut32 *offset) { 	ut32 i = 0; 	while (buf + i < max && buf[i] != eoc) { 		// TODO: calc the expresion with the bytcode (ESIL?) 		i++; 	} 	if (buf[i] != eoc) { 		return 0; static RList *r_bin_wasm_get_code_entries (RBinWasmObj *bin, RBinWasmSection *se }  static RList *r_bin_wasm_get_data_entries (RBinWasmObj *bin, RBinWasmSection *sec) { 	RList *ret = NULL; 	RBinWasmDataEntry *ptr = NULL; 	ut32 len =  sec->payload_len;  	if (!(ret = r_list_newf ((RListFree)free))) { 		return NULL; 	}  	ut8* buf = bin->buf->buf + (ut32)sec->payload_data; 	int buflen = bin->buf->length - (ut32)sec->payload_data; 	ut32 count = sec->count; 	ut32 i = 0, r = 0; 	size_t n = 0;  	while (i < len && len < buflen && r < count) { 		if (!(ptr = R_NEW0 (RBinWasmDataEntry))) { 			return ret; 		} 		if (!(consume_u32 (buf + i, buf + len, &ptr->index, &i))) { 			goto beach; 		} 		if (i + 4 >= buflen) { 			goto beach; 		} 		if (!(n = consume_init_expr (buf + i, buf + len, R_BIN_WASM_END_OF_CODE, NULL, &i))) { 			goto beach; 		} 		ptr->offset.len = n; 		if (!(consume_u32 (buf + i, buf + len, &ptr->size, &i))) {	 			goto beach; 		} 		if (i + 4 >= buflen) { 			goto beach; 		} 		ptr->data = sec->payload_data + i;  		r_list_append (ret, ptr);  		r += 1;  	} 	return ret; beach: 	free (ptr); 	return ret; }  static RList *r_bin_wasm_get_table_entries (RBinWasmObj *bin, RBinWasmSection *s static RList *r_bin_wasm_get_global_entries (RBinWasmObj *bin, RBinWasmSection *sec) { 	RList *ret = NULL; 	RBinWasmGlobalEntry *ptr = NULL;  	if (!(ret = r_list_newf ((RListFree)free))) { 		return NULL; 	}  	ut8* buf = bin->buf->buf + (ut32)sec->payload_data; 	int buflen = bin->buf->length - (ut32)sec->payload_data; 	ut32 len =  sec->payload_len; 	ut32 count = sec->count; 	ut32 i = 0, r = 0; static RList *r_bin_wasm_get_global_entries (RBinWasmObj *bin, RBinWasmSection * }  static RList *r_bin_wasm_get_element_entries (RBinWasmObj *bin, RBinWasmSection *sec) { 	RList *ret = NULL; 	RBinWasmElementEntry *ptr = NULL;  static RList *r_bin_wasm_get_element_entries (RBinWasmObj *bin, RBinWasmSection 	}  	ut8* buf = bin->buf->buf + (ut32)sec->payload_data; 	int buflen = bin->buf->length - (ut32)sec->payload_data; 	ut32 len =  sec->payload_len; 	ut32 count = sec->count; 	ut32 i = 0, r = 0;  	while (i < len && len < buflen && r < count) { 		if (!(ptr = R_NEW0 (RBinWasmElementEntry))) { 			return ret; 		} 		if (!(consume_u32 (buf + i, buf + len, &ptr->index, &i))) { 			goto beach; 		} 		if (!(consume_init_expr (buf + i, buf + len, R_BIN_WASM_END_OF_CODE, NULL, &i))) { 			goto beach; 		} 		if (!(consume_u32 (buf + i, buf + len, &ptr->num_elem, &i))) { 			goto beach; 		} 		ut32 j = 0; 		while (i < len && j < ptr->num_elem) { 			// TODO: allocate space and fill entry 			ut32 e; 			if (!(consume_u32 (buf + i, buf + len, &e, &i))) { 				free (ptr); 				return ret; 			} 		} 		r_list_append (ret, ptr);  		r += 1; 	} 	return ret; beach: 	free (ptr); 	return ret; } 

Patch:
patch: @@ -67,11 +67,12 @@ static size_t consume_str (ut8 *buf, ut8 *max, size_t sz, char *out, ut32 *offse
 	if (offset) *offset += sz;
 	return sz;
 }
+
 static size_t consume_init_expr (ut8 *buf, ut8 *max, ut8 eoc, void *out, ut32 *offset) {
 	ut32 i = 0;
 	while (buf + i < max && buf[i] != eoc) {
 		// TODO: calc the expresion with the bytcode (ESIL?)
-		i += 1;
+		i++;
 	}
 	if (buf[i] != eoc) {
 		return 0;
@@ -448,51 +449,50 @@ static RList *r_bin_wasm_get_code_entries (RBinWasmObj *bin, RBinWasmSection *se
 }
 
 static RList *r_bin_wasm_get_data_entries (RBinWasmObj *bin, RBinWasmSection *sec) {
-
 	RList *ret = NULL;
 	RBinWasmDataEntry *ptr = NULL;
+	ut32 len =  sec->payload_len;
 
 	if (!(ret = r_list_newf ((RListFree)free))) {
 		return NULL;
 	}
 
 	ut8* buf = bin->buf->buf + (ut32)sec->payload_data;
-	ut32 len =  sec->payload_len;
+	int buflen = bin->buf->length - (ut32)sec->payload_data;
 	ut32 count = sec->count;
 	ut32 i = 0, r = 0;
 	size_t n = 0;
 
-	while (i < len && r < count) {
-
+	while (i < len && len < buflen && r < count) {
 		if (!(ptr = R_NEW0 (RBinWasmDataEntry))) {
 			return ret;
 		}
-
 		if (!(consume_u32 (buf + i, buf + len, &ptr->index, &i))) {
-			free (ptr);
-			return ret;
+			goto beach;
+		}
+		if (i + 4 >= buflen) {
+			goto beach;
 		}
-
 		if (!(n = consume_init_expr (buf + i, buf + len, R_BIN_WASM_END_OF_CODE, NULL, &i))) {
-			free (ptr);
-			return ret;
+			goto beach;
 		}
-
 		ptr->offset.len = n;
-
 		if (!(consume_u32 (buf + i, buf + len, &ptr->size, &i))) {	
-			free (ptr);
-			return ret;
+			goto beach;
+		}
+		if (i + 4 >= buflen) {
+			goto beach;
 		}
-
 		ptr->data = sec->payload_data + i;
 
 		r_list_append (ret, ptr);
 
 		r += 1;
 
 	}
-
+	return ret;
+beach:
+	free (ptr);
 	return ret;
 }
 
@@ -593,16 +593,13 @@ static RList *r_bin_wasm_get_table_entries (RBinWasmObj *bin, RBinWasmSection *s
 static RList *r_bin_wasm_get_global_entries (RBinWasmObj *bin, RBinWasmSection *sec) {
 	RList *ret = NULL;
 	RBinWasmGlobalEntry *ptr = NULL;
-	int buflen = bin->buf->length;
-	if (sec->payload_data + 32 > buflen) {
-		return NULL;
-	}
 
 	if (!(ret = r_list_newf ((RListFree)free))) {
 		return NULL;
 	}
 
 	ut8* buf = bin->buf->buf + (ut32)sec->payload_data;
+	int buflen = bin->buf->length - (ut32)sec->payload_data;
 	ut32 len =  sec->payload_len;
 	ut32 count = sec->count;
 	ut32 i = 0, r = 0;
@@ -631,7 +628,6 @@ static RList *r_bin_wasm_get_global_entries (RBinWasmObj *bin, RBinWasmSection *
 }
 
 static RList *r_bin_wasm_get_element_entries (RBinWasmObj *bin, RBinWasmSection *sec) {
-
 	RList *ret = NULL;
 	RBinWasmElementEntry *ptr = NULL;
 
@@ -640,47 +636,40 @@ static RList *r_bin_wasm_get_element_entries (RBinWasmObj *bin, RBinWasmSection
 	}
 
 	ut8* buf = bin->buf->buf + (ut32)sec->payload_data;
+	int buflen = bin->buf->length - (ut32)sec->payload_data;
 	ut32 len =  sec->payload_len;
 	ut32 count = sec->count;
 	ut32 i = 0, r = 0;
 
-	while (i < len && r < count) {
-
+	while (i < len && len < buflen && r < count) {
 		if (!(ptr = R_NEW0 (RBinWasmElementEntry))) {
 			return ret;
 		}
-
 		if (!(consume_u32 (buf + i, buf + len, &ptr->index, &i))) {
-			free (ptr);
-			return ret;
+			goto beach;
 		}
-
 		if (!(consume_init_expr (buf + i, buf + len, R_BIN_WASM_END_OF_CODE, NULL, &i))) {
-			free (ptr);
-			return ret;
+			goto beach;
 		}
-
 		if (!(consume_u32 (buf + i, buf + len, &ptr->num_elem, &i))) {
-			free (ptr);
-			return ret;
+			goto beach;
 		}
-
 		ut32 j = 0;
-		while (i < len && j < ptr->num_elem	) {
+		while (i < len && j < ptr->num_elem) {
 			// TODO: allocate space and fill entry
 			ut32 e;
 			if (!(consume_u32 (buf + i, buf + len, &e, &i))) {
 				free (ptr);
 				return ret;
 			}
 		}
-
 		r_list_append (ret, ptr);
 
 		r += 1;
-
 	}
-
+	return ret;
+beach:
+	free (ptr);
 	return ret;
 }
path: libr/bin/format/wasm/wasm.c