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 — Memory leak in test__interpchannels: _PyXIData_New not freed in channel_send · Issue #140306 · python/cpython

Memory leak in test__interpchannels: _PyXIData_New not freed in channel_send #140306

New issue
Copy link
New issue
Copy link
Closed
Closed
Memory leak in test__interpchannels: _PyXIData_New not freed in channel_send#140306
Copy link
Labels
3.13only security fixesonly security fixes3.14bugs and security fixesbugs and security fixes3.15bugs and security fixesbugs and security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@ashm-dev
ashm-dev
opened on Oct 18, 2025
Issue body actions

Bug report

Bug description:

LeakSanitizer detects memory leaks in the test__interpchannels test module. The leaks occur in _PyXIData_New() function called from channel_send() in Modules/_interpchannelsmodule.c.

Affected versions:

  • Python 3.13
  • Python 3.14
  • Python main branch

Steps to reproduce:

  1. Build Python with address sanitizer:
CC=clang CXX=clang++ ./configure --with-address-sanitizer --with-pydebug --with-undefined-behavior-sanitizer --disable-optimizations && make -j$(nproc)
  1. Run the test:
./python -X dev -X showrefcount -m test test__interpchannels -j$(nproc)

Expected behavior:
No memory leaks detected.

Actual behavior:
LeakSanitizer reports multiple direct memory leaks. The exact number of leaked bytes and allocations varies between Python versions.

Problematic code locations:

All leaks originate from _PyXIData_New():

cpython/Python/crossinterp.c

Lines 306 to 314 in d86ad87

_PyXIData_t *
_PyXIData_New(void)
{
_PyXIData_t *xid = PyMem_RawCalloc(1, sizeof(_PyXIData_t));
if (xid == NULL) {
PyErr_NoMemory();
}
return xid;
}

Called through:

  1. channel_send():

    cpython/Modules/_interpchannelsmodule.c

    Lines 1773 to 1819 in d86ad87

    static int
    channel_send(_channels *channels, int64_t cid, PyObject *obj,
    _waiting_t *waiting, unboundop_t unboundop, xidata_fallback_t fallback)
    {
    PyThreadState *tstate = _PyThreadState_GET();
    PyInterpreterState *interp = tstate->interp;
    int64_t interpid = PyInterpreterState_GetID(interp);
    // Look up the channel.
    PyThread_type_lock mutex = NULL;
    _channel_state *chan = NULL;
    int err = _channels_lookup(channels, cid, &mutex, &chan);
    if (err != 0) {
    return err;
    }
    assert(chan != NULL);
    // Past this point we are responsible for releasing the mutex.
    if (chan->closing != NULL) {
    PyThread_release_lock(mutex);
    return ERR_CHANNEL_CLOSED;
    }
    // Convert the object to cross-interpreter data.
    _PyXIData_t *data = _PyXIData_New();
    if (data == NULL) {
    PyThread_release_lock(mutex);
    return -1;
    }
    if (_PyObject_GetXIData(tstate, obj, fallback, data) != 0) {
    PyThread_release_lock(mutex);
    GLOBAL_FREE(data);
    return -1;
    }
    // Add the data to the channel.
    int res = _channel_add(chan, interpid, data, waiting, unboundop);
    PyThread_release_lock(mutex);
    if (res != 0) {
    // We may chain an exception here:
    (void)_release_xid_data(data, 0);
    GLOBAL_FREE(data);
    return res;
    }
    return 0;
    }

  2. channel_send_wait():

    cpython/Modules/_interpchannelsmodule.c

    Lines 1843 to 1894 in d86ad87

    // Like channel_send(), but strictly wait for the object to be received.
    static int
    channel_send_wait(_channels *channels, int64_t cid, PyObject *obj,
    unboundop_t unboundop, PY_TIMEOUT_T timeout,
    xidata_fallback_t fallback)
    {
    // We use a stack variable here, so we must ensure that &waiting
    // is not held by any channel item at the point this function exits.
    _waiting_t waiting;
    if (_waiting_init(&waiting) < 0) {
    assert(PyErr_Occurred());
    return -1;
    }
    /* Queue up the object. */
    int res = channel_send(channels, cid, obj, &waiting, unboundop, fallback);
    if (res < 0) {
    assert(waiting.status == WAITING_NO_STATUS);
    goto finally;
    }
    /* Wait until the object is received. */
    if (wait_for_lock(waiting.mutex, timeout) < 0) {
    assert(PyErr_Occurred());
    _waiting_finish_releasing(&waiting);
    /* The send() call is failing now, so make sure the item
    won't be received. */
    channel_clear_sent(channels, cid, &waiting);
    assert(waiting.status == WAITING_RELEASED);
    if (!waiting.received) {
    res = -1;
    goto finally;
    }
    // XXX Emit a warning if not a TimeoutError?
    PyErr_Clear();
    }
    else {
    _waiting_finish_releasing(&waiting);
    assert(waiting.status == WAITING_RELEASED);
    if (!waiting.received) {
    res = ERR_CHANNEL_CLOSED_WAITING;
    goto finally;
    }
    }
    /* success! */
    res = 0;
    finally:
    _waiting_clear(&waiting);
    return res;
    }

  3. _sharednsitem_set_value():

    cpython/Python/crossinterp.c

    Lines 2065 to 2084 in d86ad87

    static int
    _sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value,
    xidata_fallback_t fallback)
    {
    assert(_sharednsitem_is_initialized(item));
    assert(item->xidata == NULL);
    item->xidata = _PyXIData_New();
    if (item->xidata == NULL) {
    return -1;
    }
    PyThreadState *tstate = PyThreadState_Get();
    if (_PyObject_GetXIData(tstate, value, fallback, item->xidata) < 0) {
    PyMem_RawFree(item->xidata);
    item->xidata = NULL;
    // The caller may want to propagate PyExc_NotShareableError
    // if currently switched between interpreters.
    return -1;
    }
    return 0;
    }

  4. _copy_string_obj_raw() via _PyXI_InitFailure():

    cpython/Python/crossinterp.c

    Lines 1041 to 1065 in d86ad87

    static const char *
    _copy_string_obj_raw(PyObject *strobj, Py_ssize_t *p_size)
    {
    Py_ssize_t size = -1;
    const char *str = PyUnicode_AsUTF8AndSize(strobj, &size);
    if (str == NULL) {
    return NULL;
    }
    if (size != (Py_ssize_t)strlen(str)) {
    PyErr_SetString(PyExc_ValueError, "found embedded NULL character");
    return NULL;
    }
    char *copied = PyMem_RawMalloc(size+1);
    if (copied == NULL) {
    PyErr_NoMemory();
    return NULL;
    }
    strcpy(copied, str);
    if (p_size != NULL) {
    *p_size = size;
    }
    return copied;
    }

    cpython/Python/crossinterp.c

    Lines 1805 to 1825 in d86ad87

    int
    _PyXI_InitFailure(_PyXI_failure *failure, _PyXI_errcode code, PyObject *obj)
    {
    PyObject *msgobj = PyObject_Str(obj);
    if (msgobj == NULL) {
    return -1;
    }
    // This will leak if not paired with clear_xi_failure().
    // That happens automatically in _capture_current_exception().
    const char *msg = _copy_string_obj_raw(msgobj, NULL);
    Py_DECREF(msgobj);
    if (PyErr_Occurred()) {
    return -1;
    }
    *failure = (_PyXI_failure){
    .code = code,
    .msg = msg,
    .msg_owned = 1,
    };
    return 0;
    }

Analysis:

The allocated memory in _PyXIData_New() is not being properly freed. The function allocates memory using _PyMem_DebugRawAlloc() but the corresponding cleanup appears to be missing in error paths or normal execution flow.

Full leak reports:
See attached log files for detailed stack traces on Python 3.13, 3.14, and main branch.

313.log
314.log
main.log

CPython versions tested on:

3.13, 3.14, CPython main branch

Operating systems tested on:

Linux

Linked PRs

  • gh-140306: Fix memory leaks in cross-interpreter data handling #140307
  • [3.14] gh-140306: Fix memory leaks in cross-interpreter data handling (GH-140307) #140338
  • [3.13] gh-140306: Fix memory leaks in cross-interpreter data handling (GH-140307) #140357
  • [3.13] gh-140306: Fix memory leaks in cross-interpreter data handling #140397
Reacted by Mikhail Efimov

Activity

  1. ashm-dev
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Oct 18, 2025
  2. sergey-miryanov
    added
    testsTests in the Lib/test dirTests in the Lib/test dir
    on Oct 18, 2025
  3. StanFromIreland
    added
    interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)
    and removed
    testsTests in the Lib/test dirTests in the Lib/test dir
    on Oct 18, 2025
  4. ashm-dev
    added a commit that references this issue on Oct 18, 2025

    pythongh-140306: Fix memory leaks in cross-interpreter data handling

    122ac6c
  5. serhiy-storchaka
    added a commit that references this issue on Oct 19, 2025

    gh-140306: Fix memory leaks in cross-interpreter data handling (GH-14…

    f932321
  6. miss-islington
    added 2 commits that reference this issue on Oct 19, 2025

    pythongh-140306: Fix memory leaks in cross-interpreter data handling (p…

    5de8a01

    pythongh-140306: Fix memory leaks in cross-interpreter data handling (p…

    ffe37c9
  7. serhiy-storchaka
    added 3 commits that reference this issue on Oct 19, 2025

    [3.14] gh-140306: Fix memory leaks in cross-interpreter data handling (…

    6cee144

    [3.13] pythongh-140306: Fix memory leaks in cross-interpreter data ha…

    45853b9

    [3.13] pythongh-140306: Fix memory leaks in cross-interpreter data ha…

    1977465
  8. serhiy-storchaka
    added
    3.13only security fixesonly security fixes
    3.14bugs and security fixesbugs and security fixes
    3.15bugs and security fixesbugs and security fixes
    on Oct 20, 2025
  9. serhiy-storchaka
    added 2 commits that reference this issue on Oct 20, 2025

    [3.13] pythongh-140306: Fix memory leaks in cross-interpreter data ha…

    10d578f

    [3.13] gh-140306: Fix memory leaks in cross-interpreter data handling (…

    d7473f7
  10. serhiy-storchaka commented on Oct 20, 2025

    @serhiy-storchaka
    serhiy-storchaka
    on Oct 20, 2025
    Member
    More actions

    Please check that all leaks are gone in 3.13. Not all changes were backported to 3.13, because the code was too different.

  11. ashm-dev commented on Oct 20, 2025

    @ashm-dev
    ashm-dev
    on Oct 20, 2025
    ContributorAuthor
    More actions

    @serhiy-storchaka Unfortunately, there is still a leak on 3.13

  12. serhiy-storchaka commented on Oct 20, 2025

    @serhiy-storchaka
    serhiy-storchaka
    on Oct 20, 2025
    Member
    More actions

    Do you mind to look what is missed and create a PR, @ashm-dev?

    Reacted by Shamil
  13. StanFromIreland
    added a commit that references this issue on Dec 6, 2025

    pythongh-140306: Fix memory leaks in cross-interpreter data handling (p…

    1e1d227
  14. serhiy-storchaka
    added a commit that references this issue on Jan 12, 2026

    [3.13] gh-140306: Clear uncaught exception info after applying error …

    ab82246
  15. serhiy-storchaka
    closed this as completedon Jan 12, 2026
  16. serhiy-storchaka commented on Jan 12, 2026

    @serhiy-storchaka
    serhiy-storchaka
    on Jan 12, 2026
    Member
    More actions

    Thank you for your report, and your fix, and chasing the runaway bugs in 3.13.

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

    3.13only security fixesonly security fixes3.14bugs and security fixesbugs and security fixes3.15bugs and security fixesbugs and security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    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.