Skip to content

Fix JOB_OBJECT_UILIMIT probe assertions in Win25H2Safe-Tests#544

Open
MGudgin wants to merge 2 commits into
mainfrom
user/gudge/job_limit_test_fixes
Open

Fix JOB_OBJECT_UILIMIT probe assertions in Win25H2Safe-Tests#544
MGudgin wants to merge 2 commits into
mainfrom
user/gudge/job_limit_test_fixes

Conversation

@MGudgin

@MGudgin MGudgin commented Jun 19, 2026

Copy link
Copy Markdown
Member

This PR fixes the failing Phase 4b/4c UI-mitigation assertions in Win25H2Safe-Tests.ps1 (HANDLES, GLOBALATOMS, WIN32K, EXITWINDOWS) so each verifies the documented behavior of the corresponding job-object UI limit rather than an incidental API failure, and adds negative controls plus a -Phases selector to make the suite self-validating and easier to run.

Details

  • GLOBALATOMS: rewrote the wxc_ui_probe probe to verify per-job atom-table isolation bidirectionally (host->guest must not find a host-planted atom; guest->host held across a ready/release file handshake) instead of treating a non-existent API failure as PASS. Added the matching Phase 4c (Phase-GlobalAtomIsolation) plus an isolation=desktop negative control that asserts the host DOES see the guest atom.
  • HANDLES: probe now verifies it cannot use a USER handle owned by a process outside the job (cross-job GetWindowTextW against a harness-owned hidden message-pumping window) rather than relying on FindWindow returning NULL. Added an isolation=desktop negative control asserting HANDLES=FAIL.
  • WIN32K: the "user32 not loadable" arm now emits a DIAG only (no WIN32K=FAIL), so the harness correctly reads a not-loadable result as Win32k-mitigation-honored; only WIN32K's None arm changed.
  • EXITWINDOWS / env delivery: deliver MXC_PROBE_DESTRUCTIVE_OK=1 to the contained child via a COMPLETE process.env block (current env minus the hidden "=C:" cwd vars and MXC_FORCE_TIER), since the AppContainer runner builds the child env with CreateEnvironmentBlock(bInherit=FALSE) and replaces the block when process.env is non-empty; a one-var block failed CreateProcessW with 0x800700CB. Probe data that can't ride env now passes via CLI flags.
  • Harness ergonomics: added a -Phases parameter and phase registry so a subset of phases can be run; build, preflight, and scratch init always run and unknown phase names abort early with the valid list.

Tests

  • cargo build -p wxc_ui_probe (debug + release), cargo clippy -p wxc_ui_probe --all-targets -- -D warnings, and cargo fmt -p wxc_ui_probe -- --check: clean.
  • End-to-end via wxc-exec for each probe, both isolated and as a negative control: GLOBALATOMS, HANDLES, and WIN32K flip correctly between PASS (isolation=container) and FAIL (isolation=desktop); the env fix lets CreateProcessW succeed and the child run to exit 0 (READCLIPBOARD=PASS), confirming the prior one-var block failed with 0x800700CB.
  • Full Win25H2Safe-Tests.ps1 re-run on the 25H2 host: Phase 4b and Phase 4c pass (confirmed by author). Script parses.
Microsoft Reviewers: Open in CodeFlow

This PR fixes the failing Phase 4b/4c UI-mitigation assertions in
Win25H2Safe-Tests.ps1 (HANDLES, GLOBALATOMS, WIN32K, EXITWINDOWS) so each
verifies the documented behavior of the corresponding job-object UI limit
rather than an incidental API failure, and adds negative controls plus a
-Phases selector to make the suite self-validating and easier to run.

Details

* GLOBALATOMS: rewrote the wxc_ui_probe probe to verify per-job atom-table
  isolation bidirectionally (host->guest must not find a host-planted atom;
  guest->host held across a ready/release file handshake) instead of treating
  a non-existent API failure as PASS. Added the matching Phase 4c
  (Phase-GlobalAtomIsolation) plus an isolation=desktop negative control that
  asserts the host DOES see the guest atom.
* HANDLES: probe now verifies it cannot *use* a USER handle owned by a process
  outside the job (cross-job GetWindowTextW against a harness-owned hidden
  message-pumping window) rather than relying on FindWindow returning NULL.
  Added an isolation=desktop negative control asserting HANDLES=FAIL.
* WIN32K: the "user32 not loadable" arm now emits a DIAG only (no WIN32K=FAIL),
  so the harness correctly reads a not-loadable result as
  Win32k-mitigation-honored; only WIN32K's None arm changed.
