python / python/cpython

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

Ouverte
#143,376 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

extension-modules topic-subinterpreters type-crash
Langage dominant
Python
Étoiles
77.2k
Forks
35.9k
Métriques de merge des PR
Métriques de PR en attente

Description

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

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez dans Modules/_interpchannelsmodule.c, au niveau de _channelend_shared et _channelid_shared, puis suivez le chemin de _get_xidata dans Python/crossinterp.c. Reproduisez la preuve de concept fournie sous ASAN et vérifiez qu’un _id invalide est rejeté sans le heap-buffer-overflow ; ajoutez une couverture de régression pour ce cas.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
c, python
Domaine
security
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
À l'abandon
Clarté
Clairement spécifiée
Accessibilité débutants
25/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.