[Bf-blender-cvs] [780c0ea0974] master: Python: support v3.11 (beta) with changes to PyFrameObject & opcodes

Campbell Barton noreply at git.blender.org
Tue Jul 5 06:00:14 CEST 2022


Commit: 780c0ea097444c3be60314dffd203c099720badb
Author: Campbell Barton
Date:   Tue Jul 5 13:41:55 2022 +1000
Branches: master
https://developer.blender.org/rB780c0ea097444c3be60314dffd203c099720badb

Python: support v3.11 (beta) with changes to PyFrameObject & opcodes

- Use API calls to access frame-data as PyFrameObject is now opaque.
- Update opcodes allowed for safe driver evaluation.

**Details**

Some opcodes have been added for safe-driver evaluation.
Python 3.11 removes many opcodes - the number of accepted opcodes in
Blender's listing dropped from 65 to 43) however some new opcodes
also needed to be added. As this relates to security details about newly
added opcodes have been noted below (see [0] for full documentation).

Newly added opcodes:

- CACHE:
  Used to control caching instructions.

- RESUME:
  A no-op. Performs internal checks.

- BINARY_OP:
  Implements the binary and in-place operators,
  replacing specific binary operations.

- CALL, PRECALL, KW_NAMES:
  Used for calling functions, replacing some existing opcodes.

- POP_JUMP_{FORWARD/BACKWARD}_IF_{TRUE/FALSE/NONE/NOT_NONE}.
  Manipulate the byte-code counter.

- SWAP, PUSH_NULL.
  Stack manipulation.

Resolves T99277.

[0]: https://docs.python.org/3.11/library/dis.html

===================================================================

M	source/blender/python/generic/py_capi_utils.c
M	source/blender/python/intern/bpy_driver.c
M	source/blender/python/intern/bpy_interface.c
M	source/blender/python/intern/bpy_traceback.c

===================================================================

diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c
index d2e3c44c1b6..8230801e184 100644
--- a/source/blender/python/generic/py_capi_utils.c
+++ b/source/blender/python/generic/py_capi_utils.c
@@ -641,6 +641,7 @@ void PyC_StackSpit(void)
 void PyC_FileAndNum(const char **r_filename, int *r_lineno)
 {
   PyFrameObject *frame;
+  PyCodeObject *code;
 
   if (r_filename) {
     *r_filename = NULL;
@@ -649,13 +650,16 @@ void PyC_FileAndNum(const char **r_filename, int *r_lineno)
     *r_lineno = -1;
   }
 
-  if (!(frame = PyThreadState_GET()->frame)) {
+  if (!(frame = PyEval_GetFrame())) {
+    return;
+  }
+  if (!(code = PyFrame_GetCode(frame))) {
     return;
   }
 
   /* when executing a script */
   if (r_filename) {
-    *r_filename = PyUnicode_AsUTF8(frame->f_code->co_filename);
+    *r_filename = PyUnicode_AsUTF8(code->co_filename);
   }
 
   /* when executing a module */
diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c
index 05def07a6d7..f71cf164e8c 100644
--- a/source/blender/python/intern/bpy_driver.c
+++ b/source/blender/python/intern/bpy_driver.c
@@ -285,6 +285,56 @@ static void pydriver_error(ChannelDriver *driver)
 #  define OK_OP(op) [op] = 1
 
 static const char secure_opcodes[255] = {
+#  if PY_VERSION_HEX >= 0x030b0000 /* Python 3.11 & newer. */
+
+    OK_OP(CACHE),
+    OK_OP(POP_TOP),
+    OK_OP(PUSH_NULL),
+    OK_OP(NOP),
+    OK_OP(UNARY_POSITIVE),
+    OK_OP(UNARY_NEGATIVE),
+    OK_OP(UNARY_NOT),
+    OK_OP(UNARY_INVERT),
+    OK_OP(BINARY_SUBSCR),
+    OK_OP(GET_LEN),
+    OK_OP(RETURN_VALUE),
+    OK_OP(SWAP),
+    OK_OP(BUILD_TUPLE),
+    OK_OP(BUILD_LIST),
+    OK_OP(BUILD_SET),
+    OK_OP(BUILD_MAP),
+    OK_OP(COMPARE_OP),
+    OK_OP(JUMP_FORWARD),
+    OK_OP(JUMP_IF_FALSE_OR_POP),
+    OK_OP(JUMP_IF_TRUE_OR_POP),
+    OK_OP(POP_JUMP_FORWARD_IF_FALSE),
+    OK_OP(POP_JUMP_FORWARD_IF_TRUE),
+    OK_OP(LOAD_GLOBAL),
+    OK_OP(IS_OP),
+    OK_OP(BINARY_OP),
+    OK_OP(LOAD_FAST),
+    OK_OP(STORE_FAST),
+    OK_OP(DELETE_FAST),
+    OK_OP(POP_JUMP_FORWARD_IF_NOT_NONE),
+    OK_OP(POP_JUMP_FORWARD_IF_NONE),
+    OK_OP(BUILD_SLICE),
+    OK_OP(LOAD_DEREF),
+    OK_OP(STORE_DEREF),
+    OK_OP(RESUME),
+    OK_OP(POP_JUMP_BACKWARD_IF_NOT_NONE),
+    OK_OP(POP_JUMP_BACKWARD_IF_NONE),
+    OK_OP(POP_JUMP_BACKWARD_IF_FALSE),
+    OK_OP(POP_JUMP_BACKWARD_IF_TRUE),
+
+    /* Special cases. */
+    OK_OP(LOAD_CONST), /* Ok because constants are accepted. */
+    OK_OP(LOAD_NAME),  /* Ok, because `PyCodeObject.names` is checked. */
+    OK_OP(CALL),       /* Ok, because we check its "name" before calling. */
+    OK_OP(KW_NAMES),   /* Ok, because it's used for calling functions with keyword arguments. */
+    OK_OP(PRECALL),    /* Ok, because it's used for calling. */
+
+#  else /* Python 3.10 and older. */
+
     OK_OP(POP_TOP),
     OK_OP(ROT_TWO),
     OK_OP(ROT_THREE),
@@ -352,6 +402,8 @@ static const char secure_opcodes[255] = {
     OK_OP(CALL_FUNCTION), /* Ok, because we check its "name" before calling. */
     OK_OP(CALL_FUNCTION_KW),
     OK_OP(CALL_FUNCTION_EX),
+
+#  endif /* Python 3.10 and older. */
 };
 
 #  undef OK_OP
@@ -388,7 +440,15 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, PyObject *d
     const _Py_CODEUNIT *codestr;
     Py_ssize_t code_len;
 
-    PyBytes_AsStringAndSize(py_code->co_code, (char **)&codestr, &code_len);
+    PyObject *co_code;
+
+#  if PY_VERSION_HEX >= 0x030b0000 /* Python 3.11 & newer. */
+    co_code = py_code->_co_code;
+#  else
+    co_code = py_code->co_code;
+#  endif
+
+    PyBytes_AsStringAndSize(co_code, (char **)&codestr, &code_len);
     code_len /= sizeof(*codestr);
 
     for (Py_ssize_t i = 0; i < code_len; i++) {
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index 0ab8b4385e5..ea64fa6c098 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -582,16 +582,17 @@ void BPY_python_use_system_env(void)
 void BPY_python_backtrace(FILE *fp)
 {
   fputs("\n# Python backtrace\n", fp);
-  PyThreadState *tstate = PyGILState_GetThisThreadState();
-  if (tstate != NULL && tstate->frame != NULL) {
-    PyFrameObject *frame = tstate->frame;
-    do {
-      const int line = PyCode_Addr2Line(frame->f_code, frame->f_lasti);
-      const char *filepath = PyUnicode_AsUTF8(frame->f_code->co_filename);
-      const char *funcname = PyUnicode_AsUTF8(frame->f_code->co_name);
-      fprintf(fp, "  File \"%s\", line %d in %s\n", filepath, line, funcname);
-    } while ((frame = frame->f_back));
+  PyFrameObject *frame;
+  if (!(frame = PyEval_GetFrame())) {
+    return;
   }
+  do {
+    PyCodeObject *code = PyFrame_GetCode(frame);
+    const int line = PyFrame_GetLineNumber(frame);
+    const char *filepath = PyUnicode_AsUTF8(code->co_filename);
+    const char *funcname = PyUnicode_AsUTF8(code->co_name);
+    fprintf(fp, "  File \"%s\", line %d in %s\n", filepath, line, funcname);
+  } while ((frame = PyFrame_GetBack(frame)));
 }
 
 void BPY_DECREF(void *pyob_ptr)
diff --git a/source/blender/python/intern/bpy_traceback.c b/source/blender/python/intern/bpy_traceback.c
index cb93843a6de..1f2458c752f 100644
--- a/source/blender/python/intern/bpy_traceback.c
+++ b/source/blender/python/intern/bpy_traceback.c
@@ -20,7 +20,8 @@
 
 static const char *traceback_filepath(PyTracebackObject *tb, PyObject **coerce)
 {
-  *coerce = PyUnicode_EncodeFSDefault(tb->tb_frame->f_code->co_filename);
+  PyCodeObject *code = PyFrame_GetCode(tb->tb_frame);
+  *coerce = PyUnicode_EncodeFSDefault(code->co_filename);
   return PyBytes_AS_STRING(*coerce);
 }



More information about the Bf-blender-cvs mailing list