Fork-lock gates cannot reach the third executable-writing door (File::create + write! + chmod), and plugin-host/tests/kill_cost.rs is a live member #271

Open
opened 2026-09-13 20:14:28 +02:00 by buildagent · 0 comments
Member

Found during I066 (#269 follow-up), round-2 review. Recorded rather than fixed, because the fix is structural and the gate could not offer that file a remedy.

Background

crates/test-support/tests/exec_copy_registry.rs refuses a test that creates an executable and then forks without the fork lock. The reason is specific: writing a file holds a write fd on it, and a fork from any other thread in the same test binary inside that window leaves the child holding that fd — Linux then refuses to exec a file held open for writing, so the exec fails ETXTBSY about a state no operator ever created.

I066 closed two of the three doors:

  1. fs::copy of a built binary — the original rule.
  2. fs::write + set_permissions with an execute bit, on the same path expression in one function body — added in I066 after it was found live in crates/cli/tests/installer_e2e.rs.

The door that is still open

  1. File::create + write! + set_permissions.

crates/plugin-host/tests/kill_cost.rs's script() is a real member: it writes a /bin/sh worker, chmods it 0o755, and the supervisor execs it.

Why the gate could not fix it

Two independent obstacles, both measured:

  • crates/plugin-host carries no code-index-test-support dev-dependency, and the_dependency_lists_are_exactly_these grades that manifest — so the gate cannot point that file at write_executable, which is the remedy it offers everyone else. A gate that names a defect while offering no reachable fix is worse than one that states its boundary.
  • The forks are inside code_index_plugin_host::Supervisor, not in Command calls a suite could redirect. The other half of the rule — route the target's spawns through locked_output/locked_status/locked_spawn — has nothing to attach to here.

So the current gate states this boundary in its own documentation instead of implying coverage it does not have.

Why it is worth closing anyway

The ETXTBSY window is a race, not a deterministic failure. It was measured at roughly 10% under a four-thread soak in the installer_e2e case, which is exactly the rate that produces an occasional inexplicable red in CI and gets retried away. A door left open in a gate whose whole purpose is to make that race impossible will eventually be walked through.

Options, none obviously right

  1. Give crates/plugin-host the code-index-test-support dev-dependency and update the manifest gate in the same change — cheapest, but it widens the sandboxed worker crate's dev graph, which that gate exists to keep narrow.
  2. Put a fork-locked write helper somewhere plugin-host already depends on.
  3. Have Supervisor take the lock around its own spawn, which fixes the general case rather than the test case, at the cost of putting test-support machinery in production code.
  4. Leave it, and keep the boundary stated. Defensible while kill_cost.rs is the only member.

Scope note

Pre-existing; not introduced by I066. The two doors I066 did close are graded with executed mutations, including the coupling that neither half alone is sufficient — reverting the write helper removes the string the spawn gate triggers on, so that target stops being checked at all.

Found during I066 (#269 follow-up), round-2 review. Recorded rather than fixed, because the fix is structural and the gate could not offer that file a remedy. ## Background `crates/test-support/tests/exec_copy_registry.rs` refuses a test that creates an executable and then forks without the fork lock. The reason is specific: writing a file holds a write fd on it, and a fork from any other thread in the same test binary inside that window leaves the child holding that fd — Linux then refuses to exec a file held open for writing, so the exec fails `ETXTBSY` about a state no operator ever created. I066 closed two of the three doors: 1. `fs::copy` of a built binary — the original rule. 2. `fs::write` + `set_permissions` with an execute bit, on the same path expression in one function body — added in I066 after it was found live in `crates/cli/tests/installer_e2e.rs`. ## The door that is still open 3. **`File::create` + `write!` + `set_permissions`.** `crates/plugin-host/tests/kill_cost.rs`'s `script()` is a real member: it writes a `/bin/sh` worker, chmods it `0o755`, and the supervisor execs it. ## Why the gate could not fix it Two independent obstacles, both measured: - **`crates/plugin-host` carries no `code-index-test-support` dev-dependency**, and `the_dependency_lists_are_exactly_these` grades that manifest — so the gate cannot point that file at `write_executable`, which is the remedy it offers everyone else. A gate that names a defect while offering no reachable fix is worse than one that states its boundary. - **The forks are inside `code_index_plugin_host::Supervisor`, not in `Command` calls a suite could redirect.** The other half of the rule — route the target's spawns through `locked_output`/`locked_status`/`locked_spawn` — has nothing to attach to here. So the current gate states this boundary in its own documentation instead of implying coverage it does not have. ## Why it is worth closing anyway The ETXTBSY window is a race, not a deterministic failure. It was measured at roughly 10% under a four-thread soak in the `installer_e2e` case, which is exactly the rate that produces an occasional inexplicable red in CI and gets retried away. A door left open in a gate whose whole purpose is to make that race impossible will eventually be walked through. ## Options, none obviously right 1. Give `crates/plugin-host` the `code-index-test-support` dev-dependency and update the manifest gate in the same change — cheapest, but it widens the sandboxed worker crate's dev graph, which that gate exists to keep narrow. 2. Put a fork-locked write helper somewhere `plugin-host` already depends on. 3. Have `Supervisor` take the lock around its own spawn, which fixes the general case rather than the test case, at the cost of putting test-support machinery in production code. 4. Leave it, and keep the boundary stated. Defensible while `kill_cost.rs` is the only member. ## Scope note Pre-existing; not introduced by I066. The two doors I066 did close are graded with executed mutations, including the coupling that neither half alone is sufficient — reverting the write helper removes the string the spawn gate triggers on, so that target stops being checked at all.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
h-dv/code-index#271
No description provided.