GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — `_decimal::CURRENT_CONTEXT`: UAF via borrowed reference across Python callbacks · Issue #148382 · python/cpython

_decimal::CURRENT_CONTEXT: UAF via borrowed reference across Python callbacks #148382

New issue
Copy link
New issue
Copy link
Open
Open
_decimal::CURRENT_CONTEXT: UAF via borrowed reference across Python callbacks#148382
Copy link
Labels
extension-modulesC modules in the Modules dirC modules in the Modules dirtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump

Description

@picnixz
picnixz
opened on Apr 11, 2026
Issue body actions

Crash report

Originally reported by @PidSS as a security advisory

What happened?

The CURRENT_CONTEXT() macro in Modules/_decimal/_decimal.c:1951 obtains a new reference from PyContextVar_Get(), then immediately calls Py_DECREF, leaving a borrowed reference. If Python code executes before the context pointer is next used — via warning handlers, GC finalizers, instancecheck, etc. — and that code calls decimal.setcontext(), the old context is freed while C code still holds a dangling pointer. The macro is used at 20+ call sites covering virtually every decimal operation.

Original reproducer (I will try to make a shorter one):

import decimal, warnings, gc, sys, ctypes

gc.disable()
ctx = decimal.getcontext()
CTX_ID, CTX_SIZE = id(ctx), sys.getsizeof(ctx)
del ctx  # refcount = 1, held only by ContextVar HAMT

def evil_warning_handler(message, category, filename, lineno, file=None, line=None):
    if 'Format specifier' not in str(message):
        return
    ctx = decimal.getcontext()                       # refcount -> 2
    decimal.setcontext(decimal.Context(prec=999))    # HAMT ref dropped -> 1
    del ctx                                          # refcount -> 0, FREED
    spray = [bytearray(b'\xCC' * (CTX_SIZE - 32)) for _ in range(100000)]

warnings.showwarning = evil_warning_handler
warnings.filterwarnings('always', category=DeprecationWarning)
format(decimal.Decimal('123.456'), 'N')

FTR, I saw that we have this macro twice, but it depends on whether we have WITH_DECIMAL_CONTEXTVAR. If I build Python with --without-decimal-contextvar, I also have a crash so it's a bit weird.

Related but not with the same root cause: #146011.

CPython versions tested on:

CPython main branch

Operating systems tested on:

No response

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

No response

