python / python/cpython

Type confusion in `_channelid_shared` via unvalidated `_id` attribute

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

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

extension-modules topic-subinterpreters 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?

_channelend_shared trusts an end's _id attribute and hands it to _channelid_shared, which blindly treats the object as a _ChannelID struct; a crafted end type can return an arbitrary tiny object, so the decoder misinterprets memory layout and walks past the allocation, producing a type confusion that ends in a heap buffer overflow.

Proof of Concept:

import _interpchannels as ch
import _interpreters

class Tiny:
    __slots__ = ()

tiny = Tiny()

class End:
    __slots__ = ()
    def __new__(cls, cid=None):
        return object.__new__(cls)
    @property
    def _id(self):
        return tiny  # bogus non-string id

class Send(End): pass
class Recv(End): pass

ch._register_end_types(Send, Recv)
chan = ch._channel_id(ch.create(), _resolve=True)

interp = _interpreters.create()
try:
    _interpreters.run_string(interp, "pass", {"chan": Send(chan)})
finally:
    _interpreters.destroy(interp)

Vulnerable Code Snippet:

Click to expand
/* Buggy Re-entrant Path */
static PyObject *
_interpreters_run_string_impl(PyObject *module, PyObject *id,
                              PyObject *script, PyObject *shared,
                              int restricted)
{
    /* ... */
    int res = _run_in_interpreter(tstate, interp, &xidata, NULL, shared, &runres);
    /* ... */
    Py_RETURN_NONE;
}

static int
_get_xidata(PyThreadState *tstate,
            PyObject *obj, xidata_fallback_t fallback, _PyXIData_t *xidata)
{
    /* ... */
    _PyXIData_getdata_t getdata = lookup_getdata(&ctx, obj);
    int res = getdata.basic != NULL
        ? getdata.basic(tstate, obj, xidata)
        : getdata.fallback(tstate, obj, fallback, xidata);
    /* ... */
    return res;
}

static int
_channelend_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data)
{
    PyObject *cidobj = PyObject_GetAttrString(obj, "_id");  /* Reentrant call site */
    if (cidobj == NULL) {
        return -1;
    }
    int res = _channelid_shared(tstate, cidobj, data);
    Py_DECREF(cidobj);
    if (res < 0) {
        return -1;
    }
    _PyXIData_SET_NEW_OBJECT(data, _channelend_from_xid);
    return 0;
}

static int
_channelid_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data)
{
    /* ... */
    struct _channelid_xid *xid = (struct _channelid_xid *)_PyXIData_DATA(data);
    channelid *cidobj = channelid_CAST(obj);  /* crashing pointer derived */
    xid->cid = cidobj->cid;                   /* Crash site */
    xid->end = cidobj->end;
    xid->resolve = cidobj->resolve;
    return 0;
}

/* Clobbering Path */
static int
_channelend_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data)
{
    PyObject *cidobj = PyObject_GetAttrString(obj, "_id");  /* state mutate site */
    /* no validation that cidobj is a _ChannelID */
    /* ... */
    return 0;
}

Sanitizer Output:

Click to expand
=================================================================
==2202758==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50300001af70 at pc 0x7ddaf5b70bab bp 0x7ffddb3f5520 sp 0x7ffddb3f5510
READ of size 8 at 0x50300001af70 thread T0
    #0 0x7ddaf5b70baa in _channelid_shared Modules/_interpchannelsmodule.c:2643
    #1 0x7ddaf5b70baa in _channelend_shared Modules/_interpchannelsmodule.c:2799
    #2 0x56723fa7ae6a in _get_xidata Python/crossinterp.c:502
    #3 0x56723fa7f779 in _sharednsitem_set_value Python/crossinterp.c:2076
    #4 0x56723fa7f779 in _sharednsitem_copy_from_ns Python/crossinterp.c:2121
    #5 0x56723fa7f779 in _fill_sharedns Python/crossinterp.c:2385
    #6 0x56723fa854a3 in _PyXI_Enter Python/crossinterp.c:2639
    #7 0x7ddaf5b58caf in _run_in_interpreter Modules/_interpretersmodule.c:674
    #8 0x7ddaf5b5a528 in _interpreters_run_string_impl Modules/_interpretersmodule.c:1202
    #9 0x7ddaf5b5a528 in _interpreters_run_string Modules/clinic/_interpretersmodule.c.h:531
    #10 0x56723f6a93e7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
    #11 0x56723f6a93e7 in PyObject_Vectorcall Objects/call.c:327
    #12 0x56723f55d5a2 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
    #13 0x56723fa27ad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #14 0x56723fa27ad6 in _PyEval_Vector Python/ceval.c:2001
    #15 0x56723fa27ad6 in PyEval_EvalCode Python/ceval.c:884
    #16 0x56723fb6d16e in run_eval_code_obj Python/pythonrun.c:1365
    #17 0x56723fb6d16e in run_mod Python/pythonrun.c:1459
    #18 0x56723fb71e17 in pyrun_file Python/pythonrun.c:1293
    #19 0x56723fb71e17 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #20 0x56723fb7293c in _PyRun_AnyFileObject Python/pythonrun.c:81
    #21 0x56723fbe5e3c in pymain_run_file_obj Modules/main.c:410
    #22 0x56723fbe5e3c in pymain_run_file Modules/main.c:429
    #23 0x56723fbe5e3c in pymain_run_python Modules/main.c:691
    #24 0x56723fbe771e in Py_RunMain Modules/main.c:772
    #25 0x56723fbe771e in pymain_main Modules/main.c:802
    #26 0x56723fbe771e in Py_BytesMain Modules/main.c:826
    #27 0x7ddaf602a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #28 0x7ddaf602a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #29 0x56723f581634 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x206634) (BuildId: 4d105290d0ad566a4d6f4f7b2f05fbc9e317b533)

