79 lines
3.7 KiB
Python
79 lines
3.7 KiB
Python
"""A stale access with somebody to decide (MAKEPAD_RUN_STALE_PARK=1): the faulting thread parks,
|
|
the app pauses, the access is listed as a parked panic, `panic` is refused, `stop` ends the process."""
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
mk = os.path.abspath(sys.argv[1])
|
|
root = os.path.dirname(os.path.abspath(__file__))
|
|
env = dict(os.environ, MAKEPAD_RUN_STALE_PARK="1", SAFE2_UNLOAD_CRASH="parked")
|
|
p = subprocess.Popen([mk, "test", os.path.join(root, "unload_inside.rs")], env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
err = []
|
|
pid = None
|
|
ident = None
|
|
t0 = time.time()
|
|
try:
|
|
while time.time() - t0 < 120:
|
|
line = p.stderr.readline()
|
|
if not line:
|
|
break
|
|
err.append(line)
|
|
m = re.search(r"stale access parked as (P\d+) -- makepad inspect (\d+) panics", line)
|
|
if m:
|
|
ident, pid = m.group(1), m.group(2)
|
|
break
|
|
assert ident, "no park line"
|
|
assert p.poll() is None, "the process ended instead of parking"
|
|
def inspect(*args):
|
|
r = subprocess.run([mk, "inspect", pid, *args], capture_output=True, text=True, timeout=60)
|
|
return r.stdout + r.stderr
|
|
listed = inspect("panics")
|
|
assert ident in listed and "stale access" in listed and "parker" in listed, f"not listed: {listed[:600]}"
|
|
told = inspect("stale")
|
|
# the parked call returns into the module's code first: its code is invalidated at the unload
|
|
# (invalid.rs), so the return parks before any access to the window's data
|
|
assert "stale access: thread" in told and "unloaded module `parker`" in told and ("shadow stack" in told or "heap" in told or "RETURNED into" in told), f"stale: {told[:800]}"
|
|
stacks = inspect("stacks", "thread=all")
|
|
assert "parker::parked" in stacks, f"stacks: {stacks[:800]}"
|
|
ev = inspect("eval", "in=test_crate", "crate::PARKED.load(std::sync::atomic::Ordering::SeqCst)")
|
|
assert "true" in ev, f"eval while paused: {ev[:600]}"
|
|
refused = inspect("resume", ident, "panic")
|
|
assert "cannot go on" in refused, f"panic was not refused: {refused[:400]}"
|
|
time.sleep(0.5)
|
|
assert p.poll() is None, "ended before the decision"
|
|
inspect("resume", ident, "stop")
|
|
rc = p.wait(timeout=60)
|
|
out = p.stdout.read()
|
|
assert rc != 0 and "out parked call" not in out, f"stop: rc {rc}, out {out[:300]}"
|
|
print("modules stale access parks until stop: PASS")
|
|
# a trusted module's fn called through a kept fn pointer after its unload: parked, named
|
|
env2 = dict(env, SAFE2_UNLOAD_CRASH="trusted_call", MAKEPAD_RUN_MODULES_TRUSTED="1")
|
|
q = subprocess.Popen([mk, "test", os.path.join(root, "unload_inside.rs")], env=env2, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
p = q
|
|
err = []
|
|
ident = None
|
|
t0 = time.time()
|
|
while time.time() - t0 < 120:
|
|
line = q.stderr.readline()
|
|
if not line:
|
|
break
|
|
err.append(line)
|
|
m = re.search(r"stale access parked as (P\d+) -- makepad inspect (\d+) panics", line)
|
|
if m:
|
|
ident, pid = m.group(1), m.group(2)
|
|
break
|
|
assert ident, "trusted_call: no park line"
|
|
told = inspect("stale")
|
|
assert "unloaded module `parker`" in told and "CALLED" in told and "Module::unload" in told, f"trusted_call stale: {told[:1500]}"
|
|
inspect("resume", ident, "stop")
|
|
rc = q.wait(timeout=60)
|
|
out = q.stdout.read()
|
|
assert rc != 0 and "out called an unloaded" not in out, f"trusted_call stop: rc {rc}, out {out[:300]}"
|
|
print("modules stale call into an unloaded trusted module parks: PASS")
|
|
except Exception as e:
|
|
if p.poll() is None:
|
|
p.kill()
|
|
print("".join(err)[-3000:])
|
|
sys.exit(f"modules stale access park: FAIL ({e})")
|