Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

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

Copy link
Copy link
@lpyu001

Description

@lpyu001
Issue body actions

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:

Function Triggered 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 _testcapi
import _interpchannels as _channels
from concurrent.interpreters import _crossinterp

REPLACE = _crossinterp._UNBOUND_CONSTANT_TO_FLAG[_crossinterp.UNBOUND]

# Case 1: _channels.create()
for n in range(1, 20):
    _testcapi.set_nomemory(n, n + 1)
    try:
        cid = _channels.create(REPLACE)
    except MemoryError:
        pass
    except SystemError as exc:
        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 channel
for n in range(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)
    except MemoryError:
        pass
    except SystemError as exc:
        print(f"[close] n={n}: {type(exc).__name__}: {exc}")
    finally:
        _testcapi.remove_mem_hooks()
        try:
            _channels.close(cid, force=True)
        except Exception:
            pass
        try:
            _channels.destroy(cid)
        except Exception:
            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

Reactions are currently unavailable

Metadata

Metadata

Assignees

No one assigned
    No fields configured for issues without a type.

    Projects

    Status
    Done
    Show more project fields

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.