]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-88116: Document that PyCodeNew is dangerous, and make PyCode_NewEmpty less dangero...
authorMark Shannon <mark@hotpy.org>
Thu, 21 Apr 2022 18:08:36 +0000 (19:08 +0100)
committerGitHub <noreply@github.com>
Thu, 21 Apr 2022 18:08:36 +0000 (19:08 +0100)
Doc/c-api/code.rst
Doc/whatsnew/3.11.rst
Lib/test/test_code.py
Objects/codeobject.c

index 840b8426dbbdc325348bcb6482a27dcf6622500a..407d8b481be4aeef65a0cc07683909881d7a2f01 100644 (file)
@@ -33,24 +33,33 @@ bound into a function.
 
    Return the number of free variables in *co*.
 
-.. c:function:: PyCodeObject* PyCode_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab)
+.. c:function:: PyCodeObject* PyCode_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *linetable, PyObject *exceptiontable)
 
    Return a new code object.  If you need a dummy code object to create a frame,
    use :c:func:`PyCode_NewEmpty` instead.  Calling :c:func:`PyCode_New` directly
-   can bind you to a precise Python version since the definition of the bytecode
-   changes often.
+   will bind you to a precise Python version since the definition of the bytecode
+   changes often. The many arguments of this function are inter-dependent in complex
+   ways, meaning that subtle changes to values are likely to result in incorrect
+   execution or VM crashes. Use this function only with extreme care.
 
-.. c:function:: PyCodeObject* PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab)
+   .. versionchanged:: 3.11
+      Added ``exceptiontable`` parameter.
+
+.. c:function:: PyCodeObject* PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *linetable, PyObject *exceptiontable)
 
    Similar to :c:func:`PyCode_New`, but with an extra "posonlyargcount" for positional-only arguments.
+   The same caveats that apply to ``PyCode_New`` also apply to this function.
 
    .. versionadded:: 3.8
 
+   .. versionchanged:: 3.11
+      Added ``exceptiontable`` parameter.
+
 .. c:function:: PyCodeObject* PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
 
    Return a new empty code object with the specified filename,
-   function name, and first line number.  It is illegal to
-   :func:`exec` or :func:`eval` the resulting code object.
+   function name, and first line number. The resulting code
+   object will raise an ``Exception`` if executed.
 
 .. c:function:: int PyCode_Addr2Line(PyCodeObject *co, int byte_offset)
 
index 8d74c9bbebad879f74a958fddf3f396e5ad09893..c3a8a7e42a110bc2ff619d618477e6573bc18f83 100644 (file)
@@ -1159,6 +1159,12 @@ C API Changes
   as its second parameter, instead of ``PyFrameObject*``.
   See :pep:`523` for more details of how to use this function pointer type.
 
+* :c:func:`PyCode_New` and :c:func:`PyCode_NewWithPosOnlyArgs` now take
+  an additional ``exception_table`` argument.
+  Using these functions should be avoided, if at all possible.
+  To get a custom code object: create a code object using the compiler,
+  then get a modified version with the ``replace`` method.
+
 New Features
 ------------
 
index a37ebd27dc3882910e8519d491f7559d809cd602..1bb138e7f3243b77190580637738ee3460b2f090 100644 (file)
@@ -176,6 +176,9 @@ class CodeTest(unittest.TestCase):
         self.assertEqual(co.co_filename, "filename")
         self.assertEqual(co.co_name, "funcname")
         self.assertEqual(co.co_firstlineno, 15)
+        #Empty code object should raise, but not crash the VM
+        with self.assertRaises(Exception):
+            exec(co)
 
     @cpython_only
     def test_closure_injection(self):
index 9a5781588275678310e4c211c58ba5cd1f3e2179..4fc4b8fec68a2bad09b1e63a5562b69f2787eacb 100644 (file)
@@ -626,12 +626,20 @@ PyCode_New(int argcount, int kwonlyargcount,
                                      exceptiontable);
 }
 
+static const char assert0[4] = {
+    LOAD_ASSERTION_ERROR,
+    0,
+    RAISE_VARARGS,
+    1
+};
+
 PyCodeObject *
 PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
 {
     PyObject *nulltuple = NULL;
     PyObject *filename_ob = NULL;
     PyObject *funcname_ob = NULL;
+    PyObject *code_ob = NULL;
     PyCodeObject *result = NULL;
 
     nulltuple = PyTuple_New(0);
@@ -646,13 +654,17 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
     if (filename_ob == NULL) {
         goto failed;
     }
+    code_ob = PyBytes_FromStringAndSize(assert0, 4);
+    if (code_ob == NULL) {
+        goto failed;
+    }
 
 #define emptystring (PyObject *)&_Py_SINGLETON(bytes_empty)
     struct _PyCodeConstructor con = {
         .filename = filename_ob,
         .name = funcname_ob,
         .qualname = funcname_ob,
-        .code = emptystring,
+        .code = code_ob,
         .firstlineno = firstlineno,
         .linetable = emptystring,
         .consts = nulltuple,
@@ -660,6 +672,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
         .localsplusnames = nulltuple,
         .localspluskinds = emptystring,
         .exceptiontable = emptystring,
+        .stacksize = 1,
     };
     result = _PyCode_New(&con);
 
@@ -667,6 +680,7 @@ failed:
     Py_XDECREF(nulltuple);
     Py_XDECREF(funcname_ob);
     Py_XDECREF(filename_ob);
+    Py_XDECREF(code_ob);
     return result;
 }