]> granicus.if.org Git - python/commitdiff
Fix bug
authorMichael W. Hudson <mwh@python.net>
Thu, 12 Aug 2004 17:56:29 +0000 (17:56 +0000)
committerMichael W. Hudson <mwh@python.net>
Thu, 12 Aug 2004 17:56:29 +0000 (17:56 +0000)
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
Python/compile.c

index 48f184e0b25bbcb465ac0aae7d6a3e66b54b9f85..33eba14ea1c8c41d8d489815ec3041ef059c1866 100644 (file)
@@ -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, (), (), (),
-                 "<string>", "<name>", 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, (), (), (),
-                 "<string>", "<name>", 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
index 5c67987a9a9ce2800866f60b3f6d537045a26409..0c405cc66071c8e6d5e5c991b170c3a6b52c022a 100644 (file)
@@ -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]))