Activity

  1. picnixz
    self-assigned this
    on Apr 11, 2026
  2. picnixz
    added
    extension-modulesC modules in the Modules dirC modules in the Modules dir
    type-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
    on Apr 11, 2026
  3. picnixz
    removed their assignment
    on Apr 11, 2026
  4. VanshAgarwal24036 commented on Apr 12, 2026

    @VanshAgarwal24036
    VanshAgarwal24036
    on Apr 12, 2026
    Contributor
    More actions

    if this issue is free to work so i want to look into it

  5. skirpichev commented on Apr 12, 2026

    @skirpichev
    skirpichev
    on Apr 12, 2026
    Member
    More actions

    Feel free to try, but I would warn you that this is not an easy issue.

  6. picnixz commented on Apr 12, 2026

    @picnixz
    picnixz
    on Apr 12, 2026
    MemberAuthor
    More actions

    if this issue is free to work so i want to look into it

    @VanshAgarwal24036 I would prefer that a core dev takes care of it, sorry. The problem is that I do not want an LLM doing this task.

  7. brijkapadia commented on May 29, 2026

    @brijkapadia
    brijkapadia
    on May 29, 2026
    Contributor
    More actions

    One potential solution would be to make PyContextVar_Get use strong references instead of borrowed ones. I don't think that there is an easy solution with borrowed references.

  8. IvyXu420 commented on Jul 15, 2026

    @IvyXu420
    IvyXu420
    on Jul 15, 2026
    Contributor
    More actions

    I can't reproduce the crash using the original reproducer in the issue (tested on CPython main branch).

  9. brijkapadia commented on Jul 15, 2026

    @brijkapadia
    brijkapadia
    on Jul 15, 2026
    Contributor
    More actions

    @ByteFlowing1337 try running it with ASan (--with-address-sanitizer). ASan gave me the following report:

    ASan heap-use-after-free

    =================================================================
    ==513990==ERROR: AddressSanitizer: heap-use-after-free on address 0x7bdfd1010f20 at pc 0x7f0fd15cd890 bp 0x7ffff5004a50 sp 0x7ffff5004a40
    READ of size 4 at 0x7bdfd1010f20 thread T0
        #0 0x7f0fd15cd88f in _decimal_Decimal___format___impl Modules/_decimal/_decimal.c:3788
        #1 0x7f0fd15cdf5e in _decimal_Decimal___format__ Modules/_decimal/clinic/_decimal.c.h:908
        #2 0x5625702ccc94 in cfunction_vectorcall_FASTCALL_KEYWORDS_METHOD Objects/methodobject.c:481
        #3 0x56257021430a in _PyObject_VectorcallTstate Include/internal/pycore_call.h:144
        #4 0x5625702144b6 in PyObject_CallOneArg Objects/call.c:395
        #5 0x5625701dee2d in PyObject_Format Objects/abstract.c:895
        #6 0x56257048ce5f in builtin_format_impl Python/bltinmodule.c:776
        #7 0x56257048cefe in builtin_format Python/clinic/bltinmodule.c.h:320
        #8 0x5625702ccf3f in cfunction_vectorcall_FASTCALL Objects/methodobject.c:449
        #9 0x56257021430a in _PyObject_VectorcallTstate Include/internal/pycore_call.h:144
        #10 0x5625702143f5 in PyObject_Vectorcall Objects/call.c:327
        #11 0x56257049d633 in _Py_VectorCallInstrumentation_StackRefSteal Python/ceval.c:768
        #12 0x5625704add49 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1906
        #13 0x5625704e9906 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:122
        #14 0x5625704e9c73 in _PyEval_Vector Python/ceval.c:2172
        #15 0x5625704e9f2c in PyEval_EvalCode Python/ceval.c:679
        #16 0x5625705f1400 in run_eval_code_obj Python/pythonrun.c:1406
        #17 0x5625705f1fb5 in run_mod Python/pythonrun.c:1509
        #18 0x5625705f2f8e in _PyRun_File Python/pythonrun.c:1332
        #19 0x5625705f4633 in _PyRun_SimpleFile Python/pythonrun.c:544
        #20 0x5625705f5fd6 in _PyRun_AnyFile Python/pythonrun.c:92
        #21 0x562570650b5e in pymain_run_file_obj Modules/main.c:478
        #22 0x562570650d4c in pymain_run_file Modules/main.c:494
        #23 0x5625706517d9 in pymain_run_python Modules/main.c:804
        #24 0x562570651e8c in Py_RunMain Modules/main.c:891
        #25 0x562570652072 in pymain_main Modules/main.c:921
        #26 0x5625706523fd in Py_BytesMain Modules/main.c:945
        #27 0x562570088bc1 in main Programs/python.c:15
        #28 0x7f0fd2227740  (/usr/lib/libc.so.6+0x27740) (BuildId: 020d6f7c33b2413f4fe10814c4729dce1387f049)
        #29 0x7f0fd2227878 in __libc_start_main (/usr/lib/libc.so.6+0x27878) (BuildId: 020d6f7c33b2413f4fe10814c4729dce1387f049)
        #30 0x562570088ae4 in _start (/home/brijk/Documents/Python/cpython/python+0x2f8ae4) (BuildId: 72f1f31ea07b5b4454ba72ea1e0642d98e2f0d75)
    
    0x7bdfd1010f20 is located 112 bytes inside of 144-byte region [0x7bdfd1010eb0,0x7bdfd1010f40)
    freed by thread T0 here:
        #0 0x7f0fd272af31  (/usr/lib/libasan.so.8+0x12af31) (BuildId: ee5fbab73143ab257a66a33afe0f038a4af7a74e)
        #1 0x5625702e3d0f in _PyMem_RawFree Objects/obmalloc.c:94
        #2 0x5625702e6221 in _PyMem_DebugRawFree Objects/obmalloc.c:3171
        #3 0x5625702e625e in _PyMem_DebugFree Objects/obmalloc.c:3316
        #4 0x56257030df91 in PyObject_Free Objects/obmalloc.c:1738
        #5 0x56257055eae2 in PyObject_GC_Del Python/gc.c:2122
        #6 0x7f0fd15a3cf0 in context_dealloc Modules/_decimal/_decimal.c:1513
        #7 0x5625702d9e96 in _Py_Dealloc Objects/object.c:3319
        #8 0x562570496717 in Py_DECREF_MORTAL Include/internal/pycore_object.h:419
        #9 0x5625704967ca in PyStackRef_XCLOSE Include/internal/pycore_stackref.h:726
        #10 0x5625704c083c in _PyEval_EvalFrameDefault Python/generated_cases.c.h:5877
        #11 0x5625704e9906 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:122
        #12 0x5625704e9c73 in _PyEval_Vector Python/ceval.c:2172
        #13 0x562570213e45 in _PyFunction_Vectorcall Objects/call.c:413
        #14 0x56257021430a in _PyObject_VectorcallTstate Include/internal/pycore_call.h:144
        #15 0x5625702144b6 in PyObject_CallOneArg Objects/call.c:395
        #16 0x562570409189 in call_show_warning Python/_warnings.c:757
        #17 0x562570409977 in warn_explicit Python/_warnings.c:881
        #18 0x562570409be1 in do_warn Python/_warnings.c:1136
        #19 0x562570409d8c in warn_unicode Python/_warnings.c:1335
        #20 0x56257040b785 in PyErr_WarnEx Python/_warnings.c:1408
        #21 0x7f0fd15cd81a in _decimal_Decimal___format___impl Modules/_decimal/_decimal.c:3770
        #22 0x7f0fd15cdf5e in _decimal_Decimal___format__ Modules/_decimal/clinic/_decimal.c.h:908
        #23 0x5625702ccc94 in cfunction_vectorcall_FASTCALL_KEYWORDS_METHOD Objects/methodobject.c:481
        #24 0x56257021430a in _PyObject_VectorcallTstate Include/internal/pycore_call.h:144
        #25 0x5625702144b6 in PyObject_CallOneArg Objects/call.c:395
        #26 0x5625701dee2d in PyObject_Format Objects/abstract.c:895
        #27 0x56257048ce5f in builtin_format_impl Python/bltinmodule.c:776
        #28 0x56257048cefe in builtin_format Python/clinic/bltinmodule.c.h:320
        #29 0x5625702ccf3f in cfunction_vectorcall_FASTCALL Objects/methodobject.c:449
    
    previously allocated by thread T0 here:
        #0 0x7f0fd272c161 in malloc (/usr/lib/libasan.so.8+0x12c161) (BuildId: ee5fbab73143ab257a66a33afe0f038a4af7a74e)
        #1 0x5625702e4629 in _PyMem_RawMalloc Objects/obmalloc.c:66
        #2 0x5625702e3a02 in _PyMem_DebugRawAlloc Objects/obmalloc.c:3103
        #3 0x5625702e3a76 in _PyMem_DebugRawMalloc Objects/obmalloc.c:3136
        #4 0x5625702e5403 in _PyMem_DebugMalloc Objects/obmalloc.c:3301
        #5 0x56257030de59 in PyObject_Malloc Objects/obmalloc.c:1709
        #6 0x56257055e557 in _PyObject_MallocWithType Include/internal/pycore_object_alloc.h:46
        #7 0x56257055e557 in gc_alloc Python/gc.c:2013
        #8 0x56257055e691 in _PyObject_GC_New Python/gc.c:2033
        #9 0x7f0fd15a4858 in context_new Modules/_decimal/_decimal.c:1443
        #10 0x562570348f6f in type_call Objects/typeobject.c:2472
        #11 0x5625702140fc in _PyObject_MakeTpCall Objects/call.c:242
        #12 0x5625702143a4 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:142
        #13 0x5625702181df in _PyObject_CallNoArgsTstate Include/internal/pycore_call.h:151
        #14 0x5625702181df in PyObject_CallObject Objects/call.c:465
        #15 0x7f0fd15a2e1b in context_copy Modules/_decimal/_decimal.c:1661
        #16 0x7f0fd15a3d41 in init_current_context Modules/_decimal/_decimal.c:1920
        #17 0x7f0fd15bc4bc in current_context Modules/_decimal/_decimal.c:1948
        #18 0x7f0fd15cbb99 in PyDec_GetCurrentContext Modules/_decimal/_decimal.c:1966
        #19 0x7f0fd15cbba4 in _decimal_getcontext_impl Modules/_decimal/_decimal.c:2012
        #20 0x7f0fd15cbbaf in _decimal_getcontext Modules/_decimal/clinic/_decimal.c.h:449
        #21 0x5625702cd0b5 in cfunction_vectorcall_NOARGS Objects/methodobject.c:508
        #22 0x56257021430a in _PyObject_VectorcallTstate Include/internal/pycore_call.h:144
        #23 0x5625702143f5 in PyObject_Vectorcall Objects/call.c:327
        #24 0x56257049d633 in _Py_VectorCallInstrumentation_StackRefSteal Python/ceval.c:768
        #25 0x5625704add49 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1906
        #26 0x5625704e9906 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:122
        #27 0x5625704e9c73 in _PyEval_Vector Python/ceval.c:2172
        #28 0x5625704e9f2c in PyEval_EvalCode Python/ceval.c:679
        #29 0x5625705f1400 in run_eval_code_obj Python/pythonrun.c:1406
        #30 0x5625705f1fb5 in run_mod Python/pythonrun.c:1509
        #31 0x5625705f2f8e in _PyRun_File Python/pythonrun.c:1332
    
    SUMMARY: AddressSanitizer: heap-use-after-free Modules/_decimal/_decimal.c:3788 in _decimal_Decimal___format___impl
    Shadow bytes around the buggy address:
      0x7bdfd1010c80: 00 00 00 00 00 00 00 00 00 00 fa fa fa fa fa fa
      0x7bdfd1010d00: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x7bdfd1010d80: 00 00 00 00 fa fa fa fa fa fa fa fa 00 00 00 00
      0x7bdfd1010e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fa fa
      0x7bdfd1010e80: fa fa fa fa fa fa fd fd fd fd fd fd fd fd fd fd
    =>0x7bdfd1010f00: fd fd fd fd[fd]fd fd fd fa fa fa fa fa fa fa fa
      0x7bdfd1010f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x7bdfd1011000: 00 00 fa fa fa fa fa fa fa fa fd fd fd fd fd fd
      0x7bdfd1011080: fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa
      0x7bdfd1011100: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00
      0x7bdfd1011180: 00 00 00 00 00 00 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
    ==513990==ABORTING
    

    Reacted by Ivy Xu
  10. picnixz
    marked AddressSanitizer: global-buffer-overflow Modules/_decimal/_decimal.c:798:37 in signaldict_richcompare #158063 as a duplicate of this issue on Sep 24, 2026
  11. picnixz
    unmarked AddressSanitizer: global-buffer-overflow Modules/_decimal/_decimal.c:798:37 in signaldict_richcompare #158063 as a duplicate of this issue on Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirC modules in the Modules dirtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.