* EXITWINDOWS / env delivery: deliver MXC_PROBE_DESTRUCTIVE_OK=1 to the
  contained child via a COMPLETE process.env block (current env minus the
  hidden "=C:" cwd vars and MXC_FORCE_TIER), since the AppContainer runner
  builds the child env with CreateEnvironmentBlock(bInherit=FALSE) and replaces
  the block when process.env is non-empty; a one-var block failed CreateProcessW
  with 0x800700CB. Probe data that can't ride env now passes via CLI flags.
* Harness ergonomics: added a -Phases parameter and phase registry so a subset
  of phases can be run; build, preflight, and scratch init always run and
  unknown phase names abort early with the valid list.

Tests

* cargo build -p wxc_ui_probe (debug + release), cargo clippy -p wxc_ui_probe
  --all-targets -- -D warnings, and cargo fmt -p wxc_ui_probe -- --check: clean.
* End-to-end via wxc-exec for each probe, both isolated and as a negative
  control: GLOBALATOMS, HANDLES, and WIN32K flip correctly between PASS
  (isolation=container) and FAIL (isolation=desktop); the env fix lets
  CreateProcessW succeed and the child run to exit 0 (READCLIPBOARD=PASS),
  confirming the prior one-var block failed with 0x800700CB.
* Full Win25H2Safe-Tests.ps1 re-run on the 25H2 host: Phase 4b and Phase 4c
  pass (confirmed by author). Script parses.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 19, 2026 21:57
@MGudgin MGudgin requested a review from a team as a code owner June 19, 2026 21:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request updates the Win25H2 safety test harness and the wxc_ui_probe helper to validate Windows job-object UI limits (notably HANDLES, GLOBALATOMS, WIN32K, EXITWINDOWS) against their documented behavior, adding negative controls and a -Phases selector to make the suite easier to run and more self-validating.

Changes:

  • Adds per-run delivery of a complete environment block (including MXC_PROBE_DESTRUCTIVE_OK=1) via process.env, and introduces -Phases filtering with a phase registry.
  • Reworks the Phase 4b HANDLES validation to test cross-job handle use (GetWindowTextW against a harness-owned window) and adds a new Phase 4c GLOBALATOMS bidirectional isolation handshake with a negative control.
  • Extends wxc_ui_probe to accept --key=value harness parameters, implements GLOBALATOMS bidirectional logic, and updates HANDLES/WIN32K probing semantics to align with expected OS behavior.
Show a summary per file
File Description
tests/scripts/Win25H2Safe-Tests.ps1 Adds -Phases, env-block delivery via config, new window/atom host helpers, and new/updated UI-mitigation phases with negative controls.
src/testing/wxc_ui_probe/src/main.rs Adds harness-parameter parsing, implements GLOBALATOMS isolation handshake + revised HANDLES probe, and adjusts WIN32K “user32 not loadable” handling.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 3

Comment thread tests/scripts/Win25H2Safe-Tests.ps1 Outdated
Comment thread tests/scripts/Win25H2Safe-Tests.ps1 Outdated
Comment thread src/testing/wxc_ui_probe/src/main.rs
…eanup

Two fixes from the Copilot review of the GLOBALATOMS isolation handshake.

Details

* wxc_ui_probe (probe_globalatoms): capture the wait_for_file result and emit a
  GLOBALATOMS_GUEST_TO_HOST=DIAG on timeout instead of silently dropping it, and
  raise the release-file wait from 15s to 60s — comfortably above the harness's
  30s readiness window — so a loaded host cannot make the probe delete its atom
  before the host-side GlobalFindAtomW check runs (which would read as a false
  "isolated").
* Win25H2Safe-Tests.ps1 (Invoke-GlobalAtomProbe): give the OutputDataReceived /
  ErrorDataReceived subscriptions explicit -SourceIdentifier values and, in the
  finally block, Remove-Job after Unregister-Event. Unregister-Event removes the
  subscription but leaves the backing PSEventJob, so without Remove-Job the jobs
  accumulated across same-session re-runs (4 per Phase 4c run).

Tests

* cargo build (debug+release) -p wxc_ui_probe, clippy -D warnings, fmt --check:
  clean. Script parses.
* Verified the new register/unregister/remove pattern leaks 0 jobs and 0
  subscriptions across 4 invocations (previously 4 jobs leaked per Phase 4c).
* Ran Phase 4c (GlobalAtomIsolation) end-to-end: 5/5 PASS (positive + negative
  controls), handshake unaffected by the probe change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants