[Python-checkins] r55386 - in python/trunk: Lib/test/test_marshal.py Misc/ACKS Misc/NEWS Python/marshal.c

neal.norwitz python-checkins at python.org
Wed May 16 22:05:18 CEST 2007


Author: neal.norwitz
Date: Wed May 16 22:05:11 2007
New Revision: 55386

Modified:
   python/trunk/Lib/test/test_marshal.py
   python/trunk/Misc/ACKS
   python/trunk/Misc/NEWS
   python/trunk/Python/marshal.c
Log:
Fix bug in marshal where bad data would cause a segfault due to
lack of an infinite recursion check.

Contributed by Damien Miller at Google.


Modified: python/trunk/Lib/test/test_marshal.py
==============================================================================
--- python/trunk/Lib/test/test_marshal.py	(original)
+++ python/trunk/Lib/test/test_marshal.py	Wed May 16 22:05:11 2007
@@ -220,6 +220,10 @@
             except Exception:
                 pass
 
+    def test_recursion(self):
+        s = 'c' + ('X' * 4*4) + '{' * 2**20
+        self.assertRaises(ValueError, marshal.loads, s)
+
 def test_main():
     test_support.run_unittest(IntTestCase,
                               FloatTestCase,

Modified: python/trunk/Misc/ACKS
==============================================================================
--- python/trunk/Misc/ACKS	(original)
+++ python/trunk/Misc/ACKS	Wed May 16 22:05:11 2007
@@ -429,6 +429,7 @@
 Greg McFarlane
 Michael McLay
 Gordon McMillan
+Damien Miller
 Jay T. Miller
 Chris McDonough
 Andrew McNamara

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Wed May 16 22:05:11 2007
@@ -207,6 +207,9 @@
 Library
 -------
 
+- Fix bug in marshal where bad data would cause a segfault due to
+  lack of an infinite recursion check.
+
 - Removed plat-freebsd2 and plat-freebsd3 directories (and IN.py in
   the directories).
 

Modified: python/trunk/Python/marshal.c
==============================================================================
--- python/trunk/Python/marshal.c	(original)
+++ python/trunk/Python/marshal.c	Wed May 16 22:05:11 2007
@@ -246,9 +246,16 @@
 				goto exit;
 			}
 			else {
+				int ok;
 				o = PyInt_FromSsize_t(PyDict_Size(p->strings));
-				PyDict_SetItem(p->strings, v, o);
-				Py_DECREF(o);
+				ok = o &&
+				     PyDict_SetItem(p->strings, v, o) >= 0;
+				Py_XDECREF(o);
+				if (!ok) {
+					p->depth--;
+					p->error = 1;
+					return;
+				}
 				w_byte(TYPE_INTERNED, p);
 			}
 		}
@@ -413,7 +420,7 @@
 
 typedef WFILE RFILE; /* Same struct with different invariants */
 
-#define rs_byte(p) (((p)->ptr != (p)->end) ? (unsigned char)*(p)->ptr++ : EOF)
+#define rs_byte(p) (((p)->ptr < (p)->end) ? (unsigned char)*(p)->ptr++ : EOF)
 
 #define r_byte(p) ((p)->fp ? getc((p)->fp) : rs_byte(p))
 
@@ -504,42 +511,60 @@
 	PyObject *v, *v2, *v3;
 	long i, n;
 	int type = r_byte(p);
+	PyObject *retval;
+
+	p->depth++;
+
+	if (p->depth > MAX_MARSHAL_STACK_DEPTH) {
+		p->depth--;
+		PyErr_SetString(PyExc_ValueError, "recursion limit exceeded");
+		return NULL;
+	}
 
 	switch (type) {
 
 	case EOF:
 		PyErr_SetString(PyExc_EOFError,
 				"EOF read where object expected");
-		return NULL;
+		retval = NULL;
+		break;
 
 	case TYPE_NULL:
-		return NULL;
+		retval = NULL;
+		break;
 
 	case TYPE_NONE:
 		Py_INCREF(Py_None);
-		return Py_None;
+		retval = Py_None;
+		break;
 
 	case TYPE_STOPITER:
 		Py_INCREF(PyExc_StopIteration);
-		return PyExc_StopIteration;
+		retval = PyExc_StopIteration;
+		break;
 
 	case TYPE_ELLIPSIS:
 		Py_INCREF(Py_Ellipsis);
-		return Py_Ellipsis;
+		retval = Py_Ellipsis;
+		break;
 
 	case TYPE_FALSE:
 		Py_INCREF(Py_False);
-		return Py_False;
+		retval = Py_False;
+		break;
 
 	case TYPE_TRUE:
 		Py_INCREF(Py_True);
-		return Py_True;
+		retval = Py_True;
+		break;
 
 	case TYPE_INT:
-		return PyInt_FromLong(r_long(p));
+		retval = PyInt_FromLong(r_long(p));
+		break;
 
 	case TYPE_INT64:
-		return r_long64(p);
+		retval = r_long64(p);
+		break;
 
 	case TYPE_LONG:
 		{
@@ -549,12 +574,15 @@
 			if (n < -INT_MAX || n > INT_MAX) {
 				PyErr_SetString(PyExc_ValueError,
 						"bad marshal data");
-				return NULL;
+				retval = NULL;
+				break;
 			}
 			size = n<0 ? -n : n;
 			ob = _PyLong_New(size);
-			if (ob == NULL)
-				return NULL;
+			if (ob == NULL) {
+				retval = NULL;
+				break;
+			}
 			ob->ob_size = n;
 			for (i = 0; i < size; i++) {
 				int digit = r_short(p);
@@ -562,11 +590,14 @@
 					Py_DECREF(ob);
 					PyErr_SetString(PyExc_ValueError,
 							"bad marshal data");
-					return NULL;
+					ob = NULL;
+					break;
 				}
-				ob->ob_digit[i] = digit;
+				if (ob != NULL)
+					ob->ob_digit[i] = digit;
 			}
-			return (PyObject *)ob;
+			retval = (PyObject *)ob;
+			break;
 		}
 
 	case TYPE_FLOAT:
@@ -577,13 +608,16 @@
 			if (n == EOF || r_string(buf, (int)n, p) != n) {
 				PyErr_SetString(PyExc_EOFError,
 					"EOF read where object expected");
-				return NULL;
+				retval = NULL;
+				break;
 			}
 			buf[n] = '\0';
-			PyFPE_START_PROTECT("atof", return 0)
+			retval = NULL;
+			PyFPE_START_PROTECT("atof", break)
 			dx = PyOS_ascii_atof(buf);
 			PyFPE_END_PROTECT(dx)
-			return PyFloat_FromDouble(dx);
+			retval = PyFloat_FromDouble(dx);
+			break;
 		}
 
 	case TYPE_BINARY_FLOAT:
@@ -593,13 +627,16 @@
 			if (r_string((char*)buf, 8, p) != 8) {
 				PyErr_SetString(PyExc_EOFError,
 					"EOF read where object expected");
-				return NULL;
+				retval = NULL;
+				break;
 			}
 			x = _PyFloat_Unpack8(buf, 1);
 			if (x == -1.0 && PyErr_Occurred()) {
-				return NULL;
+				retval = NULL;
+				break;
 			}
-			return PyFloat_FromDouble(x);
+			retval = PyFloat_FromDouble(x);
+			break;
 		}
 
 #ifndef WITHOUT_COMPLEX
@@ -611,23 +648,27 @@
 			if (n == EOF || r_string(buf, (int)n, p) != n) {
 				PyErr_SetString(PyExc_EOFError,
 					"EOF read where object expected");
-				return NULL;
+				retval = NULL;
+				break;
 			}
 			buf[n] = '\0';
-			PyFPE_START_PROTECT("atof", return 0)
+			retval = NULL;
+			PyFPE_START_PROTECT("atof", break;)
 			c.real = PyOS_ascii_atof(buf);
 			PyFPE_END_PROTECT(c)
 			n = r_byte(p);
 			if (n == EOF || r_string(buf, (int)n, p) != n) {
 				PyErr_SetString(PyExc_EOFError,
 					"EOF read where object expected");
-				return NULL;
+				retval = NULL;
+				break;
 			}
 			buf[n] = '\0';
-			PyFPE_START_PROTECT("atof", return 0)
+			PyFPE_START_PROTECT("atof", break)
 			c.imag = PyOS_ascii_atof(buf);
 			PyFPE_END_PROTECT(c)
-			return PyComplex_FromCComplex(c);
+			retval = PyComplex_FromCComplex(c);
+			break;
 		}
 
 	case TYPE_BINARY_COMPLEX:
@@ -637,22 +678,27 @@
 			if (r_string((char*)buf, 8, p) != 8) {
 				PyErr_SetString(PyExc_EOFError,
 					"EOF read where object expected");
-				return NULL;
+				retval = NULL;
+				break;
 			}
 			c.real = _PyFloat_Unpack8(buf, 1);
 			if (c.real == -1.0 && PyErr_Occurred()) {
-				return NULL;
+				retval = NULL;
+				break;
 			}
 			if (r_string((char*)buf, 8, p) != 8) {
 				PyErr_SetString(PyExc_EOFError,
 					"EOF read where object expected");
-				return NULL;
+				retval = NULL;
+				break;
 			}
 			c.imag = _PyFloat_Unpack8(buf, 1);
 			if (c.imag == -1.0 && PyErr_Occurred()) {
-				return NULL;
+				retval = NULL;
+				break;
 			}
-			return PyComplex_FromCComplex(c);
+			retval = PyComplex_FromCComplex(c);
+			break;
 		}
 #endif
 
@@ -661,32 +707,42 @@
 		n = r_long(p);
 		if (n < 0 || n > INT_MAX) {
 			PyErr_SetString(PyExc_ValueError, "bad marshal data");
-			return NULL;
+			retval = NULL;
+			break;
 		}
 		v = PyString_FromStringAndSize((char *)NULL, n);
-		if (v == NULL)
-			return v;
+		if (v == NULL) {
+			retval = NULL;
+			break;
+		}
 		if (r_string(PyString_AS_STRING(v), (int)n, p) != n) {
 			Py_DECREF(v);
 			PyErr_SetString(PyExc_EOFError,
 					"EOF read where object expected");
-			return NULL;
+			retval = NULL;
+			break;
 		}
 		if (type == TYPE_INTERNED) {
 			PyString_InternInPlace(&v);
-			PyList_Append(p->strings, v);
+			if (PyList_Append(p->strings, v) < 0) {
+				retval = NULL;
+				break;
+			}
 		}
-		return v;
+		retval = v;
+		break;
 
 	case TYPE_STRINGREF:
 		n = r_long(p);
 		if (n < 0 || n >= PyList_GET_SIZE(p->strings)) {
 			PyErr_SetString(PyExc_ValueError, "bad marshal data");
-			return NULL;
+			retval = NULL;
+			break;
 		}
 		v = PyList_GET_ITEM(p->strings, n);
 		Py_INCREF(v);
-		return v;
+		retval = v;
+		break;
 
 #ifdef Py_USING_UNICODE
 	case TYPE_UNICODE:
@@ -696,20 +752,25 @@
 		n = r_long(p);
 		if (n < 0 || n > INT_MAX) {
 			PyErr_SetString(PyExc_ValueError, "bad marshal data");
-			return NULL;
+			retval = NULL;
+			break;
 		}
 		buffer = PyMem_NEW(char, n);
-		if (buffer == NULL)
-			return PyErr_NoMemory();
+		if (buffer == NULL) {
+			retval = PyErr_NoMemory();
+			break;
+		}
 		if (r_string(buffer, (int)n, p) != n) {
 			PyMem_DEL(buffer);
 			PyErr_SetString(PyExc_EOFError,
 				"EOF read where object expected");
-			return NULL;
+			retval = NULL;
+			break;
 		}
 		v = PyUnicode_DecodeUTF8(buffer, n, NULL);
 		PyMem_DEL(buffer);
-		return v;
+		retval = v;
+		break;
 	    }
 #endif
 
@@ -717,11 +778,14 @@
 		n = r_long(p);
 		if (n < 0 || n > INT_MAX) {
 			PyErr_SetString(PyExc_ValueError, "bad marshal data");
-			return NULL;
+			retval = NULL;
+			break;
 		}
 		v = PyTuple_New((int)n);
-		if (v == NULL)
-			return v;
+		if (v == NULL) {
+			retval = NULL;
+			break;
+		}
 		for (i = 0; i < n; i++) {
 			v2 = r_object(p);
 			if ( v2 == NULL ) {
@@ -734,17 +798,21 @@
 			}
 			PyTuple_SET_ITEM(v, (int)i, v2);
 		}
-		return v;
+		retval = v;
+		break;
 
 	case TYPE_LIST:
 		n = r_long(p);
 		if (n < 0 || n > INT_MAX) {
 			PyErr_SetString(PyExc_ValueError, "bad marshal data");
-			return NULL;
+			retval = NULL;
+			break;
 		}
 		v = PyList_New((int)n);
-		if (v == NULL)
-			return v;
+		if (v == NULL) {
+			retval = NULL;
+			break;
+		}
 		for (i = 0; i < n; i++) {
 			v2 = r_object(p);
 			if ( v2 == NULL ) {
@@ -755,14 +823,17 @@
 				v = NULL;
 				break;
 			}
-			PyList_SetItem(v, (int)i, v2);
+			PyList_SET_ITEM(v, (int)i, v2);
 		}
-		return v;
+		retval = v;
+		break;
 
 	case TYPE_DICT:
 		v = PyDict_New();
-		if (v == NULL)
-			return NULL;
+		if (v == NULL) {
+			retval = NULL;
+			break;
+		}
 		for (;;) {
 			PyObject *key, *val;
 			key = r_object(p);
@@ -778,18 +849,22 @@
 			Py_DECREF(v);
 			v = NULL;
 		}
-		return v;
+		retval = v;
+		break;
 
 	case TYPE_SET:
 	case TYPE_FROZENSET:
 		n = r_long(p);
-		if (n < 0) {
+		if (n < 0 || n > INT_MAX) {
 			PyErr_SetString(PyExc_ValueError, "bad marshal data");
-			return NULL;
+			retval = NULL;
+			break;
 		}
 		v = PyTuple_New((int)n);
-		if (v == NULL)
-			return v;
+		if (v == NULL) {
+			retval = NULL;
+			break;
+		}
 		for (i = 0; i < n; i++) {
 			v2 = r_object(p);
 			if ( v2 == NULL ) {
@@ -802,21 +877,25 @@
 			}
 			PyTuple_SET_ITEM(v, (int)i, v2);
 		}
-		if (v == NULL)
-			return v;
+		if (v == NULL) {
+			retval = NULL;
+			break;
+		}
 		if (type == TYPE_SET)
 			v3 = PySet_New(v);
 		else
 			v3 = PyFrozenSet_New(v);
 		Py_DECREF(v);
-		return v3;
+		retval = v3;
+		break;
 
 	case TYPE_CODE:
 		if (PyEval_GetRestricted()) {
 			PyErr_SetString(PyExc_RuntimeError,
 				"cannot unmarshal code objects in "
 				"restricted execution mode");
-			return NULL;
+			retval = NULL;
+			break;
 		}
 		else {
 			int argcount;
@@ -888,15 +967,19 @@
 			Py_XDECREF(lnotab);
 
 		}
-		return v;
+		retval = v;
+		break;
 
 	default:
 		/* Bogus data got written, which isn't ideal.
 		   This will let you keep working and recover. */
 		PyErr_SetString(PyExc_ValueError, "bad marshal data");
-		return NULL;
+		retval = NULL;
+		break;
 
 	}
+	p->depth--;
+	return retval;
 }
 
 static PyObject *
@@ -1002,6 +1085,7 @@
 	PyObject *result;
 	rf.fp = fp;
 	rf.strings = PyList_New(0);
+	rf.depth = 0;
 	result = r_object(&rf);
 	Py_DECREF(rf.strings);
 	return result;
@@ -1016,6 +1100,7 @@
 	rf.ptr = str;
 	rf.end = str + len;
 	rf.strings = PyList_New(0);
+	rf.depth = 0;
 	result = r_object(&rf);
 	Py_DECREF(rf.strings);
 	return result;
@@ -1104,6 +1189,7 @@
 	}
 	rf.fp = PyFile_AsFile(f);
 	rf.strings = PyList_New(0);
+	rf.depth = 0;
 	result = read_object(&rf);
 	Py_DECREF(rf.strings);
 	return result;
@@ -1132,6 +1218,7 @@
 	rf.ptr = s;
 	rf.end = s + n;
 	rf.strings = PyList_New(0);
+	rf.depth = 0;
 	result = read_object(&rf);
 	Py_DECREF(rf.strings);
 	return result;


More information about the Python-checkins mailing list