python / python/cpython

Heap buffer overflow in ctypes array assignment via `__class__` swap

Ouverte
#143,005 1 commentaire 2 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

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

Swapping a ctypes array instance to a wider subclass through __class__ bypasses the length check in Array_ass_item_lock_held, so writing to the new tail positions stores past the original allocation and corrupts adjacent heap memory.

Proof of Concept:

from ctypes import c_long

Base = c_long * 3
Wide = c_long * 5

victim = Base(1, 2, 3)
victim.__class__ = Wide # Make the CPython think we have a array whose length is 5.
victim[4] = 42  # OOB write

Affected Versions:

Python Version Status Exit Code
Python 3.9.24+ (heads/3.9:111bbc15b26, Oct 27 2025, 21:34:13) ASAN 1
Python 3.10.19+ (heads/3.10:014261980b1, Oct 27 2025, 21:19:00) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.11.14+ (heads/3.11:88f3f5b5f11, Oct 27 2025, 21:20:35) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.12.12+ (heads/3.12:8cb2092bd8c, Oct 27 2025, 21:27:07) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.13.9+ (heads/3.13:9c8eade20c6, Oct 27 2025, 21:28:49) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.14.0+ (heads/3.14:2e216728038, Oct 27 2025, 21:30:55) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 27 2025, 21:32:37) [Clang 18.1.3 (1ubuntu1)] ASAN 1

Vulnerable Code:

static int
Array_ass_item_lock_held(PyObject *myself, Py_ssize_t index, PyObject *value)
{
    _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(myself);

    CDataObject *self = _CDataObject_CAST(myself);
    Py_ssize_t size, offset;
    char *ptr;

    if (value == NULL) {
        PyErr_SetString(PyExc_TypeError,
                        "Array does not support item deletion");
        return -1;
    }

    ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self)));
    StgInfo *stginfo;
    if (PyStgInfo_FromObject(st, myself, &stginfo) < 0) {
        return -1;
    }
    assert(stginfo); /* Cannot be NULL for array object instances */

    if (index < 0 || index >= stginfo->length) {
        PyErr_SetString(PyExc_IndexError,
                        "invalid index");
        return -1;
    }
    size = stginfo->size / stginfo->length;
    offset = index * size;
    ptr = self->b_ptr + offset;

    return PyCData_set(st, myself, stginfo->proto, stginfo->setfunc, value,
                       index, size, ptr);
}

Sanitizer Output:

=================================================================
==1353195==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50300001fa10 at pc 0x7d86b13e09a7 bp 0x7ffdb7d72a30 sp 0x7ffdb7d72a28
WRITE of size 8 at 0x50300001fa10 thread T0
    #0 0x7d86b13e09a6 in i64_set /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_ctypes/cfield.c:644:1
    #1 0x7d86b13cb6ff in PyCData_set /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_ctypes/_ctypes.c:3542:14
    #2 0x7d86b13cb6ff in Array_ass_item_lock_held /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_ctypes/_ctypes.c:5194:12
    #3 0x7d86b13caf37 in Array_ass_subscript_lock_held /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_ctypes/_ctypes.c:5227:16
    #4 0x7d86b13caf37 in Array_ass_subscript /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_ctypes/_ctypes.c:5271:14
    #5 0x57424f119d68 in _PyEval_EvalFrameDefault /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/generated_cases.c.h:11245:27
    #6 0x57424f10b3bb in _PyEval_EvalFrame /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Include/internal/pycore_ceval.h:121:16
    #7 0x57424f10b3bb in _PyEval_Vector /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/ceval.c:2005:12
    #8 0x57424f10b3bb in PyEval_EvalCode /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/ceval.c:888:21
    #9 0x57424f262370 in run_eval_code_obj /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:1365:12
    #10 0x57424f262370 in run_mod /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:1459:19
    #11 0x57424f25c43c in pyrun_file /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:1293:15
    #12 0x57424f25c43c in _PyRun_SimpleFileObject /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:521:13
    #13 0x57424f25bb05 in _PyRun_AnyFileObject /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:81:15
    #14 0x57424f2c3fe5 in pymain_run_file_obj /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:410:15
    #15 0x57424f2c3fe5 in pymain_run_file /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:429:15
    #16 0x57424f2c299d in pymain_run_python /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:691:21
    #17 0x57424f2c299d in Py_RunMain /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:772:5
    #18 0x57424f2c3451 in pymain_main /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:802:12
    #19 0x57424f2c35c3 in Py_BytesMain /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:826:12
    #20 0x7d86b1c2a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #21 0x7d86b1c2a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #22 0x57424ec73104 in _start (/home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/python+0x1c7104) (BuildId: 5de9d2fcbcd44bfc1b0fe256566d49ad35ca1d56)

