Skip to content

Commit 43d1bae

Browse files
committed
refact(tests/sandbox): make low level sandbox test work without corrupting the main thread's landlock state
1 parent a4fc98a commit 43d1bae

File tree

1 file changed

+59
-30
lines changed

1 file changed

+59
-30
lines changed

tests/rust/test_sandbox.py

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import errno
33
import os
44
import platform
5-
from ctypes import c_uint32
5+
import threading
66
from pathlib import Path
77

88
import pytest
@@ -54,7 +54,7 @@ def landlock_supported() -> int:
5454
__NR_landlock_create_ruleset,
5555
None,
5656
ctypes.c_size_t(0),
57-
c_uint32(LANDLOCK_CREATE_RULESET_VERSION),
57+
ctypes.c_uint32(LANDLOCK_CREATE_RULESET_VERSION),
5858
)
5959
if max_abi_version > 0:
6060
return max_abi_version
@@ -72,31 +72,60 @@ def landlock_supported() -> int:
7272
@pytest.mark.skipif(
7373
not landlock_supported(), reason="Landlock support is not available on this system"
7474
)
75-
def test_read_sandboxing(request: pytest.FixtureRequest, sandbox_path: Path):
76-
restrict_access(
77-
AccessFS.read("/"),
78-
AccessFS.read(sandbox_path),
79-
# allow pytest caching, coverage, etc...
80-
AccessFS.read_write(request.config.rootpath),
81-
)
82-
83-
with pytest.raises(PermissionError):
84-
(sandbox_path / "some-dir").mkdir()
85-
86-
with pytest.raises(PermissionError):
87-
(sandbox_path / "some-file").touch()
88-
89-
with pytest.raises(PermissionError):
90-
(sandbox_path / "some-link").symlink_to("file.txt")
91-
92-
for path in sandbox_path.rglob("**/*"):
93-
if path.is_file() or path.is_symlink():
94-
with path.open("rb") as f:
95-
assert f.read() == FILE_CONTENT
96-
with pytest.raises(PermissionError):
97-
assert path.open("r+")
98-
with pytest.raises(PermissionError):
99-
assert path.unlink()
100-
elif path.is_dir():
101-
with pytest.raises(PermissionError):
102-
path.rmdir()
75+
def test_read_sandboxing(request: pytest.FixtureRequest, sandbox_path: Path): # noqa: C901
76+
exception = None
77+
78+
def _run_catching_exceptions(fn):
79+
def wrapper():
80+
__tracebackhide__ = True
81+
nonlocal exception
82+
try:
83+
fn()
84+
except BaseException as exc:
85+
exception = exc
86+
87+
return wrapper
88+
89+
# Sandbox applies to the current thread and future threads spawned
90+
# from it.
91+
#
92+
# Running the test on a new thread keeps the main-thread
93+
# clean, so sandboxing won't interfere with other tests executed
94+
# after this one.
95+
96+
@_run_catching_exceptions
97+
def _run_in_thread():
98+
restrict_access(
99+
AccessFS.read("/"),
100+
AccessFS.read(sandbox_path),
101+
# allow pytest caching, coverage, etc...
102+
AccessFS.read_write(request.config.rootpath),
103+
)
104+
105+
with pytest.raises(PermissionError):
106+
(sandbox_path / "some-dir").mkdir()
107+
108+
with pytest.raises(PermissionError):
109+
(sandbox_path / "some-file").touch()
110+
111+
with pytest.raises(PermissionError):
112+
(sandbox_path / "some-link").symlink_to("file.txt")
113+
114+
for path in sandbox_path.rglob("**/*"):
115+
if path.is_file() or path.is_symlink():
116+
with path.open("rb") as f:
117+
assert f.read() == FILE_CONTENT
118+
with pytest.raises(PermissionError):
119+
assert path.open("r+")
120+
with pytest.raises(PermissionError):
121+
assert path.unlink()
122+
elif path.is_dir():
123+
with pytest.raises(PermissionError):
124+
path.rmdir()
125+
126+
t = threading.Thread(target=_run_in_thread)
127+
t.start()
128+
t.join()
129+
__tracebackhide__ = True
130+
if exception:
131+
raise exception

0 commit comments

Comments
 (0)