fix(session/gamelease): Windows had no launch reference, so it adopted old games
ci / web (push) Successful in 1m15s
ci / docs-site (push) Successful in 1m2s
ci / bench (push) Successful in 6m37s
windows-host / package (push) Successful in 11m1s
windows-host / winget-source (push) Skipped
ci / rust-arm64 (push) Successful in 11m53s
apple / swift (push) Successful in 7m18s
deb / build-publish (push) Successful in 10m57s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 21m16s
ci / rust (push) Failing after 13m29s
docker / build-push-arm64cross (push) Successful in 10s
arch / build-publish (push) Successful in 13m42s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
docker / deploy-docs (push) Successful in 25s
decky / build-publish (push) Successful in 25s
apple / screenshots (push) Successful in 26m5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 13s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 12s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 12s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 38s
android / android (push) Successful in 15m6s
deb / build-publish-host (push) Successful in 11m27s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 5m45s
deb / build-publish-client-arm64 (push) Successful in 8m48s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m39s

On glass (.173), with a copy of Buckshot Roulette already running: a session
launched the same title, Steam focused the existing instance rather than
starting one, the lease adopted THAT process — and a deliberate stop closed the
player's pre-existing game.

That is the one thing the design says must never happen. The rule was written
and the matcher implements it; the reference instant it filters against just
never arrived. `procscan::launch_stamp` supports Windows, but the wrapper every
launch site calls was gated `#[cfg(target_os = "linux")]` and answered `None`
everywhere else — and `None` doesn't fail, it turns the filter OFF (`find` skips
it), so every process under the install dir became adoptable.

Windows is where this matters most: the host runs as SYSTEM and can see every
process on the box, which is why `procscan/windows.rs` calls rule 1
load-bearing rather than a nicety. Both planes were affected.

No unit test could have caught it — the Linux fixtures pass an explicit
`min_start`, and the Windows live test passes `None` deliberately. So the guard
added here asserts the reference EXISTS wherever processes can be matched, which
is the shape of the failure: silent, and invisible from downstream.

.133: 282 passed (+1), same 4 environmental native::tests failures.
This commit is contained in:
2026-07-26 20:30:20 +02:00
parent dff63b2a29
commit 6e07d063c3
+21 -8
View File
@@ -260,14 +260,13 @@ pub struct LeaseRequest {
/// The reference instant for adopting this launch's processes, in seconds since boot. Call it /// The reference instant for adopting this launch's processes, in seconds since boot. Call it
/// **before** anything spawns; see [`LeaseRequest::launch_stamp`]. /// **before** anything spawns; see [`LeaseRequest::launch_stamp`].
pub fn launch_clock() -> Option<f64> { pub fn launch_clock() -> Option<f64> {
#[cfg(target_os = "linux")] // Delegate unconditionally: `procscan::launch_stamp` already answers `None` on a platform with
{ // no matcher. Gating this on Linux here silently disabled the start-time floor on Windows —
crate::procscan::launch_stamp() // `find(spec, None)` skips the filter entirely — so a copy of the game the player already had
} // open was adopted and then ended with the session (caught on glass, .173: Steam focused a
#[cfg(not(target_os = "linux"))] // running instance instead of starting one, and the lease took it). The Windows matcher is
{ // exactly where that rule matters most, since the host is SYSTEM and can see every process.
None crate::procscan::launch_stamp()
}
} }
/// What the watcher does when it concludes the game has exited. /// What the watcher does when it concludes the game has exited.
@@ -1207,6 +1206,20 @@ mod tests {
); );
} }
/// Wherever the host can match processes at all, a launch MUST have a reference instant to
/// match them against — it is the only thing standing between this feature and ending a copy of
/// the game the player already had open. `None` here doesn't fail loudly; it silently turns the
/// filter off ([`crate::procscan::Scanner::find`] skips it), so nothing downstream can notice.
#[cfg(any(target_os = "linux", windows))]
#[test]
fn a_launch_always_gets_a_reference_instant_to_adopt_against() {
assert!(
launch_clock().is_some(),
"no launch reference on a platform that matches processes — every pre-existing \
instance of a title would be adoptable, and endable"
);
}
#[test] #[test]
fn state_strings_are_stable() { fn state_strings_are_stable() {
// These reach the API and the console; renaming one is a wire change. // These reach the API and the console; renaming one is a wire change.