python / python/cpython

CPython UaF during index callbacks

未關閉
#144,128 2 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

還沒有人認領這個 Issue。

3.13 3.14 3.15 extension-modules pending type-crash
主要語言
Python
星號
77.2k
分支
35.9k
PR 合併指標
PR 指標待擷取

描述

Bug report

Bug description:

The _PyNumber_Index, PyFloat_AsDouble and PyNumber_Long functions use a borrowed reference for error formatting after calling Python slots (index/float), but the slot's frame cleanup can free the object before the error is formatted.

A use-after-free vulnerability exists in CPython's type conversion functions (_PyNumber_Index, PyFloat_AsDouble and PyNumber_Long) where a borrowed reference is used for error message formatting after a Python callback that can free the referenced object.
For example, the vulnerable code pattern could be observed in _PyNumber_Index implementation (Objects/abstract.c):

  PyObject *
  _PyNumber_Index(PyObject *item)    // item is a BORROWED reference
  {
      if (item == NULL) {
          return null_error();
      }

      if (PyLong_Check(item)) {
          return Py_NewRef(item);
      }
      if (!_PyIndex_Check(item)) {
          PyErr_Format(PyExc_TypeError,
                       "'%.200s' object cannot be interpreted "
                       "as an integer", Py_TYPE(item)->tp_name);
          return NULL;
      }

      // === PYTHON CODE EXECUTES HERE ===
      PyObject *result = Py_TYPE(item)->tp_as_number->nb_index(item);
      // === item MAY NOW BE FREED ===

      if (!result || PyLong_CheckExact(result)) {
          return result;
      }

      if (!PyLong_Check(result)) {
          PyErr_Format(PyExc_TypeError,
                       "%T.__index__() must return an int, not %T",
                       item, result);  // <== UAF: item may be freed
          Py_DECREF(result);
          return NULL;
      }
      // ... similar pattern continues
  }

Similar patterns can be found in the PyFloat_AsDouble and PyNumber_Long functions.

The Python array.fromlist() method provides an ideal attack vector because it:

Iterates over a list using PyList_GET_ITEM() (returns borrowed reference)
Passes the borrowed reference directly to type-specific setitem functions
These functions call _PyNumber_Index() or PyFloat_AsDouble() for conversion.

Running with a python interpreter compiled with ASAN (./configure --with-address-sanitizer):

import array

class Evil:
    def __init__(self, lst):
        self.lst = lst

    def __index__(self):
        self.lst.clear()
        return "not an int"

lst = []
e = Evil(lst)
lst.append(e)
del e

arr = array.array('I')
arr.fromlist(lst)

You'll get the following:

==73525==ERROR: AddressSanitizer: heap-use-after-free on address 0x6130000300b8 at pc 0x0001013458a4 bp 0x00016ee396f0 sp 0x00016ee396e8
READ of size 8 at 0x6130000300b8 thread T0
    #0 0x0001013458a0 in unicode_from_format unicodeobject.c:3075
    #1 0x0001013436a8 in PyUnicode_FromFormatV unicodeobject.c:3109
    #2 0x00010154d5c8 in PyErr_Format errors.c:1243
    #3 0x0001010fe4e8 in _PyNumber_Index abstract.c:1433
    #4 0x000109338334 in II_setitem arraymodule.c:411
    #5 0x0001093427d4 in array_array_fromlist arraymodule.c.h:495
    #6 0x00010114aeb0 in _PyObject_VectorcallTstate pycore_call.h:136
    #7 0x0001014828ac in _Py_VectorCallInstrumentation_StackRefSteal ceval.c:1094
    #8 0x0001014b4718 in _PyEval_EvalFrameDefault generated_cases.c.h:1785
    #9 0x00010148180c in _PyEval_Vector ceval.c:2516
    #10 0x00010148105c in PyEval_EvalCode ceval.c:1005
    #11 0x000101646118 in run_mod pythonrun.c:1469
    #12 0x00010163e6a0 in _PyRun_SimpleFileObject pythonrun.c:518
    #13 0x00010163da30 in _PyRun_AnyFileObject pythonrun.c:81
    #14 0x0001016ac948 in pymain_run_file main.c:429
    #15 0x0001016aabcc in Py_RunMain main.c:772
    #16 0x0001016ab9fc in pymain_main main.c:802
    #17 0x0001016abba8 in Py_BytesMain main.c:826
    #18 0x00019e4edd50  (<unknown module>)
CPython versions tested on:

3.15

Operating systems tested on:

macOS

Linked PRs
  • gh-144138

貢獻指南

開啟貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

研究方向

從 Objects/abstract.c 中的 _PyNumber_Index、PyFloat_AsDouble 和 PyNumber_Long 開始,接著檢查 arraymodule.c 的呼叫路徑,並使用 ASAN 範例重現。比較目前樹中的相關處理和測試;完成表示已在列出的所有轉換函式中處理所回報的 use-after-free,且 ASAN 重現程式不再失敗。

由索引模型根據 Issue 內容生成。

評估

技術堆疊
c, python
領域
security
Issue 類型
缺陷
難度
4/5
預估耗時
3-5 天
活躍度
停滯
描述清晰度
描述清楚
新手友好度
25/100

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。