Skip to content

Commit de06f1d

Browse files
committed
gh-150213: Fix OOM error handling in _interpchannels
1 parent c35b0f2 commit de06f1d

3 files changed

Lines changed: 70 additions & 0 deletions

File tree

Lib/test/test__interpchannels.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,5 +1838,68 @@ def test_force_close(self):
18381838
fix.clean_up()
18391839

18401840

1841+
class InterpChannelsOOMTest(TestBase):
1842+
def test_create_no_memory(self):
1843+
_testcapi = import_helper.import_module('_testcapi')
1844+
1845+
raised_memoryerror = False
1846+
for start in range(1, 20):
1847+
with self.subTest(start=start):
1848+
cid = None
1849+
_testcapi.set_nomemory(start, start + 1)
1850+
try:
1851+
try:
1852+
cid = _channels.create(REPLACE)
1853+
except MemoryError:
1854+
raised_memoryerror = True
1855+
except SystemError as exc:
1856+
self.fail(
1857+
f"missing MemoryError at start={start}: {exc}")
1858+
finally:
1859+
_testcapi.remove_mem_hooks()
1860+
if cid is not None:
1861+
_channels.destroy(cid)
1862+
1863+
self.assertTrue(
1864+
raised_memoryerror,
1865+
"_testcapi.set_nomemory did not trigger a MemoryError from "
1866+
"_channels.create() within start=1..20; widen range")
1867+
1868+
def test_close_nonempty_no_memory(self):
1869+
_testcapi = import_helper.import_module('_testcapi')
1870+
1871+
raised_memoryerror = False
1872+
for start in range(1, 30):
1873+
with self.subTest(start=start):
1874+
cid = _channels.create(REPLACE)
1875+
try:
1876+
_channels.send(cid, b'spam', blocking=False)
1877+
1878+
_testcapi.set_nomemory(start, start + 1)
1879+
try:
1880+
_channels.close(cid, send=True)
1881+
except MemoryError:
1882+
raised_memoryerror = True
1883+
except SystemError as exc:
1884+
self.fail(
1885+
f"missing MemoryError at start={start}: {exc}")
1886+
finally:
1887+
_testcapi.remove_mem_hooks()
1888+
finally:
1889+
try:
1890+
_channels.close(cid, force=True)
1891+
except Exception:
1892+
pass
1893+
try:
1894+
_channels.destroy(cid)
1895+
except Exception:
1896+
pass
1897+
1898+
self.assertTrue(
1899+
raised_memoryerror,
1900+
"_testcapi.set_nomemory did not trigger a MemoryError from "
1901+
"_channel_set_closing within start=1..30; widen range")
1902+
1903+
18411904
if __name__ == '__main__':
18421905
unittest.main()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix missing :exc:`MemoryError` in several out-of-memory error paths in
2+
:mod:`!_interpchannels`. Previously these paths could raise
3+
:exc:`SystemError` or trigger an assertion failure in debug builds.

Modules/_interpchannelsmodule.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,7 @@ _channelends_new(void)
922922
{
923923
_channelends *ends = GLOBAL_MALLOC(_channelends);
924924
if (ends== NULL) {
925+
PyErr_NoMemory();
925926
return NULL;
926927
}
927928
ends->numsendopen = 0;
@@ -1115,6 +1116,7 @@ _channel_new(PyThread_type_lock mutex, struct _channeldefaults defaults)
11151116
assert(check_unbound(defaults.unboundop));
11161117
_channel_state *chan = GLOBAL_MALLOC(_channel_state);
11171118
if (chan == NULL) {
1119+
PyErr_NoMemory();
11181120
return NULL;
11191121
}
11201122
chan->mutex = mutex;
@@ -1313,6 +1315,7 @@ _channelref_new(int64_t cid, _channel_state *chan)
13131315
{
13141316
_channelref *ref = GLOBAL_MALLOC(_channelref);
13151317
if (ref == NULL) {
1318+
PyErr_NoMemory();
13161319
return NULL;
13171320
}
13181321
ref->cid = cid;
@@ -1698,6 +1701,7 @@ _channel_set_closing(_channelref *ref, PyThread_type_lock mutex) {
16981701
}
16991702
chan->closing = GLOBAL_MALLOC(struct _channel_closing);
17001703
if (chan->closing == NULL) {
1704+
PyErr_NoMemory();
17011705
goto done;
17021706
}
17031707
chan->closing->ref = ref;

0 commit comments

Comments
 (0)