diff -r f9b4cfc19264 -r 0feab4e7b27f Lib/test/test_marshal.py --- a/Lib/test/test_marshal.py Sat Jun 18 19:34:12 2011 -0400 +++ b/Lib/test/test_marshal.py Sun Jun 26 20:41:01 2011 +0100 @@ -5,6 +5,11 @@ import sys import unittest import os +try: + import zlib + import base64 +except ImportError: + zlib = None class HelperMixin: def helper(self, sample, *extra): @@ -211,6 +216,125 @@ invalid_string = b'l\x02\x00\x00\x00\x00\x00\x00\x00' self.assertRaises(ValueError, marshal.loads, invalid_string) + @unittest.skipUnless(zlib, "requires zlib and base64") + def test_multiple_loads(self): + # Issue 12291: marshal.load() should be callable multiple times + data = """ +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 +n54hbpx14/nb/w+aV5i+""" + data = zlib.decompress(base64.b64decode(data.encode('ascii'))) + from io import BytesIO + data = BytesIO(data) + assertIsInstance = self.assertIsInstance + for i in range(1010): + o = marshal.load(data) + assertIsInstance(o, tuple) + assertIsInstance(o[0], str) + assertIsInstance(o[1], str) + assertIsInstance(o[2], int) + assertIsInstance(o[3], float) + assertIsInstance(o[4], bytes) + + def test_multiple_dumps_and_loads(self): + # Issue 12291: marshal.load() should be callable multiple times + # with interleaved data written by non-marshal code + # Adapted from a patch by Engelbert Gruber + data = (1, 'abc', b'def', 1.0) + assertEqual = self.assertEqual + for interleaved in (b'', b'0123'): + ilen = len(interleaved) + positions = [] + try: + with open(support.TESTFN, 'wb') as f: + for d in data: + marshal.dump(d, f) + if ilen: + f.write(interleaved) + positions.append(f.tell()) + with open(support.TESTFN, 'rb') as f: + for i, d in enumerate(data): + assertEqual(d, marshal.load(f)) + if ilen: + f.read(ilen) + assertEqual(positions[i], f.tell()) + finally: + support.unlink(support.TESTFN) + def test_main(): support.run_unittest(IntTestCase, diff -r f9b4cfc19264 -r 0feab4e7b27f Python/marshal.c --- a/Python/marshal.c Sat Jun 18 19:34:12 2011 -0400 +++ b/Python/marshal.c Sun Jun 26 20:41:01 2011 +0100 @@ -57,6 +57,8 @@ int error; /* see WFERR_* values */ int depth; /* If fp == NULL, the following are valid: */ + PyObject * readable; + int peeked; PyObject *str; char *ptr; char *end; @@ -466,27 +468,79 @@ #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)) +/* #define r_byte(p) ((p)->fp ? getc((p)->fp) : rs_byte(p)) */ static int r_string(char *s, int n, RFILE *p) { - if (p->fp != NULL) - /* The result fits into int because it must be <=n. */ - return (int)fread(s, 1, n, p->fp); - if (p->end - p->ptr < n) - n = (int)(p->end - p->ptr); - memcpy(s, p->ptr, n); - p->ptr += n; - return n; + char * ptr; + int read, left; + + if (!p->readable) { + if (p->fp != NULL) + /* The result fits into int because it must be <=n. */ + read = (int) fread(s, 1, n, p->fp); + else { + left = (int)(p->end - p->ptr); + read = (left < n) ? left : n; + memcpy(s, p->ptr, read); + p->ptr += read; + } + } + else { + PyObject *data = PyObject_CallMethod(p->readable, "read", "i", n); + read = 0; + if (data != NULL) { + if (PyBytes_Check(data)) { + read = PyBytes_GET_SIZE(data); + if (read > 0) { + ptr = PyBytes_AS_STRING(data); + memcpy(s, ptr, read); + } + } + Py_DECREF(data); + } + } + if (read < n) { + PyErr_SetString(PyExc_EOFError, + "EOF read where not expected"); + } + return read; +} + + +static int +r_byte(RFILE *p) +{ + int c = EOF; + unsigned char ch; + int n; + + if (!p->readable) + c = p->fp ? getc(p->fp) : rs_byte(p); + else { + if (p->peeked != EOF) { + c = p->peeked; + p->peeked = EOF; + } + else { + n = r_string((char *) &ch, 1, p); + if (n > 0) + c = ch; + } + } + return c; } static int r_short(RFILE *p) { register short x; - x = r_byte(p); - x |= r_byte(p) << 8; + unsigned char buffer[2]; + + r_string((char *) buffer, 2, p); + x = buffer[0]; + x |= buffer[1] << 8; /* Sign-extension, in case short greater than 16 bits */ x |= -(x & 0x8000); return x; @@ -496,19 +550,13 @@ r_long(RFILE *p) { register long x; - register FILE *fp = p->fp; - if (fp) { - x = getc(fp); - x |= (long)getc(fp) << 8; - x |= (long)getc(fp) << 16; - x |= (long)getc(fp) << 24; - } - else { - x = rs_byte(p); - x |= (long)rs_byte(p) << 8; - x |= (long)rs_byte(p) << 16; - x |= (long)rs_byte(p) << 24; - } + unsigned char buffer[4]; + + r_string((char *) buffer, 4, p); + x = buffer[0]; + x |= (long)buffer[1] << 8; + x |= (long)buffer[2] << 16; + x |= (long)buffer[3] << 24; #if SIZEOF_LONG > 4 /* Sign extension for 64-bit machines */ x |= -(x & 0x80000000L); @@ -526,25 +574,30 @@ static PyObject * r_long64(RFILE *p) { + PyObject * result = NULL; long lo4 = r_long(p); long hi4 = r_long(p); + + if (!PyErr_Occurred()) { #if SIZEOF_LONG > 4 - long x = (hi4 << 32) | (lo4 & 0xFFFFFFFFL); - return PyLong_FromLong(x); + long x = (hi4 << 32) | (lo4 & 0xFFFFFFFFL); + result = PyLong_FromLong(x); #else - unsigned char buf[8]; - int one = 1; - int is_little_endian = (int)*(char*)&one; - if (is_little_endian) { - memcpy(buf, &lo4, 4); - memcpy(buf+4, &hi4, 4); + unsigned char buf[8]; + int one = 1; + int is_little_endian = (int)*(char*)&one; + if (is_little_endian) { + memcpy(buf, &lo4, 4); + memcpy(buf+4, &hi4, 4); + } + else { + memcpy(buf, &hi4, 4); + memcpy(buf+4, &lo4, 4); + } + result = _PyLong_FromByteArray(buf, 8, is_little_endian, 1); +#endif } - else { - memcpy(buf, &hi4, 4); - memcpy(buf+4, &lo4, 4); - } - return _PyLong_FromByteArray(buf, 8, is_little_endian, 1); -#endif + return result; } static PyObject * @@ -556,6 +609,8 @@ digit d; n = r_long(p); + if (PyErr_Occurred()) + return NULL; if (n == 0) return (PyObject *)_PyLong_New(0); if (n < -INT_MAX || n > INT_MAX) { @@ -575,6 +630,8 @@ d = 0; for (j=0; j < PyLong_MARSHAL_RATIO; j++) { md = r_short(p); + if (PyErr_Occurred()) + break; if (md < 0 || md > PyLong_MARSHAL_BASE) goto bad_digit; d += (digit)md << j*PyLong_MARSHAL_SHIFT; @@ -584,6 +641,8 @@ d = 0; for (j=0; j < shorts_in_top_digit; j++) { md = r_short(p); + if (PyErr_Occurred()) + break; if (md < 0 || md > PyLong_MARSHAL_BASE) goto bad_digit; /* topmost marshal digit should be nonzero */ @@ -595,6 +654,10 @@ } d += (digit)md << j*PyLong_MARSHAL_SHIFT; } + if (PyErr_Occurred()) { + Py_DECREF(ob); + return NULL; + } /* top digit should be nonzero, else the resulting PyLong won't be normalized */ ob->ob_digit[size-1] = d; @@ -663,7 +726,8 @@ break; case TYPE_INT: - retval = PyLong_FromLong(r_long(p)); + n = r_long(p); + retval = PyErr_Occurred() ? NULL : PyLong_FromLong(n); break; case TYPE_INT64: @@ -773,6 +837,10 @@ case TYPE_STRING: n = r_long(p); + if (PyErr_Occurred()) { + retval = NULL; + break; + } if (n < 0 || n > INT_MAX) { PyErr_SetString(PyExc_ValueError, "bad marshal data (string size out of range)"); retval = NULL; @@ -798,6 +866,10 @@ char *buffer; n = r_long(p); + if (PyErr_Occurred()) { + retval = NULL; + break; + } if (n < 0 || n > INT_MAX) { PyErr_SetString(PyExc_ValueError, "bad marshal data (unicode size out of range)"); retval = NULL; @@ -823,6 +895,10 @@ case TYPE_TUPLE: n = r_long(p); + if (PyErr_Occurred()) { + retval = NULL; + break; + } if (n < 0 || n > INT_MAX) { PyErr_SetString(PyExc_ValueError, "bad marshal data (tuple size out of range)"); retval = NULL; @@ -850,6 +926,10 @@ case TYPE_LIST: n = r_long(p); + if (PyErr_Occurred()) { + retval = NULL; + break; + } if (n < 0 || n > INT_MAX) { PyErr_SetString(PyExc_ValueError, "bad marshal data (list size out of range)"); retval = NULL; @@ -902,6 +982,10 @@ case TYPE_SET: case TYPE_FROZENSET: n = r_long(p); + if (PyErr_Occurred()) { + retval = NULL; + break; + } if (n < 0 || n > INT_MAX) { PyErr_SetString(PyExc_ValueError, "bad marshal data (set size out of range)"); retval = NULL; @@ -955,10 +1039,20 @@ /* XXX ignore long->int overflows for now */ argcount = (int)r_long(p); + if (PyErr_Occurred()) + goto code_error; kwonlyargcount = (int)r_long(p); + if (PyErr_Occurred()) + goto code_error; nlocals = (int)r_long(p); + if (PyErr_Occurred()) + goto code_error; stacksize = (int)r_long(p); + if (PyErr_Occurred()) + goto code_error; flags = (int)r_long(p); + if (PyErr_Occurred()) + goto code_error; code = r_object(p); if (code == NULL) goto code_error; @@ -1040,6 +1134,7 @@ { RFILE rf; assert(fp); + rf.readable = NULL; rf.fp = fp; rf.strings = NULL; rf.end = rf.ptr = NULL; @@ -1051,6 +1146,7 @@ { RFILE rf; rf.fp = fp; + rf.readable = NULL; rf.strings = NULL; rf.ptr = rf.end = NULL; return r_long(&rf); @@ -1112,6 +1208,7 @@ RFILE rf; PyObject *result; rf.fp = fp; + rf.readable = NULL; rf.strings = PyList_New(0); rf.depth = 0; rf.ptr = rf.end = NULL; @@ -1126,6 +1223,7 @@ RFILE rf; PyObject *result; rf.fp = NULL; + rf.readable = NULL; rf.ptr = str; rf.end = str + len; rf.strings = PyList_New(0); @@ -1142,6 +1240,7 @@ PyObject *res = NULL; wf.fp = NULL; + wf.readable = NULL; wf.str = PyBytes_FromStringAndSize((char *)NULL, 50); if (wf.str == NULL) return NULL; @@ -1219,25 +1318,30 @@ static PyObject * marshal_load(PyObject *self, PyObject *f) { - /* XXX Quick hack -- need to do this differently */ PyObject *data, *result; RFILE rf; - data = PyObject_CallMethod(f, "read", ""); + char *p; + int n; + + /* + * Read one byte, so we can give a meaningful message early + * if something unexpected occurs. However, the stream may + * not be seekable, so we keep the peeked byte around, + * and r_byte will return it the first time. + */ + data = PyObject_CallMethod(f, "read", "i", 1); if (data == NULL) return NULL; rf.fp = NULL; if (PyBytes_Check(data)) { - rf.ptr = PyBytes_AS_STRING(data); - rf.end = rf.ptr + PyBytes_GET_SIZE(data); - } - else if (PyBytes_Check(data)) { - rf.ptr = PyBytes_AS_STRING(data); - rf.end = rf.ptr + PyBytes_GET_SIZE(data); + n = PyBytes_GET_SIZE(data); + p = PyBytes_AS_STRING(data); + rf.peeked = (n > 0) ? *p : EOF; + rf.readable = f; } else { PyErr_Format(PyExc_TypeError, - "f.read() returned neither string " - "nor bytes but %.100s", + "f.read() returned not bytes but %.100s", data->ob_type->tp_name); Py_DECREF(data); return NULL; @@ -1296,6 +1400,7 @@ s = p.buf; n = p.len; rf.fp = NULL; + rf.readable = NULL; rf.ptr = s; rf.end = s + n; rf.strings = PyList_New(0);