CVE-2022-0523: Use After Free (CWE-416)
value: Use After Free in GitHub repository radareorg/radare2 prior to 5.6.2.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 7.8
Published: 2022-02-08T21:15Z
Repository: https://github.com/radareorg/radare2
Language: C
Files changed: libr/bin/format/pyc/marshal.c
Commit date: 2022-02-08 00:55:09 +0100
Commit message: Fix UAF in pyc parser ##crash

* Reported by Cen Zhang via huntr.dev
* Reproducer: bins/fuzzed/harep2.pyc

Diff: diff --git a/libr/bin/format/pyc/marshal.c b/libr/bin/format/pyc/marshal.c
index 728bf737019..16f297198d8 100644
--- a/libr/bin/format/pyc/marshal.c
+++ b/libr/bin/format/pyc/marshal.c
@@ -1,19 +1,19 @@
-/* radare - LGPL3 - Copyright 2016-2021 - Matthieu (c0riolis) Tardy - l0stb1t*/
+/* radare - LGPL3 - Copyright 2016-2022 - Matthieu (c0riolis) Tardy - l0stb1t */
 
 #include <r_io.h>
 #include <r_bin.h>
 #include "marshal.h"
 #include "pyc_magic.h"
 
 // avoiding using r2 internals asserts
 #define if_true_return(cond,ret) if(cond){return(ret);}
 
 // TODO: kill globals
-static ut32 magic_int;
-static ut32 symbols_ordinal = 0;
-static RList *refs = NULL; // If you don't have a good reason, do not change this. And also checkout !refs in get_code_object()
+static R_TH_LOCAL ut32 magic_int;
+static R_TH_LOCAL ut32 symbols_ordinal = 0;
+static R_TH_LOCAL RList *refs = NULL; // If you don't have a good reason, do not change this. And also checkout !refs in get_code_object()
 
 /* interned_table is used to handle TYPE_INTERNED object */
 extern RList *interned_table;
 
 static pyc_object *get_object(RBuffer *buffer);
@@ -498,27 +498,22 @@ static pyc_object *get_array_object_generic(RBuffer *buffer, ut32 size) {
 		free (ret);
 		return NULL;
 	}
 	for (i = 0; i < size; i++) {
 		tmp = get_object (buffer);
-		if (!tmp) {
-			r_list_free (ret->data);
-			R_FREE (ret);
-			return NULL;
-		}
-		if (!r_list_append (ret->data, tmp)) {
+		if (!tmp || !r_list_append (ret->data, tmp)) {
 			free_object (tmp);
+			((RList*)ret->data)->free = NULL;
 			r_list_free (ret->data);
 			free (ret);
 			return NULL;
 		}
 	}
 	return ret;
 }
 
 /* small TYPE_SMALL_TUPLE doesn't exist in python2 */
-/* */
 static pyc_object *get_small_tuple_object(RBuffer *buffer) {
 	pyc_object *ret = NULL;
 	bool error = false;
 	ut8 n = 0;
 
@@ -533,36 +528,30 @@ static pyc_object *get_small_tuple_object(RBuffer *buffer) {
 	}
 	return NULL;
 }
 
 static pyc_object *get_tuple_object(RBuffer *buffer) {
-	pyc_object *ret = NULL;
 	bool error = false;
-	ut32 n = 0;
-
-	n = get_ut32 (buffer, &error);
+	ut32 n = get_ut32 (buffer, &error);
 	if (n > ST32_MAX) {
 		eprintf ("bad marshal data (tuple size out of range)\n");
 		return NULL;
 	}
 	if (error) {
 		return NULL;
 	}
-	ret = get_array_object_generic (buffer, n);
+	pyc_object *ret = get_array_object_generic (buffer, n);
 	if (ret) {
 		ret->type = TYPE_TUPLE;
-		return ret;
 	}
-	return NULL;
+	return ret;
 }
 
 static pyc_object *get_list_object(RBuffer *buffer) {
 	pyc_object *ret = NULL;
 	bool error = false;
-	ut32 n = 0;
-
-	n = get_ut32 (buffer, &error);
+	ut32 n = get_ut32 (buffer, &error);
 	if (n > ST32_MAX) {
 		eprintf ("bad marshal data (list size out of range)\n");
 		return NULL;
 	}
 	if (error) {
@@ -614,25 +603,23 @@ static pyc_object *get_dict_object(RBuffer *buffer) {
 	ret->type = TYPE_DICT;
 	return ret;
 }
 
 static pyc_object *get_set_object(RBuffer *buffer) {
-	pyc_object *ret = NULL;
 	bool error = false;
 	ut32 n = get_ut32 (buffer, &error);
 	if (n > ST32_MAX) {
 		eprintf ("bad marshal data (set size out of range)\n");
 		return NULL;
 	}
 	if (error) {
 		return NULL;
 	}
-	ret = get_array_object_generic (buffer, n);
-	if (!ret) {
-		return NULL;
+	pyc_object *ret = get_array_object_generic (buffer, n);
+	if (ret) {
+		ret->type = TYPE_SET;
 	}
-	ret->type = TYPE_SET;
 	return ret;
 }
 
 static pyc_object *get_ascii_object_generic(RBuffer *buffer, ut32 size, bool interned) {
 	pyc_object *ret = R_NEW0 (pyc_object);

Vulnerable code: /* radare - LGPL3 - Copyright 2016-2021 - Matthieu (c0riolis) Tardy - l0stb1t*/
static ut32 magic_int;
static ut32 symbols_ordinal = 0;
static RList *refs = NULL; // If you don't have a good reason, do not change this. And also checkout !refs in get_code_object()
		if (!tmp) {
			r_list_free (ret->data);
			R_FREE (ret);
			return NULL;
		}
		if (!r_list_append (ret->data, tmp)) {
/* */
	pyc_object *ret = NULL;
	ut32 n = 0;

	n = get_ut32 (buffer, &error);
	ret = get_array_object_generic (buffer, n);
		return ret;
	return NULL;
	ut32 n = 0;

	n = get_ut32 (buffer, &error);
	pyc_object *ret = NULL;
	ret = get_array_object_generic (buffer, n);
	if (!ret) {
		return NULL;
	ret->type = TYPE_SET;
Fixed code: /* radare - LGPL3 - Copyright 2016-2022 - Matthieu (c0riolis) Tardy - l0stb1t */
static R_TH_LOCAL ut32 magic_int;
static R_TH_LOCAL ut32 symbols_ordinal = 0;
static R_TH_LOCAL RList *refs = NULL; // If you don't have a good reason, do not change this. And also checkout !refs in get_code_object()
		if (!tmp || !r_list_append (ret->data, tmp)) {
			((RList*)ret->data)->free = NULL;
	ut32 n = get_ut32 (buffer, &error);
	pyc_object *ret = get_array_object_generic (buffer, n);
	return ret;
	ut32 n = get_ut32 (buffer, &error);
	pyc_object *ret = get_array_object_generic (buffer, n);
	if (ret) {
		ret->type = TYPE_SET;