From 609346273903cd848d055b046ec46d9cc831b750 Mon Sep 17 00:00:00 2001 From: "Michael W. Hudson" Date: Thu, 12 Aug 2004 17:56:29 +0000 Subject: [PATCH] Fix bug [ 1005248 ] new.code() not cleanly checking its arguments using the result of new.code() can still destroy the sun, but merely calling the function shouldn't any more. I also rewrote the existing tests of new.code() to use vastly less un-bogus arguments, and added tests for the previous insane behaviours. --- Lib/test/test_new.py | 68 ++++++++++++++++++++++--- Python/compile.c | 115 +++++++++++++++++++++++++++++++++---------- 2 files changed, 150 insertions(+), 33 deletions(-) diff --git a/Lib/test/test_new.py b/Lib/test/test_new.py index 48f184e0b25..33eba14ea1c 100644 --- a/Lib/test/test_new.py +++ b/Lib/test/test_new.py @@ -1,4 +1,4 @@ -from test.test_support import verbose, verify +from test.test_support import verbose, verify, TestFailed import sys import new @@ -99,11 +99,67 @@ print 'new.code()' # bogus test of new.code() # Note: Jython will never have new.code() if hasattr(new, 'code'): - # XXX should use less criminally bogus arguments! - d = new.code(3, 3, 3, 3, codestr, (), (), (), - "", "", 1, "", (), ()) + def f(a): pass + + c = f.func_code + argcount = c.co_argcount + nlocals = c.co_nlocals + stacksize = c.co_stacksize + flags = c.co_flags + codestring = c.co_code + constants = c.co_consts + names = c.co_names + varnames = c.co_varnames + filename = c.co_filename + name = c.co_name + firstlineno = c.co_firstlineno + lnotab = c.co_lnotab + freevars = c.co_freevars + cellvars = c.co_cellvars + + d = new.code(argcount, nlocals, stacksize, flags, codestring, + constants, names, varnames, filename, name, + firstlineno, lnotab, freevars, cellvars) + # test backwards-compatibility version with no freevars or cellvars - d = new.code(3, 3, 3, 3, codestr, (), (), (), - "", "", 1, "") + d = new.code(argcount, nlocals, stacksize, flags, codestring, + constants, names, varnames, filename, name, + firstlineno, lnotab) + + try: # this used to trigger a SystemError + d = new.code(-argcount, nlocals, stacksize, flags, codestring, + constants, names, varnames, filename, name, + firstlineno, lnotab) + except ValueError: + pass + else: + raise TestFailed, "negative co_argcount didn't trigger an exception" + + try: # this used to trigger a SystemError + d = new.code(argcount, -nlocals, stacksize, flags, codestring, + constants, names, varnames, filename, name, + firstlineno, lnotab) + except ValueError: + pass + else: + raise TestFailed, "negative co_nlocals didn't trigger an exception" + + try: # this used to trigger a Py_FatalError! + d = new.code(argcount, nlocals, stacksize, flags, codestring, + constants, (5,), varnames, filename, name, + firstlineno, lnotab) + except TypeError: + pass + else: + raise TestFailed, "non-string co_name didn't trigger an exception" + + # new.code used to be a way to mutate a tuple... + class S(str): pass + t = (S("ab"),) + d = new.code(argcount, nlocals, stacksize, flags, codestring, + constants, t, varnames, filename, name, + firstlineno, lnotab) + verify(type(t[0]) is S, "eek, tuple changed under us!") + if verbose: print d diff --git a/Python/compile.c b/Python/compile.c index 5c67987a9a9..0c405cc6607 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -88,6 +88,50 @@ static PyMemberDef code_memberlist[] = { {NULL} /* Sentinel */ }; +/* Helper for code_new: return a shallow copy of a tuple that is + guaranteed to contain exact strings, by converting string subclasses + to exact strings and complaining if a non-string is found. */ +static PyObject* +validate_and_copy_tuple(PyObject *tup) +{ + PyObject *newtuple; + PyObject *item; + int i, len; + + len = PyTuple_GET_SIZE(tup); + newtuple = PyTuple_New(len); + if (newtuple == NULL) + return NULL; + + for (i = 0; i < len; i++) { + item = PyTuple_GET_ITEM(tup, i); + if (PyString_CheckExact(item)) { + Py_INCREF(item); + } + else if (!PyString_Check(item)) { + PyErr_Format( + PyExc_TypeError, + "name tuples must contain only " + "strings, not '%.500s'", + item->ob_type->tp_name); + Py_DECREF(newtuple); + return NULL; + } + else { + item = PyString_FromStringAndSize( + PyString_AS_STRING(item), + PyString_GET_SIZE(item)); + if (item == NULL) { + Py_DECREF(newtuple); + return NULL; + } + } + PyTuple_SET_ITEM(newtuple, i, item); + } + + return newtuple; +} + PyDoc_STRVAR(code_doc, "code(argcount, nlocals, stacksize, flags, codestring, constants, names,\n\ varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]])\n\ @@ -101,14 +145,13 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kw) int nlocals; int stacksize; int flags; - PyObject *co; - PyObject *empty = NULL; + PyObject *co = NULL;; PyObject *code; PyObject *consts; - PyObject *names; - PyObject *varnames; - PyObject *freevars = NULL; - PyObject *cellvars = NULL; + PyObject *names, *ournames = NULL; + PyObject *varnames, *ourvarnames = NULL; + PyObject *freevars = NULL, *ourfreevars = NULL; + PyObject *cellvars = NULL, *ourcellvars = NULL; PyObject *filename; PyObject *name; int firstlineno; @@ -126,27 +169,48 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kw) &PyTuple_Type, &cellvars)) return NULL; - if (!PyObject_CheckReadBuffer(code)) { - PyErr_SetString(PyExc_TypeError, - "bytecode object must be a single-segment read-only buffer"); - return NULL; + if (argcount < 0) { + PyErr_SetString( + PyExc_ValueError, + "code: argcount must not be negative"); + goto cleanup; } - if (freevars == NULL || cellvars == NULL) { - empty = PyTuple_New(0); - if (empty == NULL) - return NULL; - if (freevars == NULL) - freevars = empty; - if (cellvars == NULL) - cellvars = empty; + if (nlocals < 0) { + PyErr_SetString( + PyExc_ValueError, + "code: nlocals must not be negative"); + goto cleanup; } + ournames = validate_and_copy_tuple(names); + if (ournames == NULL) + goto cleanup; + ourvarnames = validate_and_copy_tuple(varnames); + if (ourvarnames == NULL) + goto cleanup; + if (freevars) + ourfreevars = validate_and_copy_tuple(freevars); + else + ourfreevars = PyTuple_New(0); + if (ourfreevars == NULL) + goto cleanup; + if (cellvars) + ourcellvars = validate_and_copy_tuple(cellvars); + else + ourcellvars = PyTuple_New(0); + if (ourcellvars == NULL) + goto cleanup; + co = (PyObject *) PyCode_New(argcount, nlocals, stacksize, flags, - code, consts, names, varnames, - freevars, cellvars, filename, name, - firstlineno, lnotab); - Py_XDECREF(empty); + code, consts, ournames, ourvarnames, + ourfreevars, ourcellvars, filename, + name, firstlineno, lnotab); + cleanup: + Py_XDECREF(ournames); + Py_XDECREF(ourvarnames); + Py_XDECREF(ourfreevars); + Py_XDECREF(ourcellvars); return co; } @@ -302,21 +366,18 @@ all_name_chars(unsigned char *s) return 1; } -static int +static void intern_strings(PyObject *tuple) { int i; for (i = PyTuple_GET_SIZE(tuple); --i >= 0; ) { PyObject *v = PyTuple_GET_ITEM(tuple, i); - if (v == NULL || !PyString_Check(v)) { + if (v == NULL || !PyString_CheckExact(v)) { Py_FatalError("non-string found in code slot"); - PyErr_BadInternalCall(); - return -1; } PyString_InternInPlace(&PyTuple_GET_ITEM(tuple, i)); } - return 0; } #define GETARG(arr, i) ((int)((arr[i+2]<<8) + arr[i+1]))