python / python/cpython

Use-after-free in `_PyEval_LoadName` via re-entrant frame locals lookup

Đang mở
#143,236 1 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

interpreter-core type-crash
Ngôn ngữ chính
Python
Star
77.2k
Fork
35.9k
Chỉ số merge pull request
Chỉ số pull request đang chờ

Mô tả

What happened?

_PyEval_LoadName consults frame.f_locals through PyMapping_GetOptionalItem, but it does so with the locals proxy still live. A crafted key can run frame.clear() inside its __eq__, freeing the locals dict while the lookup proceeds, so the name resolution continues to read the already-freed keys table and causes a use-after-free.

Proof of Concept:

def get_frame():
    try: raise RuntimeError
    except RuntimeError as e: return e.__traceback__.tb_frame

frame = get_frame()
proxy = frame.f_locals

class Fuse(str):
    armed = False
    __hash__ = str.__hash__
    def __eq__(self, other):
        if not Fuse.armed and other == "boom":
            Fuse.armed = True
            Fuse.frame.clear()
        return True

Fuse.frame = frame
proxy[Fuse("boom")] = 0
exec("boom", {}, proxy)

Affected Versions

Python Version Status Exit Code
Python 3.9.24+ (heads/3.9:111bbc15b26, Oct 28 2025, 16:51:20) OK 0
Python 3.10.19+ (heads/3.10:014261980b1, Oct 28 2025, 16:52:08) [Clang 18.1.3 (1ubuntu1)] OK 0
Python 3.11.14+ (heads/3.11:88f3f5b5f11, Oct 28 2025, 16:53:08) [Clang 18.1.3 (1ubuntu1)] OK 0
Python 3.12.12+ (heads/3.12:8cb2092bd8c, Oct 28 2025, 16:54:14) [Clang 18.1.3 (1ubuntu1)] OK 0
Python 3.13.9+ (heads/3.13:9c8eade20c6, Oct 28 2025, 16:55:18) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.14.0+ (heads/3.14:2e216728038, Oct 28 2025, 16:56:16) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 28 2025, 19:29:54) [GCC 13.3.0] ASAN 1

Vulnerable Code

PyObject *
_PyEval_LoadName(PyThreadState *tstate, _PyInterpreterFrame *frame, PyObject *name)
{

    PyObject *value;
    if (frame->f_locals == NULL) {
        _PyErr_SetString(tstate, PyExc_SystemError,
                            "no locals found");
        return NULL;
    }

    /* Trigger the __eq__ which clear the frame->f_locals */
    if (PyMapping_GetOptionalItem(frame->f_locals, name, &value) < 0) {
        return NULL;
    }
    if (value != NULL) {
        return value;
    }

    if (PyDict_GetItemRef(frame->f_globals, name, &value) < 0) {
        return NULL;
    }
    if (value != NULL) {
        return value;
    }
    if (PyMapping_GetOptionalItem(frame->f_builtins, name, &value) < 0) {
        return NULL;
    }
    if (value == NULL) {
        _PyEval_FormatExcCheckArg(
                    tstate, PyExc_NameError,
                    NAME_ERROR_MSG, name);
    }
    return value;
}

Sanitizer Output

=================================================================
==2195217==ERROR: AddressSanitizer: heap-use-after-free on address 0x50e00000deb0 at pc 0x59ec04e2e8cf bp 0x7ffecc9b6f20 sp 0x7ffecc9b6f10
READ of size 8 at 0x50e00000deb0 thread T0
    #0 0x59ec04e2e8ce in compare_generic Objects/dictobject.c:1115
    #1 0x59ec04e2e8ce in do_lookup Objects/dictobject.c:1010
    #2 0x59ec04e2e8ce in dictkeys_generic_lookup Objects/dictobject.c:1129
    #3 0x59ec04e2e8ce in _Py_dict_lookup Objects/dictobject.c:1295
    #4 0x59ec04e3865e in _PyDict_GetItemRef_KnownHash Objects/dictobject.c:2421
    #5 0x59ec04e3865e in PyDict_GetItemRef Objects/dictobject.c:2456
    #6 0x59ec04dd2260 in framelocalsproxy_getitem Objects/frameobject.c:206
    #7 0x59ec04d0eeba in PyObject_GetItem Objects/abstract.c:163
    #8 0x59ec04d0eeba in PyMapping_GetOptionalItem Objects/abstract.c:215
    #9 0x59ec050db89b in _PyEval_LoadName Python/ceval.c:3536
    #10 0x59ec04c04c5e in _PyEval_EvalFrameDefault Python/generated_cases.c.h:9348
    #11 0x59ec050dcad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #12 0x59ec050dcad6 in _PyEval_Vector Python/ceval.c:2001
    #13 0x59ec050dcad6 in PyEval_EvalCode Python/ceval.c:884
    #14 0x59ec0522216e in run_eval_code_obj Python/pythonrun.c:1365
    #15 0x59ec0522216e in run_mod Python/pythonrun.c:1459
    #16 0x59ec05228670 in _PyRun_StringFlagsWithName Python/pythonrun.c:1259
    #17 0x59ec05228670 in PyRun_StringFlags Python/pythonrun.c:1271
    #18 0x59ec050c6b09 in builtin_exec_impl Python/bltinmodule.c:1209
    #19 0x59ec050c6b09 in builtin_exec Python/clinic/bltinmodule.c.h:571
    #20 0x59ec04d5e3e7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
    #21 0x59ec04d5e3e7 in PyObject_Vectorcall Objects/call.c:327
    #22 0x59ec04c125a2 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
    #23 0x59ec050dcad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #24 0x59ec050dcad6 in _PyEval_Vector Python/ceval.c:2001
    #25 0x59ec050dcad6 in PyEval_EvalCode Python/ceval.c:884
    #26 0x59ec0522216e in run_eval_code_obj Python/pythonrun.c:1365
    #27 0x59ec0522216e in run_mod Python/pythonrun.c:1459
    #28 0x59ec05226e17 in pyrun_file Python/pythonrun.c:1293
    #29 0x59ec05226e17 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #30 0x59ec0522793c in _PyRun_AnyFileObject Python/pythonrun.c:81
    #31 0x59ec0529ae3c in pymain_run_file_obj Modules/main.c:410
    #32 0x59ec0529ae3c in pymain_run_file Modules/main.c:429
    #33 0x59ec0529ae3c in pymain_run_python Modules/main.c:691
    #34 0x59ec0529c71e in Py_RunMain Modules/main.c:772
    #35 0x59ec0529c71e in pymain_main Modules/main.c:802
    #36 0x59ec0529c71e in Py_BytesMain Modules/main.c:826
    #37 0x7d3ef602a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #38 0x7d3ef602a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #39 0x59ec04c36634 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x206634) (BuildId: 4d105290d0ad566a4d6f4f7b2f05fbc9e317b533)