0x50300001fa10 is located 8 bytes after 24-byte region [0x50300001f9f0,0x50300001fa08)
allocated by thread T0 here:
    #0 0x57424ed0df53 in malloc (/home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/python+0x261f53) (BuildId: 5de9d2fcbcd44bfc1b0fe256566d49ad35ca1d56)
    #1 0x7d86b13bdad2 in PyCData_MallocBuffer /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_ctypes/_ctypes.c:3271:30
    #2 0x7d86b13bdad2 in generic_pycdata_new /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_ctypes/_ctypes.c:3589:15
    #3 0x57424ef91a9b in type_call /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Objects/typeobject.c:2449:11
    #4 0x57424ee427b2 in _PyObject_MakeTpCall /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Objects/call.c:242:18
    #5 0x57424f12a837 in _PyEval_EvalFrameDefault /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/generated_cases.c.h:1620:35
    #6 0x57424f10b3bb in _PyEval_EvalFrame /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Include/internal/pycore_ceval.h:121:16
    #7 0x57424f10b3bb in _PyEval_Vector /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/ceval.c:2005:12
    #8 0x57424f10b3bb in PyEval_EvalCode /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/ceval.c:888:21
    #9 0x57424f262370 in run_eval_code_obj /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:1365:12
    #10 0x57424f262370 in run_mod /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:1459:19
    #11 0x57424f25c43c in pyrun_file /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:1293:15
    #12 0x57424f25c43c in _PyRun_SimpleFileObject /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:521:13
    #13 0x57424f25bb05 in _PyRun_AnyFileObject /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:81:15
    #14 0x57424f2c3fe5 in pymain_run_file_obj /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:410:15
    #15 0x57424f2c3fe5 in pymain_run_file /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:429:15
    #16 0x57424f2c299d in pymain_run_python /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:691:21
    #17 0x57424f2c299d in Py_RunMain /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:772:5
    #18 0x57424f2c3451 in pymain_main /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:802:12
    #19 0x57424f2c35c3 in Py_BytesMain /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:826:12
    #20 0x7d86b1c2a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #21 0x7d86b1c2a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #22 0x57424ec73104 in _start (/home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/python+0x1c7104) (BuildId: 5de9d2fcbcd44bfc1b0fe256566d49ad35ca1d56)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_ctypes/cfield.c:644:1 in i64_set
Shadow bytes around the buggy address:
  0x50300001f780: 00 00 00 00 fa fa fd fd fd fd fa fa 00 00 00 00
  0x50300001f800: fa fa fd fd fd fd fa fa fd fd fd fd fa fa 00 00
  0x50300001f880: 00 00 fa fa fd fd fd fd fa fa 00 00 00 00 fa fa
  0x50300001f900: fd fd fd fd fa fa fd fd fd fd fa fa 00 00 00 00
  0x50300001f980: fa fa fd fd fd fd fa fa 00 00 00 00 fa fa 00 00
=>0x50300001fa00: 00 fa[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50300001fa80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50300001fb00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50300001fb80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50300001fc00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50300001fc80: 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
==1353195==ABORTING
Linked PRs
  • gh-143907

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 par Array_ass_item_lock_held dans Modules/_ctypes/_ctypes.c et reproduisez le swap de class fourni sous ASAN. C'est terminé lorsque l'affectation du tableau ctypes n'écrit plus au-delà de l'allocation d'origine ; consultez le PR lié gh-143907 avant de commencer.

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é
Plutôt claire
Accessibilité débutants
25/100

Recevez les nouvelles issues par e-mail

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