Skip to content

Fix closed release-producer defects before enabling controlled selection #1517

Open

Description

@prk-Jr

Part of #1235. Found in the review of #1449 (review).

These defects are in crates/trusted-server-release producer code that main cannot reach today. The production macOS and Linux selectors are unconstructible, and the ordinary release path doesn't use these modules. Each one must be fixed, with a regression test, before the corresponding controlled path is enabled. Line references are to 1860b9958.

Boundary and roots

  • Inherited descriptors bypass the macOS sandbox's write denial (process.rs:369, thread). command() doesn't close inherited descriptors 3 and up. A descriptor without FD_CLOEXEC passes through sandbox-exec, and SBPL checks at open(), not write(), so the confined child can write outside every root through it. Fix: at the start of main, before any thread exists, set FD_CLOEXEC on every descriptor above 2 listed in /dev/fd (/proc/self/fd on Linux), fail closed if the listing fails, and keep the probe as a regression test.

  • Root disjointness and alias checks are textual (recipe/fresh.rs:103, :36, boundary/roots.rs:129, thread). Path::starts_with plus canonicalize(p) == p accepts macOS firmlink aliases such as /private/tmp and /System/Volumes/Data/private/tmp, and Linux bind mounts. Fix: record (dev, ino) for each root and its existing ancestors, and reject a root whose identity appears in another root's ancestor chain.

  • Filesystem scans have no shared budget (boundary/roots.rs:507, thread). TreeFacts::scan grows pending and entries, and child_names() collects directory entries without an entry limit. Scanning and hashing take no deadline or cancellation input and run in the supervisor, outside child resource limits. Fix: thread one shared entry, byte and deadline/cancellation budget through enumeration, scanning, hashing, their callers and tests.

Recipe and process

  • recipe/ambient.rs:375-406, recipe/compare.rs:165-169: the start-time variation check compares the caller-supplied started_at_seconds. The measured start (recipe/execute.rs:304) is never compared, and execution_uid is never measured.
  • recipe/execute.rs:464-479: copy_artifact runs before inspection, the final-path checks and the qualification refusal, so a rejected executable stays in the output directory with mode 0755.
  • process/environment.rs:22-23,99-110: the backstop accepts names its docs say are denied: CARGO_TARGET_<triple>_RUSTFLAGS, the *RUSTDOCFLAGS names, RUSTUP_TOOLCHAIN, CARGO_BUILD_TARGET, CARGO_PROFILE_RELEASE_OPT_LEVEL and RUSTC_BOOTSTRAP.
  • process/supervise.rs:34-60,231-251: if the producer starts with SIGCHLD ignored on Linux, the kernel reaps the leader, waitid returns ECHILD, and settle sends killpg(SIGKILL) to a group that is no longer pinned. Fixed in 37dfc6d (thread): this was reachable from ordinary-measure and preflight, so the binary now restores the default SIGCHLD action at startup and every spawn refuses while children would be reaped automatically.
  • recipe/runtime/viceroy.rs:44-83: Viceroy is selected by its --version output alone (release-tools.json has sha256: null).

Materials and composition

  • materials/extract.rs:593-599: parents are recorded only for files. An archive with an explicit package/a/b/ entry and no package/a/ entry extracts, and then every verify_unchanged fails with "unrecorded extracted directory appeared".
  • composition/graph.rs:165-185: path proc-macros print (proc-macro) (/path) in real cargo tree output, which tree_identity rejects. The proc-macro test feeds a shape Cargo never prints.

Unpinned checks and test coverage

  • Mutation survivors with no pinning test:
    • the \ / : name rejection (materials/extract.rs:289) and the terminator-block check (:579);
    • the 100-byte GNU long-name rule (materials/extract/compatibility.rs:200);
    • both executable-mode rechecks (materials/extract.rs:121,132);
    • the tree cycle check (composition/graph.rs:281);
    • CARGO_HOME in check_child_state (recipe/plan.rs:212) and sysroot != "/" (recipe/gnu.rs:367);
    • the git-config escape rejection (source/config.rs:26-30, live through preflight).
  • test.yml runs only process:: on macOS, so the 99 boundary:: tests never run in CI. They include the private sandbox_check binding (boundary/survivors.rs:51-62), which has been observed on a single macOS version.
  • recipe/tests/complete.rs:731-739,780-783 and recipe/tests/compare.rs:87-100 assert on literals the test helpers wrote (release_qualification:false), so they prove nothing about production code.

Provenance

  • schemas/SPDX-LICENSE carries the CC-BY-3.0 text with no source, copyright holder or URL for the vendored schema.

Activity

  1. added theissue type on Nov 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions