Skip to content

Commit 2527014

Browse files
serhiy-storchakalkollar
authored andcommitted
pythongh-138204: Forbid expansion of a shared anonymous mmap on Linux (pythonGH-138220)
This is a Linux kernel bug which caused a bus error. https://bugzilla.kernel.org/show_bug.cgi?id=8691
1 parent 48e1d1c commit 2527014

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
@@ -901,35 +901,69 @@ def test_madvise(self):
901901
self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, 2), None)
902902
self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, size), None)
903903

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

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

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

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

934968
@unittest.skipUnless(os.name == 'nt', 'requires Windows')
935969
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;
@@ -882,6 +883,13 @@ mmap_resize_method(PyObject *op, PyObject *args)
882883
#else
883884
void *newmap;
884885

886+
#ifdef __linux__
887+
if (self->fd == -1 && !(self->flags & MAP_PRIVATE) && new_size > self->size) {
888+
PyErr_Format(PyExc_ValueError,
889+
"mmap: can't expand a shared anonymous mapping on Linux");
890+
return NULL;
891+
}
892+
#endif
885893
if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) {
886894
PyErr_SetFromErrno(PyExc_OSError);
887895
return NULL;
@@ -1678,6 +1686,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
16781686
else {
16791687
m_obj->fd = -1;
16801688
}
1689+
m_obj->flags = flags;
16811690

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

0 commit comments

Comments
 (0)