Skip to content

Commit dbbf181

Browse files
committed
gh-117755: Fix mimalloc for huge allocation on s390x
Fix mimalloc allocator for huge memory allocation (around 8,589,934,592 GiB) on s390x. * Abort allocation early in mimalloc if the number of slices doesn't fit into uint32_t, to prevent a integer overflow (cast 64-bit size_t to uint32_t). * Add test_large_alloc() to test_bigaddrspace. * Reenable test_maxcontext_exact_arith() of test_decimal on s390x.
1 parent deb921f commit dbbf181

File tree

4 files changed

+68
-6
lines changed

4 files changed

+68
-6
lines changed

Lib/test/test_bigaddrspace.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,25 @@
1111
from test import support
1212
from test.support import bigaddrspacetest, MAX_Py_ssize_t
1313

14-
import unittest
14+
import contextlib
1515
import operator
16+
import os
1617
import sys
18+
import unittest
19+
20+
21+
@contextlib.contextmanager
22+
def ignore_stderr():
23+
fd = 2
24+
old_stderr = os.dup(fd)
25+
try:
26+
# Redirect stderr to /dev/null
27+
with open(os.devnull, 'wb') as null:
28+
os.dup2(null.fileno(), fd)
29+
yield
30+
finally:
31+
os.dup2(old_stderr, fd)
32+
os.close(old_stderr)
1733

1834

1935
class BytesTest(unittest.TestCase):
@@ -52,6 +68,48 @@ def test_repeat(self):
5268
finally:
5369
x = None
5470

71+
def test_large_alloc(self):
72+
debug_bytes = 0
73+
if support.check_impl_detail(cpython=True) and support.Py_DEBUG:
74+
try:
75+
from _testcapi import SIZEOF_SIZE_T
76+
except ImportError:
77+
if sys.maxsize > 0xffff_ffff:
78+
SIZEOF_SIZE_T = 8
79+
else:
80+
SIZEOF_SIZE_T = 4
81+
82+
# The debug hook on memory allocator adds 3 size_t per memory block
83+
# See PYMEM_DEBUG_EXTRA_BYTES in Objects/obmalloc.c.
84+
debug_bytes = SIZEOF_SIZE_T * 3
85+
86+
try:
87+
from _testinternalcapi import pymem_getallocatorsname
88+
if not pymem_getallocatorsname().endswith('_debug'):
89+
# PYTHONMALLOC env var is used and disables the debug hook
90+
debug_bytes = 0
91+
except (ImportError, RuntimeError):
92+
pass
93+
94+
def allocate(size):
95+
length = size - sys.getsizeof(b'') - debug_bytes
96+
# allocate 'size' bytes
97+
return b'x' * length
98+
99+
# 64-bit size which cannot be allocated on any reasonable hardware
100+
# (in 2024) and must fail immediately with MemoryError.
101+
for size in (
102+
# gh-114331: test_decimal.test_maxcontext_exact_arith()
103+
0x0BAFC246_72035E58,
104+
# gh-117755: test_io.test_constructor()
105+
0x7FFFFFFF_FFFFFFFF,
106+
):
107+
with self.subTest(size=size):
108+
with self.assertRaises(MemoryError):
109+
# ignore "mimalloc: error: unable to allocate memory"
110+
with ignore_stderr():
111+
allocate(size)
112+
55113

56114
class StrTest(unittest.TestCase):
57115

Lib/test/test_decimal.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@
3838
check_disallow_instantiation)
3939
from test.support import (TestFailed,
4040
run_with_locale, cpython_only,
41-
darwin_malloc_err_warning, is_emscripten,
42-
skip_on_s390x)
41+
darwin_malloc_err_warning, is_emscripten)
4342
from test.support.import_helper import import_fresh_module
4443
from test.support import threading_helper
4544
from test.support import warnings_helper
@@ -5651,9 +5650,6 @@ def __abs__(self):
56515650
@unittest.skipIf(check_sanitizer(address=True, memory=True),
56525651
"ASAN/MSAN sanitizer defaults to crashing "
56535652
"instead of returning NULL for malloc failure.")
5654-
# gh-114331: The test allocates 784 271 641 GiB and mimalloc does not fail
5655-
# to allocate it when using mimalloc on s390x.
5656-
@skip_on_s390x
56575653
def test_maxcontext_exact_arith(self):
56585654

56595655
# Make sure that exact operations do not raise MemoryError due
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix mimalloc allocator for huge memory allocation (around 8,589,934,592 GiB) on
2+
s390x. Patch by Victor Stinner.

Objects/mimalloc/segment.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,9 @@ static mi_segment_t* mi_segment_os_alloc( size_t required, size_t page_alignment
814814
const size_t extra = align_offset - info_size;
815815
// recalculate due to potential guard pages
816816
*psegment_slices = mi_segment_calculate_slices(required + extra, ppre_size, pinfo_slices);
817+
818+
// mi_page_t.slice_count type is uint32_t
819+
if (*psegment_slices > (size_t)UINT32_MAX) return NULL;
817820
}
818821

819822
const size_t segment_size = (*psegment_slices) * MI_SEGMENT_SLICE_SIZE;
@@ -865,6 +868,9 @@ static mi_segment_t* mi_segment_alloc(size_t required, size_t page_alignment, mi
865868
size_t pre_size;
866869
size_t segment_slices = mi_segment_calculate_slices(required, &pre_size, &info_slices);
867870

871+
// mi_page_t.slice_count type is uint32_t
872+
if (segment_slices > (size_t)UINT32_MAX) return NULL;
873+
868874
// Commit eagerly only if not the first N lazy segments (to reduce impact of many threads that allocate just a little)
869875
const bool eager_delay = (// !_mi_os_has_overcommit() && // never delay on overcommit systems
870876
_mi_current_thread_count() > 1 && // do not delay for the first N threads

0 commit comments

Comments
 (0)