Skip to content

Commit 6f0d611

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 (test skipped on 32-bit platforms). * Reenable test_maxcontext_exact_arith() of test_decimal on s390x. * Reenable test_constructor() tests of test_io on s390x.
1 parent a9107fe commit 6f0d611

File tree

5 files changed

+70
-16
lines changed

5 files changed

+70
-16
lines changed

Lib/test/test_bigaddrspace.py

Lines changed: 61 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,50 @@ def test_repeat(self):
5268
finally:
5369
x = None
5470

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

56116
class StrTest(unittest.TestCase):
57117

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

Lib/test/test_io.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
assert_python_ok, assert_python_failure, run_python_until_end)
4242
from test.support import (
4343
import_helper, is_apple, os_helper, skip_if_sanitizer, threading_helper, warnings_helper,
44-
skip_on_s390x
4544
)
4645
from test.support.os_helper import FakePath
4746

@@ -1701,9 +1700,6 @@ class CBufferedReaderTest(BufferedReaderTest, SizeofTest):
17011700
@skip_if_sanitizer(memory=True, address=True, thread=True,
17021701
reason="sanitizer defaults to crashing "
17031702
"instead of returning NULL for malloc failure.")
1704-
# gh-117755: The test allocates 9 223 372 036 854 775 807 bytes
1705-
# (0x7fffffffffffffff) and mimalloc fails with a division by zero on s390x.
1706-
@skip_on_s390x
17071703
def test_constructor(self):
17081704
BufferedReaderTest.test_constructor(self)
17091705
# The allocation can succeed on 32-bit builds, e.g. with more
@@ -2072,9 +2068,6 @@ class CBufferedWriterTest(BufferedWriterTest, SizeofTest):
20722068
@skip_if_sanitizer(memory=True, address=True, thread=True,
20732069
reason="sanitizer defaults to crashing "
20742070
"instead of returning NULL for malloc failure.")
2075-
# gh-117755: The test allocates 9 223 372 036 854 775 807 bytes
2076-
# (0x7fffffffffffffff) and mimalloc fails with a division by zero on s390x.
2077-
@skip_on_s390x
20782071
def test_constructor(self):
20792072
BufferedWriterTest.test_constructor(self)
20802073
# The allocation can succeed on 32-bit builds, e.g. with more
@@ -2597,9 +2590,6 @@ class CBufferedRandomTest(BufferedRandomTest, SizeofTest):
25972590
@skip_if_sanitizer(memory=True, address=True, thread=True,
25982591
reason="sanitizer defaults to crashing "
25992592
"instead of returning NULL for malloc failure.")
2600-
# gh-117755: The test allocates 9 223 372 036 854 775 807 bytes
2601-
# (0x7fffffffffffffff) and mimalloc fails with a division by zero on s390x.
2602-
@skip_on_s390x
26032593
def test_constructor(self):
26042594
BufferedRandomTest.test_constructor(self)
26052595
# The allocation can succeed on 32-bit builds, e.g. with more
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)