Skip to content

_interpchannels: missing PyErr_NoMemory() causes SystemErroron OOM in {create,close}() #150213

Description

@lpyu001

Bug report

Bug description:

Bug description

parent issue:#146102
refs:https://gist.github.com/devdanzin/d9975354c27e57f2589acfd0a777a2f1

Several allocation failure paths in Modules/_interpchannelsmodule.c
return NULL or -1 without setting an exception.

This violates CPython's C API convention that an error return should have
an exception set. In release builds, this can surface as:

SystemError: <built-in function ...> returned NULL without setting an exception

instead of the expected MemoryError. In debug builds, the same paths may
trip assert(PyErr_Occurred()) in handle_channel_error().

Affected functions

The affected allocation sites are:

FunctionTriggered via
_channelends_new()_channels.create()
_channel_new()_channels.create()
_channelref_new()_channels.create()
_channel_set_closing()_channels.close(send=True) on a non-empty channel

Nearby helper functions in the same file already handle this correctly by
calling PyErr_NoMemory() on allocation failure, for example:

Function
_channelitem_new()
_channelqueue_new()
_channelend_new()

Reproducer

import_testcapiimport_interpchannelsas_channelsfromconcurrent.interpretersimport_crossinterpREPLACE=_crossinterp._UNBOUND_CONSTANT_TO_FLAG[_crossinterp.UNBOUND]
# Case 1: _channels.create()forninrange(1, 20):
_testcapi.set_nomemory(n, n+1)
try:
cid=_channels.create(REPLACE)
exceptMemoryError:
passexceptSystemErrorasexc:
print(f"[create] n={n}: {type(exc).__name__}: {exc}")
else:
_channels.destroy(cid)
finally:
_testcapi.remove_mem_hooks()
# Case 2: _channels.close(send=True) on a non-empty channelforninrange(1, 30):
cid=_channels.create(REPLACE)
_channels.send(cid, b"spam", blocking=False)
_testcapi.set_nomemory(n, n+1)
try:
_channels.close(cid, send=True)
exceptMemoryError:
passexceptSystemErrorasexc:
print(f"[close] n={n}: {type(exc).__name__}: {exc}")
finally:
_testcapi.remove_mem_hooks()
try:
_channels.close(cid, force=True)
exceptException:
passtry:
_channels.destroy(cid)
exceptException:
pass

Sample output on main, CPython 3.16.0a0, macOS, release build:

[create] n=1: SystemError: <built-in function create> returned NULL without setting an exception
[create] n=3: SystemError: <built-in function create> returned NULL without setting an exception
[create] n=4: SystemError: <built-in function create> returned NULL without setting an exception
[close] n=1: SystemError: <built-in function close> returned NULL without setting an exception

Expected behavior

All affected allocation failure paths should raise MemoryError, consistent
with nearby helper functions and CPython's general C API error handling
contract.

Fix

Call PyErr_NoMemory() before returning an error from the affected
allocation failure paths.

CPython versions tested on:

CPython main branch

Operating systems tested on:

macOS

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions