Skip to content

Commit 06dbf81

Browse files
[3.13] pythongh-138204: Forbid expansion of a shared anonymous mmap on Linux (pythonGH-138220) (pythonGH-138387)
This is a Linux kernel bug which caused a bus error. https://bugzilla.kernel.org/show_bug.cgi?id=8691 (cherry picked from commit 33fcb0c) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 154ec76 commit 06dbf81

File tree

3 files changed

+62
-17
lines changed

3 files changed

+62
-17
lines changed

Lib/test/test_mmap.py

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -899,35 +899,69 @@ def test_madvise(self):
899899
self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, 2), None)
900900
self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, size), None)
901901

902-
@unittest.skipUnless(os.name == 'nt', 'requires Windows')
903-
def test_resize_up_when_mapped_to_pagefile(self):
902+
def test_resize_up_anonymous_mapping(self):
904903
"""If the mmap is backed by the pagefile ensure a resize up can happen
905904
and that the original data is still in place
906905
"""
907906
start_size = PAGESIZE
908907
new_size = 2 * start_size
909-
data = bytes(random.getrandbits(8) for _ in range(start_size))
908+
data = random.randbytes(start_size)
910909

911-
m = mmap.mmap(-1, start_size)
912-
m[:] = data
913-
m.resize(new_size)
914-
self.assertEqual(len(m), new_size)
915-
self.assertEqual(m[:start_size], data[:start_size])
910+
with mmap.mmap(-1, start_size) as m:
911+
m[:] = data
912+
if sys.platform.startswith(('linux', 'android')):
913+
# Can't expand a shared anonymous mapping on Linux.
914+
# See https://bugzilla.kernel.org/show_bug.cgi?id=8691
915+
with self.assertRaises(ValueError):
916+
m.resize(new_size)
917+
else:
918+
try:
919+
m.resize(new_size)
920+
except SystemError:
921+
pass
922+
else:
923+
self.assertEqual(len(m), new_size)
924+
self.assertEqual(m[:start_size], data)
925+
self.assertEqual(m[start_size:], b'\0' * (new_size - start_size))
916926

917-
@unittest.skipUnless(os.name == 'nt', 'requires Windows')
918-
def test_resize_down_when_mapped_to_pagefile(self):
927+
@unittest.skipUnless(os.name == 'posix', 'requires Posix')
928+
def test_resize_up_private_anonymous_mapping(self):
929+
start_size = PAGESIZE
930+
new_size = 2 * start_size
931+
data = random.randbytes(start_size)
932+
933+
with mmap.mmap(-1, start_size, flags=mmap.MAP_PRIVATE) as m:
934+
m[:] = data
935+
try:
936+
m.resize(new_size)
937+
except SystemError:
938+
pass
939+
else:
940+
self.assertEqual(len(m), new_size)
941+
self.assertEqual(m[:start_size], data)
942+
self.assertEqual(m[start_size:], b'\0' * (new_size - start_size))
943+
944+
def test_resize_down_anonymous_mapping(self):
919945
"""If the mmap is backed by the pagefile ensure a resize down up can happen
920946
and that a truncated form of the original data is still in place
921947
"""
922-
start_size = PAGESIZE
948+
start_size = 2 * PAGESIZE
923949
new_size = start_size // 2
924-
data = bytes(random.getrandbits(8) for _ in range(start_size))
950+
data = random.randbytes(start_size)
925951

926-
m = mmap.mmap(-1, start_size)
927-
m[:] = data
928-
m.resize(new_size)
929-
self.assertEqual(len(m), new_size)
930-
self.assertEqual(m[:new_size], data[:new_size])
952+
with mmap.mmap(-1, start_size) as m:
953+
m[:] = data
954+
try:
955+
m.resize(new_size)
956+
except SystemError:
957+
pass
958+
else:
959+
self.assertEqual(len(m), new_size)
960+
self.assertEqual(m[:], data[:new_size])
961+
if sys.platform.startswith(('linux', 'android')):
962+
# Can't expand to its original size.
963+
with self.assertRaises(ValueError):
964+
m.resize(start_size)
931965

932966
@unittest.skipUnless(os.name == 'nt', 'requires Windows')
933967
def test_resize_fails_if_mapping_held_elsewhere(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Forbid expansion of shared anonymous :mod:`memory maps <mmap>` on Linux,
2+
which caused a bus error.

Modules/mmapmodule.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ typedef struct {
120120
#ifdef UNIX
121121
int fd;
122122
_Bool trackfd;
123+
int flags;
123124
#endif
124125

125126
PyObject *weakreflist;
@@ -871,6 +872,13 @@ mmap_resize_method(mmap_object *self,
871872
#else
872873
void *newmap;
873874

875+
#ifdef __linux__
876+
if (self->fd == -1 && !(self->flags & MAP_PRIVATE) && new_size > self->size) {
877+
PyErr_Format(PyExc_ValueError,
878+
"mmap: can't expand a shared anonymous mapping on Linux");
879+
return NULL;
880+
}
881+
#endif
874882
if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) {
875883
PyErr_SetFromErrno(PyExc_OSError);
876884
return NULL;
@@ -1651,6 +1659,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
16511659
else {
16521660
m_obj->fd = -1;
16531661
}
1662+
m_obj->flags = flags;
16541663

16551664
Py_BEGIN_ALLOW_THREADS
16561665
m_obj->data = mmap(NULL, map_size, prot, flags, fd, offset);

0 commit comments

Comments
 (0)