0x50300001af70 is located 0 bytes after 32-byte region [0x50300001af50,0x50300001af70)
allocated by thread T0 here:
    #0 0x7ddaf64fd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x56723f84e88e in _PyObject_MallocWithType Include/internal/pycore_object_alloc.h:46
    #2 0x56723f84e88e in _PyType_AllocNoTrack Objects/typeobject.c:2504
    #3 0x56723f84eaf4 in PyType_GenericAlloc Objects/typeobject.c:2535
    #4 0x56723f846118 in type_call Objects/typeobject.c:2448
    #5 0x56723f6a79cd in _PyObject_MakeTpCall Objects/call.c:242
    #6 0x56723f565f33 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
    #7 0x56723fa27ad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #8 0x56723fa27ad6 in _PyEval_Vector Python/ceval.c:2001
    #9 0x56723fa27ad6 in PyEval_EvalCode Python/ceval.c:884
    #10 0x56723fb6d16e in run_eval_code_obj Python/pythonrun.c:1365
    #11 0x56723fb6d16e in run_mod Python/pythonrun.c:1459
    #12 0x56723fb71e17 in pyrun_file Python/pythonrun.c:1293
    #13 0x56723fb71e17 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #14 0x56723fb7293c in _PyRun_AnyFileObject Python/pythonrun.c:81
    #15 0x56723fbe5e3c in pymain_run_file_obj Modules/main.c:410
    #16 0x56723fbe5e3c in pymain_run_file Modules/main.c:429
    #17 0x56723fbe5e3c in pymain_run_python Modules/main.c:691
    #18 0x56723fbe771e in Py_RunMain Modules/main.c:772
    #19 0x56723fbe771e in pymain_main Modules/main.c:802
    #20 0x56723fbe771e in Py_BytesMain Modules/main.c:826
    #21 0x7ddaf602a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #22 0x7ddaf602a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #23 0x56723f581634 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x206634) (BuildId: 4d105290d0ad566a4d6f4f7b2f05fbc9e317b533)

SUMMARY: AddressSanitizer: heap-buffer-overflow Modules/_interpchannelsmodule.c:2643 in _channelid_shared
Shadow bytes around the buggy address:
  0x50300001ac80: fd fd fd fd fa fa fd fd fd fd fa fa fd fd fd fd
  0x50300001ad00: fa fa fd fd fd fd fa fa fd fd fd fd fa fa fd fd
  0x50300001ad80: fd fd fa fa fd fd fd fd fa fa fd fd fd fd fa fa
  0x50300001ae00: fd fd fd fd fa fa fd fd fd fd fa fa fd fd fd fd
  0x50300001ae80: fa fa fd fd fd fd fa fa 00 00 00 00 fa fa fd fd
=>0x50300001af00: fd fd fa fa 00 00 00 00 fa fa 00 00 00 00[fa]fa
  0x50300001af80: 00 00 00 00 fa fa 00 00 00 00 fa fa 00 00 00 00
  0x50300001b000: fa fa 00 00 00 fa fa fa 00 00 00 00 fa fa 00 00
  0x50300001b080: 00 00 fa fa fd fd fd fa fa fa fd fd fd fd fa fa
  0x50300001b100: fd fd fd fa fa fa fd fd fd fa fa fa fd fd fd fd
  0x50300001b180: fa fa fd fd fd fd fa fa 00 00 00 00 fa fa 00 00
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
==2202758==ABORTING
CPython versions tested on:
Python Version Status Exit Code
Python 3.9.24+ (heads/3.9:111bbc15b26, Oct 28 2025, 16:51:20) Exception 1
Python 3.10.19+ (heads/3.10:014261980b1, Oct 28 2025, 16:52:08) [Clang 18.1.3 (1ubuntu1)] Exception 1
Python 3.11.14+ (heads/3.11:88f3f5b5f11, Oct 28 2025, 16:53:08) [Clang 18.1.3 (1ubuntu1)] Exception 1
Python 3.12.12+ (heads/3.12:8cb2092bd8c, Oct 28 2025, 16:54:14) [Clang 18.1.3 (1ubuntu1)] Exception 1
Python 3.13.9+ (heads/3.13:9c8eade20c6, Oct 28 2025, 16:55:18) [Clang 18.1.3 (1ubuntu1)] Exception 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
Operating systems tested on:

Linux

Output from running 'python -VV' on the command line:

Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 28 2025, 19:29:54) [GCC 13.3.0]

Linked PRs
  • gh-146574

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 Modules/_interpchannelsmodule.c tại _channelend_shared và _channelid_shared, sau đó theo dõi đường đi _get_xidata trong Python/crossinterp.c. Tái hiện proof of concept được cung cấp dưới ASAN và xác minh rằng _id không hợp lệ bị từ chối mà không xảy ra heap-buffer-overflow; bổ sung kiểm thử hồi quy cho trường hợp này.

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
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
Đặc tả rõ ràng
Mức phù hợp với người mới
25/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.