Page 42 - 《软件学报》2026年第7期
P. 42

胡明哲 等: Python  软件包库中   C/C++外部语言调用的安全性分析                                        2727


                 符串对从   Python  侧传入的  Python  对象实参进行解包和跨语言类型转换. 跨语言的类型一致性建立在                    3  个要素的
                 一致性上: 格式化字符串的声明、Python          侧传入的对象、C/C++侧解包后接收参数的变量类型. 这种复杂性源自
                 Python  动态类型系统与   C/C++静态类型系统之间的本质差异, 并可能存在多种不匹配的漏洞.
                    如代码   6  所示是格式化字符串不匹配         (T2.1) 的漏洞测例, 在代码    6  的测例  1  中, 格式化字符串“i”要求传入参
                 数为  Python  整型对象并将其转换为      C  的  int 类型对象, 若  Python  侧传入其他类型对象, PyArg_ParseTuple 无法完
                 成类型转换, 返回     false 并设置  TypeError 异常. 在代码  6  的测例  2  中, 格式化字符串“ii”声明解析两个整型位置参
                 数, 调用者仅提供一个参数时, 跨语言传参在尝试解包并转换第                   2  个变量时发生错误.
                 代码  6. 格式化字符串不匹配      (T2.1) 测例.
                 //测例 1: 格式字符串与实际参数类型不匹配           (要求整型, 传入其他类型)
                 static PyObject* wrong_format_int(PyObject *self, PyObject *args) {
                   int value;
                   //格式字符串声明为“i” (整型), 传入其他类型触发         TypeError
                   if (!PyArg_ParseTuple(args, “i”, &value)) return NULL;
                   return PyLong_FromLong(value * 2);
                 }
                 //测例 2: 参数数量不匹配     (要求  2  个位置参数, 实际   C/C++侧接收变量只有      1  个)
                 static PyObject* wrong_arg_count(PyObject *self, PyObject *args) {
                   int a;
                   //格式字符串声明为“ii” (两个整型位置参数), 但        C/C++侧只有一个变量      a 去接收}
                   if (!PyArg_ParseTuple(args, “ii”, &a)) return NULL;
                   return PyLong_FromLong(a);
                 }
                    在使用   Python/C API 编写扩展模块时, 每一个外部函数         (或方法) 都必须通过     PyMethodDef 结构体进行注册.
                 该结构体包含     4  个字段  (如图  2  中  message.c 第  15–18  行), 其中第  3  个字段是一个比特位域, 用于指明该外部函数
                 的调用约定. 该位域的值决定解释器在运行时如何封装参数并调用外部函数实现. 因此, 该位域的值必须与实际外
                 部函数实现的参数类型相匹配, 调用约定不匹配                (T2.2) 将导致调用栈破坏、参数传递错误等安全漏洞. 所有合法
                 的位域组合及其含义和约束如表           3  所示.

                               表 3 PyMethodDef 结构体调用约定位域标志及其适配的外部函数实现类型

                                                标志值
                            标志组合                                用法          参数数量      适配外部函数实现类型
                                               (十六进制)
                         METH_VARARGS           0x0001   处理位置参数 (元组形式)         2          PyCFunction
                 METH_VARARGS | METH_KEYWORDS   0x0003   处理位置和关键字参数            3     PyCFunctionWithKeywords
                         METH_FASTCALL          0x0080   快速调用 (仅位置参数)          3        PyCFunctionFast
                 METH_FASTCALL | METH_KEYWORDS  0x0082  快速调用 (支持关键字参数)         4   PyCFunctionFastWithKeywords
                  METH_METHOD | METH_FASTCALL |  0x0282    支持定义式类方法            5          PyCMethod
                        METH_KEYWORDS
                          METH_NOARGS           0x0004         无参数             2          PyCFunction
                            METH_O              0x0008       单个对象参数            2          PyCFunction
                          METH_CLASS            0x0010     类方法 (绑定到类)          2          PyCFunction
                          METH_STATIC           0x0020   静态方法 (无实例绑定)          2          PyCFunction

                    如代码   7  所示, 测例  1  的外部函数实现    wrong_flags_noargs 使用  METH_NOARGS  标志, 表明该函数调用应仅
                 接受代表外部函数所属模块或外部方法所属类对应的实例对象                      self 作为隐藏参数而不包含实际参数元组            args, 但
   37   38   39   40   41   42   43   44   45   46   47