Skip to content

Commit e85e8de

Browse files
authored
pythongh-118209: Add Windows structured exception handling to mmap module (pythonGH-118213)
1 parent 7e6fcab commit e85e8de

File tree

4 files changed

+410
-48
lines changed

4 files changed

+410
-48
lines changed

Doc/whatsnew/3.13.rst

+3
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,9 @@ mmap
796796
* :class:`mmap.mmap` now has a *trackfd* parameter on Unix; if it is ``False``,
797797
the file descriptor specified by *fileno* will not be duplicated.
798798
(Contributed by Zackery Spytz and Petr Viktorin in :gh:`78502`.)
799+
* :class:`mmap.mmap` is now protected from crashing on Windows when the mapped memory
800+
is inaccessible due to file system errors or access violations.
801+
(Contributed by Jannis Weigend in :gh:`118209`.)
799802

800803
opcode
801804
------

Lib/test/test_mmap.py

+77
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
)
44
from test.support.import_helper import import_module
55
from test.support.os_helper import TESTFN, unlink
6+
from test.support.script_helper import assert_python_ok
67
import unittest
78
import errno
89
import os
@@ -12,6 +13,7 @@
1213
import socket
1314
import string
1415
import sys
16+
import textwrap
1517
import weakref
1618

1719
# Skip test if we can't import mmap.
@@ -1058,6 +1060,81 @@ def __exit__(self, exc_type, exc_value, traceback):
10581060
with self.assertRaisesRegex(ValueError, "mmap closed or invalid"):
10591061
m.write_byte(X())
10601062

1063+
@unittest.skipUnless(os.name == 'nt', 'requires Windows')
1064+
@unittest.skipUnless(hasattr(mmap.mmap, '_protect'), 'test needs debug build')
1065+
def test_access_violations(self):
1066+
from test.support.os_helper import TESTFN
1067+
1068+
code = textwrap.dedent("""
1069+
import faulthandler
1070+
import mmap
1071+
import os
1072+
import sys
1073+
from contextlib import suppress
1074+
1075+
# Prevent logging access violations to stderr.
1076+
faulthandler.disable()
1077+
1078+
PAGESIZE = mmap.PAGESIZE
1079+
PAGE_NOACCESS = 0x01
1080+
1081+
with open(sys.argv[1], 'bw+') as f:
1082+
f.write(b'A'* PAGESIZE)
1083+
f.flush()
1084+
1085+
m = mmap.mmap(f.fileno(), PAGESIZE)
1086+
m._protect(PAGE_NOACCESS, 0, PAGESIZE)
1087+
with suppress(OSError):
1088+
m.read(PAGESIZE)
1089+
assert False, 'mmap.read() did not raise'
1090+
with suppress(OSError):
1091+
m.read_byte()
1092+
assert False, 'mmap.read_byte() did not raise'
1093+
with suppress(OSError):
1094+
m.readline()
1095+
assert False, 'mmap.readline() did not raise'
1096+
with suppress(OSError):
1097+
m.write(b'A'* PAGESIZE)
1098+
assert False, 'mmap.write() did not raise'
1099+
with suppress(OSError):
1100+
m.write_byte(0)
1101+
assert False, 'mmap.write_byte() did not raise'
1102+
with suppress(OSError):
1103+
m[0] # test mmap_subscript
1104+
assert False, 'mmap.__getitem__() did not raise'
1105+
with suppress(OSError):
1106+
m[0:10] # test mmap_subscript
1107+
assert False, 'mmap.__getitem__() did not raise'
1108+
with suppress(OSError):
1109+
m[0:10:2] # test mmap_subscript
1110+
assert False, 'mmap.__getitem__() did not raise'
1111+
with suppress(OSError):
1112+
m[0] = 1
1113+
assert False, 'mmap.__setitem__() did not raise'
1114+
with suppress(OSError):
1115+
m[0:10] = b'A'* 10
1116+
assert False, 'mmap.__setitem__() did not raise'
1117+
with suppress(OSError):
1118+
m[0:10:2] = b'A'* 5
1119+
assert False, 'mmap.__setitem__() did not raise'
1120+
with suppress(OSError):
1121+
m.move(0, 10, 1)
1122+
assert False, 'mmap.move() did not raise'
1123+
with suppress(OSError):
1124+
list(m) # test mmap_item
1125+
assert False, 'mmap.__getitem__() did not raise'
1126+
with suppress(OSError):
1127+
m.find(b'A')
1128+
assert False, 'mmap.find() did not raise'
1129+
with suppress(OSError):
1130+
m.rfind(b'A')
1131+
assert False, 'mmap.rfind() did not raise'
1132+
""")
1133+
rt, stdout, stderr = assert_python_ok("-c", code, TESTFN)
1134+
self.assertEqual(stdout.strip(), b'')
1135+
self.assertEqual(stderr.strip(), b'')
1136+
1137+
10611138
class LargeMmapTests(unittest.TestCase):
10621139

10631140
def setUp(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Avoid crashing in :mod:`mmap` on Windows when the mapped memory is inaccessible
2+
due to file system errors or access violations.

0 commit comments

Comments
 (0)