0x50e00000deb0 is located 48 bytes inside of 160-byte region [0x50e00000de80,0x50e00000df20)
freed by thread T0 here:
    #0 0x7d3ef64fc4d8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52
    #1 0x59ec04e2b3df in free_keys_object Objects/dictobject.c:823
    #2 0x59ec04e2b3df in dictkeys_decref Objects/dictobject.c:473
    #3 0x59ec04e2b3df in dictkeys_decref Objects/dictobject.c:446
    #4 0x59ec04e2b3df in dict_dealloc Objects/dictobject.c:3354
    #5 0x59ec04e2b3df in dict_dealloc Objects/dictobject.c:3328
    #6 0x59ec04e7b1d8 in _Py_Dealloc Objects/object.c:3200
    #7 0x59ec04dcc7a4 in Py_DECREF Include/refcount.h:418
    #8 0x59ec04dcc7a4 in frame_tp_clear Objects/frameobject.c:1985
    #9 0x59ec04dcc8aa in frame_clear_impl Objects/frameobject.c:2028
    #10 0x59ec04dcc8aa in frame_clear Objects/clinic/frameobject.c.h:407
    #11 0x59ec04d5e3e7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
    #12 0x59ec04d5e3e7 in PyObject_Vectorcall Objects/call.c:327
    #13 0x59ec04c125a2 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
    #14 0x59ec050dd2a5 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #15 0x59ec050dd2a5 in _PyEval_Vector Python/ceval.c:2001
    #16 0x59ec04f105e2 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
    #17 0x59ec04f105e2 in vectorcall_unbound Objects/typeobject.c:3033
    #18 0x59ec04f105e2 in maybe_call_special_one_arg Objects/typeobject.c:3175
    #19 0x59ec04f105e2 in _PyObject_MaybeCallSpecialOneArg Objects/typeobject.c:3190
    #20 0x59ec04f105e2 in slot_tp_richcompare Objects/typeobject.c:10729
    #21 0x59ec04e822af in do_richcompare Objects/object.c:1059
    #22 0x59ec04e822af in PyObject_RichCompare Objects/object.c:1108
    #23 0x59ec04e822af in PyObject_RichCompareBool Objects/object.c:1130
    #24 0x59ec04e2e373 in compare_generic Objects/dictobject.c:1110
    #25 0x59ec04e2e373 in do_lookup Objects/dictobject.c:1010
    #26 0x59ec04e2e373 in dictkeys_generic_lookup Objects/dictobject.c:1129
    #27 0x59ec04e2e373 in _Py_dict_lookup Objects/dictobject.c:1295
    #28 0x59ec04e3865e in _PyDict_GetItemRef_KnownHash Objects/dictobject.c:2421
    #29 0x59ec04e3865e in PyDict_GetItemRef Objects/dictobject.c:2456
    #30 0x59ec04dd2260 in framelocalsproxy_getitem Objects/frameobject.c:206
    #31 0x59ec04d0eeba in PyObject_GetItem Objects/abstract.c:163
    #32 0x59ec04d0eeba in PyMapping_GetOptionalItem Objects/abstract.c:215
    #33 0x59ec050db89b in _PyEval_LoadName Python/ceval.c:3536
    #34 0x59ec04c04c5e in _PyEval_EvalFrameDefault Python/generated_cases.c.h:9348
    #35 0x59ec050dcad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #36 0x59ec050dcad6 in _PyEval_Vector Python/ceval.c:2001
    #37 0x59ec050dcad6 in PyEval_EvalCode Python/ceval.c:884
    #38 0x59ec0522216e in run_eval_code_obj Python/pythonrun.c:1365
    #39 0x59ec0522216e in run_mod Python/pythonrun.c:1459
    #40 0x59ec05228670 in _PyRun_StringFlagsWithName Python/pythonrun.c:1259
    #41 0x59ec05228670 in PyRun_StringFlags Python/pythonrun.c:1271
    #42 0x59ec050c6b09 in builtin_exec_impl Python/bltinmodule.c:1209
    #43 0x59ec050c6b09 in builtin_exec Python/clinic/bltinmodule.c.h:571
    #44 0x59ec04d5e3e7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
    #45 0x59ec04d5e3e7 in PyObject_Vectorcall Objects/call.c:327
    #46 0x59ec04c125a2 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
    #47 0x59ec050dcad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #48 0x59ec050dcad6 in _PyEval_Vector Python/ceval.c:2001
    #49 0x59ec050dcad6 in PyEval_EvalCode Python/ceval.c:884
    #50 0x59ec0522216e in run_eval_code_obj Python/pythonrun.c:1365
    #51 0x59ec0522216e in run_mod Python/pythonrun.c:1459
    #52 0x59ec05226e17 in pyrun_file Python/pythonrun.c:1293
    #53 0x59ec05226e17 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #54 0x59ec0522793c in _PyRun_AnyFileObject Python/pythonrun.c:81
    #55 0x59ec0529ae3c in pymain_run_file_obj Modules/main.c:410
    #56 0x59ec0529ae3c in pymain_run_file Modules/main.c:429
    #57 0x59ec0529ae3c in pymain_run_python Modules/main.c:691
    #58 0x59ec0529c71e in Py_RunMain Modules/main.c:772
    #59 0x59ec0529c71e in pymain_main Modules/main.c:802
    #60 0x59ec0529c71e in Py_BytesMain Modules/main.c:826
    #61 0x7d3ef602a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #62 0x7d3ef602a28a in __libc_start_main_impl ../csu/libc-start.c:360

