Skip to content

Out-of-bound Read in pylong_int_divmod via override _pylong.int_divmod method #142554

Description

@jackfromeast

What happened?

Missing length check on the return value of _pylong.int_divmod method leads to out-of-bound read.

Proof of Concept:

import _pylong

_pylong.int_divmod = lambda a, b: (123,)  # wrong shape

huge  = 10**8000
small = 10**4000
divmod(huge, small)

Affected Versions:

Python Version Status Exit Code
Python 3.9.24+ (heads/3.9:9c4638d, Oct 17 2025, 11:19:30) Exception 1
Python 3.10.19+ (heads/3.10:0142619, Oct 17 2025, 11:20:05) [GCC 13.3.0] Exception 1
Python 3.11.14+ (heads/3.11:88f3f5b, Oct 17 2025, 11:20:44) [GCC 13.3.0] Exception 1
Python 3.12.12+ (heads/3.12:8cb2092, Oct 17 2025, 11:21:35) [GCC 13.3.0] ASAN 1
Python 3.13.9+ (heads/3.13:0760a57, Oct 17 2025, 11:22:25) [GCC 13.3.0] ASAN 1
Python 3.14.0+ (heads/3.14:889e918, Oct 17 2025, 11:23:02) [GCC 13.3.0] ASAN 1
Python 3.15.0a1+ (heads/main:fbf0843, Oct 17 2025, 11:23:37) [GCC 13.3.0] ASAN 1

Vulnerable Code:

pylong_int_divmod(PyLongObject *v, PyLongObject *w,
                  PyLongObject **pdiv, PyLongObject **pmod)
{
    PyObject *mod = PyImport_ImportModule("_pylong");
    if (mod == NULL) {
        return -1;
    }
    PyObject *result = PyObject_CallMethod(mod, "int_divmod", "OO", v, w);
    Py_DECREF(mod);
    if (result == NULL) {
        return -1;
    }
    if (!PyTuple_Check(result)) { // Bug: Only Check the type, but not the shape.
        Py_DECREF(result);
        PyErr_SetString(PyExc_ValueError,
                        "tuple is required from int_divmod()");
        return -1;
    }
    PyObject *q = PyTuple_GET_ITEM(result, 0);
    PyObject *r = PyTuple_GET_ITEM(result, 1);
    if (!PyLong_Check(q) || !PyLong_Check(r)) {
        Py_DECREF(result);
        PyErr_SetString(PyExc_ValueError,
                        "tuple of int is required from int_divmod()");
        return -1;
    }
    if (pdiv != NULL) {
        *pdiv = (PyLongObject *)Py_NewRef(q);
    }

Sanitizer Output:

(base) jackfromeast@blue-sea:~/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython-afl/bin$ AFL_IGNORE_PROBLEMS_COVERAGE=1 AFL_IGNORE_PROBLEMS=1 ./python3.15 /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-issue-128049/inputs/longobject_poc.py
AddressSanitizer:DEADLYSIGNAL
=================================================================
==523923==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x569e4215d019 bp 0x7ffe84615a40 sp 0x7ffe846159f0 T0)
==523923==The signal is caused by a READ memory access.
==523923==Hint: this fault was caused by a dereference of a high value address (see register values below).  Disassemble the provided pc to learn which register was used.
    #0 0x569e4215d019 in _Py_TYPE /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/./Include/object.h:277:20
    #1 0x569e4215d019 in pylong_int_divmod /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Objects/longobject.c:4445:30
    #2 0x569e4215d019 in l_divmod /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Objects/longobject.c:4522:16
    #3 0x569e4214d031 in long_divmod /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Objects/longobject.c:4885:9
    #4 0x569e41f329df in binary_op1 /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Objects/abstract.c:966:13
    #5 0x569e41f31c43 in binary_op /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Objects/abstract.c:1005:24
    #6 0x569e41fdc934 in _PyObject_VectorcallTstate /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/./Include/internal/pycore_call.h:169:11
    #7 0x569e426ca823 in _PyEval_EvalFrameDefault /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Python/generated_cases.c.h:1620:35
    #8 0x569e42673fe1 in _PyEval_EvalFrame /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/./Include/internal/pycore_ceval.h:121:16
    #9 0x569e42673fe1 in _PyEval_Vector /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Python/ceval.c:2001:12
    #10 0x569e42673088 in PyEval_EvalCode /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Python/ceval.c:884:21
    #11 0x569e429c8b52 in run_eval_code_obj /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Python/pythonrun.c:1365:12
    #12 0x569e429c8b52 in run_mod /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Python/pythonrun.c:1459:19
    #13 0x569e429b81c6 in pyrun_file /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Python/pythonrun.c:1293:15
    #14 0x569e429b81c6 in _PyRun_SimpleFileObject /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Python/pythonrun.c:521:13
    #15 0x569e429b679f in _PyRun_AnyFileObject /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Python/pythonrun.c:81:15
    #16 0x569e42a96477 in pymain_run_file_obj /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Modules/main.c:410:15
    #17 0x569e42a96477 in pymain_run_file /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Modules/main.c:429:15
    #18 0x569e42a922a8 in pymain_run_python /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Modules/main.c:691:21
    #19 0x569e42a922a8 in Py_RunMain /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Modules/main.c:772:5
    #20 0x569e42a944a6 in pymain_main /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Modules/main.c:802:12
    #21 0x569e42a94666 in Py_BytesMain /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/Modules/main.c:826:12
    #22 0x7e03b662a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #23 0x7e03b662a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #24 0x569e41c3d1b4 in _start (/home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython-afl/bin/python3.15+0x2941b4) (BuildId: b82aaf57a7b62badba1c531fa33370927ffe8140)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/./Include/object.h:277:20 in _Py_TYPE
