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 堆申请了