previously allocated by thread T0 here:
    #0 0x7d3ef64fd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x59ec04e27f14 in new_keys_object Objects/dictobject.c:784
    #2 0x59ec04e27f14 in insert_to_emptydict Objects/dictobject.c:1948
    #3 0x59ec04c01cbc in _PyEval_EvalFrameDefault Python/generated_cases.c.h:11245
    #4 0x59ec050dcad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #5 0x59ec050dcad6 in _PyEval_Vector Python/ceval.c:2001
    #6 0x59ec050dcad6 in PyEval_EvalCode Python/ceval.c:884
    #7 0x59ec0522216e in run_eval_code_obj Python/pythonrun.c:1365
    #8 0x59ec0522216e in run_mod Python/pythonrun.c:1459
    #9 0x59ec05226e17 in pyrun_file Python/pythonrun.c:1293
    #10 0x59ec05226e17 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #11 0x59ec0522793c in _PyRun_AnyFileObject Python/pythonrun.c:81
    #12 0x59ec0529ae3c in pymain_run_file_obj Modules/main.c:410
    #13 0x59ec0529ae3c in pymain_run_file Modules/main.c:429
    #14 0x59ec0529ae3c in pymain_run_python Modules/main.c:691
    #15 0x59ec0529c71e in Py_RunMain Modules/main.c:772
    #16 0x59ec0529c71e in pymain_main Modules/main.c:802
    #17 0x59ec0529c71e in Py_BytesMain Modules/main.c:826
    #18 0x7d3ef602a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #19 0x7d3ef602a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #20 0x59ec04c36634 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x206634) (BuildId: 4d105290d0ad566a4d6f4f7b2f05fbc9e317b533)

SUMMARY: AddressSanitizer: heap-use-after-free Objects/dictobject.c:1115 in compare_generic
Shadow bytes around the buggy address:
  0x50e00000dc00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x50e00000dc80: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x50e00000dd00: fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa
  0x50e00000dd80: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00
  0x50e00000de00: 00 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa
=>0x50e00000de80: fd fd fd fd fd fd[fd]fd fd fd fd fd fd fd fd fd
  0x50e00000df00: fd fd fd fd fa fa fa fa fa fa fa fa fd fd fd fd
  0x50e00000df80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x50e00000e000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50e00000e080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50e00000e100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==2195217==ABORTING
Linked PRs
  • gh-143283

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Bắt đầu trong Python/ceval.c tại _PyEval_LoadName, sau đó lần theo việc tra cứu các biến cục bộ của frame qua Objects/frameobject.c và Objects/dictobject.c. Tái hiện proof of concept được cung cấp bằng bản build ASAN và bổ sung độ bao phủ hồi quy; hoàn thành khi việc tra cứu không còn tạo ra use-after-free đã được báo cáo và các bài kiểm thử liên quan đều vượt qua.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
c, python
Lĩnh vực
compilers, security
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
35/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.