==523923==ABORTING

Linked PRs

Activity

  1. added
    interpreter-core(Objects, Python, Grammar, and Parser dirs)
    pendingThe issue will be closed if no feedback is provided
    on Dec 11, 2025
  2. skirpichev commented on Dec 11, 2025

    @skirpichev
    Member

    You can override int_divmod() in the private module _pylong with a broken implementation. Why do you think it's an issue? CPython's implementation is not broken, no reasons to waste time in useless checks.

  3. jackfromeast commented on Dec 11, 2025

    @jackfromeast
    Author

    Hi @skirpichev, thank you for your prompt response!

    I originally believed that we should check not only the type but also the shape (length) of the returned value before using any type casting and length-based operation. It doesn’t hurt to add a length check in addition to the type check, and it helps avoid a hard crash of the program.

        PyObject *result = PyObject_CallMethod(mod, "int_divmod", "OO", v, w);
    
        if (!PyTuple_Check(result)) { // Bug: Only Check the type, but not the shape.
            Py_DECREF(result);
            PyErr_SetString(PyExc_ValueError,
                            "tuple is required from int_divmod()");
            return -1;
        }
        PyObject *q = PyTuple_GET_ITEM(result, 0);
        PyObject *r = PyTuple_GET_ITEM(result, 1);
    

    However, I’m not very familiar with CPython’s preferences on what should be treated as a bug versus what is considered acceptable behavior, even if it may cause a crash. I’d be happy to follow the project’s conventions here.

  4. skirpichev commented on Dec 11, 2025

    @skirpichev
    Member

    It doesn’t hurt to add a length check in addition to the type check

    Hmm, the problem looks rather artificial. On another hand, PyTuple_Check is relatively expensive, and probably additional PyTuple_GET_SIZE() test will not hurt performance.

  5. added
    type-bugAn unexpected behavior, bug, or error
    and removed
    pendingThe issue will be closed if no feedback is provided
    on Dec 11, 2025
  6. added
    type-crashA hard crash of the interpreter, possibly with a core dump
    and removed
    type-bugAn unexpected behavior, bug, or error
    on Dec 11, 2025
  7. self-assigned this
    on Dec 13, 2025
  8. added 2 commits that reference this issue on Dec 13, 2025
  9. added a commit that references this issue on Dec 14, 2025
  10. picnixz commented on Dec 14, 2025

    @picnixz
    Member

    Thank you for the report!

  11. added 2 commits that reference this issue on Dec 14, 2025
  12. added a commit that references this issue on Dec 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

interpreter-core(Objects, Python, Grammar, and Parser dirs)type-crashA 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