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

2724                                                       软件学报  2026  年第  37  卷第  7  期


                 Python  对象, 其引用计数在生命周期结束时仍然为            1, 导致其无法被回收, 形成内存泄漏 (M1.1). 代码         1  的测例  2
                 中, 外部函数实现     bad_return_ref 通过  Python/C API 函数  PyList_GetItem  获取列表对象中的元素, 由于该函数返回
                 一个借引用, 在将其返回至        Python  侧时应增加其引用计数, 以避免原列表对象被垃圾回收而使得                   Python  侧持有
                 一个悬空引用 (M1.2), 该漏洞可能导致段错误或未定义行为.
                 代码  1. 引用计数错误测例.

                 /*测例  1: 引用计数内存泄漏     (M1.1)*/
                 static PyObject* leaky_creation(PyObject *self, PyObject *args) {
                   PyObject *num = PyLong_FromLong(42);
                   //错误: 创建对象但未正确管理引用, 导致内存泄漏
                   Py_RETURN_NONE;
                 }
                 /*测例  2: 引用计数悬空引用     (M1.2)*/
                 static PyObject* bad_return_ref(PyObject *self, PyObject *args) {
                   PyObject *list;
                   if (!PyArg_ParseTuple(args, “O!”, &PyList_Type, &list)) return NULL;
                   PyObject *item = PyList_GetItem(list, 0);
                   //错误: 直接返回借引用, 未增加引用计数, 可能导致悬空引用
                   return item;
                 }

                  2.1.2    内存管理错误 (M2)
                    Python  和  C/C++在内存管理机制上存在显著差异. C/C++依赖标准库函数             (如  malloc 和  free) 进行显式的内存
                 分配与释放, 而    Python  则采用其内建的内存管理器在私有堆上进行分配. Python             和  C/C++通过外部接口    Python/C
                 API 互操作时, 共享地址空间中的        Python  对象需要使用   Python  运行时的内存接口进行分配和释放. 这些内存接口
                 并不直接向操作系统请求资源, 而是通过其内部内存分配策略进行统一调度, 以提升性能与跨平台的一致性. 如
                 表  2  所示, 根据内存域的不同, Python-C/C++互操作程序中包含几族作用不同的内存接口. 构造析构不匹配                      (M2.1)
                 可能破坏共享地址空间, 导致内存泄漏或未定义行为.

                                             表 2 Python-C/C++互操作程序内存接口

                      内存域           分配函数                 分配函数作用                 释放函数        释放函数作用
                                               分配原始内存块, 返回未初始化的内存指针,
                                 PyMem_RawMalloc                             PyMem_RawFree
                 Raw Memory接口 (线                    请求0字节返回非NULL指针                        释放Raw域分配的内
                   程安全/无需GIL)    PyMem_RawCalloc 分配并清零的连续内存块, 适合数组初始化 PyMem_RawFree            存块
                                 PyMem_RawRealloc 调整内存块大小, 支持原地扩展或新分配        PyMem_RawFree
                                               从Python堆分配通用内存, 默认使用pymalloc
                                  PyMem_Malloc                                 PyMem_Free
                                                          优化小对象                           释放Mem域分配的
                 通用内存接口 (需GIL)
                                  PyMem_Calloc    分配清零内存块, 用于初始化场景             PyMem_Free      内存
                                  PyMem_Realloc      重新分配通用内存块大小               PyMem_Free
                                  PyObject_Malloc  专为Python对象设计的内存分配接口        PyObject_Free
                                                                                          释放Object域分配的
                 对象内存接口 (需GIL)    PyObject_Calloc   分配并初始化为0的对象内存             PyObject_Free
                                                                                              对象内存
                                  PyObject_Realloc  调整已分配对象内存的大小              PyObject_Free
                                                     类型安全的封装, 等价于                         等价PyMem_Free(p),
                     类型安全宏         PyMem_New                                   PyMem_Del
                                                 ((type*)PyMem_Malloc(n*sizeof(type)))        类型安全

                    代码  2  包含两个漏洞测例. 外部函数实现         demo_pymem_malloc_free 通过  PyMem_Malloc 从  Python  堆申请了
   34   35   36   37   38   39   40   41   42   43   44