Skip to content

Commit dbb011a

Browse files
miss-islingtonAgent-Hellboysunmy2019JelleZijlstra
authored
[3.11] gh-103987: fix several crashes in mmap module (GH-103990) (#104677)
gh-103987: fix several crashes in mmap module (GH-103990) (cherry picked from commit ceaa4c3) Co-authored-by: Prince Roshan <[email protected]> Co-authored-by: sunmy2019 <[email protected]> Co-authored-by: Jelle Zijlstra <[email protected]>
1 parent 2801b3f commit dbb011a

File tree

3 files changed

+101
-3
lines changed

3 files changed

+101
-3
lines changed

Lib/test/test_mmap.py

+86-1
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,6 @@ def test_move(self):
407407
m.move(0, 0, 1)
408408
m.move(0, 0, 0)
409409

410-
411410
def test_anonymous(self):
412411
# anonymous mmap.mmap(-1, PAGE)
413412
m = mmap.mmap(-1, PAGESIZE)
@@ -887,6 +886,92 @@ def test_resize_succeeds_with_error_for_second_named_mapping(self):
887886
self.assertEqual(m1[:data_length], data)
888887
self.assertEqual(m2[:data_length], data)
889888

889+
def test_mmap_closed_by_int_scenarios(self):
890+
"""
891+
gh-103987: Test that mmap objects raise ValueError
892+
for closed mmap files
893+
"""
894+
895+
class MmapClosedByIntContext:
896+
def __init__(self, access) -> None:
897+
self.access = access
898+
899+
def __enter__(self):
900+
self.f = open(TESTFN, "w+b")
901+
self.f.write(random.randbytes(100))
902+
self.f.flush()
903+
904+
m = mmap.mmap(self.f.fileno(), 100, access=self.access)
905+
906+
class X:
907+
def __index__(self):
908+
m.close()
909+
return 10
910+
911+
return (m, X)
912+
913+
def __exit__(self, exc_type, exc_value, traceback):
914+
self.f.close()
915+
916+
read_access_modes = [
917+
mmap.ACCESS_READ,
918+
mmap.ACCESS_WRITE,
919+
mmap.ACCESS_COPY,
920+
mmap.ACCESS_DEFAULT,
921+
]
922+
923+
write_access_modes = [
924+
mmap.ACCESS_WRITE,
925+
mmap.ACCESS_COPY,
926+
mmap.ACCESS_DEFAULT,
927+
]
928+
929+
for access in read_access_modes:
930+
with MmapClosedByIntContext(access) as (m, X):
931+
with self.assertRaisesRegex(ValueError, "mmap closed or invalid"):
932+
m[X()]
933+
934+
with MmapClosedByIntContext(access) as (m, X):
935+
with self.assertRaisesRegex(ValueError, "mmap closed or invalid"):
936+
m[X() : 20]
937+
938+
with MmapClosedByIntContext(access) as (m, X):
939+
with self.assertRaisesRegex(ValueError, "mmap closed or invalid"):
940+
m[X() : 20 : 2]
941+
942+
with MmapClosedByIntContext(access) as (m, X):
943+
with self.assertRaisesRegex(ValueError, "mmap closed or invalid"):
944+
m[20 : X() : -2]
945+
946+
with MmapClosedByIntContext(access) as (m, X):
947+
with self.assertRaisesRegex(ValueError, "mmap closed or invalid"):
948+
m.read(X())
949+
950+
with MmapClosedByIntContext(access) as (m, X):
951+
with self.assertRaisesRegex(ValueError, "mmap closed or invalid"):
952+
m.find(b"1", 1, X())
953+
954+
for access in write_access_modes:
955+
with MmapClosedByIntContext(access) as (m, X):
956+
with self.assertRaisesRegex(ValueError, "mmap closed or invalid"):
957+
m[X() : 20] = b"1" * 10
958+
959+
with MmapClosedByIntContext(access) as (m, X):
960+
with self.assertRaisesRegex(ValueError, "mmap closed or invalid"):
961+
m[X() : 20 : 2] = b"1" * 5
962+
963+
with MmapClosedByIntContext(access) as (m, X):
964+
with self.assertRaisesRegex(ValueError, "mmap closed or invalid"):
965+
m[20 : X() : -2] = b"1" * 5
966+
967+
with MmapClosedByIntContext(access) as (m, X):
968+
with self.assertRaisesRegex(ValueError, "mmap closed or invalid"):
969+
m.move(1, 2, X())
970+
971+
with MmapClosedByIntContext(access) as (m, X):
972+
with self.assertRaisesRegex(ValueError, "mmap closed or invalid"):
973+
m.write_byte(X())
974+
890975
class LargeMmapTests(unittest.TestCase):
891976

892977
def setUp(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
In :mod:`mmap`, fix several bugs that could lead to access to memory-mapped files after
2+
they have been invalidated.

Modules/mmapmodule.c

+13-2
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,8 @@ mmap_read_method(mmap_object *self,
292292

293293
CHECK_VALID(NULL);
294294
if (!PyArg_ParseTuple(args, "|O&:read", _Py_convert_optional_to_ssize_t, &num_bytes))
295-
return(NULL);
295+
return NULL;
296+
CHECK_VALID(NULL);
296297

297298
/* silently 'adjust' out-of-range requests */
298299
remaining = (self->pos < self->size) ? self->size - self->pos : 0;
@@ -333,6 +334,7 @@ mmap_gfind(mmap_object *self,
333334
end = self->size;
334335

335336
Py_ssize_t res;
337+
CHECK_VALID(NULL);
336338
if (reverse) {
337339
res = _PyBytes_ReverseFind(
338340
self->data + start, end - start,
@@ -396,7 +398,7 @@ mmap_write_method(mmap_object *self,
396398

397399
CHECK_VALID(NULL);
398400
if (!PyArg_ParseTuple(args, "y*:write", &data))
399-
return(NULL);
401+
return NULL;
400402

401403
if (!is_writable(self)) {
402404
PyBuffer_Release(&data);
@@ -409,6 +411,7 @@ mmap_write_method(mmap_object *self,
409411
return NULL;
410412
}
411413

414+
CHECK_VALID(NULL);
412415
memcpy(&self->data[self->pos], data.buf, data.len);
413416
self->pos += data.len;
414417
PyBuffer_Release(&data);
@@ -428,6 +431,7 @@ mmap_write_byte_method(mmap_object *self,
428431
if (!is_writable(self))
429432
return NULL;
430433

434+
CHECK_VALID(NULL);
431435
if (self->pos < self->size) {
432436
self->data[self->pos++] = value;
433437
Py_RETURN_NONE;
@@ -732,6 +736,7 @@ mmap_move_method(mmap_object *self, PyObject *args)
732736
if (self->size - dest < cnt || self->size - src < cnt)
733737
goto bounds;
734738

739+
CHECK_VALID(NULL);
735740
memmove(&self->data[dest], &self->data[src], cnt);
736741

737742
Py_RETURN_NONE;
@@ -857,6 +862,7 @@ mmap_madvise_method(mmap_object *self, PyObject *args)
857862
length = self->size - start;
858863
}
859864

865+
CHECK_VALID(NULL);
860866
if (madvise(self->data + start, length, option) != 0) {
861867
PyErr_SetFromErrno(PyExc_OSError);
862868
return NULL;
@@ -955,6 +961,7 @@ mmap_subscript(mmap_object *self, PyObject *item)
955961
"mmap index out of range");
956962
return NULL;
957963
}
964+
CHECK_VALID(NULL);
958965
return PyLong_FromLong(Py_CHARMASK(self->data[i]));
959966
}
960967
else if (PySlice_Check(item)) {
@@ -965,6 +972,7 @@ mmap_subscript(mmap_object *self, PyObject *item)
965972
}
966973
slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
967974

975+
CHECK_VALID(NULL);
968976
if (slicelen <= 0)
969977
return PyBytes_FromStringAndSize("", 0);
970978
else if (step == 1)
@@ -978,6 +986,7 @@ mmap_subscript(mmap_object *self, PyObject *item)
978986

979987
if (result_buf == NULL)
980988
return PyErr_NoMemory();
989+
981990
for (cur = start, i = 0; i < slicelen;
982991
cur += step, i++) {
983992
result_buf[i] = self->data[cur];
@@ -1062,6 +1071,7 @@ mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
10621071
"in range(0, 256)");
10631072
return -1;
10641073
}
1074+
CHECK_VALID(-1);
10651075
self->data[i] = (char) v;
10661076
return 0;
10671077
}
@@ -1087,6 +1097,7 @@ mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
10871097
return -1;
10881098
}
10891099

1100+
CHECK_VALID(-1);
10901101
if (slicelen == 0) {
10911102
}
10921103
else if (step == 1) {

0 commit comments

Comments
 (0)