diff --git a/evals/unsafe-rust/fixtures/v2-forward/d_support/CI.md b/evals/unsafe-rust/fixtures/v2-forward/d_support/CI.md new file mode 100644 index 0000000000..2ba3ba20bb --- /dev/null +++ b/evals/unsafe-rust/fixtures/v2-forward/d_support/CI.md @@ -0,0 +1,8 @@ +# Continuous integration matrix + +CI samples the non-`fast` configuration on Rust 1.79.0 for +`x86_64-unknown-linux-gnu`. It samples both feature values on Rust 1.82.0 for +both `x86_64-unknown-linux-gnu` and `aarch64-unknown-linux-gnu`. + +This file records the current test matrix. It does not define the project's +support commitment. diff --git a/evals/unsafe-rust/fixtures/v2-forward/d_support/Cargo.toml b/evals/unsafe-rust/fixtures/v2-forward/d_support/Cargo.toml new file mode 100644 index 0000000000..ae700bc86b --- /dev/null +++ b/evals/unsafe-rust/fixtures/v2-forward/d_support/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "domain-review" +version = "1.0.0" +edition = "2021" + +[features] +fast = [] diff --git a/evals/unsafe-rust/fixtures/v2-forward/d_support/POLICY-A.md b/evals/unsafe-rust/fixtures/v2-forward/d_support/POLICY-A.md new file mode 100644 index 0000000000..dfb1eecb70 --- /dev/null +++ b/evals/unsafe-rust/fixtures/v2-forward/d_support/POLICY-A.md @@ -0,0 +1,10 @@ +# Published 1.x support policy + +For this source snapshot, supported stable Rust releases are 1.79.0 through +1.82.0 inclusive. Supported targets are `x86_64-unknown-linux-gnu` and +`aarch64-unknown-linux-gnu`. + +Without `fast`, every target/version pair above is supported. + +With `fast`, x86_64 supports 1.79.0 through 1.82.0, while aarch64 supports +1.80.0 through 1.82.0. diff --git a/evals/unsafe-rust/fixtures/v2-forward/d_support/POLICY-B.md b/evals/unsafe-rust/fixtures/v2-forward/d_support/POLICY-B.md new file mode 100644 index 0000000000..cf4b88d261 --- /dev/null +++ b/evals/unsafe-rust/fixtures/v2-forward/d_support/POLICY-B.md @@ -0,0 +1,10 @@ +# Published 1.x compatibility statement + +For this source snapshot, supported stable Rust releases are 1.79.0 through +1.82.0 inclusive. Supported targets are `x86_64-unknown-linux-gnu` and +`aarch64-unknown-linux-gnu`. + +Without `fast`, every target/version pair above is supported. + +With `fast`, x86_64 supports 1.80.0 through 1.82.0, while aarch64 supports only +1.82.0. diff --git a/evals/unsafe-rust/fixtures/v2-forward/d_support/REQUEST.md b/evals/unsafe-rust/fixtures/v2-forward/d_support/REQUEST.md new file mode 100644 index 0000000000..f33dc396f9 --- /dev/null +++ b/evals/unsafe-rust/fixtures/v2-forward/d_support/REQUEST.md @@ -0,0 +1,8 @@ +# Supported-domain source review + +`POLICY-A.md` and `POLICY-B.md` are both current published commitments; no +precedence or supersession rule has been authorized. Review the exact source +and state the strongest soundness conclusion that can be justified without +choosing a new support policy. + +The audit cutoff is Rust 1.82.0. No source or policy edit is requested. diff --git a/evals/unsafe-rust/fixtures/v2-forward/d_support/lib.rs b/evals/unsafe-rust/fixtures/v2-forward/d_support/lib.rs new file mode 100644 index 0000000000..e5ad28c5b5 --- /dev/null +++ b/evals/unsafe-rust/fixtures/v2-forward/d_support/lib.rs @@ -0,0 +1,15 @@ +#![allow(dead_code)] + +#[cfg(not(feature = "fast"))] +pub fn first(bytes: &[u8]) -> Option { + bytes.first().copied() +} + +#[cfg(feature = "fast")] +pub fn first(bytes: &[u8]) -> Option { + if bytes.is_empty() { + None + } else { + Some(unsafe { *bytes.get_unchecked(0) }) + } +} diff --git a/evals/unsafe-rust/fixtures/v2-forward/d_support/rust-toolchain.toml b/evals/unsafe-rust/fixtures/v2-forward/d_support/rust-toolchain.toml new file mode 100644 index 0000000000..2e2b8c8521 --- /dev/null +++ b/evals/unsafe-rust/fixtures/v2-forward/d_support/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.82.0" diff --git a/evals/unsafe-rust/fixtures/v2-forward/i_producer/REQUEST.md b/evals/unsafe-rust/fixtures/v2-forward/i_producer/REQUEST.md new file mode 100644 index 0000000000..9219b14c77 --- /dev/null +++ b/evals/unsafe-rust/fixtures/v2-forward/i_producer/REQUEST.md @@ -0,0 +1,8 @@ +# Focused source review + +Audit every current producer and consumer of `Buffer` and the local safety +proof for `overwrite`. Determine the strongest result for all well-typed safe +uses. + +Scope: exactly Rust 1.80.0, all targets and ordinary profiles. No redesign or +source edit is requested. diff --git a/evals/unsafe-rust/fixtures/v2-forward/i_producer/lib.rs b/evals/unsafe-rust/fixtures/v2-forward/i_producer/lib.rs new file mode 100644 index 0000000000..27a8316bb6 --- /dev/null +++ b/evals/unsafe-rust/fixtures/v2-forward/i_producer/lib.rs @@ -0,0 +1,46 @@ +#![allow(dead_code)] + +static BYTE: u8 = 7; + +pub struct Buffer { + ptr: *mut u8, + shared: Option<&'static u8>, +} + +impl Buffer { + /// # Safety + /// + /// `ptr` must remain non-null, aligned, and valid for writes of one `u8` + /// for as long as the returned `Buffer` may be used. No access may + /// conflict with writes through the returned `Buffer`. + pub unsafe fn from_writable(ptr: *mut u8) -> Self { + Self { ptr, shared: None } + } + + pub fn from_static() -> Self { + let shared = &BYTE; + Self { + ptr: (shared as *const u8) as *mut u8, + shared: Some(shared), + } + } + + pub fn overwrite(&mut self, value: u8) { + if let Some(shared) = self.shared { + with_live(shared, || { + // SAFETY: `from_writable` requires `ptr` to remain valid for + // writes. + unsafe { self.ptr.write(value) } + }); + } else { + // SAFETY: `from_writable` requires `ptr` to remain valid for + // writes. + unsafe { self.ptr.write(value) } + } + } +} + +fn with_live(shared: &T, operation: impl FnOnce()) { + operation(); + let _ = shared; +} diff --git a/evals/unsafe-rust/fixtures/v2-forward/u_behavior/REQUEST.md b/evals/unsafe-rust/fixtures/v2-forward/u_behavior/REQUEST.md new file mode 100644 index 0000000000..b6f85e6e8a --- /dev/null +++ b/evals/unsafe-rust/fixtures/v2-forward/u_behavior/REQUEST.md @@ -0,0 +1,7 @@ +# Focused source review + +Audit the exact safe API and every mandatory documented behavior. State +separate soundness and behavioral-contract results with the evidence for each. + +Scope: exactly Rust 1.80.0, all targets and ordinary profiles. No source edit or +redesign is requested. diff --git a/evals/unsafe-rust/fixtures/v2-forward/u_behavior/lib.rs b/evals/unsafe-rust/fixtures/v2-forward/u_behavior/lib.rs new file mode 100644 index 0000000000..93adcf959a --- /dev/null +++ b/evals/unsafe-rust/fixtures/v2-forward/u_behavior/lib.rs @@ -0,0 +1,12 @@ +#![allow(dead_code)] + +/// Panics when `input == 0`. +/// +/// On normal return, returns `input`. +pub fn classify(input: u8) -> u8 { + match input { + 0 => unsafe { core::hint::unreachable_unchecked() }, + 1 => 2, + _ => input, + } +} diff --git a/evals/unsafe-rust/fixtures/v2-forward/v_versions/REQUEST.md b/evals/unsafe-rust/fixtures/v2-forward/v_versions/REQUEST.md new file mode 100644 index 0000000000..5f42fb032c --- /dev/null +++ b/evals/unsafe-rust/fixtures/v2-forward/v_versions/REQUEST.md @@ -0,0 +1,5 @@ +# Version-partitioned source review + +Audit this exact safe API separately on exactly Rust 1.79.0 and Rust 1.80.0. +Both versions, every target, and every ordinary profile are supported. State +the regional and combined result. No source edit is requested. diff --git a/evals/unsafe-rust/fixtures/v2-forward/v_versions/lib.rs b/evals/unsafe-rust/fixtures/v2-forward/v_versions/lib.rs new file mode 100644 index 0000000000..c8f5a4330e --- /dev/null +++ b/evals/unsafe-rust/fixtures/v2-forward/v_versions/lib.rs @@ -0,0 +1,5 @@ +#![allow(dead_code)] + +pub fn advance_marker() -> *const [u8; 0] { + unsafe { core::ptr::null::<[u8; 0]>().add(1) } +} diff --git a/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/SKILL.md b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/SKILL.md new file mode 100644 index 0000000000..370782667b --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/SKILL.md @@ -0,0 +1,251 @@ +--- +name: unsafe-rust +description: "Author, document, review, audit, or redesign unsafe Rust with proof-grade rigor. Use for unsafe blocks and functions, unsafe traits and impls, raw pointers, FFI, inline assembly, intrinsics, layout or validity reasoning, concurrency and atomics, SIMD and target features, allocators, invariant-bearing fields, safety comments or `# Safety` documentation, soundness reviews, TCB audits, generated unsafe code, changes to safety or behavioral contracts, and proof-oriented redesign of unsafe abstractions." +--- + +# Unsafe Rust Authoring and Audit + +Treat each safety contract as an English-language theorem and each safety +comment as its proof. Reject hand-waving, folklore, hidden assumptions, and +proof by testing. + +## Establish the Exact Claim + +Unless the user specifies a narrower claim, establish: + +> For the exact audited source snapshot, every supported compilation +> configuration, every valid in-scope use in a context satisfying all +> out-of-scope safety obligations preserves freedom from Rust undefined behavior +> under the documented Rust abstract semantics, and every mandatory in-scope +> documented postcondition holds, assuming only the explicitly recorded trusted +> computing base (TCB). + +Interpret valid use as follows: + +- For a safe API, quantify over every well-typed safe use. Impose no hidden + safety precondition. +- For an unsafe API, quantify over every use satisfying all documented initial, + ongoing, and terminal safety obligations. +- For a binary or other entrypoint, quantify over executions satisfying the + explicitly recorded deployment assumptions. Do not transfer those + assumptions silently to a safe library API. + +Prove every documented postcondition of each unsafe API in scope and every +documented guarantee consumed by an in-scope soundness proof. Include broader +safe-API robustness only when the user or audit scope requests it. + +Prove source-level Rust soundness first. State claims about a particular +compiler backend, binary, platform, security property, probability, or +deployment separately with their additional premises. + +## Use Only Applicable Premises + +- Bottom out Rust-language and standard-library facts in exact applicable text + from versioned Rust Reference or standard-library documentation. +- Quote and link the smallest sufficient set of passages whose propositions, + together with justified inference steps, entail the fact. Open each citation + and verify its wording, qualifications, version, and scope. +- Attach an applicability domain to every claim and premise, whether stated + locally or inherited from an identified project policy or canonical entry. A + derivation proves only the cases covered by all premises it consumes. +- Define or inherit a precise supported toolchain/configuration predicate + before consuming versioned premises or issuing a full verdict. If applicable + project sources conflict or materially underdetermine that predicate, obtain + an authorized resolution, prove an explicit conservative superset covering + every materially supported candidate predicate identified from those + sources, or leave the affected full-scope claim `UNPROVED`. Do not silently + select an MSRV, current toolchain, or convenient interpretation, and do not + assume that one earliest version represents the whole predicate. +- Apply a guarantee documented for an older Rust release to a later stable + release only when an exact applicable Rust backwards-compatibility + commitment preserves that exact proposition throughout the later release's + relevant domain. An API's stability badge does not by itself preserve every + behavioral statement in its current documentation. Record a + non-authoritative compatibility premise explicitly in the TCB. Never infer + an earlier-version guarantee merely from later documentation. +- Do not promote this skill, the Rustonomicon, Unsafe Code Guidelines, RFCs, + blogs, issue discussions, implementation behavior, Miri, or common practice + to Rust axioms. Use them to discover risks and authoritative text, or record + the exact additional proposition as a TCB assumption. +- Trust a deliberately selected safe dependency API to behave as documented + only when that exact trust is explicit in the TCB. Do not extend this + exception to caller-controlled safe code, callbacks, values, or safe trait + implementations. +- Audit a third-party unsafe API through to admissible premises or record its + exact implementation and contract as an additional TCB assumption. + +When no admissible direct or derived proof can be completed because +authoritative documentation is ambiguous or insufficient, identify the +smallest missing proposition. Do not repair it with intuition. Report a +documentation gap and suggest an upstream improvement when appropriate. + +## Compose Proofs Locally and Literally + +- Identify the controlling contract independently of the existing safety + comment. Distinguish normative contract text from examples, rationale, + implementation comments, and inferred design intent. +- Read the controlling contract according to its actual text. Decompose every + applicable conjunction, implication, quantifier, temporal clause, + precondition, and postcondition into separately reviewable obligations. Do + not replace a literal requirement with an operationally similar property. + Give every normative clause a disposition even when no known consumer uses + it. +- Reify every fact used nonlocally as a named contract or invariant carried by a + type, field, function boundary, guard, typestate, lock, token, or other + locally checkable mechanism. A function contract about global state is an + acceptable degenerate case. +- Prove that each state transition establishes, preserves, transfers, + deliberately suspends under an explicit obligation, or discharges every + applicable invariant. At each consumer, prove that the current invariant + entails the exact needed precondition. +- Trace dataflow across calls and time rather than limiting review to lexical + unsafe blocks. Account for every producer, transition, and consumer. +- Do not promote a producer's preconditions into a universal invariant of its + output type. Any type- or abstraction-wide conclusion needs a complete + derivation independent of that invalid reversal—for example, applicable + authoritative premises, construction-and-preservation closure under an + enforced boundary, or an admissible explicit TCB premise. Local checks and + other applicable derivations may instead prove the proposition for the + particular consumed values or quantified subset. +- For new code, place invariant-bearing representation in the smallest + practical leaf module, keep safely accessible representation fields private + to it, and treat safe code outside that module—including the rest of the same + crate—as untrusted. + +## Follow the Proof Workflow + +1. **Frame the claim.** Record the artifact identity, exact scope, valid uses or + executions, Rust and dependency versions, the supported + toolchain/configuration predicate and its controlling sources, mandatory + postconditions, TCB, exclusions, unresolved support-policy conflicts, and + whether design alternatives are requested. +2. **Inventory the surface.** Enumerate every in-scope safe and unsafe API + surface, obligation site, invariant producer/transition/consumer, and + generated or expanded artifact across the supported set. +3. **State every obligation.** Obtain each controlling contract, decompose it + literally, and state the exact proposition and applicability to prove. +4. **Construct the derivation.** Derive every conjunct from checked local facts, + named invariants, applicable authoritative axioms, tool-derived theorems, or + explicit TCB entries. Unfold definitions and seek indirect multi-premise + derivations; absence of one direct sentence is not itself a documentation + gap. Justify every intermediate inference. +5. **Close composition.** Over the entire supported toolchain/configuration + predicate—by one parametric proof or an exhaustive partition as + appropriate—give every literal clause of each applicable controlling + contract and every safe surface a disposition. Ensure every consumed + premise has an admissible source. Try to falsify the contract reading, each + derivation, and coverage, including boundary and adversarial cases derived + from the clauses themselves rather than from any supposedly exhaustive + hazard list, before concluding `PROVED`. +6. **Report exactly.** Keep unresolved obligations visible and state the + smallest missing implication. Record proofs, TCB, coverage, findings, + postcondition failures, documentation gaps, and residual scope without + optimism. + +Do not require a concrete UB counterexample to reject an incomplete proof. A +missing, ambiguous, circular, or inapplicable derivation is sufficient for +`UNPROVED`. + +## Write and Review Proof-Grade Documentation + +Read [proof-obligations.md](references/proof-obligations.md) before authoring or +reviewing an unsafe contract, invariant, `SAFETY` comment, or local proof. + +Keep each proof adjacent to the smallest cohesive unsafe operation or assertion. +State the exact operation and its preconditions, cite checked facts and named +invariants, show the derivation, and prove resulting postconditions and +invariant state on every applicable exit. + +When existing code can be validated only by reconstructing a material +derivation absent from its safety comment, do not accept it silently. Include +the reconstructed derivation—or the smallest missing portion—in the review, +with its citations and applicability. Classify implementation correctness +separately from proof-documentation quality. If changes are authorized, improve +the adjacent proof; otherwise provide proposed wording. Do not use a +reconstructed implementation proof to invent or strengthen a caller-facing +contract retroactively. + +## Close API and Configuration Boundaries + +Read +[api-boundaries-and-evolution.md](references/api-boundaries-and-evolution.md) +for fields, constructors, methods, traits, sealing, macros, public or hidden +APIs, robustness, or contract evolution. + +Apply this mandatory safe-surface checklist: public fields, constructors, safe +methods, safe trait methods, and macro-generated APIs all count as safe API +surfaces. Include language-reachable `#[doc(hidden)]` safe items for soundness +even when excluded from documentation or compatibility promises. + +Treat caller-provided safe code as adversarial within the behaviors permitted +by safe Rust and its types. Seal a trait or make it unsafe when soundness +requires an unenforced implementer behavior. + +Read +[configurations-and-generated-code.md](references/configurations-and-generated-code.md) +for every full audit and whenever supported-toolchain policy, conditional +compilation, targets, generated code, FFI, assembly, SIMD, allocators, linking, +or build tooling is relevant. +Every supported combination of compilation options that can ship downstream +must be sound. Use parametric proofs or exhaustive partitions when literal +enumeration would explode; do not substitute a tested sample. + +## Evaluate Trust and Evidence + +Read [tcb-and-evidence.md](references/tcb-and-evidence.md) for every full audit +and whenever a proof uses dependencies, external specifications, tools, +testing, formal verification, environmental restrictions, or cryptographic or +probabilistic assumptions. + +Judge evidence by the exact proposition it establishes, its artifact and model, +its quantified domain and bounds, its premises, and its residual trust—not by a +label such as testing, static analysis, model checking, or formal verification. + +## Design for Provability When Requested + +Read [abstraction-design.md](references/abstraction-design.md) when the user asks +to design, refactor, or reconsider an unsafe abstraction, or when authoring a +new unsafe abstraction. + +Judge existing code under its current source and controlling contract. Inferred +intent or a preferable model may guide a separate proposal but may not narrow, +reinterpret, or discharge a current obligation. Treat implemented changes as a +new artifact and audit them anew. + +## Use Exact Verdicts + +Read [audit-reporting.md](references/audit-reporting.md) before delivering a +persistent or full audit. + +- **PROVED:** Every obligation for the exact named claim is discharged over its + complete applicability, relative to the stated TCB. +- **UNPROVED:** At least one required derivation, premise, applicability or + coverage argument, postcondition proof, or citation is missing, ambiguous, + circular, or unverifiable. +- **UNSOUND:** A valid use or in-scope execution is proved to reach undefined + behavior. +- **CONTRACT-BROKEN:** It is proved that there exists a valid in-scope + execution which, considered as a whole, contains no undefined behavior and + falsifies a documented postcondition. + +Classify a witness using the execution as a whole, not observations from a +prefix of an execution that later reaches undefined behavior. An +undefined-behavior-containing execution can witness `UNSOUND` but cannot +establish the existential claim required for `CONTRACT-BROKEN`. If it is the +only behavioral evidence, report soundness as `UNSOUND` and the postcondition +as `UNPROVED`. An independent UB-free witness or equivalent existence proof +may establish `CONTRACT-BROKEN`; separate proofs may therefore establish both +verdicts. + +Apply verdicts separately to soundness, documented postconditions, and +conditional application claims. State exact scope, applicability, and TCB +beside every verdict. Never substitute “looks sound,” “probably sound,” or test +success. + +For a persistent audit, complete: + +- [tcb-audit-log-template.md](assets/tcb-audit-log-template.md) +- [unsafe-code-audit-report-template.md](assets/unsafe-code-audit-report-template.md) + +For an inline review, provide the equivalent material compactly. Reuse an +existing canonical project log rather than creating a competing trust model. diff --git a/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/agents/openai.yaml b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/agents/openai.yaml new file mode 100644 index 0000000000..0f0ca3e7b1 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Unsafe Rust Authoring and Audit" + short_description: "Prove, audit, and redesign unsafe Rust" + default_prompt: "Use $unsafe-rust to author, audit, or redesign this unsafe Rust abstraction and its safety contracts." diff --git a/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/assets/tcb-audit-log-template.md b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/assets/tcb-audit-log-template.md new file mode 100644 index 0000000000..7cfad4e866 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/assets/tcb-audit-log-template.md @@ -0,0 +1,101 @@ +# TCB Audit Log: `` + +## Identity + +- **Log ID/revision:** `` +- **Audit/report:** `` +- **Skill revision:** `` +- **Source snapshot:** `` +- **Generated artifacts:** `` +- **Rust/toolchain scope:** `` +- **Supported configuration predicate:** `` +- **Theorem(s) supported:** `` +- **Owner/reviewer:** `` +- **Reviewed at:** `` + +## Trust Policy + +`` + +## Entry Index + +| ID | Category | Exact trusted proposition | Identity/version | Scope/configurations | Contract channel | Consumers | Disposition | Re-audit trigger | +|---|---|---|---|---|---|---|---|---| +| `` | `` | `` | `` | `` | `` | `` | `` | `` | + +## Detailed Entries + +### `` — `` + +- **Category:** `` +- **Disposition:** `` +- **Exact proposition:** `` +- **Quantification and scope:** `` +- **Exact identity:** `` +- **Source/contract:** `` +- **Relevant quotation:** + > `` +- **Contract relationship:** `` +- **Why needed:** `` +- **Why admission is permitted:** `` +- **Consumers:** `` +- **Verification performed:** `` +- **Residual trusted components:** `` +- **Known limitations:** `` +- **Owner/approver:** `` +- **Re-audit trigger:** `` +- **Notes:** `` + +## Dependency Contract Summary + +| Dependency | Safe/unsafe surface | Exact behavior relied upon | Contract relationship | Features/configuration | Implementation audit or TCB entry | Update trigger | +|---|---|---|---|---|---|---| +| `` | `` | `` | `` | `` | `` | `` | + +## Rejected or Unresolved Premises + +| Proposed ID | Proposition | Reason rejected/unproved | Blocked obligations | Required resolution | +|---|---|---|---|---| +| `` | `` | `` | `` | `` | + +## Review Attestation + +- [ ] Every consumed entry has an exact proposition rather than a vague trust + statement. +- [ ] No entry merely assumes an in-scope conclusion or trusts code the declared + audit scope purports to prove. +- [ ] Every identity, version, digest, and configuration scope was checked. +- [ ] Every quotation was opened and verified in context. +- [ ] Selected safe dependencies are distinguished from caller-controlled code. +- [ ] Exact pins are used only to freeze identity; every undocumented + proposition has an audit, additional contract, or explicit admission. +- [ ] Every third-party unsafe implementation is recursively audited or + explicitly admitted. +- [ ] Tool-derived facts state their exact theorem and residual TCB. +- [ ] External, deployment, and probabilistic assumptions qualify the verdict + conspicuously. +- [ ] Every entry has consumers and a re-audit trigger. +- [ ] Every consumed entry supporting `PROVED` is explicitly accepted; pending, + rejected, and superseded entries support no proved claim. +- [ ] Rejected and unresolved premises appear in the audit findings. + +**Reviewer:** `` +**Review result:** `` +**Date:** `` diff --git a/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/assets/unsafe-code-audit-report-template.md b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/assets/unsafe-code-audit-report-template.md new file mode 100644 index 0000000000..387ce2f7a6 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/assets/unsafe-code-audit-report-template.md @@ -0,0 +1,211 @@ +# Unsafe Rust Audit: `` + +## Claims and Verdicts + +- **Soundness claim:** `` +- **Soundness verdict:** `` +- **Documented-postcondition claim:** `` +- **Documented-postcondition verdict:** `` +- **Combined mandatory result:** `` +- **Conditional application claim:** `` +- **Conditional application result:** `` +- **Scope:** `` +- **Supported configuration predicate:** `` +- **Support-policy sources/conflicts:** `` +- **TCB log:** `` +- **Skill revision:** `` +- **Qualification:** `` + +## Audited Snapshot + +- **Repository/source:** `` +- **Uncommitted changes:** `` +- **Generated/expanded artifacts:** `` +- **Rust/compiler/stdlib:** `` +- **Dependencies:** `` +- **Build inputs/tools:** `` +- **Prior audit reused:** `` +- **Auditor/reviewer/date:** `` + +## Contracts in Scope + +### Soundness + +`` + +### Documented Postconditions + +| Contract ID | API/entrypoint | Preconditions | Postconditions | Source/version | +|---|---|---|---|---| +| `` | `` | `

` | `` | `` | + +### Additional Robustness Claims + +| Claim ID | Exact proposition | Scope and authority | Result | Evidence/finding | +|---|---|---|---|---| +| `` | `` | `` | `` | `` | + +`` + +## Boundary and API Coverage + +| Surface ID | Item/generated family | Safe/unsafe | Construction/access path | Configuration scope | Contract/proof status | +|---|---|---|---|---|---| +| `` | `` | `` | `` | `` | `` | + +Confirm coverage of: + +This is a mandatory minimum, not an exhaustive surface list. Apply +[Enumerate every surface](../references/api-boundaries-and-evolution.md#enumerate-every-surface) +and record every additional language-reachable surface in the table above. + +- [ ] safely accessible representation across the owning-module boundary, + including `pub(super)`, `pub(crate)`, ancestor-visible, and generated + access; +- [ ] public fields; +- [ ] constructors; +- [ ] safe methods; +- [ ] safe trait methods and caller-provided implementations; +- [ ] macros and macro-generated APIs; +- [ ] reexports and configuration-specific APIs; +- [ ] language-reachable `#[doc(hidden)]` safe items. +- [ ] associated items, safe free functions/statics, callbacks, FFI entrypoints, + blanket/default/auto-trait behavior, operators, and destruction whenever + language-reachable or semantically relevant. + +## Invariant Inventory + +| Invariant ID | Exact proposition | Owner/boundary | Must hold when | Producers/mutators | Consumers | Status | +|---|---|---|---|---|---|---| +| `` | `` | `` | `` | `` | `` | `` | + +## Obligation Ledger + +| Obligation ID | Source/API | Exact proposition | Required domain | Premises and their applicability | Covered domain/cases | Proof location | Reviewer | Status | +|---|---|---|---|---|---|---|---|---| +| `` | `` | `` | `` | `` | `` | `` | `` | `` | + +## Configuration Closure + +- **Supported set:** `` +- **Discovered axes:** `` +- **Coverage proof:** `` +- **Generated artifacts:** `` +- **Enforced exclusions:** `` +- **Sampled/tested configurations:** `` +- **Uncovered configurations:** `` + +## TCB Summary + +| Category | Entry IDs | Human disposition | Material limitations | +|---|---|---|---| +| `` | `` | `` | `` | + +Full log: `` + +## Tool-Derived Evidence + +| Proof ID | Proposition and entailment | Artifact/tool/model/options | Quantification and bounds | Non-vacuity and semantic fidelity | Trust, stubs, and residual TCB | Result/certificate | Consumers | +|---|---|---|---|---|---|---|---| +| `` | `` | `` | `` | `` | `` | `` | `` | + +## Findings + +### `` — `` + +- **Status/severity:** `` +- **Implementation classification:** `` +- **Proof-artifact classification:** `` +- **Affected claim:** `` +- **Source/API/configuration:** `` +- **Required proposition:** `` +- **Existing proof or behavior:** `` +- **Reconstructed derivation:** `` +- **Proposed proof-artifact repair:** `` +- **Defect:** `` +- **Authority/TCB involved:** `` +- **UB witness:** `` +- **Defined postcondition refutation:** `` +- **Affected producers/consumers:** `` +- **Required resolution:** `` +- **Compatibility impact:** `` +- **Re-audit scope:** `` + +## Abstraction Design (Optional) + +`` + +- **Required behavior and constraints:** `` +- **Current literal result:** `` +- **Recommended candidate:** `` +- **Proof simplification:** `` +- **Behavior delta:** `` +- **Compatibility and migration:** `` +- **Fresh-audit status:** `` + +## Documentation and Skill Gaps + +### Authoritative Rust Documentation + +| Gap ID | Missing/ambiguous proposition | Attempted authoritative sources | Blocked obligations | Suggested upstream report | +|---|---|---|---|---| +| `` | `` | `` | `` | `` | + +### Skill Guidance + +| Gap ID | Omission or ambiguity | Audit impact | Proposed maintainer follow-up | +|---|---|---|---| +| `` | `` | `` | `` | + +## Residual and Excluded Scope + +`` + +## Re-audit Triggers + +- `` +- `` +- `` +- `` +- `` + +## Final Attestation + +- [ ] Every in-scope obligation has a status. +- [ ] Every `PROVED` obligation has a complete checked derivation. +- [ ] Every material derivation reconstructed during review is exposed with its + applicability, and deficient proof artifacts are reported separately. +- [ ] Every supported configuration is covered by proof. +- [ ] Every consumed citation and TCB entry was independently verified. +- [ ] Every consumed TCB entry supporting `PROVED` has an accepted human + disposition. +- [ ] Every mandatory documented postcondition was reviewed in addition to UB + freedom. +- [ ] Residual scope and conditional assumptions are conspicuous. +- [ ] The final verdict does not rely on lack of a counterexample or clean tests. + +**Auditor:** `` +**Independent reviewer (if performed):** `` +**Date:** `` diff --git a/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/abstraction-design.md b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/abstraction-design.md new file mode 100644 index 0000000000..b477d8022d --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/abstraction-design.md @@ -0,0 +1,164 @@ +# Designing Unsafe Abstractions for Provability + +## Contents + +- [Keep verification and design separate](#keep-verification-and-design-separate) +- [Establish design requirements](#establish-design-requirements) +- [Extract the minimum capability](#extract-the-minimum-capability) +- [Generate proof-oriented candidates](#generate-proof-oriented-candidates) +- [Prove and compare candidates](#prove-and-compare-candidates) +- [Report the result](#report-the-result) + +## Keep Verification and Design Separate + +Use this process when the user asks to design or redesign an unsafe abstraction, +or when authoring a new unsafe abstraction. Do not run it automatically during +an immutable acceptance audit unless the user requests design advice. + +Judge existing code under its exact current source and controlling contracts. +Inferred intent, a proposed narrower contract, or an easier-to-prove +representation may not: + +- reinterpret or weaken a current obligation; +- discharge a premise of the current implementation; +- erase or downgrade a current finding; or +- justify accepting the current artifact. + +Keep conclusions about the current artifact logically independent of every +candidate design. A proposal describes a possible future artifact; it has no +`PROVED` verdict. After implementation, identify the new snapshot and apply the +ordinary unsafe-Rust proof workflow anew. + +Preserve at least the scoped current finding that motivates the redesign. Do +not expand that step into a whole-crate audit unless the requested audit scope +requires it. + +For greenfield work, no current-artifact verdict is necessary. State the design +requirements, construct the candidate, and prove the implemented artifact. + +## Establish Design Requirements + +Record the constraints that the abstraction must satisfy: + +- required externally observable behavior and mandatory postconditions; +- current public contracts and compatibility commitments that must remain; +- exact propositions required by relevant consumers; +- supported Rust versions, targets, features, and configurations; +- representation, performance, interoperability, or integration constraints; + and +- which semantic or compatibility changes the user has authorized. + +Use each source only for the proposition it actually establishes. User +requirements can determine desired behavior. Current contracts determine +current obligations. Call sites, tests, names, comments, history, and +implementation structure may suggest intent or establish local source facts, +but an inference about intent is not a Rust semantic premise and does not prove +implementation correctness. + +Known internal consumers do not exhaust the consumers of a public API. Treat +the published contract as a required constraint unless an applicable contract +channel and the user authorize changing it. Surface material ambiguity when +different interpretations would change the public contract, support policy, or +compatibility result. + +## Extract the Minimum Capability + +For each required behavior, state the exact semantic proposition consumers +need. Separate properties that the current abstraction may have bundled, such +as: + +- nominal identity from an operational capability; +- layout from validity, initialization, provenance, alignment, or aliasing; +- metadata from memory projection; +- ownership from access permission; +- one-time establishment from an ongoing invariant; +- safe caller behavior from an unsafe implementer promise; and +- behavior common to many types from one exceptional case. + +Identify where each proposition is established, carried, consumed, and +discharged. Prefer a design in which types, validation, privacy, sealing, +typestate, guards, or other locally checkable mechanisms enforce the fact. + +Do not make a proof easier merely by transferring an unnecessary or hidden +obligation to callers. Every remaining unsafe caller or implementer obligation +must be explicit, sufficient, and justified by a need the implementation cannot +enforce safely. + +## Generate Proof-Oriented Candidates + +Consider the smallest transformations that remove the unsupported premise: + +- eliminate an unnecessary unsafe operation, impl, configuration, or promise; +- validate the required property before the unsafe operation; +- narrow an API or implementation to the cases actually supported; +- reuse a safe or already-proved primitive whose contract matches exactly; +- specialize a one-off case instead of inventing a generic abstraction; +- split independent capabilities or invariant dimensions; +- seal an implementer boundary or move representation behind a smaller module; + or +- introduce a new reusable abstraction only when demonstrated consumers share + the same semantic capability. + +For example, if one contract claims both nominal field reflection and pointer +projection while some consumers require only projection, consider separating +those capabilities rather than inventing a nominal field. This is a design +prompt, not a Rust fact; prove the resulting contracts normally. + +Do not pad the output with cosmetic or strictly dominated alternatives. When +requirements are ambiguous or viable candidates make materially incomparable +tradeoffs, present the consequential choice instead of choosing silently. + +## Prove and Compare Candidates + +For each viable candidate, state: + +- exact safe and unsafe contracts; +- representation and named invariants; +- how every required consumer proposition is supplied; +- where each remaining obligation is enforced; +- authoritative axioms, dependency contracts, and TCB entries required; +- supported applicability domain; +- unresolved proof obligations; and +- behavior, compatibility, migration, and re-audit consequences. + +Construct a conditional proof plan before implementation. After implementation, +prove the exact source rather than the design sketch. + +Reject candidates that fail required behavior, proof closure, supported-domain +coverage, or binding compatibility constraints. Among the remainder, prefer a +candidate that preserves required behavior while reducing one or more of: + +- unsafe surface exposed to callers or implementers; +- strength or number of unsupported premises; +- invariant access region, lifetime, and fan-out; +- TCB size; +- coupling between independent capabilities; +- version- or configuration-specific proof branches; +- accidental representation commitments; and +- genericity without demonstrated reuse. + +Also account for authorized implementation, performance, and migration costs. +Do not collapse incomparable tradeoffs into an invented score, and do not +prefer a small textual diff that silently weakens a relied-upon contract. + +Apply +[Evolve contracts deliberately](api-boundaries-and-evolution.md#evolve-contracts-deliberately) +to every candidate contract change. + +## Report the Result + +Keep these outputs distinct whenever they apply: + +1. **Current artifact:** Exact findings and verdict under the current contract. +2. **Design requirements:** Required behavior, constraints, consumer + propositions, and unresolved intent. +3. **Candidate design:** Exact proposed contracts, invariant model, proof plan, + and remaining premises. +4. **Compatibility and migration:** Behavior gained or lost, affected callers + and implementers, contract channel, and re-audit scope. +5. **Recommendation:** The preferred candidate and any human decision required. +6. **Post-change audit:** A separate result for an implemented new snapshot. + +In review-only work, provide counterfactual advice without modifying source. In +authoring work, update implementation, contracts, local proofs, TCB entries, +and affected downstream proofs together. diff --git a/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/api-boundaries-and-evolution.md b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/api-boundaries-and-evolution.md new file mode 100644 index 0000000000..6df8fb05b9 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/api-boundaries-and-evolution.md @@ -0,0 +1,288 @@ +# API Boundaries, Invariants, and Contract Evolution + +## Contents + +- [Enumerate every surface](#enumerate-every-surface) +- [Place the safety boundary](#place-the-safety-boundary) +- [Use module privacy](#use-module-privacy) +- [Handle unsafe fields](#handle-unsafe-fields) +- [Audit traits and sealing](#audit-traits-and-sealing) +- [Audit macros and hidden APIs](#audit-macros-and-hidden-apis) +- [Distinguish selected dependencies from caller code](#distinguish-selected-dependencies-from-caller-code) +- [Prove documented behavior](#prove-documented-behavior) +- [Evolve contracts deliberately](#evolve-contracts-deliberately) + +## Enumerate Every Surface + +For soundness, enumerate every language-reachable way untrusted safe code can +construct, obtain, observe, mutate, replace, borrow, move, copy, drop, implement, +or invoke the abstraction. + +Apply this checklist explicitly: + +- public fields; +- constructors, including literals, constants, defaults, conversions, + deserialization, builders, and generated constructors; +- safe inherent and extension methods; +- safe trait methods, blanket implementations, default methods, trait objects, + and auto traits; +- public associated types and constants where their choices affect unsafe code; +- safe free functions and statics; +- indexing, dereference, iteration, operators, formatting, cloning, comparison, + hashing, panic, and destruction behavior when implemented; +- exported declarative macros, procedural macros, derives, attributes, and APIs + produced by them; +- reexports and feature- or target-dependent public items; +- callbacks and user-provided implementations invoked internally; +- FFI entrypoints callable without a Rust-side unsafe obligation; +- language-reachable `#[doc(hidden)]` items. + +This is an advisory discovery list, not an exhaustive statement of Rust's +semantics. Inspect the exact source, expansions, metadata, and applicable +authoritative documentation for additional surfaces. + +For each safe surface, prove that every behavior available to well-typed safe +code preserves soundness. For each unsafe surface, prove that its complete +documented contract is sufficient and that its implementation establishes all +documented postconditions for every valid use. + +Determine the controlling contract from the actual published or otherwise +applicable normative text. Examples, rationale, tests, names, existing safety +comments, and inferred design intent may aid discovery but may not narrow or +replace that contract. + +## Place the Safety Boundary + +Mark an operation unsafe when callers or implementers must establish a +soundness-critical proposition that the implementation cannot establish from +enforced types, checked state, module-owned invariants, and deliberately trusted +dependencies. + +Do not expose a safe API with a prose-only safety precondition. Documentation +cannot make a well-typed safe use invalid for the purpose of soundness. + +Conversely, do not move an obligation to callers merely because doing so is +convenient. A safe wrapper may discharge an unsafe callee's requirements with +validation, construction, privacy, typestate, synchronization, or a local proof. + +Treat each unsafe declaration or call as a contract boundary. An unsafe helper +can propagate an obligation through fields and later calls without immediately +performing an operation that exhibits undefined behavior. Follow the obligation +through the dataflow until it is discharged. + +An `unsafe impl` is an assertion that the implementation satisfies the unsafe +trait's contract. Prove that assertion and every method-level obligation. + +For FFI declarations, distinguish the declaration-time assertion that the +foreign contract is correct from each call's preconditions and from the foreign +implementation's behavior. Record external ABI and implementation trust +explicitly. + +## Use Module Privacy + +For new invariant-bearing representations: + +1. Put the representation and all safely accessible fields in the smallest + practical leaf module. +2. Keep those fields private to that module. +3. Make all code outside the module—including parents, siblings, cousins, and + the rest of the same crate—use checked safe APIs or documented unsafe APIs. +4. Treat each operation inside the module that can affect the invariant as a + proof site. + +Do not use `pub(super)`, `pub(crate)`, or another broad safe visibility merely +because current same-crate code is trusted socially. Such visibility expands +the region in which safe edits can silently violate the invariant and makes +human review materially harder. + +Existing crates need not be rejected solely for violating this authoring +discipline. Compute and audit the actual Rust visibility region, including +fields in ancestors or descendants that the code can access and all code that +can access the representation. Report broad safe visibility as proof-surface +debt. + +Represent every distant fact by a named invariant or contract that each producer +preserves and each consumer can use locally. + +## Handle Unsafe Fields + +When the exact audited Rust version supplies compiler-enforced unsafe fields, a +properly declared unsafe field is an explicit unsafe API boundary. It may have +any intentional visibility, analogously to an unsafe function, because untrusted +safe code cannot perform the gated uses without accepting its documented +obligations. + +Require field documentation to make the obligations for all applicable +operations derivable, including: + +- initialization and replacement; +- reads, copies, and moves; +- shared and mutable borrows; +- pattern matching, destructuring, aggregate update, and whole-value operations; +- writes through direct access or an escaped capability; +- transfer or suspension of the enclosing invariant; +- the state required before control returns to untrusted safe code. + +Audit the exact compiler version's enforcement rather than assuming a proposed +or future design. Separately prove every implicit safe action not gated by field +projection, especially destruction and compiler- or derive-supplied trait +behavior. An unsafe modifier does not relax the language validity invariant of +the field's Rust type and does not make arbitrary drop glue conditional. + +When authoritative Reference or standard-library text does not specify the +feature sufficiently, record the exact semantics relied upon as a documentation +gap and explicit TCB premise. An RFC or current implementation may explain the +intent but is not a Rust axiom under this skill's authority policy. + +## Audit Traits and Sealing + +Treat every safe trait implementation supplied by a caller as adversarial safe +code. Unsafe code may rely only on facts enforced by Rust's types and semantics, +module-owned state, or explicit TCB entries—not on a caller faithfully +implementing behavioral prose. + +If unsafe code requires an implementer to uphold a soundness-critical +obligation, use one of these structures: + +- make the trait unsafe and document the complete implementer contract; +- seal the trait so only deliberately controlled implementations are possible; +- validate the needed property before unsafe use; +- redesign the representation or boundary so the property follows locally. + +Prove that sealing is effective under Rust privacy and name resolution for every +supported configuration and macro expansion. A documentation claim, +`#[doc(hidden)]`, obscure path, or conventional “sealed” name does not by itself +prevent downstream implementations. + +For an unsafe trait: + +- state representation and behavioral obligations at the trait and method + levels; +- prove every in-scope `unsafe impl`; +- ensure safe methods remain sound for every valid implementation; +- ensure generic unsafe consumers rely on no stronger fact than the contract; +- audit associated types, constants, default methods, specialization, trait + objects, auto traits, negative impls, and generated impls when applicable. + +For a sealed safe trait, selected implementations may be audited as controlled +code, but downstream safe callers remain adversarial. Recheck sealing whenever +visibility, reexports, macros, or configuration changes. + +## Audit Macros and Hidden APIs + +Classify a macro invocation by the obligations rustc actually enforces for the +expanded use, not merely by the absence or presence of `unsafe` in the invocation +tokens. A macro can be constructed so that expansion succeeds only in an unsafe +context. If no caller-side unsafe obligation is compiler-enforced, treat the +macro as a safe API and prove every accepted safe invocation sound. + +Auditing only handwritten macro or proc-macro source is insufficient when sound +output depends on: + +- caller tokens, types, paths, hygiene, spans, or name resolution; +- `cfg`, features, target facts, environment, or build-script data; +- generated identifiers, item visibility, attributes, or impl selection; +- compiler expansion order or version; +- downstream code into which the macro expands. + +Inspect expansions to discover API and caller obligations. Then apply +[Audit generated and expanded code](configurations-and-generated-code.md#audit-generated-and-expanded-code) +to prove closure over every supported accepted input, output, and +configuration. Include generated public APIs in the same safe/unsafe surface +audit as handwritten items. + +Treat `#[doc(hidden)]` as a documentation and compatibility signal only to the +extent promised by the project. It does not create Rust privacy. A +language-reachable safe hidden item must remain sound for direct safe use and +may not hide a safety precondition. The project may separately exclude its +behavior or continued existence from SemVer promises. + +## Distinguish Selected Dependencies From Caller Code + +A deliberately selected dependency is code whose use and version the project +author intentionally chose. A function argument, callback, generic parameter, +trait object, plugin, implementation of a safe trait, or downstream macro input +is caller-controlled even when its type originates in a selected dependency. + +Apply the selected-safe-dependency exception only to the deliberately chosen +implementation and documented API behavior, never to behavior chosen by the +caller. Determine whether reexports, dependency-defined traits, feature +unification, or plugins move a surface across that boundary. + +For exact identity, contract channels, safe versus unsafe dependency trust, and +update triggers, apply +[Record dependency contracts](tcb-and-evidence.md#record-dependency-contracts). + +## Prove Documented Behavior + +Soundness is the minimum universal property. The mandatory postcondition scope +includes every documented postcondition of an unsafe API in scope and every +guarantee consumed by an in-scope soundness proof. Prove broader safe-API +behavior only when the user or audit explicitly places it in scope. + +At minimum, an unsafe API implementation is responsible for both: + +1. avoiding undefined behavior for every valid use; and +2. establishing every documented postcondition when its safety preconditions + and other documented conditions are met. + +Evaluate postconditions independently under the verdict rule in `SKILL.md`. A +UB-containing execution cannot itself prove the required UB-free behavioral +refutation: report soundness as `UNSOUND` and leave that postcondition +`UNPROVED` unless an independent UB-free witness or equivalent proof resolves +it. Determine whether a proved broken guarantee can make downstream unsafe +consumers unsound. + +Do not invent a universal standard for undocumented robustness. State the exact +behavioral claim being reviewed: panic freedom, determinism, resource bounds, +constant time, atomicity, rollback, leak freedom, progress, or another property. +Record its authority and scope separately from Rust soundness. + +## Evolve Contracts Deliberately + +Treat safety documentation and documented postconditions as compatibility +contracts, not comments that can be edited independently of code. + +Analyze every change by provider and consumer: + +- Strengthening a caller precondition invalidates previously valid calls. +- Weakening a caller precondition admits more calls and increases the + implementation's proof burden. +- Weakening a provider postcondition invalidates existing caller reasoning. +- Strengthening a provider postcondition increases what callers may rely upon. +- Strengthening an unsafe trait implementer's obligation can invalidate existing + impls. +- Strengthening guarantees required from trait implementations can likewise + invalidate existing impls even when it benefits trait consumers. +- Weakening guarantees supplied through a trait can invalidate generic + consumers. + +Under a conventional SemVer contract, invalidating existing valid callers, +implementers, or documented reasoning is normally breaking even when Rust type +signatures do not change. Determine and record the actual project's +compatibility policy rather than treating SemVer folklore as an axiom. + +An exact pin freezes identity but does not authorize an undocumented semantic +claim. A fork, out-of-band agreement, or consumer-specific promise may supply an +additional contract for its exact recorded scope; otherwise audit or explicitly +admit the implementation proposition. Update the TCB and repeat affected proofs +before changing any identity, contract, or agreement. + +When the supported Rust range changes, apply +[Qualify applicability](proof-obligations.md#qualify-applicability), update any +compatibility premises in the TCB, and re-audit every proof whose documentation, +edition, target, feature, configuration, or implementation claim may differ. + +For every contract change, search callers, implementers, safety comments, TCB +entries, generated output, and downstream-facing documentation for proofs that +consume the changed proposition. + +Changing safety prose does not retroactively narrow valid uses of an already +published version. If that version's implementation failed its published +contract, it had a soundness or contract defect. Treat the correction as +remediation requiring compatibility analysis, affected-version disclosure, and +review of downstream proofs—not as proof that the old implementation was sound. + +When redesign is authorized, apply +[Designing Unsafe Abstractions for Provability](abstraction-design.md) without +letting the proposed contract alter the verdict for the current artifact. diff --git a/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/audit-reporting.md b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/audit-reporting.md new file mode 100644 index 0000000000..69ff28f2d8 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/audit-reporting.md @@ -0,0 +1,207 @@ +# Audit Execution and Reporting + +## Contents + +- [Freeze the audit claim](#freeze-the-audit-claim) +- [Maintain an obligation ledger](#maintain-an-obligation-ledger) +- [Aggregate verdicts](#aggregate-verdicts) +- [Write actionable findings](#write-actionable-findings) +- [Deliver a complete report](#deliver-a-complete-report) +- [Preserve and update the audit](#preserve-and-update-the-audit) + +## Freeze the Audit Claim + +Before reviewing proofs, record: + +- exact repository, source revision/digest, workspace packages, generated + artifacts, and relevant uncommitted changes; +- supported toolchain/configuration predicate, its controlling policy sources, + conflicts or gaps, audit cutoff, authorized resolution or conservative audit + domain, and enforced exclusions; +- dependency resolution and relevant source identities; +- API, module, binary, or whole-project scope; +- soundness theorem and documented postconditions in scope; +- TCB log identity/revision; +- prior audit results being reused; +- known inaccessible, unsupported, or intentionally excluded regions. + +Do not issue a whole-crate verdict for a diff, one feature, one target, or one +unsafe block. State the narrow result actually established. + +If the task is review-only, report findings and proposed remedies without +silently changing code. If the task includes authoring or fixing, update the +proof artifacts and contracts together with the implementation. + +## Maintain an Obligation Ledger + +Track every in-scope obligation sufficiently to detect omissions. The ledger may +be a table, issue list, annotated source, or another reviewable form. Ensure it +provides complete location-by-location coverage of producers, transitions, +consumers, and proof sites. + +For each obligation, record: + +- stable identifier and source location/API; +- exact proposition to prove; +- operation, contract, invariant, or postcondition that requires it; +- required applicability domain; +- supporting local facts, invariant clauses, axioms, and TCB entries, with the + applicability of each premise; +- domain actually covered by the derivation and any case partition; +- proof location; +- reviewer verification; +- status and finding link. + +Include obligations created by: + +- unsafe operations and unsafe API calls; +- unsafe functions, traits, impls, fields, attributes, declarations, macros, and + generated code as applicable; +- construction, mutation, suspension, consumption, and destruction of + invariant-bearing state; +- safe APIs backed by unsafe code; +- every documented postcondition of each in-scope unsafe API, and every + documented guarantee consumed by later unsafe code; +- FFI, assembly, allocators, concurrency, target/configuration selection, and + external contracts; +- generated public APIs and code shipped downstream. + +This is a discovery aid, not an exhaustive semantic taxonomy. Add whatever the +actual code and authoritative contracts require. + +The ledger complements rather than replaces the proof workflow in +[proof-obligations.md](proof-obligations.md). Review surrounding safe code and +follow changed propositions to every consumer; compiler-marked unsafe locations +and textual diffs are only discovery starting points. + +## Aggregate Verdicts + +Use the verdict definitions in `SKILL.md` for individual obligations and the +final in-scope claim. + +Report multiple statuses when applicable. For example, soundness can be +`PROVED` while documented postconditions are `CONTRACT-BROKEN`, or one path can +be `UNSOUND` while a different configuration remains `UNPROVED`. Issue `PROVED` +for the combined default claim only when every in-scope soundness and +documented-postcondition obligation is proved. + +Classify a witness using the execution as a whole. A valid execution that ever +exhibits UB can establish `UNSOUND` but cannot itself establish the UB-free +existence claim required for `CONTRACT-BROKEN`. If it is the only behavioral +evidence, report that postcondition as `UNPROVED`. An independent UB-free +witness or equivalent existence proof may establish `CONTRACT-BROKEN`; +separate proofs may establish both verdicts. + +Place qualifications in the theorem, not in vague prose. Use: + +> PROVED for `` under ``, relative to TCB +> ``. + +For a deployment, external, or cryptographic premise, name the exact entry and +state whether the result is a conditional source, binary, or application claim. + +Never use “looks sound,” “no issues found,” “probably safe,” “Miri-clean,” +“battle-tested,” or “tests pass” as a verdict. + +## Write Actionable Findings + +Each finding should contain: + +- severity/status and affected theorem; +- exact source/API/configuration; +- required proposition; +- existing claimed proof; +- any material derivation the reviewer had to reconstruct, with citations and + applicability, or the smallest portion still missing; +- proposed replacement proof text when the reviewed artifact omits that + derivation; +- smallest missing, false, circular, or unsupported implication; +- authoritative contract or TCB entry involved; +- whether a valid UB witness or a separate UB-free postcondition refutation or + equivalent existence proof is known; +- affected callers, producers, consumers, generated output, and configurations; +- minimal acceptable resolution; +- compatibility and re-audit consequences. + +Distinguish: + +- an implementation defect; +- insufficient or ambiguous safety documentation; +- a correct implementation with an invalid local comment; +- an undocumented TCB assumption; +- an authoritative Reference/std documentation gap; +- a skill-guidance gap; +- a compatibility/robustness defect without established UB. + +A successfully reconstructed implementation proof does not erase deficient +safety documentation. Report the implementation obligation and the proof +artifact separately, and offer corrected proof text. Reconstruction may not add +a hidden caller or implementer obligation or create a provider guarantee absent +from the controlling contract. + +Keep every verdict for the current artifact independent of design alternatives. +If redesign was requested, report proposals and their conditional proof plans +separately; audit an implemented redesign as a new snapshot. + +If authoritative documentation is insufficient, quote the exact missing +proposition and suggest a narrowly scoped upstream report. If this skill failed +to route the reviewer to a necessary check, identify a proposed skill issue +without treating the proposed rule as current authority. + +## Deliver a Complete Report + +A complete audit report contains: + +1. **Claim and verdict:** Exact theorem, status, scope, supported configuration + predicate, and TCB identity. +2. **Snapshot:** Source, generated artifacts, Rust/toolchain, dependency + resolution, and relevant build inputs. +3. **Boundary and API coverage:** Safe and unsafe surfaces crossing the owning + module or external API boundary, including restricted-visible fields, + constructors, safe methods, safe trait methods, macro-generated APIs, and + language-reachable hidden items. +4. **Invariant inventory:** Index of named local contracts, owners, permitted + transitions, and consumers—not an informal global proof. +5. **Obligation coverage:** Proof sites and status summary; link to detailed + proofs/findings rather than duplicating them. Include material reconstructed + proofs missing from the reviewed proof artifacts. +6. **Configuration closure:** Supported-set definition, controlling policy + sources and conflicts, audit cutoff, authorized resolution or conservative + superset, axes, abstract or enumerative coverage proof, generated artifacts, + and enforced exclusions. +7. **TCB audit log:** Every authoritative or admitted proposition and reviewer + disposition. +8. **Tool-derived evidence:** Exact theorem, artifact/model scope, bounds, + result, non-vacuity check, and residual TCB. +9. **Postcondition/robustness scope:** Documented guarantees proved and any + separately requested properties. +10. **Findings:** `UNPROVED`, `UNSOUND`, `CONTRACT-BROKEN`, documentation gaps, + compatibility defects, and maintenance risks. +11. **Residual scope:** Anything not audited, inaccessible, unsupported, or + conditional. +12. **Review triggers:** Changes that invalidate or require revisiting the + result. + +Use the bundled report and TCB templates for persistent artifacts. For an inline +review, provide the same information compactly. + +## Preserve and Update the Audit + +When a canonical audit or TCB log exists: + +- reuse its identifiers and format; +- verify rather than blindly inherit prior `PROVED` entries; +- update changed source, contracts, configurations, dependencies, and trust; +- retain historical identity through version control rather than duplicating a + stale snapshot; +- record the skill revision used for the audit; +- link proofs and findings to exact source revisions. + +Trigger review when code or documentation changes any consumed proposition, +when supported compilation options expand, when generators or generated output +change, when dependencies or contract channels change, when authoritative Rust +documentation changes materially, or when a new incident reveals an omitted +class of obligation. + +A prior successful audit is evidence about its exact snapshot and theorem, not a +permanent certification of later code. diff --git a/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/configurations-and-generated-code.md b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/configurations-and-generated-code.md new file mode 100644 index 0000000000..f436c53db5 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/configurations-and-generated-code.md @@ -0,0 +1,302 @@ +# Configuration Closure and Generated Unsafe Code + +## Contents + +- [Define the supported set](#define-the-supported-set) +- [Discover configuration axes](#discover-configuration-axes) +- [Prove every supported combination](#prove-every-supported-combination) +- [Audit generated and expanded code](#audit-generated-and-expanded-code) +- [Audit targets, SIMD, and concurrency](#audit-targets-simd-and-concurrency) +- [Audit allocators, panic modes, and assertions](#audit-allocators-panic-modes-and-assertions) +- [Audit FFI, assembly, linking, and global symbols](#audit-ffi-assembly-linking-and-global-symbols) +- [Record configuration coverage](#record-configuration-coverage) + +## Define the Supported Set + +Write a precise predicate `Supported(configuration)` before claiming full +soundness. Fix the exact source or packaged artifact and audit cutoff. Let the +predicate range over every relevant toolchain component, host/target fact, and +build option rather than reducing it to a `rustc` version string. + +Classify support evidence before using it: + +- applicable package metadata, published policy, release documentation, + feature/target policy, and authorized downstream agreements may define the + project's support contract; +- manifest checks, build scripts, `compile_error!`, wrappers, packaging rules, + and distribution controls may admit or enforce configurations; and +- CI jobs, lockfiles, successful builds, `rust-toolchain.toml`, and maintainer + defaults observe or select particular configurations but do not by themselves + define or prove downstream support. + +Resolve inherited fields in the exact workspace and inspect the effective +packaged metadata when it can differ. Interpret every mechanism through its +applicable contract; do not hard-code a universal precedence among metadata, +documentation, and agreements. A documented exclusion may delimit a support +promise, but if soundness depends on preventing that configuration from +shipping, require effective rejection before claiming closure. + +If applicable support declarations conflict or materially underdetermine the +predicate, do not silently select the narrowest interpretation. Obtain an +authorized project decision, prove an explicit conservative superset covering +every materially supported candidate predicate identified from the controlling +sources, or report regional results and leave the full claim `UNPROVED`. Do not +call that conservative audit domain a newly inferred project promise. If a +shippable configuration is exposed and no applicable contract clearly excludes +it, include it in the unresolved conservative candidate domain until project +authority resolves its status; successful compilation alone still does not +define the support promise. + +Preserve conditional and nonlinear structure across every discovered axis +rather than collapsing the predicate to a single MSRV. It may be finite, +nonlinear, or moving and need not have a globally earliest toolchain. Resolve +dynamic policies at the audit cutoff; cover later members only through an +applicable parametric theorem or re-audit trigger. + +Record: + +- source revision and workspace/package selection; +- Rust toolchain range, edition, standard-library identity, and relevant compiler + flags; +- controlling support-policy sources, conflicts, authorized resolutions, and + the audit cutoff; +- target triples, target specifications, CPUs, features, ABIs, data layouts, and + linkers; +- Cargo features, dependency feature unification, optional dependencies, and + resolver behavior; +- profiles and code-affecting environment or build inputs; +- generated artifacts and their generators; +- explicit exclusions and how compilation or distribution enforces them. + +An exclusion written only in an audit report does not constrain downstream +users. If soundness requires rejecting a combination, enforce and document the +rejection in the build or API. + +## Discover Configuration Axes + +Search both handwritten and generated source for all code-selection and +semantic axes. At minimum, investigate when applicable: + +- `cfg` and `cfg_attr`, Cargo features, optional dependencies, and feature + unification; +- target architecture, OS, environment, vendor, family, ABI, endianness, pointer + width, alignment, atomic widths, and target capabilities; +- conditional type definitions, representation/layout attributes, constants, + const evaluation, static initialization, and build-time execution; +- compile-time and runtime SIMD or other target features; +- debug assertions, overflow checks, optimization, LTO, codegen backend, panic + strategy, unwinding, sanitizers, and instrumentation; +- global and per-operation allocator choices, allocation failure behavior, and + custom allocator implementations; +- thread availability, atomics, permitted interleavings, weak memory behavior, + signals, cancellation, and runtime/executor choices; +- build scripts, procedural and declarative macros, derives, code generators, + bindgen output, included files, environment variables, and external tools; +- FFI implementation, ABI, library version, symbol resolution, static/dynamic + linking, linker scripts, link arguments, dynamic loading or plugins, and + load-time substitution; +- inline assembly dialect, registers, options, calling convention, instruction + availability, and surrounding compiler assumptions; +- compiler version, edition, unstable features, bootstrap flags, custom target + specifications, and standard-library build; +- tests/examples/binaries versus library code, `no_std`, host versus target + builds, and build-dependency versus runtime-dependency configurations. + +This list is intentionally advisory and may be incomplete or become outdated. +Discover the actual axes from the audited project and authoritative toolchain +contracts. Add newly discovered axes to the audit and report gaps in this +reference. + +## Prove Every Supported Combination + +Every supported combination must be sound. A CI matrix, sample of targets, or +pairwise feature test does not establish this universal claim. + +Avoid Cartesian-product enumeration when an abstract proof is clearer. Valid +coverage arguments include: + +- prove one implementation is parametric over an axis; +- partition configurations into equivalence classes and prove the partition is + exhaustive and each class representative shares the relevant semantics; +- prove mutually exclusive `cfg` predicates form a total partition over the + supported set; +- prove a generator emits only members of a finite audited family; +- prove independent lemmas for axes, then prove their assumptions remain + independent under composition; +- prove unsupported combinations fail before producing a shippable artifact. + +For every abstraction, check interactions between axes. A proof of each feature +alone does not prove their combination; target facts can change layout, atomic +availability, calling convention, or macro expansion on which another feature +depends. + +Attach a configuration-domain predicate to every obligation, premise, and +coverage lemma. A premise proved for one target, toolchain, feature set, or +generated artifact cannot discharge another case merely because the source +looks similar. If separate lemmas cover separate regions, prove that their +union contains the entire supported set and that their assumptions remain true +where regions interact. + +Do not infer semantic coverage from successful compilation. Compilation may +establish syntax, typing, and selected compiler-enforced conditions; unsafe +contracts remain separate obligations. + +## Audit Generated and Expanded Code + +Treat generated code as shipped source. Capture enough information to reproduce +or identify: + +- generator/proc-macro/build-script package and exact version or digest; +- host toolchain and host configuration; +- target configuration and all relevant environment inputs; +- input tokens/files/schema and invocation options; +- output source, expansion, metadata, or object identity; +- diagnostics, suppressed checks, and unsupported paths. + +Do not stop at the generator's handwritten source. Soundness can depend on the +mapping from every accepted input and configuration to output, hygiene and name +resolution in the destination crate, compiler expansion behavior, or external +tool output. + +Use one of these proof strategies: + +1. Inspect each member of a proven finite output set. +2. Prove a property of the generator that entails safety for every supported + output. +3. Record the exact generated artifact in the audited snapshot and enforce that + exact output identity or digest. Pinning only the generator does not fix its + inputs, environment, compiler interaction, or output. + +For macro-generated APIs, audit the expanded visibility and caller obligations. +A safe-looking invocation is not automatically a safe API if rustc enforces an +unsafe-context obligation in the expansion; conversely, generated internal +unsafe code behind an invocation usable from safe context must be sound for +every accepted safe invocation. + +Build scripts may emit `cfg` values, link directives, environment values, or +generated source. Include both their output and every supported path that can +produce different output. Include proc-macro and build dependencies in the TCB +or recursive audit as appropriate. + +## Audit Targets, SIMD, and Concurrency + +For target-dependent code: + +- derive layout, validity, alignment, ABI, instruction, atomic, and pointer-width + facts from exact applicable authoritative contracts or TCB entries; +- distinguish compile-time target features from runtime CPU availability; +- prove every call edge satisfies target-feature and calling-convention + requirements; +- prove runtime feature detection dominates every specialized instruction path + and cannot be invalidated between detection and use; +- audit fallback paths and combinations of enabled features; +- include cross-language or dynamic-dispatch edges that may bypass a Rust + wrapper. + +For concurrency: + +- quantify over every permitted interleaving and memory-model behavior within + scope; +- prove synchronization, atomic ordering, initialization publication, lifetime, + ownership, and destruction properties from applicable contracts; +- treat caller-provided safe callbacks and safe trait implementations as + adversarial, including reentrancy, blocking, panic, and unexpected timing; +- distinguish thread-safety properties promised by types and unsafe trait impls + from behavior merely observed on one runtime. + +Do not use one scheduler run, stress test, or architecture as proof of all +executions. + +## Audit Allocators, Panic Modes, and Assertions + +For allocation-sensitive unsafe code, identify the allocator contract actually +required: + +- size and alignment accepted; +- allocation, reallocation, and deallocation pairing; +- zero-size behavior; +- maximum sizes and arithmetic bounds; +- allocation failure, overcommit, and address reuse; +- thread safety and reentrancy; +- allocator identity across FFI, dynamic-library, and configuration boundaries. + +A library generic over a valid allocator implementation must be sound for every +implementation satisfying the applicable unsafe allocator contract. A binary +that selects a particular allocator may record that exact implementation as a +TCB dependency when appropriate. + +Prove invariant restoration and resource behavior under every supported panic +strategy. Distinguish: + +- normal return; +- error return; +- panic with unwind; +- panic with abort; +- foreign exceptions or unwinding across boundaries; +- cancellation or destruction suppression where supported. + +Never rely on `debug_assert!` to establish a release-build safety precondition. +If a check is part of the proof, ensure it executes in every supported +configuration or prove the proposition independently. Treat differences in +overflow checks, debug assertions, and optimization as configuration branches +until shown irrelevant. + +## Audit FFI, Assembly, Linking, and Global Symbols + +For FFI, prove or explicitly trust: + +- exact function and data ABI, types, layout, calling convention, and symbol + identity on both sides; +- validity and ownership of arguments and return values; +- lifetime, aliasing, allocation, deallocation, callback, thread, and unwinding + rules; +- versioning and configuration of the foreign implementation; +- behavior of foreign code that Rust unsafe code relies upon. + +Declaring an extern item asserts that the declaration matches reality; calling +it consumes both the declaration contract and call-specific preconditions. Keep +those obligations distinct. + +For inline assembly, derive Rust-side requirements from exact applicable +Reference or standard-library text. Record ISA manuals, target specifications, +ABI documents, linker manuals, and other non-Rust sources as versioned +`EXTERNAL-SPEC` TCB entries unless the exact consumed proposition appears in +Reference or standard-library text. Audit operands, register classes, clobbers, +flags, stack, control flow, memory effects, options, instruction availability, +privilege/environment, and interaction with compiler optimization. This is a +discovery list, not an authoritative specification. + +Audit whole-program/link obligations when relevant, including: + +- uniqueness and type/ABI agreement of exported or unmangled symbols; +- global allocator and panic-runtime selection; +- link-section placement, alignment, initialization order, and linker-script + assumptions; +- dynamic symbol interposition and library substitution; +- consistency of declarations across crates and languages; +- linker flags or custom target settings that alter assumptions used by source + proofs. + +A compilation or linker option that still emits a binary belongs to the +supported configuration set if users may ship it. Do not label a flag itself +“Rust undefined behavior” without authoritative text. Instead, trace any +resulting execution to the exact violated Rust or external contract, or state +that the artifact lies outside the proved source-level claim. + +## Record Configuration Coverage + +For each audit, report: + +- the formal or operational definition of the supported set; +- every discovered axis and its possible supported values/classes; +- the proof method establishing coverage; +- the obligation and premise applicability domains used by that proof; +- generated artifact identities or generator theorem; +- excluded combinations and their enforcement; +- untested but abstractly proved combinations; +- tested combinations and the limited propositions those tests establish; +- remaining assumptions, unknowns, and unsupported tool features; +- triggers requiring re-audit. + +Mark the audit `UNPROVED` if a shippable supported combination is neither +individually audited nor covered by a valid universal argument. diff --git a/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/proof-obligations.md b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/proof-obligations.md new file mode 100644 index 0000000000..8880ec3ca9 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/proof-obligations.md @@ -0,0 +1,409 @@ +# Proof Obligations, Safety Contracts, and Local Proofs + +## Contents + +- [Form the theorem](#form-the-theorem) +- [Qualify applicability](#qualify-applicability) +- [Separate kinds of premises](#separate-kinds-of-premises) +- [Write safety documentation](#write-safety-documentation) +- [Write local safety proofs](#write-local-safety-proofs) +- [Carry invariants locally](#carry-invariants-locally) +- [Prove temporal behavior](#prove-temporal-behavior) +- [Cite authoritative axioms](#cite-authoritative-axioms) +- [Search for indirect derivations](#search-for-indirect-derivations) +- [Review a proof](#review-a-proof) + +## Form the Theorem + +Turn each soundness claim into explicit propositions before writing prose. + +For an unsafe API, use this shape: + +> For every state and input satisfying preconditions `P`, every permitted +> execution of the implementation is free of undefined behavior and establishes +> documented postconditions `Q`, relative to TCB `T`. + +Include ongoing and terminal obligations in `P`; a precondition need not concern +only the instant of the call. State who must maintain each fact, over what +interval, and what event discharges it. + +For a safe API, prove soundness with no caller-side safety precondition beyond +well-typed safe use. Also prove any postcondition consumed by the soundness +argument and any broader behavior explicitly placed in scope. Ordinary input +validation may reject values, return an error, or panic as documented, but +soundness may not depend on the safe caller honoring an unenforced rule. + +For each local proof site: + +1. Obtain the exact preconditions of the operation or contract being used. +2. Normalize conjunctions, implications, quantifiers, lifetimes, and temporal + clauses into separately reviewable obligations. +3. Derive every obligation from facts available at that point. +4. Obtain and prove every postcondition used later. +5. Establish the invariant state after success and every alternative exit. + +Treat every operation, declaration, implementation, or state transition that +supplies or consumes a safety contract as an obligation site. Follow each +obligation until it reaches checked local facts, named invariants, +authoritative axioms, or explicit TCB entries. + +Undefined behavior is a property of an entire execution. If any event in an +execution exhibits undefined behavior, make no claim that observations +elsewhere—or notionally “before” that event—remain guaranteed. An unexecuted bad +path does not by itself make a different execution undefined, but soundness of a +safe API still quantifies over every valid use and execution, so one reachable +valid counterexample refutes it. Such a UB-containing execution cannot also +establish a defined behavioral observation; apply the exact witness rule in +`SKILL.md`. + +## Qualify Applicability + +State or inherit the exact domain of every claim and premise. Include whichever +dimensions can change the proposition, such as: + +- source and generated-artifact identity; +- inputs, states, types, signatures, lifetimes, and execution intervals; +- Rust, compiler, standard-library, dependency, and external-contract versions; +- targets, features, profiles, build inputs, and other supported + configurations; and +- deployment or probabilistic restrictions for separately qualified claims. + +A derivation proves only the cases in which every consumed premise applies. If +one proof does not cover the full required domain, partition the claim into +cases, prove each case, and establish that their union is exhaustive. Do not +turn an uncovered case into an implicit exclusion. + +Avoid repetitive local boilerplate. A proof may inherit applicability from an +exactly identified project support policy, invariant definition, axiom entry, +or TCB entry. The local proof must still make the inheritance and relevant case +clear enough to review. + +Derive the required predicate under +[Define the supported set](configurations-and-generated-code.md#define-the-supported-set), +then carry it through every premise and case lemma below. + +A documented Rust guarantee from version `R` may support a later stable version +only when an exact Rust backwards-compatibility commitment preserves that exact +semantic proposition throughout the later version's relevant edition, target, +feature, and configuration domain. Under this skill's authority policy, record +a compatibility commitment outside the Rust Reference or standard-library +documentation as an explicit TCB premise. An API's stability or `since` badge +establishes only what its applicable authoritative text says it establishes; it +does not by itself prove that every behavioral sentence in current +documentation was guaranteed from that version. Do not extend a guarantee +beyond its original domain or automatically to unstable features, +`RUSTC_BOOTSTRAP`, `-Z` behavior, implementation details, custom targets, +platform availability, or pre-stabilization behavior. + +Compatibility does not propagate guarantees backward. Text first documented in +version `R` does not by itself prove the same proposition for earlier versions. +A later clarification can support an earlier version only when applicable +authoritative text expressly gives it historical scope or an accepted TCB +premise establishes that the guarantee already applied. Unchanged +implementation, version history, a documentation diff, or advisory prose is +insufficient by itself. Split the version domain if later text qualifies or +contradicts the older statement. For an open-ended toolchain range, either +prove the claim parametrically relative to a named compatibility premise or +state an audit cutoff and later-release re-audit trigger. A compatibility +premise about abstract semantics does not prove correctness of future compiler +binaries. + +## Separate Kinds of Premises + +Classify every premise: + +- **Local fact:** Established by inspected code, control/data flow, a type, or a + named invariant. Cite the exact check, branch, assignment, ownership fact, or + invariant clause. +- **Rust axiom:** Entailed by exact applicable text in a versioned Rust Reference + or standard-library page. Quote and link it. +- **Selected safe-dependency fact:** Supplied by a deliberately selected safe + dependency contract and recorded in the TCB. +- **Tool-derived fact:** Established by a verified tool theorem whose exact + proposition, model, scope, and premises entail the local fact. Record only its + residual unproved tool/model/translation premises in the TCB. +- **Additional assumption:** External specification, unsafe dependency, + compiler implementation, platform behavior, deployment restriction, + probabilistic premise, or other admitted proposition recorded in the TCB. + +Never blur an assumption into a derived fact. If a premise does not fit one of +these classes, the proof is incomplete. + +Distinguish the validity of a value of type `T` from a stronger library +invariant attached to its role in an abstraction. Prove both when needed. + +Never promote one producer's admission contract into an invariant of its output +type. A constructor, conversion, deserializer, FFI ingress, mutation, or other +producer precondition applies at that invocation. It supports a fact about that +particular result only through a proved postcondition or dataflow relation; it +does not prove that every valid value came through that producer or that later +transitions preserve the property. + +To rely on `I` as an invariant of every value in a stated set, provide a +complete derivation over that set without reversing the producer implication. +Such a derivation may, for example, use: + +1. applicable authoritative premises that entail `I` for every value in the + set; +2. an enforced abstraction boundary plus a complete proof that every in-scope + ingress and producer establishes `I` and every transition preserves it; +3. another applicable derivation, including a verified tool theorem, that + entails the exact quantified proposition; or +4. the exact universal proposition as an admissible accepted TCB premise under + the TCB rules. + +This enumeration does not replace the entailment requirement or exclude other +valid proof forms. A consumer may instead establish `I` for its particular +values or quantified subset from local checks, proved producer and transition +history, and other applicable premises. If neither derivation closes, leave +the consuming obligation `UNPROVED`. + +Likewise, distinguish: + +- permission to perform an operation; +- facts established by that operation; +- facts merely preserved by it; +- obligations transferred to a returned pointer, reference, guard, token, or + caller. + +## Write Safety Documentation + +Give every unsafe function, trait, impl, field, macro boundary, and other unsafe +contract a precise safety specification regardless of visibility. Use `# Safety` +documentation for public contracts. A private contract may cite module-owned +invariants, but must still state every fact its callers or implementers must +establish or continue to uphold. Use precise subjects, intervals, and +quantification. + +A complete unsafe API contract should make the following derivable whenever +applicable: + +- which values, memory regions, objects, threads, or executions it covers; +- validity, initialization, alignment, size, provenance, accessibility, + lifetime, aliasing, exclusivity, mutability, and ownership requirements; +- concurrency, atomic ordering, synchronization, reentrancy, callback, signal, + and thread-affinity requirements; +- target, ABI, feature, allocator, unwinding, linkage, or environmental + restrictions; +- what may be observed, read, written, moved, copied, destroyed, or retained; +- whether an invariant may be suspended, for how long, and what must not happen + before restoration; +- obligations attached to return values or capabilities; +- behavior on panic, unwind, cancellation, early return, or partial progress; +- documented postconditions on success and every other documented outcome. + +Use this list as a discovery prompt. Derive the actual requirements from the +exact operation and applicable authoritative contracts, and add every other +obligation those contracts create. + +Define relative terms. Replace phrases such as “valid pointer,” “properly +initialized,” “no aliases,” “live,” “same allocation,” “correct layout,” and +“used normally” with the exact propositions intended. Do not use “the caller +guarantees” unless the current boundary is unsafe and its documentation actually +requires the cited fact. + +Safety preconditions must be sufficient; they need not be mathematically +weakest. Nevertheless, avoid irrelevant or unknowable conditions. Every stated +condition becomes part of the API contract and its evolution constraints. + +Document postconditions with the same precision. If callers may rely on a +result, state: + +- the state/value relationship established; +- the resources, aliases, or ownership transferred; +- which prior invariants remain true; +- when the guarantee begins and ends; +- distinctions among normal return, error, panic, and unwind. + +## Write Local Safety Proofs + +Place a `SAFETY` comment immediately adjacent to the smallest cohesive unsafe +operation or block. Prefer one proof unit per independently reviewable +obligation set. + +For new code, require an explicit `unsafe { ... }` block for each unsafe +operation even inside an `unsafe fn`, and enable `unsafe_op_in_unsafe_fn` at +`deny` or `forbid` when compatible with project policy. Use documentation and +undocumented-unsafe-block lints as completeness aids where available; lint +success is not a proof. + +Use this structure: + +```rust +// SAFETY: +// Obligation: `` requires P1, P2, and P3. +// Facts: +// - F1 follows from . +// - F2 follows from TCB-... / AXIOM-... . +// Derivation: +// - F1 and F2 imply P1 because ... +// - ... +// Result: +// - The operation establishes Q. +// - Q re-establishes/preserves/transfers invariant I. +unsafe { operation() } +``` + +Use ordinary prose when clearer, but retain each logical component. Do not write: + +- “safe because this is unsafe code”; +- “the pointer is valid” without defining and proving the required properties; +- “checked above” without identifying the dominating check and relevant values; +- “guaranteed by the type/caller/API” without naming the exact contract clause; +- “this is how the standard library does it”; +- “Miri/tests pass” as a universal derivation; +- “obviously,” “trivially,” or “cannot happen” in place of proof; +- circular arguments in which an invariant is justified only by code that + already assumes it. + +A proof may cite a canonical checked proof or TCB entry to avoid duplicating +large quotations. Keep enough local text to show which proposition is used and +how it entails the local obligation. + +When one unsafe block contains multiple operations, prove each operation in +program order. Include facts established by earlier operations only after +proving those operations' postconditions. + +## Carry Invariants Locally + +State each safety invariant near the representation or boundary that owns it. +Give it a stable name when multiple proofs cite it. Specify: + +- the objects and states over which it quantifies; +- when it is required to hold; +- who may rely on it; +- every operation permitted to establish, mutate, suspend, transfer, consume, + or destroy it; +- what must be true while it is suspended; +- how panic, unwind, cancellation, reentrancy, callbacks, and destruction affect + it. + +Define the invariant's actual enforcement boundary and prove every producer, +transition, and consumer within it. Apply +[Use module privacy](api-boundaries-and-evolution.md#use-module-privacy) to +choose that boundary for new code or compute the real access region of existing +code. + +An invariant is local when each consumer can cite a named proposition whose +current truth is established by a local boundary. Its subject may still be +global state. Do not accept an informal “global invariant” that no boundary +owns or re-establishes. + +## Prove Temporal Behavior + +Treat time and interference explicitly: + +- Determine the interval during which each pointer, reference, lock, capability, + borrow, allocation, and invariant fact remains usable. +- Check every possible intervening call, callback, destructor, panic, unwind, + cancellation point, signal interaction, and reentrant entry. +- For concurrency, quantify over every permitted thread interleaving and weak + memory behavior within scope, not one observed schedule. +- If an operation returns a capability whose safe methods could violate an + invariant, place the ongoing obligation in the unsafe boundary's contract or + return a representation that enforces it. +- If a guard restores an invariant in `Drop`, prove restoration on all paths on + which `Drop` runs and separately address paths on which destruction can be + skipped, duplicated, reordered, or aborted. +- If an invariant is suspended across code not controlled by the abstraction, + treat that code as adversarial unless it is an explicitly trusted dependency. + +Cryptographic infeasibility and low probability do not turn a possible +execution into an unconditional Rust soundness proof. Move such premises to an +explicit conditional application claim and TCB entry. + +## Cite Authoritative Axioms + +For every Rust or standard-library ground-truth proposition: + +1. Select documentation applicable to the audited compiler/library version. +2. Link the narrowest applicable sections, including versions in the URLs. +3. Quote the smallest sufficient set of excerpts whose propositions participate + in the derivation. +4. State the proposition derived from each excerpt and justify the inference + that combines them. +5. Check that qualifications, definitions, linked clauses, and surrounding + scope do not weaken it. +6. Have the reviewer open the source and independently confirm the derivation. + +Apply [Qualify applicability](#qualify-applicability) when a citation and the +claim concern different Rust versions. + +If the Reference or standard-library documentation is missing, ambiguous, +internally inconsistent, or too weak, record the exact missing proposition. +Treat explanatory sources or current implementation behavior only as leads or +explicit additional assumptions. Recommend an upstream documentation report +when appropriate. + +## Search for Indirect Derivations + +Do not equate the absence of a single direct documentation sentence with the +absence of a proof. Before reporting an authoritative documentation gap or +finalizing an important obligation as unproved: + +1. Restate the exact semantic property required and unfold relevant project + definitions. +2. Search for applicable direct guarantees. +3. Search for stronger, more general, or orthogonal authoritative facts whose + conjunction could entail the property. +4. State every intermediate lemma and justify each inference rather than merely + collecting citations. +5. Check the applicability of every premise and intermediate lemma. +6. Try to construct a model that satisfies the premises while falsifying the + conclusion. If one remains possible, identify the missing implication. + +This search does not weaken the fail-closed rule. If no complete admissible +derivation is established, the obligation remains unproved. Distinguish “this +audit did not complete a proof” from the stronger claim that authoritative +documentation cannot support one. + +## Review a Proof + +For each proof: + +1. Reconstruct the required preconditions from the callee or language/library + contract rather than trusting the comment's summary. +2. Open every citation and verify its exact proposition, version, and scope. +3. Check each claimed local fact—including its quantifier, producer/transition + history, and applicability domain—against the actual dataflow and all + alternative paths. +4. Expand every named invariant and ensure it is established initially and + preserved by every permitted transition. +5. Check quantifiers, arithmetic boundaries, zero-sized and empty cases, + overflow, partial initialization, overlapping ranges, alias duration, + provenance, destruction, unwinding, reentrancy, concurrency, and + configuration-dependent behavior when relevant. +6. Verify every postcondition used downstream. +7. Search for circularity, vacuity, hidden trust, and stronger conclusions than + the cited facts entail. +8. Record every missing implication so it cannot be forgotten, apply + [Search for indirect derivations](#search-for-indirect-derivations), and + report `UNPROVED` if a required implication remains absent. + +If validation requires a material derivation absent from the existing safety +comment, include that reconstructed derivation—or the smallest missing +portion—in the review. A derivation is material when it supplies a necessary +logical bridge that is neither stated nor an immediate syntactic or +type-enforced fact visible at the proof site. Give its citations, +applicability, and relationship to the required preconditions and +postconditions. Report the implementation result separately from the deficient +proof artifact: + +- If the reconstruction succeeds, the implementation obligation may be proved, + but report the inadequate comment and provide proposed replacement wording. +- If the reconstruction fails, leave the obligation unproved. +- If it yields a valid execution containing UB, report `UNSOUND`. If it proves + that a valid UB-free execution falsifies a postcondition, report + `CONTRACT-BROKEN`. Do not use the UB-containing execution itself for both. + +When changes are authorized, update the adjacent proof rather than leaving the +reconstructed reasoning only in the review. A canonical checked proof or named +invariant may hold shared detail; do not demand redundant prose when the local +comment already identifies the exact proposition and complete derivation path. + +Do not use reconstruction to repair a caller-facing contract retroactively. An +undocumented caller obligation remains hidden under the current API contract, +even if adding it would make the implementation proof succeed. + +These examples identify common omissions; they are not a substitute for reading +the applicable authoritative contracts. diff --git a/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/tcb-and-evidence.md b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/tcb-and-evidence.md new file mode 100644 index 0000000000..8e1be81aa4 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897/references/tcb-and-evidence.md @@ -0,0 +1,294 @@ +# Trusted Computing Base and Evidence + +## Contents + +- [Maintain an explicit trust boundary](#maintain-an-explicit-trust-boundary) +- [Classify TCB entries](#classify-tcb-entries) +- [Record dependency contracts](#record-dependency-contracts) +- [Record external and deployment assumptions](#record-external-and-deployment-assumptions) +- [Handle probabilistic claims](#handle-probabilistic-claims) +- [Judge tools by their theorem](#judge-tools-by-their-theorem) +- [Audit a tool-derived proof](#audit-a-tool-derived-proof) +- [Review and evolve the TCB](#review-and-evolve-the-tcb) + +## Maintain an Explicit Trust Boundary + +A TCB audit log lists every proposition the audit accepts as authoritative or +correct without proving it from more primitive in-scope premises. Its purpose is +not to make assumptions respectable; it makes the exact conditional theorem +visible and reviewable. + +For every entry, record: + +- stable identifier and category; +- exact proposition admitted; +- exact source, artifact, implementation, version, revision, or digest; +- contract text or other evidence; +- scope, configurations, and consumers; +- why admission is permitted; +- validation or audit already performed; +- compatibility/update channel; +- owner and review trigger; +- status and unresolved limitations. + +Do not use entries such as “the platform works,” “dependencies are correct,” +“normal allocator,” “valid environment,” or “the compiler is sound.” Split them +into the smallest propositions actually consumed by proofs. + +Minimize the TCB where practical, but never hide an assumption to make the list +look small. Every unproved premise must become either another proof obligation +or an explicit entry. + +Do not make the theorem vacuous by adding an entry that merely assumes the +in-scope conclusion or trusts the implementation that the declared audit scope +purports to prove. Either prove that code, or narrow the theorem and expose the +code as a precisely identified excluded dependency/TCB component. + +When a proof applies an older documented Rust guarantee to a later version via +Rust's backwards-compatibility commitment, record the exact compatibility +proposition as a TCB entry unless it is itself entailed by applicable Reference +or standard-library text. Neither an API stability badge nor a general +expectation of stability silently expands the older guarantee's semantic or +configuration domain. + +The default source-level theorem is relative to the documented Rust abstract +semantics. It does not require trusting one compiler backend to emit a correct +binary. A binary-level theorem additionally requires a compiler/toolchain, +target, linker, loader, platform, and external-runtime story appropriate to the +claim. + +## Classify TCB Entries + +Use categories that expose why a proposition is admitted. Suitable categories +include: + +- **AXIOM:** Exact versioned Rust Reference or standard-library proposition. +- **SAFE-DEP:** Documented behavior of a deliberately selected safe dependency + API. +- **UNSAFE-DEP:** Correctness of a specific unsafe dependency implementation and + contract not recursively proved by this audit. +- **EXTERNAL-SPEC:** ABI, ISA, OS, hardware, foreign-language, allocator, linker, + or other non-Rust contract. +- **IMPLEMENTATION:** Exact compiler, standard-library build, foreign library, + runtime, generator, proc macro, build tool, or other implementation assumed + correct for a non-source-level claim. +- **TOOL:** Residual trusted components or model correspondence supporting a + tool-derived proof. +- **ENVIRONMENT/DEPLOYMENT:** Restriction on entry inputs, load environment, + symbols, CPU, privileges, resources, or other execution context. +- **CRYPTO/PROBABILISTIC:** Explicit computational or probabilistic premise for + a separately labeled conditional claim. +- **OUT-OF-BAND:** A bilateral or project-specific promise beyond the published + default contract. + +Projects may use different names. Preserve the semantic distinctions. + +A proof result produced by a tool is not automatically a TCB assumption. It can +derive a fact when its theorem and premises are verified. Record only the +remaining unproved tool correctness, translation, model, solver, certificate +checker, harness, or environmental premises as TCB entries. + +Only a consumed entry explicitly accepted by the authorized human reviewer may +support `PROVED`. A pending entry makes every consuming claim `UNPROVED`. A +rejected or superseded entry may not be consumed; replace it with a proof or an +accepted entry, or narrow the claim and expose the exclusion. + +## Record Dependency Contracts + +For every dependency proposition, identify whether code is deliberately +selected or caller-controlled. + +The project may trust a deliberately selected safe dependency API to behave as +documented. Record: + +- package/source identity and exact resolved version; +- safe API and exact behavior consumed; +- documentation version; +- enabled features and relevant target/configuration scope; +- contract channel: SemVer range, exact pin, in-tree fork, out-of-band + agreement, consumer-specific promise, or another explicit arrangement; +- compatibility and re-audit trigger. + +An exact pin freezes identity; it does not establish an undocumented semantic +fact. Prove such a fact by auditing the pinned implementation, obtain an +applicable additional contract, or admit the exact implementation proposition +explicitly. + +Do not apply this exception to behavior supplied by a caller merely because it +uses a dependency-defined type or trait. Values, callbacks, closures, plugins, +generic parameters, trait objects, and safe trait implementations selected by +the caller remain adversarial safe code. + +For a third-party unsafe API: + +1. Obtain its exact caller safety contract and prove the local call satisfies + it. +2. Separately establish that the dependency implementation upholds its promise + for every valid call. +3. Discharge step 2 by recursively auditing the implementation or recording a + precise `UNSAFE-DEP` assumption. + +Do not silently include unsafe dependencies in the safe-dependency exception. + +When depending on a fork or out-of-band agreement, record the actual authority +for the additional promise, parties, exact covered uses, duration, notification +mechanism, and update process. Do not generalize a consumer-specific guarantee +to other uses. + +## Record External and Deployment Assumptions + +External specifications are not Rust axioms. Admit only the exact propositions +needed, with version and scope, for example: + +- a foreign function has a stated ABI and obeys stated ownership/lifetime rules; +- a CPU instruction has stated effects when a named feature and privilege level + are present; +- a linker binds a symbol to a specific definition with a specific layout; +- a custom allocator satisfies a named contract; +- a loader, OS, embedded runtime, kernel, or device maintains specified memory + or concurrency behavior; +- a binary entrypoint receives inputs restricted by a deployment boundary. + +Distinguish three claims: + +1. **Safe library soundness:** every well-typed safe use is sound; deployment + restrictions cannot be hidden premises. +2. **Unsafe API soundness:** every use satisfying documented safety obligations + is sound; external conditions may be explicit obligations. +3. **Binary/application soundness:** executions satisfying stated entry and + deployment assumptions are sound. + +A cryptographic signature check, authenticated input channel, kernel policy, or +restricted device state may narrow a binary theorem. It may not make an +otherwise safe library API conditionally sound without exposing an unsafe +boundary or enforcing the restriction in safe code. + +If a compilation or linker flag still emits an artifact, record it as part of +the configuration or toolchain scope. Do not call the flag itself undefined +behavior unless an authoritative contract uses that classification. Identify +the exact execution contract that the resulting artifact satisfies or violates. + +## Handle Probabilistic Claims + +Rust soundness is universal over valid uses and permitted executions. A +non-zero, negligible, computationally infeasible, or empirically unobserved +chance of undefined behavior is not unconditional soundness. + +A user may explicitly admit a cryptographic or probabilistic premise in the TCB, +such as collision resistance or unforgeability. Then: + +- state the exact security experiment or probability bound; +- identify the primitive, parameters, implementation, threat model, and time + horizon; +- state how the premise restricts executions or inputs; +- label the result as a conditional computational/application theorem; +- keep the ordinary unconditional Rust soundness verdict separate. + +Do not write `PROVED` without qualification when the result depends on such an +entry. Use wording such as `PROVED relative to CRYPTO-...` and explain that this +is not unconditional Rust soundness. + +## Judge Tools by Their Theorem + +Classify evidence by what the exact result proves: + +- A concrete failing execution can refute a universal claim when the execution + is in scope and valid. +- A clean sampled test, fuzzing run, sanitizer run, interpreter execution, or + stress run usually establishes only that the explored executions did not + trigger the modeled failure. +- An alarm-free sound over-approximation can prove absence of its modeled bad + states over its stated domain. +- Exhaustive model checking can prove a property over the exhaustively covered + state space. +- Bounded model checking proves only the bounded proposition unless a + completeness bound is established. +- Deductive or interactive verification can prove the encoded theorem relative + to its logic, axioms, models, specifications, and trusted components. +- Successful compilation establishes only the exact properties the applicable + compiler contract and checks are relied upon to enforce. + +These are examples, not rules attached permanently to tool categories. One tool +can provide different guarantees in different modes or results. Read its exact +documentation and output. + +Apply this rule: + +> A tool result discharges an obligation only if the documented guarantee of +> that exact result, together with all explicit premises and trusted components, +> logically implies the obligation for the exact audited artifact and supported +> configuration set. + +Never infer more than the theorem. A tool model is not an additional Rust +authority; prove its correspondence to exact applicable Reference and +standard-library contracts or admit the missing correspondence explicitly. + +## Audit a Tool-Derived Proof + +Before accepting a tool result, verify: + +1. **Proposition:** State the exact property proved and why it entails the Rust + soundness obligation or documented postcondition. +2. **Artifact identity:** Record exact source, expansion/generated output, IR, + harness, specifications, compiler, target, tool, solver/backend, versions, + options, and configuration. +3. **Quantification:** Check coverage of inputs, states, executions, call + contexts, nondeterminism, thread interleavings, and supported configurations. +4. **Bounds:** Identify loop, recursion, allocation, object-count, integer, + search-depth, thread, time, and other bounds. Establish completeness or limit + the conclusion. +5. **Non-vacuity:** Check that the property, assertion, or unsafe operation is + reachable under permitted inputs and that assumptions do not make the + harness inconsistent or empty. +6. **Semantic fidelity:** Check validity, layout, provenance, aliasing, + initialization, concurrency, panic/unwind, FFI, assembly, allocation, target, + and environment modeling whenever relevant. +7. **Trust and stubs:** List trusted functions, contracts, abstractions, + dependency models, unsupported features, suppressions, skipped checks, and + manual lemmas. +8. **Terminal result:** Require the documented successful proof result. Timeout, + unknown, incomplete, unsupported, disabled checks, or ignored alarms do not + prove the target. +9. **TCB:** Identify verifier/analyzer correctness, source-to-model translation, + semantic models, solver/backend, proof checker, and specification adequacy + that remain trusted. + +An independently checked certificate may remove the producer or solver from the +TCB, depending on its guarantee. It does not by itself prove that the encoded +specification matches the needed Rust theorem or that source-to-model +translation is faithful. + +Suppressing a sound analyzer alarm creates a new proof obligation. A false +positive does not invalidate the analyzer's soundness guarantee; an unjustified +suppression invalidates the claimed conclusion. + +Tests and dynamic tools remain valuable for finding counterexamples, exercising +configuration paths, and checking that proof assumptions match reality. Report +their actual contribution without treating a clean run as a universal proof. + +## Review and Evolve the TCB + +Reuse the project's canonical TCB log when present. For every audit: + +- open and verify every consumed entry; +- remove unused, expired, superseded, or duplicate entries; +- add newly discovered assumptions before relying on them; +- map entries to proof consumers; +- check versions, feature/configuration scope, and contract channels; +- distinguish reviewed facts from proposed or unresolved assumptions; +- identify entries the human reviewer may reject. + +Trigger re-audit when: + +- a consumed authoritative document changes materially; +- supported Rust, target, feature, allocator, tool, or environment scope changes; +- a dependency resolves to a new version or changes contract channel; +- a fork or out-of-band agreement changes; +- generated output or its inputs/generator change; +- a tool, model, harness, bound, suppression, or proof specification changes; +- a TCB proposition is weakened, invalidated, or replaced; +- new code consumes an existing entry in a stronger way. + +Record the TCB revision or digest in every audit verdict. A `PROVED` result is +always relative to that stated trust boundary even when it contains only +authoritative Rust axioms and deliberately permitted safe-dependency trust. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/adjudication-prompt.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/adjudication-prompt.md new file mode 100644 index 0000000000..5fd603008d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/adjudication-prompt.md @@ -0,0 +1,24 @@ +Act as a fresh blind adjudicator. Read every file under `[PACKET]`, including +the source, `SCORER.md`, `RUBRIC.md`, `DISAGREEMENTS.md`, all anonymous reports +A–O, and both prior blind scores. Follow the frozen scoring instructions +exactly. + +Resolve only the semantic disagreements listed in `DISAGREEMENTS.md` and +preserve every agreed atom and hard-error decision. Decide from the report's +actual evidence: external authority may verify a premise the report invokes, +but may not silently add a material premise or derivation the report omitted. +Do not decide by majority, style preference, generating-condition speculation, +or similarity among reports. + +Inspect only `[PACKET]` and exact versioned official Rust Reference or +standard-library documentation needed to resolve a material claim. Do not +inspect sibling directories, the enclosing repository, manifests, skill +packages, condition maps, other scores, or other adjudications. Do not identify, +cluster, or speculate about generating conditions. Do not modify the packet and +do not spawn helper agents. + +Write the complete adjudication to `[OUTPUT]/adjudication.md` using +`apply_patch`, then return the same adjudication in your final response. Include +a final A–O atom table, hard-error decisions, compact decisive evidence for +each disputed cell, and any genuine rubric ambiguity. Keep the result no +longer than 3,500 words. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/aggregate_scores.py b/evals/unsafe-rust/runs/2026-07-31-v2-forward/aggregate_scores.py new file mode 100644 index 0000000000..5da9e3d520 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/aggregate_scores.py @@ -0,0 +1,283 @@ +#!/usr/bin/env python3 +"""Aggregate the frozen final blind matrices after adjudication and unblinding.""" + +from __future__ import annotations + +import re +from collections import defaultdict +from pathlib import Path + + +RUN_ROOT = Path(__file__).resolve().parent +MODE_ORDER = ("U", "D", "V", "I", "T", "C", "H", "A", "P", "N") +CONDITION_ORDER = ("v2", "v1", "core") +CONDITION_LABEL = {"v2": "V2", "v1": "V1", "core": "Core"} +RUN_RE = re.compile(r"^(r\d{3}) (U|D|V|I|T|C|H|A|P|N) (v2|v1|core) ([1-5])$") + + +def cells(line: str) -> list[str]: + return [cell.strip() for cell in line.strip().strip("|").split("|")] + + +def clean(value: str) -> str: + return value.replace("**", "").replace("`", "").strip() + + +def load_schedule() -> dict[str, tuple[str, str, int]]: + schedule: dict[str, tuple[str, str, int]] = {} + for line in (RUN_ROOT / "manifest.md").read_text().splitlines(): + match = RUN_RE.fullmatch(line) + if match: + run, mode, condition, replicate = match.groups() + schedule[run] = (mode, condition, int(replicate)) + assert len(schedule) == 150, f"expected 150 scheduled runs, found {len(schedule)}" + return schedule + + +def load_blind_map() -> dict[str, dict[str, str]]: + mapping: dict[str, dict[str, str]] = {} + for line in (RUN_ROOT / "blind-map.md").read_text().splitlines(): + row = cells(line) + if row and row[0] in MODE_ORDER: + assert len(row) == 16, (row[0], len(row)) + mapping[row[0]] = { + chr(ord("A") + index): clean(run) + for index, run in enumerate(row[1:]) + } + assert tuple(mapping) == MODE_ORDER, tuple(mapping) + return mapping + + +def load_final_matrix(mode: str) -> tuple[list[str], dict[str, tuple[list[str], str]]]: + lines = (RUN_ROOT / "blind-scores" / "final" / f"{mode}.md").read_text().splitlines() + header_index = None + header: list[str] = [] + for index, line in enumerate(lines): + row = [clean(value) for value in cells(line)] + if row and row[0] == "Report" and row[-1] == "Hard error": + header_index = index + header = row + break + assert header_index is not None, f"missing atom table in mode {mode}" + atoms = header[1:-1] + assert atoms and all(atom.startswith(mode) for atom in atoms), (mode, atoms) + + matrix: dict[str, tuple[list[str], str]] = {} + for line in lines[header_index + 2 :]: + row = [clean(value) for value in cells(line)] + if not row or not re.fullmatch(r"[A-O]", row[0]): + if matrix: + break + continue + assert len(row) == len(header), (mode, row) + statuses = [value.upper() for value in row[1:-1]] + assert all(value in {"PASS", "FAIL"} for value in statuses), (mode, row) + matrix[row[0]] = (statuses, row[-1]) + assert len(matrix) == 15, (mode, len(matrix)) + return atoms, matrix + + +def hard_error_present(value: str) -> bool: + return clean(value).lower() not in {"none", "no"} + + +def proposal_laundering(value: str) -> bool: + normalized = clean(value).lower() + return "proposal laundering" in normalized or normalized == "pl" + + +def delta(values: list[int]) -> str: + rendered = [] + for value in values: + rendered.append(f"+{value}" if value > 0 else str(value)) + return ", ".join(rendered) + + +def main() -> None: + schedule = load_schedule() + blind_map = load_blind_map() + + atoms_by_mode: dict[str, list[str]] = {} + counts: dict[str, dict[str, list[int]]] = {} + hard_errors: dict[str, dict[str, list[tuple[str, str, str]]]] = {} + failures: list[tuple[str, str, str, int, str, str]] = [] + + for mode in MODE_ORDER: + atoms, matrix = load_final_matrix(mode) + atoms_by_mode[mode] = atoms + counts[mode] = {condition: [0] * len(atoms) for condition in CONDITION_ORDER} + hard_errors[mode] = {condition: [] for condition in CONDITION_ORDER} + seen = defaultdict(int) + + for label, run in blind_map[mode].items(): + scheduled_mode, condition, replicate = schedule[run] + assert scheduled_mode == mode, (mode, label, run, scheduled_mode) + seen[condition] += 1 + statuses, hard_error = matrix[label] + for index, status in enumerate(statuses): + if status == "PASS": + counts[mode][condition][index] += 1 + else: + failures.append((mode, condition, run, replicate, label, atoms[index])) + if hard_error_present(hard_error): + hard_errors[mode][condition].append((run, label, hard_error)) + + assert dict(seen) == {condition: 5 for condition in CONDITION_ORDER}, (mode, seen) + + print("# V2 Forward Evaluation: Unblinded Aggregate") + print() + print("Each cell is a pass count out of five independent reports. Hard errors are") + print("reported per mode and condition; heterogeneous modes are not pooled.") + print() + print("## Per-mode condition results") + print() + print("| Mode | Condition | Atom pass counts | Hard errors |") + print("|---|---|---|---:|") + for mode in MODE_ORDER: + atoms = atoms_by_mode[mode] + for condition in CONDITION_ORDER: + atom_result = "; ".join( + f"{atom} {count}/5" for atom, count in zip(atoms, counts[mode][condition]) + ) + print( + f"| {mode} | {CONDITION_LABEL[condition]} | {atom_result} | " + f"{len(hard_errors[mode][condition])} |" + ) + + print() + print("## Condition differences") + print() + print("Deltas use atom order shown in the final column.") + print() + print("| Mode | Atoms | V2−V1 | V1−Core |") + print("|---|---|---|---|") + for mode in MODE_ORDER: + v2_v1 = [a - b for a, b in zip(counts[mode]["v2"], counts[mode]["v1"])] + v1_core = [a - b for a, b in zip(counts[mode]["v1"], counts[mode]["core"])] + print( + f"| {mode} | {', '.join(atoms_by_mode[mode])} | " + f"{delta(v2_v1)} | {delta(v1_core)} |" + ) + + print() + print("## Preregistered V2 gates") + print() + v2_atom_failures = [failure for failure in failures if failure[1] == "v2"] + v2_hard_errors = [ + (mode, *entry) + for mode in MODE_ORDER + for entry in hard_errors[mode]["v2"] + ] + v2_proposal_laundering = [ + (mode, *entry) + for mode in MODE_ORDER + for entry in hard_errors[mode]["v2"] + if proposal_laundering(entry[2]) + ] + passed = not v2_atom_failures and not v2_hard_errors + print(f"**Overall gate result: {'PASS' if passed else 'FAIL'}.**") + print() + print(f"- V2 atom failures: {len(v2_atom_failures)}") + print(f"- V2 hard errors: {len(v2_hard_errors)}") + print( + "- V2 proposal-laundering reports: " + f"{len(v2_proposal_laundering)}" + ) + print() + gate_checks = [ + ("Zero V2 hard errors", not v2_hard_errors), + ("Every atom passes in all five V2 reports", not v2_atom_failures), + ("No V2 proposal laundering", not v2_proposal_laundering), + ( + "U2, T2, and C1 apply the UB/postcondition rule 5/5", + counts["U"]["v2"][atoms_by_mode["U"].index("U2")] == 5 + and counts["T"]["v2"][atoms_by_mode["T"].index("T2")] == 5 + and counts["C"]["v2"][atoms_by_mode["C"].index("C1")] == 5, + ), + ( + "V1–V4 and H1 close exact-version reasoning 5/5", + all(value == 5 for value in counts["V"]["v2"]) + and counts["H"]["v2"][atoms_by_mode["H"].index("H1")] == 5, + ), + ( + "D1–D3 recover and audit the ambiguous union 5/5", + all(value == 5 for value in counts["D"]["v2"]), + ), + ( + "I1–I3 reject producer-premise promotion 5/5", + all(value == 5 for value in counts["I"]["v2"]), + ), + ( + "Every A, P, and N control atom passes 5/5", + all( + value == 5 + for mode in ("A", "P", "N") + for value in counts[mode]["v2"] + ), + ), + ] + print("| Gate | Result |") + print("|---|---|") + for description, result in gate_checks: + print(f"| {description} | {'PASS' if result else 'FAIL'} |") + print() + if v2_atom_failures: + print("### V2 failed atom cells") + print() + print("| Mode | Atom | Run | Replicate | Blind label |") + print("|---|---|---|---:|---|") + for mode, _condition, run, replicate, label, atom in v2_atom_failures: + print(f"| {mode} | {atom} | {run} | {replicate} | {label} |") + print() + if v2_hard_errors: + print("### V2 hard errors") + print() + print("| Mode | Run | Blind label | Decision |") + print("|---|---|---|---|") + for mode, run, label, decision in v2_hard_errors: + print(f"| {mode} | {run} | {label} | {decision} |") + print() + + print("## All non-passing atom cells") + print() + if failures: + print("| Mode | Condition | Atom | Run | Replicate | Blind label |") + print("|---|---|---|---|---:|---|") + condition_rank = {condition: index for index, condition in enumerate(CONDITION_ORDER)} + for mode, condition, run, replicate, label, atom in sorted( + failures, + key=lambda item: ( + MODE_ORDER.index(item[0]), + condition_rank[item[1]], + item[3], + item[5], + ), + ): + print( + f"| {mode} | {CONDITION_LABEL[condition]} | {atom} | {run} | " + f"{replicate} | {label} |" + ) + else: + print("None.") + + print() + print("## All hard errors") + print() + any_hard_error = False + for mode in MODE_ORDER: + for condition in CONDITION_ORDER: + for run, label, decision in hard_errors[mode][condition]: + if not any_hard_error: + print("| Mode | Condition | Run | Blind label | Decision |") + print("|---|---|---|---|---|") + any_hard_error = True + print( + f"| {mode} | {CONDITION_LABEL[condition]} | {run} | {label} | " + f"{decision} |" + ) + if not any_hard_error: + print("None.") + + +if __name__ == "__main__": + main() diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-map.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-map.md new file mode 100644 index 0000000000..2d618ddd9c --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-map.md @@ -0,0 +1,28 @@ +# V2 Forward Blind-Scoring Map + +> **Evaluator-only material.** Do not expose this map, run IDs, package +> identities, or condition identities to blind scorers. + +The labels below were independently shuffled per mode after all 150 reports +were frozen and integrity-checked. Each scorer receives one mode's source, +mode-specific oracle, and mechanically normalized copies under labels A–O. + +| Mode | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | +|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +| U | `r009` | `r121` | `r012` | `r029` | `r087` | `r062` | `r040` | `r041` | `r150` | `r044` | `r119` | `r082` | `r100` | `r002` | `r090` | +| D | `r124` | `r102` | `r014` | `r123` | `r149` | `r004` | `r073` | `r141` | `r058` | `r074` | `r136` | `r035` | `r057` | `r093` | `r026` | +| V | `r114` | `r048` | `r130` | `r133` | `r015` | `r097` | `r144` | `r116` | `r094` | `r028` | `r066` | `r020` | `r101` | `r099` | `r033` | +| I | `r134` | `r148` | `r069` | `r005` | `r112` | `r045` | `r145` | `r098` | `r104` | `r072` | `r051` | `r042` | `r131` | `r037` | `r127` | +| T | `r088` | `r128` | `r025` | `r023` | `r068` | `r017` | `r142` | `r137` | `r107` | `r031` | `r036` | `r011` | `r115` | `r013` | `r071` | +| C | `r110` | `r006` | `r078` | `r105` | `r008` | `r086` | `r147` | `r080` | `r089` | `r059` | `r010` | `r103` | `r060` | `r109` | `r138` | +| H | `r111` | `r079` | `r022` | `r106` | `r049` | `r122` | `r139` | `r039` | `r047` | `r132` | `r052` | `r075` | `r021` | `r077` | `r055` | +| A | `r146` | `r056` | `r084` | `r053` | `r125` | `r018` | `r019` | `r140` | `r046` | `r118` | `r070` | `r135` | `r061` | `r085` | `r126` | +| P | `r054` | `r001` | `r043` | `r034` | `r076` | `r081` | `r003` | `r117` | `r096` | `r032` | `r024` | `r027` | `r016` | `r091` | `r050` | +| N | `r129` | `r095` | `r063` | `r083` | `r065` | `r030` | `r092` | `r067` | `r143` | `r038` | `r113` | `r108` | `r007` | `r064` | `r120` | + +## Normalization + +The immutable originals remain byte-for-byte in `reports/rNNN.md`. Blind +copies replace the runtime root, opaque package/target labels, and run-number +tokens with neutral placeholders. This prevents incidental path text from +revealing or clustering conditions; no substantive report prose is changed. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/A.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/A.md new file mode 100644 index 0000000000..3336dc2875 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/A.md @@ -0,0 +1,141 @@ +# Mode A blind adjudication + +## Governing resolution + +I preserve every undisputed atom and hard-error decision. For A2, an +affirmative soundness scope is material: the acceptance request asks for the +strongest *scoped* result, and `SCORER.md` requires every material Rust premise +to have applicable, version-matched authority or to be left unresolved. +Accordingly, correctly separating contract failure from UB does not cure an +unsupported positive soundness range. + +The packet's authority rule also distinguishes an exact premise from a range +inference. External authority may verify an exact-version premise that a report +actually invokes and for which it supplies the derivation. It may not provide +an omitted proposition that relevant rules persisted across every release in +an interval. + +## Final atom table + +| Report | A1 | A2 | A3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | **PASS** | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | **FAIL** | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | **FAIL** | PASS | None | +| J | PASS | **FAIL** | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | **FAIL** | PASS | None | + +## Preserved agreed evidence + +- **A1, all reports — PASS.** Every report expressly identifies + `Pair(pub [u32; 2])` as having only the direct array field `.0`, rejects + `"tail"` as a direct declared field name (and `u32` as a direct field type), + and distinguishes the nested array selection `.0[1]` from a direct field. +- **A3, all reports — PASS.** Every report applies the literal current + contract, rejects the immutable snapshot, and proposes no replacement + contract, patch, migration, or alternative API. References to the actual + modular update do not redesign the acceptance candidate. +- **Undisputed A2 cells — PASS.** A–D, F, H, and K–N expressly distinguish + `CONTRACT-BROKEN` from `UNSOUND`, derive at an authority-supported exact + version that `.0[1]` is a live, initialized, aligned, in-bounds `u32`, and do + not invent a UB counterexample. Their unsupported open-ended future region is + left `UNPROVED`. A's additional exact-1.97.1 result and the other agreed A2 + decisions remain as scored by both blind scorers. +- **Hard error, all reports — None.** The sole frozen hard error is certifying + an unimplemented proposal. No report proposes or certifies a redesign; all + positive claims concern the displayed `project` and `increment_tail` bodies. + An overbroad version scope for existing code is an A2 issue, not this hard + error. + +## Decisive evidence for the disputed A2 cells + +### E — PASS + +E:7–9 makes the required separation: both direct-field promises are false, +the concrete implementation is claimed sound only at the two exact versions +1.70.0 and 1.97.1, and the intervening and future releases are expressly +`UNPROVED`. E:15–16 supplies the material derivation: the precondition gives a +live exclusive `Pair`; `.0[1]` is initialized, aligned, and in bounds; +`addr_of_mut!` produces its raw address; the immediate `&mut *` reborrow has no +competitor; and `wrapping_add` preserves a valid `u32`. It explicitly says this +proves UB freedom while proving the wrong postcondition. + +E:9 invokes an exact check of the governing text at 1.97.1. That invoked +endpoint premise is verifiable in the exact official 1.97.1 authorities: the +[array rules](https://doc.rust-lang.org/1.97.1/reference/types/array.html), +[layout rules](https://doc.rust-lang.org/1.97.1/reference/type-layout.html), +[`addr_of_mut!` contract](https://doc.rust-lang.org/1.97.1/std/ptr/macro.addr_of_mut.html), +[UB/reference rules](https://doc.rust-lang.org/1.97.1/reference/behavior-considered-undefined.html), +[coercion rules](https://doc.rust-lang.org/1.97.1/reference/type-coercions.html), +and [`wrapping_add`](https://doc.rust-lang.org/1.97.1/std/primitive.u32.html#method.wrapping_add) +support the already-stated derivation. Consulting them verifies E's invoked +premise; it does not add a missing derivation or continuity assumption. E does +not claim the releases between its two endpoints, so A2 passes. + +### G — FAIL + +G:18–19 and 51–74 correctly separate the contract defect from UB and derive a +valid nested `u32`. But G:26–29 defines its supported set as *every* stable +release from 1.70.0 through 1.97.1, while G:56–72 uses only 1.70.0 and 1.97.1 +authority for the material macro, reference-validity, aliasing, and arithmetic +rules. G:99–101 admits no compatibility premise. The report neither verifies +the intervening releases nor leaves them unresolved, so its material +interval-wide `PROVED` result is unsupported and A2 fails. + +### I — FAIL + +I:7–9 correctly states that the pointer is valid and no UB is established, but +I:11 extends `PROVED` to every released stable version from 1.70.0 through +1.97.1. I:23–27 supplies paired endpoint authority, not version-matched +authority for each intervening release; I:17 expressly disclaims any +compatibility promise. External authority cannot insert that missing +cross-release proposition. The overbroad affirmative scope is material, so A2 +fails. + +### J — FAIL + +J:10–17 makes the correct contract/soundness distinction but certifies all +stable releases from 1.70.0 through 1.97.1. J:58–68 samples the +`addr_of_mut!` text at 1.70, 1.75, 1.78, and 1.97.1, then infers rules for +1.70–1.74 and 1.75 onward. J:70–78 cites reference-validity and wrapping rules +only at the endpoints. Because J:19–21 admits no compatibility premise, those +samples do not establish every claimed version. A2 fails. + +### O — FAIL + +O:15–21 correctly says the concrete result is a valid nested `u32` and that no +UB witness follows from the broken postcondition. Its supported set, however, +is every stable release from 1.70.0 through 1.97.1 (O:9–16). O:47–65 infers +rules for 1.70–1.74 and 1.75 onward from 1.70, 1.75, and 1.97.1 documents and +uses endpoint-only validity and arithmetic authority. O:97–99 admits no +compatibility assumption. The omitted release-continuity/version-by-version +premise cannot be supplied externally, so A2 fails. + +## Genuine rubric/authority ambiguity + +1. A2's short wording can be read as testing only the minimum distinction + between a false contract and a valid nested pointer. The global instruction + that *all material propositions* need version-matched support, together with + the request for the strongest scoped result, makes a report's affirmative + soundness range part of A2 here. That resolves G, I, J, and O adversely. +2. The packet does not prescribe a mechanical citation count for an + exact-version verification. E states that it checked the exact 1.97.1 + governing text and supplies the complete derivation, although most inline + links in that derivation point to 1.70. Under the adjudication rule allowing + external verification of an invoked premise, the exact 1.97.1 documents can + verify E without adding reasoning. This is materially different from adding + a release-continuity premise to an interval report. +3. Whether tuple-struct fields are described as anonymous or as having numeric + field names is immaterial: under either terminology, `"tail"` is not a + direct field, the sole direct field has type `[u32; 2]`, and `[1]` selects a + nested array element. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/C.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/C.md new file mode 100644 index 0000000000..c3a67463d8 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/C.md @@ -0,0 +1,142 @@ +# Mode C adjudication + +## Result + +The sole disputed cell, **J/C1, is FAIL**. Every other atom and every hard-error decision is preserved from the scorers' agreement. + +J expressly establishes a supported compact build with debug assertions disabled in which safe `decode(0xD800)` produces an invalid surrogate `char` and reaches UB. It also calls the existing implementation `UNSOUND over the published support set`. But C1 additionally requires the compact surrogate-panic promise to be only `UNPROVED` from that UB-containing execution. J never states that disposition and never explicitly gives the permitted equivalent reasoning that the UB execution cannot be used as a defined failure-to-panic counterexample. Its only documented-behavior verdict for the compact branch is regional: with debug assertions enabled, the implementation and documented behavior are `PROVED`. The later statement that no safety proof can be reconstructed for the failing branch addresses the unchecked conversion's safety precondition, not the panic postcondition. + +That missing material proposition cannot be supplied from Rust authority or inferred from the report's partition. Exact Rust documentation could verify the premise that the execution has UB, but it cannot add J's omitted behavior-status derivation. This is therefore an atom failure, not the separate hard error for affirmatively using UB as a defined behavioral counterexample; J does not make that affirmative misuse. + +## Final atom table + +| Report | C1 | C2 | C3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | FAIL | Proposal laundering | +| G | PASS | PASS | PASS | None | +| H | PASS | PASS | FAIL | Proposal laundering | +| I | PASS | PASS | PASS | None | +| J | **FAIL** | PASS | FAIL | Proposal laundering | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | FAIL | Proposal laundering | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | FAIL | Proposal laundering | + +Totals: C1 14/15 PASS; C2 15/15 PASS; C3 10/15 PASS. Five reports have a hard error. + +## Report-by-report evidence + +### A + +- **C1 PASS:** A gives the Rust 1.70 compact/optimized `decode(0xD800)` invalid-`char` UB witness, calls the result `UNSOUND`, and explicitly calls the compact postcondition `UNPROVED` because that execution reaches UB. +- **C2 PASS:** It separately derives the noncompact scalar-or-`None` behavior directly from checked `char::from_u32`. +- **C3 PASS:** Its checked `from_u32(...).expect(...)` candidate preserves both signatures, behavior, cfg partition, MSRV, targets, widths, and profiles, and it explicitly withholds a `PROVED` artifact verdict. +- **Hard error: None:** A distinguishes UB from a defined behavioral counterexample, does not prove an unimplemented artifact, and conditions post-1.70 claims rather than back-projecting exact-version authority. + +### B + +- **C1 PASS:** B supplies the disabled-assertion surrogate UB witness and expressly says the UB witness proves `UNSOUND` but leaves compact behavior `UNPROVED`, not `CONTRACT-BROKEN`. +- **C2 PASS:** It separately establishes the checked noncompact branch's exact `Some`/`None` behavior. +- **C3 PASS:** The checked candidate closes signatures, behavior, feature, target, width, profile, panic-strategy, and MSRV obligations while denying it a post-change `PROVED` verdict. +- **Hard error: None:** Its proposal/artifact and UB/defined-behavior distinctions are explicit, and later-version coverage is left conditional. + +### C + +- **C1 PASS:** C derives UB for compact `decode(0xD800)` without debug assertions, assigns `UNSOUND`, and explicitly labels the panic promise `UNPROVED`, not `CONTRACT-BROKEN`. +- **C2 PASS:** It separately calls the direct checked noncompact branch `PROVED` at Rust 1.70. +- **C3 PASS:** The checked/`expect` design preserves all requested surfaces and axes, and C says it is not a post-change `PROVED` verdict. +- **Hard error: None:** No laundering occurs; the associated `from_u32` availability-by-1.52 statement is compatible with its cited Rust 1.70 API page. + +### D + +- **C1 PASS:** D supplies the disabled-debug-assertion witness, value-preserving cast, invalid-`char` UB rule, `UNSOUND` result, and explicit `UNPROVED` rather than `CONTRACT-BROKEN` behavior disposition. +- **C2 PASS:** It derives the noncompact branch's soundness and documented result from checked conversion. +- **C3 PASS:** Its checked/`expect` proposal preserves both signatures and all support axes and is expressly not given a post-change `PROVED` verdict. +- **Hard error: None:** D neither treats UB as defined behavior nor launders the proposal, and its future claim is explicitly conditional. + +### E + +- **C1 PASS:** E derives the Rust 1.70 release-profile UB witness and explicitly calls the compact postcondition `UNPROVED`, not `CONTRACT-BROKEN`. +- **C2 PASS:** It separately establishes the safe noncompact checked conversion's documented behavior. +- **C3 PASS:** The checked candidate preserves the complete configuration set and surfaces, with `PROVED` withheld until implementation and audit. +- **Hard error: None:** The report avoids both laundering forms and makes later semantics conditional; its 1.52 availability statement is not an incorrect exact-version result. + +### F + +- **C1 PASS:** F proves disabled-assertion surrogate UB and says it prevents establishment of the promised compact panic. +- **C2 PASS:** It separately derives the noncompact checked branch's soundness and behavior. +- **C3 FAIL:** Despite no source edit, F states `Proposed redesign verdict: PROVED at Rust 1.70.0` and conditionally `PROVED` later. +- **Hard error: Proposal laundering:** That explicit verdict launders an unimplemented candidate. F does not also use UB as a defined behavior counterexample or give an incorrect exact-version result. + +### G + +- **C1 PASS:** G gives the supported UB witness and `UNSOUND`, calls full-set behavior `UNPROVED`, and expressly rejects a separate defined `CONTRACT-BROKEN` finding. +- **C2 PASS:** Its table separately proves the noncompact checked behavior at Rust 1.70. +- **C3 PASS:** The checked `match` plan preserves the required surfaces and axes and is explicitly a design proof plan, not an implemented-snapshot verdict. +- **Hard error: None:** No proposal, behavior, or exact-version laundering occurs. + +### H + +- **C1 PASS:** H completely derives compact disabled-assertion UB and explicitly says the universal panic claim is `UNPROVED` because the execution has UB. +- **C2 PASS:** It separately proves the noncompact direct checked conversion. +- **C3 FAIL:** H says `Redesign verdict: PROVED` on Rust 1.70 although it also says no source edit was requested. +- **Hard error: Proposal laundering:** The unimplemented redesign receives a forbidden verdict; no additional hard-error category applies. + +### I + +- **C1 PASS:** I supplies the safe surrogate UB witness, `UNSOUND`, and explicit `UNPROVED`, not `CONTRACT-BROKEN`, treatment of the compact panic promise. +- **C2 PASS:** It separately derives the noncompact branch's represented-scalar/`None` behavior. +- **C3 PASS:** The checked candidate preserves all requested signatures and configurations, and I says the unimplemented design receives no artifact verdict. +- **Hard error: None:** The immaterial off-by-one source-line reference is not an exact-version result, and neither laundering form appears. + +### J + +- **C1 FAIL (adjudicated):** J establishes the compact/no-debug-assertions safe-call UB witness and calls the implementation `UNSOUND`, but omits the required `UNPROVED` disposition for the compact panic promise and any explicit equivalent UB-versus-defined-behavior reasoning. Regional proof of behavior with debug assertions enabled does not fill that omission. +- **C2 PASS:** J separately says the noncompact `char::from_u32` branch is `PROVED` at Rust 1.70 and states its checked result behavior. +- **C3 FAIL:** J labels the displayed but unimplemented candidate `Redesigned implementation: PROVED for Rust 1.70`. +- **Hard error: Proposal laundering:** That candidate verdict is proposal laundering. J does not affirmatively use the UB execution as a defined behavioral counterexample, and its exact-version API statements are not erroneous. + +### K + +- **C1 PASS:** K gives the disabled-assertion surrogate UB witness and explicitly assigns `UNPROVED`, with no separate `CONTRACT-BROKEN` verdict. +- **C2 PASS:** It separately proves the noncompact checked scalar/`None` behavior. +- **C3 PASS:** Its checked/`expect` plan preserves the complete support set and surfaces and explicitly has no `PROVED` verdict before implementation and audit. +- **Hard error: None:** K avoids both laundering forms and makes later-version coverage conditional. + +### L + +- **C1 PASS:** L derives compact release-profile UB and says the panic guarantee is not established, with no separate defined-execution counterexample. +- **C2 PASS:** It separately derives the noncompact checked behavior. +- **C3 FAIL:** Its code is headed `proposal only`, yet it calls the proposed redesign `PROVED for Rust 1.70.0` and conditionally `PROVED` for 1.70+. +- **Hard error: Proposal laundering:** The proposal-only/`PROVED` combination triggers the hard error. Its stated 1.97.1 endpoint and explicit compatibility premise add no exact-version hard error. + +### M + +- **C1 PASS:** M gives every step of the disabled-assertion surrogate UB witness and explicitly assigns `UNPROVED` to documented behavior, with no UB-free `CONTRACT-BROKEN` case. +- **C2 PASS:** It separately establishes the noncompact checked conversion's soundness and behavior. +- **C3 PASS:** The checked candidate preserves all surfaces and axes, and M withholds a post-change `PROVED` verdict. +- **Hard error: None:** Current claims are exact-versioned, later coverage is conditional, and neither laundering form occurs. + +### N + +- **C1 PASS:** N supplies the disabled-debug-assertion UB witness and explicitly says the panic cannot be proved and is not a defined `CONTRACT-BROKEN` counterexample. +- **C2 PASS:** It separately proves the noncompact checked branch's precise behavior. +- **C3 PASS:** The checked/`unwrap` candidate preserves the requested support set and surfaces, with no post-change `PROVED` verdict before implementation and review. +- **Hard error: None:** N avoids proposal laundering, UB-path laundering, and incorrect exact-version claims. + +### O + +- **C1 PASS:** O gives the compact disabled-assertion UB witness, `UNSOUND`, and explicit `UNPROVED` behavior because UB occurs rather than a proved panic. +- **C2 PASS:** It separately proves the noncompact checked branch at Rust 1.70. +- **C3 FAIL:** Although no source was edited, O declares `Proposed implementation — PROVED at Rust 1.70` and conditionally `PROVED` later. +- **Hard error: Proposal laundering:** The proposed-yet-`PROVED` verdict triggers the hard error; O does not commit either other hard-error category. + +## Ambiguity + +No genuine rubric or Rust-authority ambiguity changes a score. J presents a scoring boundary: its exhaustive configuration discussion gives a reader facts from which the missing `UNPROVED` conclusion could be derived. But the frozen instructions resolve that boundary: equivalent reasoning must be explicit, and a material premise may not be inferred from silence. Conversely, omission alone is not the hard error for using UB as a defined behavioral counterexample; that hard error requires affirmative misuse, which J does not make. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/H.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/H.md new file mode 100644 index 0000000000..734dc73981 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/H.md @@ -0,0 +1,158 @@ +# Mode H adjudication + +## Basis and scope + +Only the listed disagreements—H3 and proposal laundering for A, B, F, I, +and K—are adjudicated here. Every other atom and hard-error decision is +preserved from the two blind scores. + +The controlling scoring instruction is to score propositions, not keywords or +preferred vocabulary. H2 requires the report to establish that the safe +wrapping-iterator expression preserves the required source behavior while +leaving benchmark compliance unproved. Read coherently with that requirement, +H3's statement that “the candidate is not yet `PROVED`” concerns the candidate +as an implemented, benchmark-compliant replacement—not whether a report may +prove the displayed expression's specifically scoped Rust semantics as a +candidate proof plan. + +All five disputed reports keep the material propositions separate: (1) the +current source is unsound; (2) the displayed safe expression has a +source-level soundness/wrapping argument; and (3) benchmark compliance and +adoption remain unproved. All five also reject the current unsound loop as a +fallback. Their scoped use of `PROVED` therefore is not proposal laundering. + +## Final atom table + +| Report | H1 | H2 | H3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | FAIL | PASS | PASS | Incorrect exact-Rust-version result | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | FAIL | PASS | PASS | Incorrect exact-Rust-version result | +| N | PASS | PASS | PASS | None | +| O | FAIL | PASS | PASS | Incorrect exact-Rust-version result | + +Atom totals: H1 12/15; H2 15/15; H3 15/15. Hard errors: 3. + +## Compact report evidence + +- **A:** H1 gives the contract-satisfying dangling empty slice and applies + Rust 1.70's same-allocation rule to `add(0)`. H2 proves the copied wrapping + fold and calls performance `UNPROVED`. H3 benchmark-gates adoption and, on a + miss, permits only a sound guarded/proved fallback. No hard error. +- **B:** H1 derives Rust-1.70 UB at line 6 from the valid empty input. H2 proves + the safe fold's modular behavior and leaves the 2% claim `UNPROVED`. H3 + requires more measurement or another safe formulation, never the current + loop. No hard error. +- **C:** H1 supplies the exact dangling-empty-slice UB witness. H2 establishes + the safe wrapping fold but not performance. H3 expressly says “conditional + design proof” and “no artifact verdict” and allows only sound fallbacks. No + hard error. +- **D:** H1 establishes the Rust-1.70 `add(0)` failure. H2 proves the safe + iterator behavior and leaves performance unproved. H3 calls it a proposal, + not an audited snapshot, and requires a guarded/proved fallback. No hard + error. +- **E:** H1 establishes `UNSOUND` from the valid dangling empty input. H2 + proves the wrapping iterator loop while leaving the benchmark unproved. H3 + labels it a candidate proof plan, requires fresh review, and rejects the + status quo after a miss. No hard error. +- **F:** H1 correctly applies the literal Rust 1.70 contracts. H2 proves only + the candidate's source-level modular behavior and separately marks the 2% + proposition `UNPROVED`. H3 benchmark-gates adoption and requires another + safe candidate or an empty-guarded, proof-documented pointer loop after a + miss. No hard error. +- **G:** H1 gives the valid empty-input UB derivation. H2 proves the safe loop's + wrapping recurrence and rejects source shape as performance evidence. H3 + calls the snippet a design, requires post-change review, and permits only a + repaired fallback. No hard error. +- **H:** H1 fails because the report recites the decisive Rust 1.70 premises + yet says no valid UB counterexample was established. H2 proves the safe + wrapping fold and leaves performance unproved. H3 withholds an artifact + verdict and keeps the gate separate. Preserved hard error: incorrect exact- + Rust-version result. +- **I:** H1 supplies the exact valid-use Rust-1.70 UB witness. H2 proves the + candidate's target-local source semantics while calling performance + `UNPROVED`. H3 requires benchmark evidence before adoption and, after a miss + or inconclusive result, only continued safe work or a repaired/proved pointer + version. No hard error. +- **J:** H1 establishes immediate Rust-1.70 UB. H2 proves the safe modular fold + and leaves performance unproved. H3 calls it an unimplemented design, + requires audit after implementation, and rejects unsound fallbacks. No hard + error; its warning about a UB baseline avoids treating UB as defined. +- **K:** H1 establishes the exact Rust-1.70 failure. H2 proves the candidate's + expressly scoped source-level soundness/postcondition and marks replacement + performance `UNPROVED`. H3 goes further and says adoption is `UNPROVED`, + benchmark-gates merging, and permits only sound alternatives after a miss. + No hard error. +- **L:** H1 derives UB from the permitted dangling empty slice. H2 proves the + iterator's modular behavior and leaves the benchmark unproved. H3 calls it a + design proof, prohibits both proof-only merging and retention of the current + loop, and requires a sound fallback. No hard error. +- **M:** H1 fails because it treats the explicit Rust-1.70 precondition + violation as merely a missing proposition. H2 proves the safe wrapping loop + and leaves performance unproved. H3 calls it an unimplemented conditional + proof plan and requires only sound fallbacks. Preserved hard error: incorrect + exact-Rust-version result. +- **N:** H1 explicitly chooses a dangling pointer with no covering live + allocation and derives Rust-1.70 UB. H2 proves the copied wrapping fold and + leaves performance unproved. H3 calls it a design proof plan and requires an + empty-return repair if it misses. No hard error. +- **O:** H1 fails because it states the decisive conflicting Rust-1.70 premises + but stops at `UNPROVED`. H2 proves the safe wrapping fold and leaves + performance unproved. H3 gives no post-change verdict and requires any + fallback to remove the empty-`add(0)` obligation. Preserved hard error: + incorrect exact-Rust-version result. + +## Disputed-cell decisions + +- **A—H3 PASS; no proposal laundering.** “Rust 1.70 semantics: PROVED” is + explicitly scoped, while future semantics are conditional, performance is + `UNPROVED`, adoption depends on the benchmark, and a miss leads only to a + sound fallback. +- **B—H3 PASS; no proposal laundering.** “Design soundness and behavior: + PROVED for Rust 1.70” is a source-semantic subclaim. The report separately + denies a performance verdict and requires benchmark evidence before choosing + the replacement. +- **F—H3 PASS; no proposal laundering.** Its verdict is expressly “source + soundness and modular result” under cited contracts. Benchmark compliance is + independently `UNPROVED`, and its recommendation does not approve adoption + before measurement. +- **I—H3 PASS; no proposal laundering.** “Target-local source soundness and + wrapping behavior” is the proved proposition; the report separately says + redesign performance is `UNPROVED` and makes adoption benchmark-conditional. +- **K—H3 PASS; no proposal laundering.** The proved proposition is explicitly + limited to Rust-1.70 source-level soundness and the modular postcondition. + K expressly states both replacement performance and adoption are + `UNPROVED`, which forecloses proposal-to-artifact promotion. + +## Hard-error decisions + +- **Proposal laundering:** none. In particular, A, B, F, I, and K prove only + scoped semantic subclaims required by H2; they do not claim an implemented, + benchmark-compliant replacement. +- **UB-containing execution used as a defined behavioral counterexample:** + none (agreed and preserved). +- **Incorrect exact-Rust-version result:** H, M, and O only (agreed and + preserved). Each declines the Rust-1.70 `UNSOUND` consequence despite + stating the valid dangling-empty-slice and `add(0)` premises. + +## Genuine rubric ambiguity + +H2's demand that the candidate's source behavior be established is in wording +tension with H3's unqualified phrase “the candidate is not yet `PROVED`.” The +hard-error term “proposal laundering” is also not separately defined. A +keyword-literal reading would forbid the word `PROVED` even for the semantic +subclaim H2 asks reports to establish; a proposition-based reading permits +that subclaim while withholding an artifact, performance, or adoption verdict. +The frozen scorer instruction expressly selects the latter approach by saying +to score propositions rather than keywords. No authority ambiguity is needed +to resolve the disputed cells. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/N.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/N.md new file mode 100644 index 0000000000..4e8341cc77 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/N.md @@ -0,0 +1,74 @@ +# Mode N blind adjudication + +I resolved only the cells named in `DISAGREEMENTS.md`. Every other atom and every hard-error decision is preserved from the two blind scores. + +## Final atom table + +| Report | N1 | N2 | N3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | FAIL | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | FAIL | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +## Decisive evidence for disputed cells + +### A + +- **N1 — PASS.** A freshly labels the exact snapshot `UNSOUND`, gives the safe retained-`get`/later-`get_mut` program, establishes same-pointer access and liveness through the final read, and expressly adds that repeated `get_mut` calls can produce two simultaneously usable `&'a mut T` values. A also invokes the well-typedness premise rather than omitting it: the results use `'a` rather than the receiver-borrow lifetime, so the receiver borrow may end while the result lives. +- **N2 — PASS.** A identifies the enabling defect as `&'a T` and `&'a mut T` “rather than” receiver-tied results. The raw pointer appears only in the separate same-pointee derivation; A does not diagnose raw-pointer presence alone as the enabling defect. +- **N3 — PASS.** A changes both outputs to `&T`/`&mut T`, explicitly says elision ties them to their receivers, and calls the change an unimplemented candidate with “no verdict” pending fresh audit. + +These lifetime propositions are invoked in A and may therefore be checked rather than supplied. The exact [Rust 1.70 lifetime-elision rule](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html#lifetime-elision-in-functions) says each elided parameter lifetime becomes distinct and assigns the receiver lifetime only to elided output lifetimes. It therefore verifies both A's diagnosis of the current explicit `'a` outputs and A's receiver-bound elided repair. This adds no missing report premise or derivation. + +### D + +- **N1 — FAIL.** D freshly reports `UNSOUND` and fully derives the shared/mutable witness. It does not identify the other required witness: two live mutable results from repeated `get_mut` calls. Its only relevant sentence says safe code can call “another method” while a mutable result remains live. That does not state that the next call is `get_mut` or that it produces the second mutable result. Instantiating the generic phrase as a repeated `get_mut` call would add the material witness derivation that D omitted. + +### H + +- **N1 — FAIL.** H freshly reports `UNSOUND` and fully derives the mixed shared/mutable witness in `conflict`. It never identifies two simultaneous results of repeated `get_mut`. The repair discussion's phrase “allowing reuse of the capability” explains why a consuming accessor could be useful, but it does not instantiate that reuse as two `get_mut` calls or derive the duplicate-mutable witness. The missing route cannot be inferred for H. + +### O + +- **N1 — PASS.** O freshly reports `UNSOUND` and completely derives the safe repeated-`get_mut` witness: two independent receiver loans, two `'a` results from the unchanged pointer, simultaneous liveness in `take_both`, and the exact Rust 1.70 `UnsafeCell` multiple-`&mut` rule. It also explicitly gives the mixed route twice: `get_mut` can follow `get`, and fixing only `get_mut` would still let an old `'a` shared result overlap a later mutable result. +- **N2 — PASS.** O expressly diagnoses that both accessors return stored `'a`, not the receiver-loan lifetime, and says `PhantomData` carries the original borrow but does not connect results to individual receiver loans. Raw-pointer presence is not offered as the enabling defect. + +O invokes, rather than omits, the proposition that explicit `'a` outputs are unrelated to the elided receiver loans. The exact Rust 1.70 lifetime-elision text linked above verifies that proposition. Using that authority is permitted premise verification; it does not add a causal step or witness absent from O. + +## Evidence for preserved cells and hard-error decisions + +- **A — hard error none:** the repair is an “unimplemented candidate” with “no verdict” until an exact implementation is freshly audited. +- **B — N1/N2/N3 PASS; hard error none:** `collide` derives the mixed witness and the report expressly adds repeated `get_mut`; it identifies explicit `'a` outputs bypassing receiver elision; both replacement outputs are receiver-bound. The proposal is “unimplemented,” not `PROVED`, and requires fresh audit. +- **C — N1/N2/N3 PASS; hard error none:** C derives the mixed witness and expressly states the repeated-mutable route; it distinguishes struct `'a` from the receiver lifetime under Rust 1.70 elision; both repairs explicitly use receiver `'s`. The proposal has no verdict until implemented and re-audited. +- **D — N2/N3 PASS; hard error none:** D identifies explicit `'a` outputs rather than receiver lifetimes and cites the exact Rust 1.70 rule; both repaired outputs are receiver-elided. The patch is `UNIMPLEMENTED / UNPROVED` and must be re-audited. +- **E — N1/N2/N3 PASS; hard error none:** E derives repeated `get_mut` aliases in `touch` and separately the retained-shared route; it writes the effective receiver-`'s`/output-`'a` signature; both proposed outputs explicitly use `'s`. The proposal is unimplemented and unaudited. +- **F — N1/N2/N3 PASS; hard error none:** F derives the mixed witness and its obligation ledger expressly adds repeated mutable aliases; it expands both effective signatures and attributes the defect to `'a` outputs; both outputs are repaired. The candidate is `UNIMPLEMENTED and UNPROVED` pending fresh audit. +- **G — N1/N2/N3 PASS; hard error none:** G derives repeated mutable aliases through `clash` and expressly gives the retained-`get` analogue; it distinguishes the receiver lifetime from explicit `'a`; both outputs become receiver-bound. Both candidate designs are `UNIMPLEMENTED / NOT AUDITED`. +- **H — N2/N3 PASS; hard error none:** H says both outputs use `'a`, not the receiver-borrow lifetime, under exact Rust 1.70 authority; both repairs use receiver-elided outputs. All candidates receive no verdict and require fresh audit. +- **I — N1/N2/N3 PASS; hard error none:** I derives two `get_mut` results in `use_both` and expressly names the analogous mixed route; it identifies the independent receiver and impl lifetimes; both repair variants use receiver-bound outputs. The repair is unimplemented and must be freshly audited before `PROVED`. +- **J — N1/N2/N3 PASS; hard error none:** J derives repeated mutable results in `collide` and explicitly gives the analogous mixed route; it attributes both failures to outputs not tied to receiver borrows; both signatures are repaired. The proposal is not implemented and receives no verdict pending full review. +- **K — N1/N2/N3 PASS; hard error none:** K derives repeated mutable aliases through `write_both` and separately identifies retained `get` versus later `get_mut`; it identifies distinct receiver and `'a` result lifetimes; both outputs are changed. The candidates are expressly not audited or `PROVED`. +- **L — N1/N2/N3 PASS; hard error none:** L derives the mixed witness and expressly says successive `get_mut` calls yield coexisting aliases; it identifies `'a` as unrelated to the receiver lifetime; both repairs are receiver-bound. The proposal is `UNIMPLEMENTED / UNPROVED AS SOURCE` pending fresh review. +- **M — N1/N2/N3 PASS; hard error none:** M fully derives two mutable results in `duplicate` and expressly gives the analogous mixed route; it writes the effective receiver-`'s`/output-`'a` signature; both repaired outputs explicitly use `'s`. The change is an unproved proposal requiring implementation and fresh review. +- **N — N1/N2/N3 PASS; hard error none:** N fully derives the mixed witness and expressly adds repeated `get_mut`; it displays both effective signatures with independent receiver lifetimes and `'a` outputs; both repairs are receiver-elided. The proposal is unimplemented, not `PROVED`, and requires implementation audit. +- **O — N3 PASS; hard error none:** O changes both signatures to explicit receiver/output `'s`, requires both changes, and labels the proposal `UNPROVED` until implemented and re-audited as a new artifact. + +Thus no report certifies an unimplemented proposal, so the hard error applies to none of A–O. + +## Genuine rubric ambiguity + +N1 names “both safe alias witnesses” without specifying how much of the second derivation may be expressed by shorthand. Under `SCORER.md`, a second code block and duplicated UB proof are not required: an explicitly named analogous route passes when the already-stated lifetime, pointer-identity, liveness, and alias-rule reasoning covers it. But a generic phrase such as “another method” or “capability reuse” does not identify the repeated-`get_mut` route. This distinction follows the frozen prohibition on inferring a material premise and produces D/H `FAIL` without imposing a formatting preference. + +The phrase “the report verifies” could, in isolation, be read to require an inline report citation for every Rust premise. The controlling adjudication instruction resolves that issue here: exact external authority may verify a premise the report invokes, but may not add one it omitted. A and O explicitly invoke the relevant lifetime relationship, so the exact Rust 1.70 text verifies it; D and H omit a witness derivation, which authority cannot supply. No other genuine ambiguity changes a cell. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/P.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/P.md new file mode 100644 index 0000000000..ca8371ef4d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/adjudicated/P.md @@ -0,0 +1,63 @@ +# Mode P blind adjudication + +## Final atom table + +| Report | P1 | P2 | P3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | **FAIL** | PASS | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +**Tally:** 44/45 atoms pass; no hard errors. + +`DISAGREEMENTS.md` makes G:P1 the only live cell. Every other atom and every hard-error decision is preserved exactly as agreed. + +## Disputed cell: G:P1 — FAIL + +G proves the complete `Page` side: `ALIGN = 16`; `repr(C)` puts the sole field at offset zero; `repr(align(16))` raises the containing alignment; `as_ptr` selects the live array buffer; and the `[u8; 16]` is initialized, non-null, 16-aligned, and live for the receiver borrow. G also expressly chooses the receiver-borrow interval and correctly says a shorter interval would leave `first` `UNPROVED`. + +The generic-`first` derivation has a different, material gap. G says the contract supplies “readability,” then says A3 “consequently permits `*p`” and that every possible byte is a valid `u8`. It never states, for an arbitrary conforming `Block`, that it is operationally reading `readable` to entail an initialized byte and a non-atomic load permitted without aliasing or data-race UB; nor does it condition the proof on that implication. Its statement that `Page`'s initialized array is therefore readable proves only the `Page` direction, not the converse needed from the public prose for arbitrary implementers. “Every possible byte is a valid `u8`” addresses initialized `u8` bit patterns, not whether the memory contains an initialized value. + +G's own A3 is summarized as forbidding dangling/misaligned loads and invalid produced values. The exact Rust 1.70 [undefined-behavior page](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html) also treats data races and aliasing violations separately and warns that its list is non-exhaustive. That authority verifies the negative rules G invokes; it does not establish the converse that eliminating G's listed cases positively permits the load. Supplying the missing contract-to-initialized/read-permitted implication during adjudication would violate the frozen rule against silently adding a material premise. + +Thus G establishes `Page` and the temporal part of `first`, but not `first`'s current generic soundness. This is `UNPROVED`, not `UNSOUND`; the atom fails because P1 requires both conjuncts. + +## Compact evidence for preserved cells + +- **A:** P1 enumerates and proves the full `Page` contract and gives exact C1/C2 conditional premises for `first`; P2 preserves all clauses for unknown consumers/implementers; P3 keeps proof work and an additive lane in 1.x while reserving weakening/sealing/removal for 2.0. **Hard error: none**—the future design expressly needs a fresh audit. +- **B:** P1 explicitly reads `readable` as initialized read permission through the borrow and proves `Page` plus the one-byte load; P2 says local search cannot close either public side; P3 separates private proof factoring/additive runway from major contract changes. **Hard error: none**—the capability split is prospective and requires re-audit. +- **C:** P1 lists all five obligations, proves them for `Page`, and states the initialized/shared-read operational meaning used by `first`; P2 treats downstream implementers and consumers as open quantified sets; P3 retains `Block` for local/additive work and reserves changing it for 2.0. **Hard error: none**—C says no edit is authorized and offers only a migration possibility. +- **D:** P1 normalizes the contract to 16 initialized readable bytes for the live receiver borrow and proves both producer and one-byte consumer; P2 rejects repository search as authority over downstreams; P3 distinguishes local proof work from breaking extent, alignment, item, or boundary changes. **Hard error: none**—the replacement choices are future major-version options. +- **E:** P1 proves B1–B5 for `Page` and precisely conditions `first` on initialized read permission plus a post-return interval; P2 preserves the open public boundary; P3 retains the legacy surface for proof/additive work and assigns strengthening or weakening to 2.0. **Hard error: none**—the unimplemented replacement is explicitly subject to fresh audit. +- **F:** P1 proves `Page` and makes `first` conditional on a live, initialized, provenance/access-permitted, non-conflicting read; P2 states unknown downstream uses and impls prevent narrowing; P3 confines 1.x to equivalent proof/internal/additive work. **Hard error: none**—the safe method is a future endpoint, not a certified artifact. +- **G (agreed cells):** P2 says unknown consumers may use the full extent/alignment and unknown implementers prevent sealing or strengthening; P3 limits 1.x to proof/documentation and independent APIs and places capability splitting in 2.0. **Hard error: none**—G requires a fresh audit of any implemented 2.0 snapshot. +- **H:** P1's ledger proves `Page` and the load while expressly caveating any newly added initialization/provenance/interference duty; P2 covers both unknown public sides; P3 keeps narrow/additive 1.x work distinct from the shown 2.0 replacement. **Hard error: none**—the code is labeled a design sketch whose implemented form needs audit. +- **I:** P1 explicitly defines readable as initialized non-atomic load permission without lifetime, aliasing, or race UB and proves both sites; P2 preserves the open ecosystem contract; P3 distinguishes comments/internal/parallel work from breaking changes. **Hard error: none**—the migration artifact is prospective and requires fresh audit. +- **J:** P1 states the live-allocation, initialized-byte, permitted-read meaning and the weaker-meaning conditional, then proves `Page` and `first`; P2 covers consumer weakening and implementer strengthening; P3 separates compatible proof/additive work from 2.0 changes. **Hard error: none**—both designs are future and require fresh audit. +- **K:** P1 proves the strong `Page` contract and precisely leaves `first` conditional on initialization, live access, and race freedom without manufacturing unsoundness; P2 preserves every clause for unknown public parties; P3 reserves strengthening, weakening, sealing, and replacement for 2.0. **Hard error: none**—no edit is authorized and no absent code is certified. +- **L:** P1 proves `Page` and expressly identifies the live/provenance/initialized-load implication used conditionally by `first`; P2 states both downstream sets remain unknown; P3 permits an internal lemma/equivalent proof text while reserving contract reduction or strengthening for 2.0. **Hard error: none**—the safe alternatives are choices, not audited implementations. +- **M:** P1 proves every `Page` conjunct and gives the exact missing initialized, non-conflicting byte-zero implication and conditional closure for `first`; P2 protects unknown consumers and implementations; P3 keeps proof/additive migration work in 1.x and breaking changes in 2.0. **Hard error: none**—the sample is explicitly a proposed migration needing fresh audit. +- **N:** P1 explicitly expands readable into live, allocated, initialized read permission and proves the complete provider plus immediate load; P2 rejects local search for both public directions; P3 stages a safe additive lane while retaining legacy `Block` until 2.0. **Hard error: none**—the replacement remains prospective and must be re-audited. +- **O:** P1 proves the whole `Page` contract and precisely states the live-allocation, initialized-`u8`, alias/race-safe, post-return condition needed by `first`, then closes conditionally; P2 preserves unknown consumers and impls; P3 separates equivalent proof/additive work from authorized-major changes. **Hard error: none**—the preferred design is future work explicitly requiring fresh audit. + +## Hard-error decisions + +All A–O are **None**. Each report treats its changed API as a proposal, migration option, design sketch, or future major-version endpoint; none certifies an implemented replacement. Several expressly demand that implemented source receive a fresh audit. G's deficient proof of the current `first` is an atom failure, not the sole frozen hard error (“certifying an unimplemented proposal”). + +## Genuine ambiguities + +1. The source does not define whether “readable” entails initialization, provenance/access permission, alias/race freedom, or precisely which borrow interval survives `base`'s return. The rubric deliberately permits either an explicit operational reading or an exact conditional proof, so reports taking either complete route pass. +2. There is a real boundary between an operational reading and a conclusory assertion. No preferred vocabulary is required, but the report must actually state the missing implication or make the proof conditional on it. I resolve G on logical content: “A3 consequently permits `*p`” is the conclusion whose material premise is absent, not an operational definition of that premise. +3. Exact Rust 1.70 authority cannot prove an unbounded future `1.70+` range. The reports preserve this as a compatibility premise, cutoff qualification, or unresolved portion; under `SCORER.md`, that treatment is acceptable and does not alter the frozen cells. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/A.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/A.md new file mode 100644 index 0000000000..f699284511 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/A.md @@ -0,0 +1,7 @@ +# Semantic disagreements to adjudicate + +- Reports E, G, I, J, and O: A2. + +The scorers agree on every other atom and hard-error decision. Preserve those +agreed cells. Resolve the listed cells from the packet evidence and frozen +rubric; do not infer generating conditions. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/C.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/C.md new file mode 100644 index 0000000000..c35bc47db3 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/C.md @@ -0,0 +1,7 @@ +# Semantic disagreements to adjudicate + +- Report J: C1. + +The scorers agree on every other atom and hard-error decision. Preserve those +agreed cells. Resolve the listed cell from the packet evidence and frozen +rubric; do not infer generating conditions. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/H.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/H.md new file mode 100644 index 0000000000..a43d720476 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/H.md @@ -0,0 +1,8 @@ +# Semantic disagreements to adjudicate + +- Reports A, B, F, I, and K: H3 and the associated proposal-laundering + hard-error decision. + +The scorers agree on every other atom and hard-error decision. Preserve those +agreed cells. Resolve the listed cells from the packet evidence and frozen +rubric; do not infer generating conditions. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/N.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/N.md new file mode 100644 index 0000000000..823a55240e --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/N.md @@ -0,0 +1,10 @@ +# Semantic disagreements to adjudicate + +- Report A: N1, N2, and N3. +- Report D: N1. +- Report H: N1. +- Report O: N1 and N2. + +The scorers agree on every other atom and hard-error decision. Preserve those +agreed cells. Resolve the listed cells from the packet evidence and frozen +rubric; do not infer generating conditions. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/P.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/P.md new file mode 100644 index 0000000000..cb5cb13f7e --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/disagreements/P.md @@ -0,0 +1,7 @@ +# Semantic disagreements to adjudicate + +- Report G: P1. + +The scorers agree on every other atom and hard-error decision. Preserve those +agreed cells. Resolve the listed cell from the packet evidence and frozen +rubric; do not infer generating conditions. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/A.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/A.md new file mode 100644 index 0000000000..3336dc2875 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/A.md @@ -0,0 +1,141 @@ +# Mode A blind adjudication + +## Governing resolution + +I preserve every undisputed atom and hard-error decision. For A2, an +affirmative soundness scope is material: the acceptance request asks for the +strongest *scoped* result, and `SCORER.md` requires every material Rust premise +to have applicable, version-matched authority or to be left unresolved. +Accordingly, correctly separating contract failure from UB does not cure an +unsupported positive soundness range. + +The packet's authority rule also distinguishes an exact premise from a range +inference. External authority may verify an exact-version premise that a report +actually invokes and for which it supplies the derivation. It may not provide +an omitted proposition that relevant rules persisted across every release in +an interval. + +## Final atom table + +| Report | A1 | A2 | A3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | **PASS** | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | **FAIL** | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | **FAIL** | PASS | None | +| J | PASS | **FAIL** | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | **FAIL** | PASS | None | + +## Preserved agreed evidence + +- **A1, all reports — PASS.** Every report expressly identifies + `Pair(pub [u32; 2])` as having only the direct array field `.0`, rejects + `"tail"` as a direct declared field name (and `u32` as a direct field type), + and distinguishes the nested array selection `.0[1]` from a direct field. +- **A3, all reports — PASS.** Every report applies the literal current + contract, rejects the immutable snapshot, and proposes no replacement + contract, patch, migration, or alternative API. References to the actual + modular update do not redesign the acceptance candidate. +- **Undisputed A2 cells — PASS.** A–D, F, H, and K–N expressly distinguish + `CONTRACT-BROKEN` from `UNSOUND`, derive at an authority-supported exact + version that `.0[1]` is a live, initialized, aligned, in-bounds `u32`, and do + not invent a UB counterexample. Their unsupported open-ended future region is + left `UNPROVED`. A's additional exact-1.97.1 result and the other agreed A2 + decisions remain as scored by both blind scorers. +- **Hard error, all reports — None.** The sole frozen hard error is certifying + an unimplemented proposal. No report proposes or certifies a redesign; all + positive claims concern the displayed `project` and `increment_tail` bodies. + An overbroad version scope for existing code is an A2 issue, not this hard + error. + +## Decisive evidence for the disputed A2 cells + +### E — PASS + +E:7–9 makes the required separation: both direct-field promises are false, +the concrete implementation is claimed sound only at the two exact versions +1.70.0 and 1.97.1, and the intervening and future releases are expressly +`UNPROVED`. E:15–16 supplies the material derivation: the precondition gives a +live exclusive `Pair`; `.0[1]` is initialized, aligned, and in bounds; +`addr_of_mut!` produces its raw address; the immediate `&mut *` reborrow has no +competitor; and `wrapping_add` preserves a valid `u32`. It explicitly says this +proves UB freedom while proving the wrong postcondition. + +E:9 invokes an exact check of the governing text at 1.97.1. That invoked +endpoint premise is verifiable in the exact official 1.97.1 authorities: the +[array rules](https://doc.rust-lang.org/1.97.1/reference/types/array.html), +[layout rules](https://doc.rust-lang.org/1.97.1/reference/type-layout.html), +[`addr_of_mut!` contract](https://doc.rust-lang.org/1.97.1/std/ptr/macro.addr_of_mut.html), +[UB/reference rules](https://doc.rust-lang.org/1.97.1/reference/behavior-considered-undefined.html), +[coercion rules](https://doc.rust-lang.org/1.97.1/reference/type-coercions.html), +and [`wrapping_add`](https://doc.rust-lang.org/1.97.1/std/primitive.u32.html#method.wrapping_add) +support the already-stated derivation. Consulting them verifies E's invoked +premise; it does not add a missing derivation or continuity assumption. E does +not claim the releases between its two endpoints, so A2 passes. + +### G — FAIL + +G:18–19 and 51–74 correctly separate the contract defect from UB and derive a +valid nested `u32`. But G:26–29 defines its supported set as *every* stable +release from 1.70.0 through 1.97.1, while G:56–72 uses only 1.70.0 and 1.97.1 +authority for the material macro, reference-validity, aliasing, and arithmetic +rules. G:99–101 admits no compatibility premise. The report neither verifies +the intervening releases nor leaves them unresolved, so its material +interval-wide `PROVED` result is unsupported and A2 fails. + +### I — FAIL + +I:7–9 correctly states that the pointer is valid and no UB is established, but +I:11 extends `PROVED` to every released stable version from 1.70.0 through +1.97.1. I:23–27 supplies paired endpoint authority, not version-matched +authority for each intervening release; I:17 expressly disclaims any +compatibility promise. External authority cannot insert that missing +cross-release proposition. The overbroad affirmative scope is material, so A2 +fails. + +### J — FAIL + +J:10–17 makes the correct contract/soundness distinction but certifies all +stable releases from 1.70.0 through 1.97.1. J:58–68 samples the +`addr_of_mut!` text at 1.70, 1.75, 1.78, and 1.97.1, then infers rules for +1.70–1.74 and 1.75 onward. J:70–78 cites reference-validity and wrapping rules +only at the endpoints. Because J:19–21 admits no compatibility premise, those +samples do not establish every claimed version. A2 fails. + +### O — FAIL + +O:15–21 correctly says the concrete result is a valid nested `u32` and that no +UB witness follows from the broken postcondition. Its supported set, however, +is every stable release from 1.70.0 through 1.97.1 (O:9–16). O:47–65 infers +rules for 1.70–1.74 and 1.75 onward from 1.70, 1.75, and 1.97.1 documents and +uses endpoint-only validity and arithmetic authority. O:97–99 admits no +compatibility assumption. The omitted release-continuity/version-by-version +premise cannot be supplied externally, so A2 fails. + +## Genuine rubric/authority ambiguity + +1. A2's short wording can be read as testing only the minimum distinction + between a false contract and a valid nested pointer. The global instruction + that *all material propositions* need version-matched support, together with + the request for the strongest scoped result, makes a report's affirmative + soundness range part of A2 here. That resolves G, I, J, and O adversely. +2. The packet does not prescribe a mechanical citation count for an + exact-version verification. E states that it checked the exact 1.97.1 + governing text and supplies the complete derivation, although most inline + links in that derivation point to 1.70. Under the adjudication rule allowing + external verification of an invoked premise, the exact 1.97.1 documents can + verify E without adding reasoning. This is materially different from adding + a release-continuity premise to an interval report. +3. Whether tuple-struct fields are described as anonymous or as having numeric + field names is immaterial: under either terminology, `"tail"` is not a + direct field, the sole direct field has type `[u32; 2]`, and `[1]` selects a + nested array element. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/C.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/C.md new file mode 100644 index 0000000000..c3a67463d8 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/C.md @@ -0,0 +1,142 @@ +# Mode C adjudication + +## Result + +The sole disputed cell, **J/C1, is FAIL**. Every other atom and every hard-error decision is preserved from the scorers' agreement. + +J expressly establishes a supported compact build with debug assertions disabled in which safe `decode(0xD800)` produces an invalid surrogate `char` and reaches UB. It also calls the existing implementation `UNSOUND over the published support set`. But C1 additionally requires the compact surrogate-panic promise to be only `UNPROVED` from that UB-containing execution. J never states that disposition and never explicitly gives the permitted equivalent reasoning that the UB execution cannot be used as a defined failure-to-panic counterexample. Its only documented-behavior verdict for the compact branch is regional: with debug assertions enabled, the implementation and documented behavior are `PROVED`. The later statement that no safety proof can be reconstructed for the failing branch addresses the unchecked conversion's safety precondition, not the panic postcondition. + +That missing material proposition cannot be supplied from Rust authority or inferred from the report's partition. Exact Rust documentation could verify the premise that the execution has UB, but it cannot add J's omitted behavior-status derivation. This is therefore an atom failure, not the separate hard error for affirmatively using UB as a defined behavioral counterexample; J does not make that affirmative misuse. + +## Final atom table + +| Report | C1 | C2 | C3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | FAIL | Proposal laundering | +| G | PASS | PASS | PASS | None | +| H | PASS | PASS | FAIL | Proposal laundering | +| I | PASS | PASS | PASS | None | +| J | **FAIL** | PASS | FAIL | Proposal laundering | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | FAIL | Proposal laundering | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | FAIL | Proposal laundering | + +Totals: C1 14/15 PASS; C2 15/15 PASS; C3 10/15 PASS. Five reports have a hard error. + +## Report-by-report evidence + +### A + +- **C1 PASS:** A gives the Rust 1.70 compact/optimized `decode(0xD800)` invalid-`char` UB witness, calls the result `UNSOUND`, and explicitly calls the compact postcondition `UNPROVED` because that execution reaches UB. +- **C2 PASS:** It separately derives the noncompact scalar-or-`None` behavior directly from checked `char::from_u32`. +- **C3 PASS:** Its checked `from_u32(...).expect(...)` candidate preserves both signatures, behavior, cfg partition, MSRV, targets, widths, and profiles, and it explicitly withholds a `PROVED` artifact verdict. +- **Hard error: None:** A distinguishes UB from a defined behavioral counterexample, does not prove an unimplemented artifact, and conditions post-1.70 claims rather than back-projecting exact-version authority. + +### B + +- **C1 PASS:** B supplies the disabled-assertion surrogate UB witness and expressly says the UB witness proves `UNSOUND` but leaves compact behavior `UNPROVED`, not `CONTRACT-BROKEN`. +- **C2 PASS:** It separately establishes the checked noncompact branch's exact `Some`/`None` behavior. +- **C3 PASS:** The checked candidate closes signatures, behavior, feature, target, width, profile, panic-strategy, and MSRV obligations while denying it a post-change `PROVED` verdict. +- **Hard error: None:** Its proposal/artifact and UB/defined-behavior distinctions are explicit, and later-version coverage is left conditional. + +### C + +- **C1 PASS:** C derives UB for compact `decode(0xD800)` without debug assertions, assigns `UNSOUND`, and explicitly labels the panic promise `UNPROVED`, not `CONTRACT-BROKEN`. +- **C2 PASS:** It separately calls the direct checked noncompact branch `PROVED` at Rust 1.70. +- **C3 PASS:** The checked/`expect` design preserves all requested surfaces and axes, and C says it is not a post-change `PROVED` verdict. +- **Hard error: None:** No laundering occurs; the associated `from_u32` availability-by-1.52 statement is compatible with its cited Rust 1.70 API page. + +### D + +- **C1 PASS:** D supplies the disabled-debug-assertion witness, value-preserving cast, invalid-`char` UB rule, `UNSOUND` result, and explicit `UNPROVED` rather than `CONTRACT-BROKEN` behavior disposition. +- **C2 PASS:** It derives the noncompact branch's soundness and documented result from checked conversion. +- **C3 PASS:** Its checked/`expect` proposal preserves both signatures and all support axes and is expressly not given a post-change `PROVED` verdict. +- **Hard error: None:** D neither treats UB as defined behavior nor launders the proposal, and its future claim is explicitly conditional. + +### E + +- **C1 PASS:** E derives the Rust 1.70 release-profile UB witness and explicitly calls the compact postcondition `UNPROVED`, not `CONTRACT-BROKEN`. +- **C2 PASS:** It separately establishes the safe noncompact checked conversion's documented behavior. +- **C3 PASS:** The checked candidate preserves the complete configuration set and surfaces, with `PROVED` withheld until implementation and audit. +- **Hard error: None:** The report avoids both laundering forms and makes later semantics conditional; its 1.52 availability statement is not an incorrect exact-version result. + +### F + +- **C1 PASS:** F proves disabled-assertion surrogate UB and says it prevents establishment of the promised compact panic. +- **C2 PASS:** It separately derives the noncompact checked branch's soundness and behavior. +- **C3 FAIL:** Despite no source edit, F states `Proposed redesign verdict: PROVED at Rust 1.70.0` and conditionally `PROVED` later. +- **Hard error: Proposal laundering:** That explicit verdict launders an unimplemented candidate. F does not also use UB as a defined behavior counterexample or give an incorrect exact-version result. + +### G + +- **C1 PASS:** G gives the supported UB witness and `UNSOUND`, calls full-set behavior `UNPROVED`, and expressly rejects a separate defined `CONTRACT-BROKEN` finding. +- **C2 PASS:** Its table separately proves the noncompact checked behavior at Rust 1.70. +- **C3 PASS:** The checked `match` plan preserves the required surfaces and axes and is explicitly a design proof plan, not an implemented-snapshot verdict. +- **Hard error: None:** No proposal, behavior, or exact-version laundering occurs. + +### H + +- **C1 PASS:** H completely derives compact disabled-assertion UB and explicitly says the universal panic claim is `UNPROVED` because the execution has UB. +- **C2 PASS:** It separately proves the noncompact direct checked conversion. +- **C3 FAIL:** H says `Redesign verdict: PROVED` on Rust 1.70 although it also says no source edit was requested. +- **Hard error: Proposal laundering:** The unimplemented redesign receives a forbidden verdict; no additional hard-error category applies. + +### I + +- **C1 PASS:** I supplies the safe surrogate UB witness, `UNSOUND`, and explicit `UNPROVED`, not `CONTRACT-BROKEN`, treatment of the compact panic promise. +- **C2 PASS:** It separately derives the noncompact branch's represented-scalar/`None` behavior. +- **C3 PASS:** The checked candidate preserves all requested signatures and configurations, and I says the unimplemented design receives no artifact verdict. +- **Hard error: None:** The immaterial off-by-one source-line reference is not an exact-version result, and neither laundering form appears. + +### J + +- **C1 FAIL (adjudicated):** J establishes the compact/no-debug-assertions safe-call UB witness and calls the implementation `UNSOUND`, but omits the required `UNPROVED` disposition for the compact panic promise and any explicit equivalent UB-versus-defined-behavior reasoning. Regional proof of behavior with debug assertions enabled does not fill that omission. +- **C2 PASS:** J separately says the noncompact `char::from_u32` branch is `PROVED` at Rust 1.70 and states its checked result behavior. +- **C3 FAIL:** J labels the displayed but unimplemented candidate `Redesigned implementation: PROVED for Rust 1.70`. +- **Hard error: Proposal laundering:** That candidate verdict is proposal laundering. J does not affirmatively use the UB execution as a defined behavioral counterexample, and its exact-version API statements are not erroneous. + +### K + +- **C1 PASS:** K gives the disabled-assertion surrogate UB witness and explicitly assigns `UNPROVED`, with no separate `CONTRACT-BROKEN` verdict. +- **C2 PASS:** It separately proves the noncompact checked scalar/`None` behavior. +- **C3 PASS:** Its checked/`expect` plan preserves the complete support set and surfaces and explicitly has no `PROVED` verdict before implementation and audit. +- **Hard error: None:** K avoids both laundering forms and makes later-version coverage conditional. + +### L + +- **C1 PASS:** L derives compact release-profile UB and says the panic guarantee is not established, with no separate defined-execution counterexample. +- **C2 PASS:** It separately derives the noncompact checked behavior. +- **C3 FAIL:** Its code is headed `proposal only`, yet it calls the proposed redesign `PROVED for Rust 1.70.0` and conditionally `PROVED` for 1.70+. +- **Hard error: Proposal laundering:** The proposal-only/`PROVED` combination triggers the hard error. Its stated 1.97.1 endpoint and explicit compatibility premise add no exact-version hard error. + +### M + +- **C1 PASS:** M gives every step of the disabled-assertion surrogate UB witness and explicitly assigns `UNPROVED` to documented behavior, with no UB-free `CONTRACT-BROKEN` case. +- **C2 PASS:** It separately establishes the noncompact checked conversion's soundness and behavior. +- **C3 PASS:** The checked candidate preserves all surfaces and axes, and M withholds a post-change `PROVED` verdict. +- **Hard error: None:** Current claims are exact-versioned, later coverage is conditional, and neither laundering form occurs. + +### N + +- **C1 PASS:** N supplies the disabled-debug-assertion UB witness and explicitly says the panic cannot be proved and is not a defined `CONTRACT-BROKEN` counterexample. +- **C2 PASS:** It separately proves the noncompact checked branch's precise behavior. +- **C3 PASS:** The checked/`unwrap` candidate preserves the requested support set and surfaces, with no post-change `PROVED` verdict before implementation and review. +- **Hard error: None:** N avoids proposal laundering, UB-path laundering, and incorrect exact-version claims. + +### O + +- **C1 PASS:** O gives the compact disabled-assertion UB witness, `UNSOUND`, and explicit `UNPROVED` behavior because UB occurs rather than a proved panic. +- **C2 PASS:** It separately proves the noncompact checked branch at Rust 1.70. +- **C3 FAIL:** Although no source was edited, O declares `Proposed implementation — PROVED at Rust 1.70` and conditionally `PROVED` later. +- **Hard error: Proposal laundering:** The proposed-yet-`PROVED` verdict triggers the hard error; O does not commit either other hard-error category. + +## Ambiguity + +No genuine rubric or Rust-authority ambiguity changes a score. J presents a scoring boundary: its exhaustive configuration discussion gives a reader facts from which the missing `UNPROVED` conclusion could be derived. But the frozen instructions resolve that boundary: equivalent reasoning must be explicit, and a material premise may not be inferred from silence. Conversely, omission alone is not the hard error for using UB as a defined behavioral counterexample; that hard error requires affirmative misuse, which J does not make. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/D.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/D.md new file mode 100644 index 0000000000..e02eae52e8 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/D.md @@ -0,0 +1,144 @@ +# Mode D blind score + +## Scoring basis + +The published interval is not limited to the four `.0` releases. Rust 1.80.1 was a stable release between 1.80.0 and 1.81.0 (official Rust release notes, `Version 1.80.1 (2024-08-08)`), and the exact 1.80.1 Reference/std pages exist. Its slice page contains the same material clauses used by report A: `is_empty` returns true for length zero, and an out-of-bounds `get_unchecked` call is UB. Therefore the literal policy union has 19 configurations: + +- non-`fast`: five releases (1.79.0, 1.80.0, 1.80.1, 1.81.0, 1.82.0) on two targets; +- `fast` x86_64: all five releases; +- `fast` aarch64: 1.80.0, 1.80.1, 1.81.0, and 1.82.0. + +The disputed region is `fast` x86_64/1.79.0 plus `fast` aarch64/1.80.0, 1.80.1, and 1.81.0. An explicit four-release set, a count of 15 or 16, or a claim that there are only three disputed configurations contracts the union. A report that states the full interval but expressly leaves pre-1.82 contracts unresolved fails D3 without the closure hard error. + +## Atom table + +| Report | D1 | D2 | D3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | No | +| B | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| C | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| D | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| E | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| F | PASS | FAIL | FAIL | No | +| G | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| H | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| I | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| J | FAIL | FAIL | FAIL | Yes — contracts/does not cover 1.80.1, yet asserts closure | +| K | FAIL | FAIL | FAIL | Yes — contracts/does not cover 1.80.1, yet asserts closure | +| L | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| M | PASS | FAIL | FAIL | Yes — asserts closure without 1.80.1 evidence | +| N | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| O | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | + +## Report-by-report evidence + +### A + +- **D1 PASS:** Lines 7–14 enumerate all five releases, both policy regions, 19 union configurations, and all four disputed configurations, including aarch64/1.80.1. Lines 14–16 expressly leave policy identity unresolved. +- **D2 PASS:** Lines 14–16 call the union a conservative review domain rather than the actual promise and say both the pin and sampled CI neither resolve the conflict nor provide universal evidence. +- **D3 PASS:** Lines 22 and 28–31 give exhaustive complementary-`cfg` and non-`fast`/`fast` proofs, cite exact Reference/std text separately for all five releases including 1.80.1, and make the bounds proof target-parametric. +- **Hard error: No.** Line 14 explicitly rejects choosing a policy or recovering the promise; all 19 union configurations are covered, and lines 16 and 31 reject pin/CI exhaustiveness and cross-version back-projection. + +### B + +- **D1 FAIL:** Lines 15–18 define the policies using only four releases; lines 80–82 likewise omit aarch64/1.80.1 from the disputed region. The no-precedence conclusion is correct but the predicates are incomplete. +- **D2 FAIL:** Lines 12–22 call that contracted four-release set the union. Lines 32–33 correctly limit the pin and CI, but the actual conservative union is not audited. +- **D3 FAIL:** Lines 47–68 prove and assert closure using only 1.79.0, 1.80.0, 1.81.0, and 1.82.0 documentation; no 1.80.1 premise or unresolved qualification appears. +- **Hard error: Yes.** Lines 15–22 contract a union region, and lines 65–68 assert full configuration closure without covering 1.80.1. + +### C + +- **D1 FAIL:** Lines 9–10 define a four-release domain; lines 41–43 say the policies have eight non-`fast` cells and omit aarch64/1.80.1 from the disputed cells. +- **D2 FAIL:** Lines 18–21 claim both policies are subsets of the 16-cell domain, but the real policies contain 1.80.1 cells. Lines 44–45 properly reject pin/CI authority, which does not cure the contracted audit domain. +- **D3 FAIL:** Lines 34–39 and 51–84 close only the four enumerated releases. The exact cited Rust premises omit 1.80.1. +- **Hard error: Yes.** Lines 18–21 and 38–39 assert a covering theorem and closure while contracting the intervening stable release. + +### D + +- **D1 FAIL:** Lines 13–18 define `V` as four releases and consequently omit 1.80.1 from both predicates and the disputed aarch64 region. +- **D2 FAIL:** Lines 13–20 label that contracted set `U`. Line 20 correctly treats the pin and CI as non-authoritative, but it does not restore the missing union cells. +- **D3 FAIL:** Lines 24–30 assert complete closure while checking slice contracts for only four versions; 1.80.1 is neither proved nor left unresolved. +- **Hard error: Yes.** The report contracts 1.80.1 in lines 15–18 and asserts complete union coverage in lines 24–30. + +### E + +- **D1 FAIL:** Lines 7–14 explicitly set `V` to four releases and describe the disputed set without aarch64/1.80.1. +- **D2 FAIL:** Lines 9–14 call the contracted set the union. Lines 14 and 20 correctly preserve policy uncertainty and limit the pin/CI, but the conservative domain is incomplete. +- **D3 FAIL:** Lines 24–30 cite and prove only the four `.0` versions, then claim every member of `U`; 1.80.1 is missing. +- **Hard error: Yes.** Lines 9–14 contract the union and lines 24–30 assert closure over it. + +### F + +- **D1 PASS:** Lines 13–15 state both policies as inclusive release ranges, identify their differing `fast` regions, and reject unauthorized precedence. Nothing limits the ranges to `.0` releases. +- **D2 FAIL:** Lines 15 and 23 audit the union conservatively by leaving the older interval unproved, and line 15 correctly treats CI as a sample, but the report never addresses the developer toolchain pin. D2 makes that a material proposition, so it cannot be inferred from silence. +- **D3 FAIL:** Lines 7–9 prove only the 1.82.0 slice and expressly mark a whole-domain result unproved; lines 23 and 27 identify the missing version-matched contracts. +- **Hard error: No.** The report neither contracts the interval nor asserts full closure: lines 9 and 23 explicitly leave the uncovered versions unresolved. It also does not treat CI as exhaustive or select a policy. + +### G + +- **D1 FAIL:** Lines 5–11 define a 16-case, four-release envelope and state the policy differences against that contracted release set, omitting 1.80.1. +- **D2 FAIL:** Lines 5–9 treat the 16-case envelope as containing both policies, which is false for 1.80.1. Line 17 properly treats the pin and CI as non-exhaustive, but the union audit remains incomplete. +- **D3 FAIL:** Lines 19 and 23–25 verify only four versioned `cfg`/slice contracts and claim the entire envelope is proved. +- **Hard error: Yes.** Lines 5–9 contract the union and lines 19–25 assert closure without the 1.80.1 cells. + +### H + +- **D1 FAIL:** Lines 7–13 explicitly define `R` as four releases; the purported union and disputed aarch64 region consequently omit 1.80.1. +- **D2 FAIL:** Lines 7–13 call that contracted domain `U`. Line 19 correctly limits the pin and CI, but it does not audit the full union. +- **D3 FAIL:** Lines 23–31 prove only four exact versions while claiming every point of `U`; no 1.80.1 evidence or qualification is supplied. +- **Hard error: Yes.** The four-release definition contracts the union, and lines 29–31 assert full closure. + +### I + +- **D1 FAIL:** Lines 13–17 say both policies cover exactly four enumerated releases and define the corresponding contracted union. +- **D2 FAIL:** Lines 13–19 audit that contracted `U`; lines 19 and 45 correctly reject pin/CI authority and preserve policy uncertainty, but the 1.80.1 region is absent. +- **D3 FAIL:** Lines 23–31 rely on exact pages for only the four `.0` versions and then assert uniform proof throughout `U`. +- **Hard error: Yes.** Lines 13–17 contract 1.80.1 and line 31 asserts full closure. + +### J + +- **D1 FAIL:** Although line 7 writes inclusive ranges, line 11 says the disputed set consists only of x86_64/1.79.0 and aarch64/1.80.0 and 1.81.0; aarch64/1.80.1 is omitted. +- **D2 FAIL:** Lines 7–11 present the union as the review envelope and line 27 correctly rejects CI exhaustiveness, but the developer toolchain pin is never evaluated and the report's own disputed-set statement contracts the union. +- **D3 FAIL:** Lines 21–27 cite exact `cfg`/slice documentation for only four releases while claiming every point of `U`; 1.80.1 is not proved or reserved. +- **Hard error: Yes.** Line 11 contracts the disputed region, and lines 25–27 assert closure without 1.80.1 coverage. + +### K + +- **D1 FAIL:** Lines 19–21 use inclusive ranges, but line 23 expressly defines `A \ B` as only three configurations, omitting fast aarch64/1.80.1. +- **D2 FAIL:** Lines 23–25 call the resulting envelope complete and correctly reject CI as proof, but never address the developer pin; the explicit disputed-set contraction also makes the union treatment incomplete. +- **D3 FAIL:** Lines 25 and 31–38 claim per-release/full-envelope proof while checking only four exact version pages. +- **Hard error: Yes.** Line 23 contracts the union, and lines 25 and 31–38 assert closure without 1.80.1 evidence. + +### L + +- **D1 FAIL:** Lines 7–14 enumerate only four releases and therefore omit 1.80.1 from both the envelope and the disputed region. +- **D2 FAIL:** Lines 5–14 call the four-release set the commitment envelope. Line 20 correctly limits both the pin and CI, but the actual union is not audited. +- **D3 FAIL:** Lines 24–30 prove only the four enumerated versions, then claim configuration closure over the envelope. +- **Hard error: Yes.** Lines 7–14 contract 1.80.1 and lines 24–30 assert full closure. + +### M + +- **D1 PASS:** Lines 9–15 give both predicates as inclusive ranges in a side-by-side table and explicitly leave the authoritative predicate unresolved. The table itself exposes both disputed regions without imposing precedence. +- **D2 FAIL:** Lines 5–15 correctly use the envelope only as a coverage device and call CI sampling evidence, but the report never states what the 1.82.0 developer toolchain pin does or does not establish. That material D2 premise is unstated. +- **D3 FAIL:** Lines 21–29 claim a total parametric proof, but the only version-matched `is_empty`, indexing, and `get_unchecked` pages cited are 1.79.0, 1.80.0, 1.81.0, and 1.82.0. The inclusive envelope also contains 1.80.1. +- **Hard error: Yes.** The domain notation need not be read as contracted, but lines 21 and 29 assert total closure without covering 1.80.1. + +### N + +- **D1 FAIL:** Lines 14–20 enumerate four releases, count only 15 union configurations, and lines 75–78 omit aarch64/1.80.1 from the conflict. +- **D2 FAIL:** Lines 11–22 call that 15-cell set the union/review domain. Lines 38–40 correctly limit the pin and CI, but the conservative union is contracted. +- **D3 FAIL:** Lines 48–71 prove exactly four version pages and assert all claimed combinations are covered; 1.80.1 is absent. +- **Hard error: Yes.** The “15 combinations” statement contracts the union and the report asserts complete closure in lines 20–21 and 48–71. + +### O + +- **D1 FAIL:** Lines 15–24 enumerate four releases, claim exactly 15 combinations, and say there are only three disputed `fast` combinations, all of which excludes aarch64/1.80.1. +- **D2 FAIL:** Lines 11–25 call that contracted set the union. Lines 30–31 properly limit the pin and CI and preserve uncertainty, but the actual union is not audited. +- **D3 FAIL:** Lines 35–48 and 56–69 assert closure over all 15 members using documentation for only four exact releases. +- **Hard error: Yes.** Lines 20–24 contract the union, and lines 43–48 assert domain closure without 1.80.1. + +## Rubric/authority ambiguity + +No genuine ambiguity changes a score. The shorthand `1.79–1.82` might in isolation be mistaken for four minor-version baselines, but the source policies say **stable Rust releases** from exact endpoint 1.79.0 through exact endpoint 1.82.0 **inclusive**. The official Rust release archive confirms that 1.80.1 is an intervening stable release, and its exact versioned Reference/std documentation is available. Thus including 1.80.1 is required rather than a discretionary interpretation. + +For hard errors, I read “asserting closure without covering the union” as applicable when a report claims a full theorem but supplies version-matched Rust premises only for the four `.0` releases. I did not apply it to F because F expressly leaves the uncovered pre-1.82 interval unresolved. I applied it to M even though M's interval notation can include 1.80.1, because its asserted closure has no 1.80.1 authority. This follows the hard-error text directly and presents no unresolved authority conflict. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/H.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/H.md new file mode 100644 index 0000000000..734dc73981 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/H.md @@ -0,0 +1,158 @@ +# Mode H adjudication + +## Basis and scope + +Only the listed disagreements—H3 and proposal laundering for A, B, F, I, +and K—are adjudicated here. Every other atom and hard-error decision is +preserved from the two blind scores. + +The controlling scoring instruction is to score propositions, not keywords or +preferred vocabulary. H2 requires the report to establish that the safe +wrapping-iterator expression preserves the required source behavior while +leaving benchmark compliance unproved. Read coherently with that requirement, +H3's statement that “the candidate is not yet `PROVED`” concerns the candidate +as an implemented, benchmark-compliant replacement—not whether a report may +prove the displayed expression's specifically scoped Rust semantics as a +candidate proof plan. + +All five disputed reports keep the material propositions separate: (1) the +current source is unsound; (2) the displayed safe expression has a +source-level soundness/wrapping argument; and (3) benchmark compliance and +adoption remain unproved. All five also reject the current unsound loop as a +fallback. Their scoped use of `PROVED` therefore is not proposal laundering. + +## Final atom table + +| Report | H1 | H2 | H3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | FAIL | PASS | PASS | Incorrect exact-Rust-version result | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | FAIL | PASS | PASS | Incorrect exact-Rust-version result | +| N | PASS | PASS | PASS | None | +| O | FAIL | PASS | PASS | Incorrect exact-Rust-version result | + +Atom totals: H1 12/15; H2 15/15; H3 15/15. Hard errors: 3. + +## Compact report evidence + +- **A:** H1 gives the contract-satisfying dangling empty slice and applies + Rust 1.70's same-allocation rule to `add(0)`. H2 proves the copied wrapping + fold and calls performance `UNPROVED`. H3 benchmark-gates adoption and, on a + miss, permits only a sound guarded/proved fallback. No hard error. +- **B:** H1 derives Rust-1.70 UB at line 6 from the valid empty input. H2 proves + the safe fold's modular behavior and leaves the 2% claim `UNPROVED`. H3 + requires more measurement or another safe formulation, never the current + loop. No hard error. +- **C:** H1 supplies the exact dangling-empty-slice UB witness. H2 establishes + the safe wrapping fold but not performance. H3 expressly says “conditional + design proof” and “no artifact verdict” and allows only sound fallbacks. No + hard error. +- **D:** H1 establishes the Rust-1.70 `add(0)` failure. H2 proves the safe + iterator behavior and leaves performance unproved. H3 calls it a proposal, + not an audited snapshot, and requires a guarded/proved fallback. No hard + error. +- **E:** H1 establishes `UNSOUND` from the valid dangling empty input. H2 + proves the wrapping iterator loop while leaving the benchmark unproved. H3 + labels it a candidate proof plan, requires fresh review, and rejects the + status quo after a miss. No hard error. +- **F:** H1 correctly applies the literal Rust 1.70 contracts. H2 proves only + the candidate's source-level modular behavior and separately marks the 2% + proposition `UNPROVED`. H3 benchmark-gates adoption and requires another + safe candidate or an empty-guarded, proof-documented pointer loop after a + miss. No hard error. +- **G:** H1 gives the valid empty-input UB derivation. H2 proves the safe loop's + wrapping recurrence and rejects source shape as performance evidence. H3 + calls the snippet a design, requires post-change review, and permits only a + repaired fallback. No hard error. +- **H:** H1 fails because the report recites the decisive Rust 1.70 premises + yet says no valid UB counterexample was established. H2 proves the safe + wrapping fold and leaves performance unproved. H3 withholds an artifact + verdict and keeps the gate separate. Preserved hard error: incorrect exact- + Rust-version result. +- **I:** H1 supplies the exact valid-use Rust-1.70 UB witness. H2 proves the + candidate's target-local source semantics while calling performance + `UNPROVED`. H3 requires benchmark evidence before adoption and, after a miss + or inconclusive result, only continued safe work or a repaired/proved pointer + version. No hard error. +- **J:** H1 establishes immediate Rust-1.70 UB. H2 proves the safe modular fold + and leaves performance unproved. H3 calls it an unimplemented design, + requires audit after implementation, and rejects unsound fallbacks. No hard + error; its warning about a UB baseline avoids treating UB as defined. +- **K:** H1 establishes the exact Rust-1.70 failure. H2 proves the candidate's + expressly scoped source-level soundness/postcondition and marks replacement + performance `UNPROVED`. H3 goes further and says adoption is `UNPROVED`, + benchmark-gates merging, and permits only sound alternatives after a miss. + No hard error. +- **L:** H1 derives UB from the permitted dangling empty slice. H2 proves the + iterator's modular behavior and leaves the benchmark unproved. H3 calls it a + design proof, prohibits both proof-only merging and retention of the current + loop, and requires a sound fallback. No hard error. +- **M:** H1 fails because it treats the explicit Rust-1.70 precondition + violation as merely a missing proposition. H2 proves the safe wrapping loop + and leaves performance unproved. H3 calls it an unimplemented conditional + proof plan and requires only sound fallbacks. Preserved hard error: incorrect + exact-Rust-version result. +- **N:** H1 explicitly chooses a dangling pointer with no covering live + allocation and derives Rust-1.70 UB. H2 proves the copied wrapping fold and + leaves performance unproved. H3 calls it a design proof plan and requires an + empty-return repair if it misses. No hard error. +- **O:** H1 fails because it states the decisive conflicting Rust-1.70 premises + but stops at `UNPROVED`. H2 proves the safe wrapping fold and leaves + performance unproved. H3 gives no post-change verdict and requires any + fallback to remove the empty-`add(0)` obligation. Preserved hard error: + incorrect exact-Rust-version result. + +## Disputed-cell decisions + +- **A—H3 PASS; no proposal laundering.** “Rust 1.70 semantics: PROVED” is + explicitly scoped, while future semantics are conditional, performance is + `UNPROVED`, adoption depends on the benchmark, and a miss leads only to a + sound fallback. +- **B—H3 PASS; no proposal laundering.** “Design soundness and behavior: + PROVED for Rust 1.70” is a source-semantic subclaim. The report separately + denies a performance verdict and requires benchmark evidence before choosing + the replacement. +- **F—H3 PASS; no proposal laundering.** Its verdict is expressly “source + soundness and modular result” under cited contracts. Benchmark compliance is + independently `UNPROVED`, and its recommendation does not approve adoption + before measurement. +- **I—H3 PASS; no proposal laundering.** “Target-local source soundness and + wrapping behavior” is the proved proposition; the report separately says + redesign performance is `UNPROVED` and makes adoption benchmark-conditional. +- **K—H3 PASS; no proposal laundering.** The proved proposition is explicitly + limited to Rust-1.70 source-level soundness and the modular postcondition. + K expressly states both replacement performance and adoption are + `UNPROVED`, which forecloses proposal-to-artifact promotion. + +## Hard-error decisions + +- **Proposal laundering:** none. In particular, A, B, F, I, and K prove only + scoped semantic subclaims required by H2; they do not claim an implemented, + benchmark-compliant replacement. +- **UB-containing execution used as a defined behavioral counterexample:** + none (agreed and preserved). +- **Incorrect exact-Rust-version result:** H, M, and O only (agreed and + preserved). Each declines the Rust-1.70 `UNSOUND` consequence despite + stating the valid dangling-empty-slice and `add(0)` premises. + +## Genuine rubric ambiguity + +H2's demand that the candidate's source behavior be established is in wording +tension with H3's unqualified phrase “the candidate is not yet `PROVED`.” The +hard-error term “proposal laundering” is also not separately defined. A +keyword-literal reading would forbid the word `PROVED` even for the semantic +subclaim H2 asks reports to establish; a proposition-based reading permits +that subclaim while withholding an artifact, performance, or adoption verdict. +The frozen scorer instruction expressly selects the latter approach by saying +to score propositions rather than keywords. No authority ambiguity is needed +to resolve the disputed cells. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/I.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/I.md new file mode 100644 index 0000000000..8fb321df16 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/I.md @@ -0,0 +1,134 @@ +# Mode I blind score + +## Atom table + +| Report | I1 | I2 | I3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +I1 requires both producers and constructor-specific treatment of `from_writable`'s contract. I2 requires the write-validity obligation, its failure for `from_static`, and rejection of both local comments. I3 requires a wholly safe witness and an authoritative Rust 1.80 UB derivation, or an explicitly equivalent derivation. The hard-error column was decided independently from the atom columns. + +## Report-by-report evidence + +### A + +- **I1 — PASS:** “The current producer set is exhaustive” names unsafe `from_writable` and safe `from_static`; states W and S are separated, and the W result is expressly conditional on `from_writable`'s ongoing caller contract. +- **I2 — PASS:** The ledger says `from_static` establishes S, not W, and the `Some` arm is unsound. Finding F2 calls the `Some` comment false and the `None` comment incomplete because it omits alignment and the state-to-producer bridge. +- **I3 — PASS:** It gives the entirely safe `from_static(); overwrite(0)` witness and cites exact Rust 1.80 liveness, shared-reference immutability, nonzero mutation, `ptr::write`, and one-byte-`u8` rules before concluding UB and `UNSOUND`. +- **Hard error — None:** It does not universalize `from_writable` or rely on privacy alone; it includes the safe witness and completes the authoritative UB derivation. + +### B + +- **I1 — PASS:** The producer table separately lists `from_writable` as producing `None` under its continuing unsafe-caller obligations and `from_static` as safely producing `Some(&BYTE)`. +- **I2 — PASS:** It says `from_static` creates the state behind the unsound consumer, identifies write validity and alignment as `ptr::write` requirements, rejects the `Some` comment as false, and rejects the `None` comment as missing the producer/privacy bridge and alignment. +- **I3 — PASS:** The safe `from_static(); overwrite(9)` witness is followed by exact Rust 1.80 `ptr::write` and Reference liveness/immutable-byte reasoning, yielding UB and an `UNSOUND` verdict. +- **Hard error — None:** Both producers, the safe witness, and a direct authoritative derivation are present; privacy is used only with an exhaustive producer inventory. + +### C + +- **I1 — PASS:** The boundary inventory names the sole unsafe producer and sole safe producer, then O1 limits `from_writable`'s result to calls satisfying its documented unsafe-caller obligations. +- **I2 — PASS:** It states the needed write-validity invariant, says `from_static` does not establish it, calls the `Some` comment false, and calls the `None` comment incomplete for omitting producer partition, alignment, and conflict facts. +- **I3 — PASS:** It supplies `from_static(); overwrite(9)` and explicitly derives same-byte identity, full-call reference liveness, a one-byte overlapping mutation, UB, and safe-API `UNSOUND`, with exact Rust 1.80 sources. +- **Hard error — None:** It distinguishes both histories, does not use privacy as sole proof, and neither misses nor weakens the safe UB witness. + +### D + +- **I1 — PASS:** The surface inventory identifies exactly `from_writable` and `from_static`; the `None`-arm proof is explicitly only for a valid `from_writable` call satisfying the ongoing contract. +- **I2 — PASS:** It says the `Some` comment's `from_writable` premise is inapplicable and that `from_static` establishes the opposite needed fact; it separately calls the `None` comment proof-documentation deficient. +- **I3 — PASS:** It gives the entirely safe `from_static(); overwrite(0)` witness and derives UB from the exact Rust 1.80 immutable-static-byte rule plus `ptr::write`'s write-validity contract, concluding `UNSOUND`. This is an explicit equivalent treatment: immutable `static BYTE` alone closes the execution without needing the additional shared-reference-liveness route. +- **Hard error — None:** It audits both producers and supplies a concrete safe witness with a direct authoritative UB proof, rather than stopping at aliasing concern or proof debt. + +### E + +- **I1 — PASS:** The W/S inventory names both producers and confines the ongoing non-null/aligned/write-valid/non-conflict promise to state W created by valid `from_writable` use. +- **I2 — PASS:** The ledger says `from_static` establishes addressability and alignment but not write permission, marks the S write unsound, calls the S comment invalid, and calls the W comment deficient. +- **I3 — PASS:** It gives the safe `from_static(); overwrite(0)` execution and, using exact Rust 1.80 `ptr::write`, liveness/immutable-byte, and `u8`-size text, derives an overlapping write during `with_live`, UB, and `UNSOUND`. +- **Hard error — None:** The report neither closes all states through `from_writable` nor relies on privacy by itself, and it contains the required safe witness and complete derivation. + +### F + +- **I1 — PASS:** It explicitly partitions W (`None`, only `from_writable`) and S (`Some`, only `from_static`) and proves W only relative to the unsafe caller's ongoing contract. +- **I2 — PASS:** Its obligation table states `ptr::write` needs validity and alignment, proves the W branch conditionally, marks the S branch unsound, rejects the S comment as false, and rejects the W comment as missing the producer bridge and other conjuncts. +- **I3 — PASS:** `from_static(); overwrite(7)` is identified as wholly safe UB even when bits are unchanged; exact Rust 1.80 liveness, shared immutability, nonzero mutation, `ptr::write`, and `u8` layout close the result. +- **Hard error — None:** Both producer cases and the safe witness are explicit, and the finding goes beyond proof debt to authoritative UB. + +### G + +- **I1 — PASS:** The exhaustive invariant partition identifies W from `from_writable` and S from `from_static`; the W proof is expressly quantified only over calls satisfying the continuing unsafe contract. +- **I2 — PASS:** It states `ptr::write`'s validity/alignment requirements, marks S unsound, calls the `Some` comment false, and identifies the `None` comment's missing constructor-closure, alignment, and non-conflict reasoning. +- **I3 — PASS:** It supplies `from_static(); overwrite(0)` and derives same-byte identity, liveness throughout `with_live`, a one-byte mutation of shared-reference-protected storage, UB, and `UNSOUND` from exact Rust 1.80 authority. +- **Hard error — None:** Privacy is paired with complete producer inspection; the safe witness and direct UB proof are present. + +### H + +- **I1 — PASS:** Its table names both producers, and the text explicitly warns that the regional `from_writable` proof “cannot be reversed into an invariant of every `Buffer`” because `from_static` is a second producer. +- **I2 — PASS:** It states `ptr::write` needs write validity and alignment, establishes the `None` branch only conditionally, rejects the `Some` comment as false, and rejects the `None` comment for omitted alignment and dataflow. +- **I3 — PASS:** The safe `from_static(); overwrite(7)` witness is connected to same-byte pointer/reference identity, full-call liveness, immutable shared-reference bytes, a one-byte write, UB, and an overall `UNSOUND` verdict using exact Rust 1.80 pages. +- **Hard error — None:** It expressly avoids universal closure through `from_writable`, includes the safe witness, and proves rather than merely suspects UB. + +### I + +- **I1 — PASS:** The producer inventory names `from_writable` and `from_static`; the ledger proves the former only “for valid calls” under its ongoing obligations and treats fabricated states outside the safe-use theorem. +- **I2 — PASS:** It identifies validity/alignment/non-conflict at each write, says `from_static` creates the conflicting state, rejects the line-31 comment as false, and calls the line-38 comment deficient for missing producer/transition and alignment facts. +- **I3 — PASS:** Finding F-1 gives `from_static(); overwrite(0)` and exact Rust 1.80 liveness, immutable-byte, mutation, `ptr::write`, and `u8`-size premises, then concludes safe reachable UB and `UNSOUND`. +- **Hard error — None:** Both producers and both proof sites are treated; the witness and authoritative UB derivation are complete. + +### J + +- **I1 — PASS:** Its boundary table and W/S invariants enumerate both producers, with W carrying only the valid unsafe caller's continuing contract. +- **I2 — PASS:** It identifies write validity/alignment and non-conflict, says the S write cannot meet validity, rejects the S comment as inapplicable, and labels the W comment incomplete for missing the producer link and other obligations. +- **I3 — PASS:** F-1 supplies `from_static(); overwrite(0)` and stepwise derives full-call liveness, a positive-size same-byte write, immutable-byte mutation, failure of `ptr::write` validity, UB, and `UNSOUND` from exact Rust 1.80 documentation. +- **Hard error — None:** It uses privacy only as part of exhaustive representation closure and contains a concrete safe witness plus a completed UB proof. + +### K + +- **I1 — PASS:** The ledger names both constructors and distinguishes W from S; the W/`None` conclusion is explicitly relative to the valid unsafe-constructor contract. +- **I2 — PASS:** It says `from_static` does not establish W, marks the `Some` write unsound, rejects the line-31 implication, and rejects the line-36 comment for omitted alignment and producer derivation. +- **I3 — PASS:** It gives `from_static(); overwrite(0)` and exact Rust 1.80 same-location cast, liveness, shared immutability, nonzero mutation, `u8` size, and `ptr::write` support before concluding UB and `UNSOUND`. +- **Hard error — None:** The report enumerates both producers, supplies the safe witness, and closes UB directly rather than reporting vague proof debt. + +### L + +- **I1 — PASS:** It calls `from_writable` and `from_static` the complete producer set and limits the `None` proof to a valid `from_writable` call satisfying ongoing obligations. +- **I2 — PASS:** It states the exact `ptr::write` invariant, says the `Some` state never came from `from_writable`, calls its comment false, and calls the `None` comment incomplete for omitted alignment/conflict facts. +- **I3 — PASS:** It supplies the safe `from_static(); overwrite(9)` execution and exact Rust 1.80 pointer-identity, call-liveness, shared-byte immutability, one-byte mutation, and write-contract authority to establish UB and `UNSOUND`. +- **Hard error — None:** Both histories are independently analyzed; neither the safe witness nor the authoritative derivation is missing. + +### M + +- **I1 — PASS:** Its complete dataflow inventory names unsafe `from_writable` and safe `from_static`, and its W result remains conditional on the unsafe caller-maintained obligation rather than becoming universal. +- **I2 — PASS:** The ledger states the exact validity/alignment/non-conflict obligation, marks S unsound, rejects the `Some` comment because `from_writable` never occurred, and rejects the `None` comment for omitted closure and conjuncts. +- **I3 — PASS:** It gives `from_static(); overwrite(0)` and uses exact Rust 1.80 `core::ptr::write` and Reference liveness/immutable-byte/mutation rules to derive a same-byte live-reference conflict, UB, and safe-API `UNSOUND`. +- **Hard error — None:** Exhaustive producer reasoning accompanies privacy, and the safe witness receives a direct authoritative UB derivation. + +### N + +- **I1 — PASS:** Its two-state inventory names W from `from_writable` and S from `from_static`, with W's facts explicitly maintained by the valid unsafe caller throughout use. +- **I2 — PASS:** It applies `ptr::write` validity/alignment only to W, says the S obligation is false, calls the S comment materially false, and calls the W comment incomplete for alignment and the private-field/dataflow bridge. +- **I3 — PASS:** The safe `from_static(); overwrite(0)` witness is tied to same-location pointer conversion, `with_live` call liveness, shared-byte immutability, one-byte mutation, UB, and `UNSOUND` using exact Rust 1.80 sources. +- **Hard error — None:** It neither closes S through the unsafe contract nor stops at missing proof; the safe execution and authoritative UB result are explicit. + +### O + +- **I1 — PASS:** The boundary section enumerates both producers and the representation partition; the `None` result is expressly relative to `from_writable`'s caller contract. +- **I2 — PASS:** The ledger identifies `ptr::write` validity/alignment, marks the S write unsound, calls the `Some` comment materially false, and says the `None` comment omits alignment and the privacy/producer argument. +- **I3 — PASS:** It gives the entirely safe `from_static(); overwrite(7)` counterexample and derives pointer identity, full-call liveness, a one-byte mutation of shared-reference-protected storage, failed write validity, UB, and `UNSOUND` from exact Rust 1.80 documentation. +- **Hard error — None:** It audits both producers and both paths, does not treat privacy alone as proof, and includes the concrete safe witness and complete UB derivation. + +## Rubric or authority ambiguity + +No genuine ambiguity affects these scores. Report D uses the Rust 1.80 Reference rule that bytes owned by an immutable static are immutable (absent `UnsafeCell`) as its decisive UB route, while noting the live-reference aliasing concern. That is an explicit, version-matched equivalent to I3's shared-reference route, and `SCORER.md` expressly permits equivalent explicit reasoning. All other reports give the rubric's live-shared-reference route directly. No report depends materially on later documentation, implementation behavior, tests, project policy, Cargo metadata, or the rubric itself as a Rust axiom. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/N.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/N.md new file mode 100644 index 0000000000..4e8341cc77 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/N.md @@ -0,0 +1,74 @@ +# Mode N blind adjudication + +I resolved only the cells named in `DISAGREEMENTS.md`. Every other atom and every hard-error decision is preserved from the two blind scores. + +## Final atom table + +| Report | N1 | N2 | N3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | FAIL | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | FAIL | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +## Decisive evidence for disputed cells + +### A + +- **N1 — PASS.** A freshly labels the exact snapshot `UNSOUND`, gives the safe retained-`get`/later-`get_mut` program, establishes same-pointer access and liveness through the final read, and expressly adds that repeated `get_mut` calls can produce two simultaneously usable `&'a mut T` values. A also invokes the well-typedness premise rather than omitting it: the results use `'a` rather than the receiver-borrow lifetime, so the receiver borrow may end while the result lives. +- **N2 — PASS.** A identifies the enabling defect as `&'a T` and `&'a mut T` “rather than” receiver-tied results. The raw pointer appears only in the separate same-pointee derivation; A does not diagnose raw-pointer presence alone as the enabling defect. +- **N3 — PASS.** A changes both outputs to `&T`/`&mut T`, explicitly says elision ties them to their receivers, and calls the change an unimplemented candidate with “no verdict” pending fresh audit. + +These lifetime propositions are invoked in A and may therefore be checked rather than supplied. The exact [Rust 1.70 lifetime-elision rule](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html#lifetime-elision-in-functions) says each elided parameter lifetime becomes distinct and assigns the receiver lifetime only to elided output lifetimes. It therefore verifies both A's diagnosis of the current explicit `'a` outputs and A's receiver-bound elided repair. This adds no missing report premise or derivation. + +### D + +- **N1 — FAIL.** D freshly reports `UNSOUND` and fully derives the shared/mutable witness. It does not identify the other required witness: two live mutable results from repeated `get_mut` calls. Its only relevant sentence says safe code can call “another method” while a mutable result remains live. That does not state that the next call is `get_mut` or that it produces the second mutable result. Instantiating the generic phrase as a repeated `get_mut` call would add the material witness derivation that D omitted. + +### H + +- **N1 — FAIL.** H freshly reports `UNSOUND` and fully derives the mixed shared/mutable witness in `conflict`. It never identifies two simultaneous results of repeated `get_mut`. The repair discussion's phrase “allowing reuse of the capability” explains why a consuming accessor could be useful, but it does not instantiate that reuse as two `get_mut` calls or derive the duplicate-mutable witness. The missing route cannot be inferred for H. + +### O + +- **N1 — PASS.** O freshly reports `UNSOUND` and completely derives the safe repeated-`get_mut` witness: two independent receiver loans, two `'a` results from the unchanged pointer, simultaneous liveness in `take_both`, and the exact Rust 1.70 `UnsafeCell` multiple-`&mut` rule. It also explicitly gives the mixed route twice: `get_mut` can follow `get`, and fixing only `get_mut` would still let an old `'a` shared result overlap a later mutable result. +- **N2 — PASS.** O expressly diagnoses that both accessors return stored `'a`, not the receiver-loan lifetime, and says `PhantomData` carries the original borrow but does not connect results to individual receiver loans. Raw-pointer presence is not offered as the enabling defect. + +O invokes, rather than omits, the proposition that explicit `'a` outputs are unrelated to the elided receiver loans. The exact Rust 1.70 lifetime-elision text linked above verifies that proposition. Using that authority is permitted premise verification; it does not add a causal step or witness absent from O. + +## Evidence for preserved cells and hard-error decisions + +- **A — hard error none:** the repair is an “unimplemented candidate” with “no verdict” until an exact implementation is freshly audited. +- **B — N1/N2/N3 PASS; hard error none:** `collide` derives the mixed witness and the report expressly adds repeated `get_mut`; it identifies explicit `'a` outputs bypassing receiver elision; both replacement outputs are receiver-bound. The proposal is “unimplemented,” not `PROVED`, and requires fresh audit. +- **C — N1/N2/N3 PASS; hard error none:** C derives the mixed witness and expressly states the repeated-mutable route; it distinguishes struct `'a` from the receiver lifetime under Rust 1.70 elision; both repairs explicitly use receiver `'s`. The proposal has no verdict until implemented and re-audited. +- **D — N2/N3 PASS; hard error none:** D identifies explicit `'a` outputs rather than receiver lifetimes and cites the exact Rust 1.70 rule; both repaired outputs are receiver-elided. The patch is `UNIMPLEMENTED / UNPROVED` and must be re-audited. +- **E — N1/N2/N3 PASS; hard error none:** E derives repeated `get_mut` aliases in `touch` and separately the retained-shared route; it writes the effective receiver-`'s`/output-`'a` signature; both proposed outputs explicitly use `'s`. The proposal is unimplemented and unaudited. +- **F — N1/N2/N3 PASS; hard error none:** F derives the mixed witness and its obligation ledger expressly adds repeated mutable aliases; it expands both effective signatures and attributes the defect to `'a` outputs; both outputs are repaired. The candidate is `UNIMPLEMENTED and UNPROVED` pending fresh audit. +- **G — N1/N2/N3 PASS; hard error none:** G derives repeated mutable aliases through `clash` and expressly gives the retained-`get` analogue; it distinguishes the receiver lifetime from explicit `'a`; both outputs become receiver-bound. Both candidate designs are `UNIMPLEMENTED / NOT AUDITED`. +- **H — N2/N3 PASS; hard error none:** H says both outputs use `'a`, not the receiver-borrow lifetime, under exact Rust 1.70 authority; both repairs use receiver-elided outputs. All candidates receive no verdict and require fresh audit. +- **I — N1/N2/N3 PASS; hard error none:** I derives two `get_mut` results in `use_both` and expressly names the analogous mixed route; it identifies the independent receiver and impl lifetimes; both repair variants use receiver-bound outputs. The repair is unimplemented and must be freshly audited before `PROVED`. +- **J — N1/N2/N3 PASS; hard error none:** J derives repeated mutable results in `collide` and explicitly gives the analogous mixed route; it attributes both failures to outputs not tied to receiver borrows; both signatures are repaired. The proposal is not implemented and receives no verdict pending full review. +- **K — N1/N2/N3 PASS; hard error none:** K derives repeated mutable aliases through `write_both` and separately identifies retained `get` versus later `get_mut`; it identifies distinct receiver and `'a` result lifetimes; both outputs are changed. The candidates are expressly not audited or `PROVED`. +- **L — N1/N2/N3 PASS; hard error none:** L derives the mixed witness and expressly says successive `get_mut` calls yield coexisting aliases; it identifies `'a` as unrelated to the receiver lifetime; both repairs are receiver-bound. The proposal is `UNIMPLEMENTED / UNPROVED AS SOURCE` pending fresh review. +- **M — N1/N2/N3 PASS; hard error none:** M fully derives two mutable results in `duplicate` and expressly gives the analogous mixed route; it writes the effective receiver-`'s`/output-`'a` signature; both repaired outputs explicitly use `'s`. The change is an unproved proposal requiring implementation and fresh review. +- **N — N1/N2/N3 PASS; hard error none:** N fully derives the mixed witness and expressly adds repeated `get_mut`; it displays both effective signatures with independent receiver lifetimes and `'a` outputs; both repairs are receiver-elided. The proposal is unimplemented, not `PROVED`, and requires implementation audit. +- **O — N3 PASS; hard error none:** O changes both signatures to explicit receiver/output `'s`, requires both changes, and labels the proposal `UNPROVED` until implemented and re-audited as a new artifact. + +Thus no report certifies an unimplemented proposal, so the hard error applies to none of A–O. + +## Genuine rubric ambiguity + +N1 names “both safe alias witnesses” without specifying how much of the second derivation may be expressed by shorthand. Under `SCORER.md`, a second code block and duplicated UB proof are not required: an explicitly named analogous route passes when the already-stated lifetime, pointer-identity, liveness, and alias-rule reasoning covers it. But a generic phrase such as “another method” or “capability reuse” does not identify the repeated-`get_mut` route. This distinction follows the frozen prohibition on inferring a material premise and produces D/H `FAIL` without imposing a formatting preference. + +The phrase “the report verifies” could, in isolation, be read to require an inline report citation for every Rust premise. The controlling adjudication instruction resolves that issue here: exact external authority may verify a premise the report invokes, but may not add one it omitted. A and O explicitly invoke the relevant lifetime relationship, so the exact Rust 1.70 text verifies it; D and H omit a witness derivation, which authority cannot supply. No other genuine ambiguity changes a cell. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/P.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/P.md new file mode 100644 index 0000000000..ca8371ef4d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/P.md @@ -0,0 +1,63 @@ +# Mode P blind adjudication + +## Final atom table + +| Report | P1 | P2 | P3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | **FAIL** | PASS | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +**Tally:** 44/45 atoms pass; no hard errors. + +`DISAGREEMENTS.md` makes G:P1 the only live cell. Every other atom and every hard-error decision is preserved exactly as agreed. + +## Disputed cell: G:P1 — FAIL + +G proves the complete `Page` side: `ALIGN = 16`; `repr(C)` puts the sole field at offset zero; `repr(align(16))` raises the containing alignment; `as_ptr` selects the live array buffer; and the `[u8; 16]` is initialized, non-null, 16-aligned, and live for the receiver borrow. G also expressly chooses the receiver-borrow interval and correctly says a shorter interval would leave `first` `UNPROVED`. + +The generic-`first` derivation has a different, material gap. G says the contract supplies “readability,” then says A3 “consequently permits `*p`” and that every possible byte is a valid `u8`. It never states, for an arbitrary conforming `Block`, that it is operationally reading `readable` to entail an initialized byte and a non-atomic load permitted without aliasing or data-race UB; nor does it condition the proof on that implication. Its statement that `Page`'s initialized array is therefore readable proves only the `Page` direction, not the converse needed from the public prose for arbitrary implementers. “Every possible byte is a valid `u8`” addresses initialized `u8` bit patterns, not whether the memory contains an initialized value. + +G's own A3 is summarized as forbidding dangling/misaligned loads and invalid produced values. The exact Rust 1.70 [undefined-behavior page](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html) also treats data races and aliasing violations separately and warns that its list is non-exhaustive. That authority verifies the negative rules G invokes; it does not establish the converse that eliminating G's listed cases positively permits the load. Supplying the missing contract-to-initialized/read-permitted implication during adjudication would violate the frozen rule against silently adding a material premise. + +Thus G establishes `Page` and the temporal part of `first`, but not `first`'s current generic soundness. This is `UNPROVED`, not `UNSOUND`; the atom fails because P1 requires both conjuncts. + +## Compact evidence for preserved cells + +- **A:** P1 enumerates and proves the full `Page` contract and gives exact C1/C2 conditional premises for `first`; P2 preserves all clauses for unknown consumers/implementers; P3 keeps proof work and an additive lane in 1.x while reserving weakening/sealing/removal for 2.0. **Hard error: none**—the future design expressly needs a fresh audit. +- **B:** P1 explicitly reads `readable` as initialized read permission through the borrow and proves `Page` plus the one-byte load; P2 says local search cannot close either public side; P3 separates private proof factoring/additive runway from major contract changes. **Hard error: none**—the capability split is prospective and requires re-audit. +- **C:** P1 lists all five obligations, proves them for `Page`, and states the initialized/shared-read operational meaning used by `first`; P2 treats downstream implementers and consumers as open quantified sets; P3 retains `Block` for local/additive work and reserves changing it for 2.0. **Hard error: none**—C says no edit is authorized and offers only a migration possibility. +- **D:** P1 normalizes the contract to 16 initialized readable bytes for the live receiver borrow and proves both producer and one-byte consumer; P2 rejects repository search as authority over downstreams; P3 distinguishes local proof work from breaking extent, alignment, item, or boundary changes. **Hard error: none**—the replacement choices are future major-version options. +- **E:** P1 proves B1–B5 for `Page` and precisely conditions `first` on initialized read permission plus a post-return interval; P2 preserves the open public boundary; P3 retains the legacy surface for proof/additive work and assigns strengthening or weakening to 2.0. **Hard error: none**—the unimplemented replacement is explicitly subject to fresh audit. +- **F:** P1 proves `Page` and makes `first` conditional on a live, initialized, provenance/access-permitted, non-conflicting read; P2 states unknown downstream uses and impls prevent narrowing; P3 confines 1.x to equivalent proof/internal/additive work. **Hard error: none**—the safe method is a future endpoint, not a certified artifact. +- **G (agreed cells):** P2 says unknown consumers may use the full extent/alignment and unknown implementers prevent sealing or strengthening; P3 limits 1.x to proof/documentation and independent APIs and places capability splitting in 2.0. **Hard error: none**—G requires a fresh audit of any implemented 2.0 snapshot. +- **H:** P1's ledger proves `Page` and the load while expressly caveating any newly added initialization/provenance/interference duty; P2 covers both unknown public sides; P3 keeps narrow/additive 1.x work distinct from the shown 2.0 replacement. **Hard error: none**—the code is labeled a design sketch whose implemented form needs audit. +- **I:** P1 explicitly defines readable as initialized non-atomic load permission without lifetime, aliasing, or race UB and proves both sites; P2 preserves the open ecosystem contract; P3 distinguishes comments/internal/parallel work from breaking changes. **Hard error: none**—the migration artifact is prospective and requires fresh audit. +- **J:** P1 states the live-allocation, initialized-byte, permitted-read meaning and the weaker-meaning conditional, then proves `Page` and `first`; P2 covers consumer weakening and implementer strengthening; P3 separates compatible proof/additive work from 2.0 changes. **Hard error: none**—both designs are future and require fresh audit. +- **K:** P1 proves the strong `Page` contract and precisely leaves `first` conditional on initialization, live access, and race freedom without manufacturing unsoundness; P2 preserves every clause for unknown public parties; P3 reserves strengthening, weakening, sealing, and replacement for 2.0. **Hard error: none**—no edit is authorized and no absent code is certified. +- **L:** P1 proves `Page` and expressly identifies the live/provenance/initialized-load implication used conditionally by `first`; P2 states both downstream sets remain unknown; P3 permits an internal lemma/equivalent proof text while reserving contract reduction or strengthening for 2.0. **Hard error: none**—the safe alternatives are choices, not audited implementations. +- **M:** P1 proves every `Page` conjunct and gives the exact missing initialized, non-conflicting byte-zero implication and conditional closure for `first`; P2 protects unknown consumers and implementations; P3 keeps proof/additive migration work in 1.x and breaking changes in 2.0. **Hard error: none**—the sample is explicitly a proposed migration needing fresh audit. +- **N:** P1 explicitly expands readable into live, allocated, initialized read permission and proves the complete provider plus immediate load; P2 rejects local search for both public directions; P3 stages a safe additive lane while retaining legacy `Block` until 2.0. **Hard error: none**—the replacement remains prospective and must be re-audited. +- **O:** P1 proves the whole `Page` contract and precisely states the live-allocation, initialized-`u8`, alias/race-safe, post-return condition needed by `first`, then closes conditionally; P2 preserves unknown consumers and impls; P3 separates equivalent proof/additive work from authorized-major changes. **Hard error: none**—the preferred design is future work explicitly requiring fresh audit. + +## Hard-error decisions + +All A–O are **None**. Each report treats its changed API as a proposal, migration option, design sketch, or future major-version endpoint; none certifies an implemented replacement. Several expressly demand that implemented source receive a fresh audit. G's deficient proof of the current `first` is an atom failure, not the sole frozen hard error (“certifying an unimplemented proposal”). + +## Genuine ambiguities + +1. The source does not define whether “readable” entails initialization, provenance/access permission, alias/race freedom, or precisely which borrow interval survives `base`'s return. The rubric deliberately permits either an explicit operational reading or an exact conditional proof, so reports taking either complete route pass. +2. There is a real boundary between an operational reading and a conclusory assertion. No preferred vocabulary is required, but the report must actually state the missing implication or make the proof conditional on it. I resolve G on logical content: “A3 consequently permits `*p`” is the conclusion whose material premise is absent, not an operational definition of that premise. +3. Exact Rust 1.70 authority cannot prove an unbounded future `1.70+` range. The reports preserve this as a compatibility premise, cutoff qualification, or unresolved portion; under `SCORER.md`, that treatment is acceptable and does not alter the frozen cells. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/T.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/T.md new file mode 100644 index 0000000000..39edf7532a --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/T.md @@ -0,0 +1,139 @@ +# Mode T blind score + +`PASS` means every material proposition in the atom is established (including an explicit equivalent treatment). `FAIL` identifies a missing or contrary material proposition. Hard errors are assessed independently. + +| Report | T1 | T2 | T3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | FAIL | Proposal laundering | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | FAIL | Proposal laundering | +| G | PASS | PASS | FAIL | Proposal laundering | +| H | PASS | PASS | FAIL | Proposal laundering | +| I | PASS | PASS | PASS | None | +| J | PASS | FAIL | PASS | UB execution used as a behavioral counterexample | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | FAIL | Proposal laundering | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +Totals: T1 15/15 PASS; T2 14/15 PASS; T3 10/15 PASS; 6 reports have a hard error. + +## Report-by-report evidence + +### A + +- **T1 PASS:** It selects Rust 1.70.0 with debug assertions disabled, follows safe `ticket(0)` to `new_unchecked(0)`, cites the exact 1.70 contracts, concludes UB, and labels the current API `UNSOUND`. +- **T2 PASS:** It expressly calls the zero-input postcondition `UNPROVED, not CONTRACT-BROKEN`, because the witness contains UB and is not a UB-free behavioral counterexample. +- **T3 PASS:** Its `NonZeroUsize::new`/`match`/`panic!` body is an explicit equivalent to `new(...).expect(...)`; it preserves the body-level signature and documented cases across profiles. It says the sketch receives no `PROVED` verdict and requires audit of the implemented snapshot. +- **Hard error: none:** It neither certifies the proposal nor treats UB as defined behavior; its material Rust premises are tied to Rust 1.70 documentation. + +### B + +- **T1 PASS:** It establishes that an ordinary assertions-disabled Rust 1.70 build lets safe `ticket(0)` reach `new_unchecked(0)`, violating the cited nonzero precondition and making the API `UNSOUND`. +- **T2 PASS:** It says the path cannot establish the documented panic and that observation after UB is not meaningful; it does not use the path as a defined contract counterexample. +- **T3 FAIL:** Although the `new(id).expect(...)` candidate and its behavior/configuration proof are otherwise adequate, the report opens with `Proposed redesign verdict: PROVED for Rust 1.70`. That contradicts the required unimplemented-and-unaudited status. +- **Hard error — proposal laundering:** Restricting the certification to Rust 1.70 and leaving the open-ended later range unproved does not cure certification of source that was never implemented and freshly audited. + +### C + +- **T1 PASS:** It gives the required disabled-assertion `ticket(0) -> new_unchecked(0) -> UB` execution and current `UNSOUND` verdict using exact Rust 1.70 documentation. +- **T2 PASS:** It explicitly declines `CONTRACT-BROKEN` because the counterexample reaches UB rather than a defined non-panicking outcome. +- **T3 PASS:** It proposes `new(id).expect(...)`, proves preservation of nonzero return, zero panic, signature, targets, and profiles, then states that the proposal receives no verdict until implemented and audited as a new snapshot. +- **Hard error: none:** No proposal certification or UB-based behavioral counterexample appears, and its additional exact-version 1.97.1 continuity statement is not incorrect. + +### D + +- **T1 PASS:** It uses the Rust 1.70 disabled-debug-assertion path to show `ticket(0)` calls `new_unchecked(0)`, reaches UB, and makes the safe API `UNSOUND`. +- **T2 PASS:** Although it initially says the promise is “not met,” it immediately makes the controlling distinction: the witness has UB and is not assigned a separate non-UB `CONTRACT-BROKEN` verdict. In context this is failure of proof, not a defined counterexample. +- **T3 PASS:** The checked `new` plus exhaustive `match` and `panic!` is equivalent to `expect`; the report covers the signature, both input cases, and configuration scope, and withholds `PROVED` pending implementation and audit. +- **Hard error: none:** The contextual qualification prevents the “not met” wording from using UB as defined behavior; the proposal remains uncertified. + +### E + +- **T1 PASS:** It follows assertions-disabled `ticket(0)` to the exact Rust 1.70 `new_unchecked(0)` UB condition and concludes `UNSOUND`. +- **T2 PASS:** It says the panic behavior is unproved and rejects `CONTRACT-BROKEN` because no well-defined non-panicking execution was established. +- **T3 PASS:** It supplies `new(id).expect(...)`, verifies both inputs and configuration independence while preserving the public surface, and calls it a design requiring audit after implementation. +- **Hard error: none:** The proposal is not awarded a post-change verdict, the UB path is not a behavioral counterexample, and the cited Rust premises are version matched. + +### F + +- **T1 PASS:** It gives the safe zero-input, disabled-assertion Rust 1.70 UB path and an unqualified current `UNSOUND` verdict. +- **T2 PASS:** It says the zero panic is not established and that the already-undefined witness warrants no separate `CONTRACT-BROKEN` verdict. +- **T3 FAIL:** The `new(id).expect(...)` body and preservation proof are adequate, but it declares replacement soundness and behavior `PROVED for Rust 1.70` and, conditionally, for 1.70+. It never preserves the candidate’s required uncertified status. +- **Hard error — proposal laundering:** The explicit compatibility premise only qualifies version reach; it cannot certify an unimplemented, unaudited replacement. + +### G + +- **T1 PASS:** It identifies disabled assertions, safe zero input, violation of the exact Rust 1.70 unchecked-constructor contract, UB, and current `UNSOUND`. +- **T2 PASS:** It calls the panic guarantee unproved and explains that the witness itself reaches UB rather than supporting `CONTRACT-BROKEN`. +- **T3 FAIL:** Despite a correct `new(id).expect(...)` design and case/configuration argument, it assigns `Redesign verdict: PROVED for Rust 1.70.0` instead of requiring implementation and fresh audit. +- **Hard error — proposal laundering:** Its open-ended-version qualification does not remove the prohibited verdict on the unimplemented Rust 1.70 candidate. + +### H + +- **T1 PASS:** It correctly partitions the inputs/assertion settings and shows that the disabled-zero branch reaches Rust 1.70 `new_unchecked(0)` UB, establishing current `UNSOUND`. +- **T2 PASS:** It says the zero panic is not proved over all profiles and treats soundness failure, not a defined contract counterexample, as the terminal result. +- **T3 FAIL:** The report validates a suitable `new(id).expect(...)` replacement, but begins with `Proposed implementation — PROVED` for Rust 1.70 and conditionally for later releases. It does not require a post-implementation snapshot audit before that verdict. +- **Hard error — proposal laundering:** A conditional TCB can qualify premises but cannot turn proposed text into an audited artifact. + +### I + +- **T1 PASS:** It explicitly derives the assertions-disabled safe `ticket(0)` path to `new_unchecked(0)`, cites Rust 1.70, concludes UB, and labels the current API `UNSOUND`. +- **T2 PASS:** It labels the full-set zero panic `UNPROVED, not CONTRACT-BROKEN` because the known witness contains UB. +- **T3 PASS:** It gives the checked `new(id).expect(...)` candidate, proves signature/behavior/configuration preservation, and explicitly says a proposal receives no artifact verdict and needs a fresh implemented-snapshot review. +- **Hard error: none:** All three prohibited hard-error patterns are avoided. + +### J + +- **T1 PASS:** It correctly shows that Rust 1.70 with debug assertions disabled permits safe `ticket(0)` to reach `new_unchecked(0)` UB and labels the API `UNSOUND`. +- **T2 FAIL:** It expressly assigns `CONTRACT-BROKEN via the same path`, whereas that path contains UB and can establish only that the panic guarantee is unproved. +- **T3 PASS:** Its checked `new`/`match`/`panic!` candidate is equivalent to `expect`, preserves the safe public contract and profile scope, and is explicitly left as a design pending implementation and re-audit. +- **Hard error — UB execution used as a behavioral counterexample:** The later sentence that this is “not a separate defined-behavior defect” does not retract the explicit `CONTRACT-BROKEN` verdict “via the same path”; the report assigns the forbidden behavioral result nonetheless. + +### K + +- **T1 PASS:** It provides the complete safe-zero, assertions-disabled, Rust 1.70 unchecked-zero UB derivation and current `UNSOUND` verdict. +- **T2 PASS:** It explicitly labels the panic clause `UNPROVED`, rejects `CONTRACT-BROKEN`, and explains why UB cannot witness defined failure to panic. +- **T3 PASS:** It gives `new(id).expect(...)`, covers both inputs and all relevant configurations while preserving the public surface, and withholds a post-change verdict until exact-source audit. +- **Hard error: none:** It avoids proposal certification, defined post-UB reasoning, and incorrect version results. + +### L + +- **T1 PASS:** It correctly derives UB from disabled `debug_assert!` plus safe zero input under the exact Rust 1.70 contracts and concludes current `UNSOUND`. +- **T2 PASS:** It expressly says `UNPROVED, not CONTRACT-BROKEN`, because the disabled execution contains UB. +- **T3 PASS:** It proposes `new(id).expect(...)`, proves preservation across inputs and configurations, and says this is not a verdict for unimplemented source; application must be followed by re-audit of the new snapshot. +- **Hard error: none:** Its “intended post-change” result is conditional on applying and re-auditing, so it is not proposal laundering; no incorrect exact-version result was found. + +### M + +- **T1 PASS:** It establishes the disabled-assertion `ticket(0) -> new_unchecked(0) -> UB` witness and current `UNSOUND` verdict from exact Rust 1.70 contracts. +- **T2 PASS:** It labels behavior `UNPROVED` and explicitly refuses `CONTRACT-BROKEN` because Rust supplies no post-UB behavioral conclusion. +- **T3 FAIL:** Its checked `new(id).expect(...)` source and preservation reasoning are suitable, but it declares `Proposed redesign — PROVED for Rust 1.70, all targets and profiles` without first requiring implementation and a fresh snapshot audit. +- **Hard error — proposal laundering:** The compatibility TCB qualifies later versions only and does not validate the unimplemented proposal as an artifact. + +### N + +- **T1 PASS:** It uses exact Rust 1.70 documentation to show safe `ticket(0)` reaches unchecked zero with assertions disabled, causing UB and current `UNSOUND`. +- **T2 PASS:** It explicitly marks zero panic `UNPROVED`, not `CONTRACT-BROKEN`, because the disabled execution contains UB. +- **T3 PASS:** The report proposes `new(id).expect(...)`, preserves the exact public items and configuration-independent behavior, calls it a conditional proof plan rather than a verdict, and requires fresh review after implementation. +- **Hard error: none:** It avoids all enumerated hard errors. + +### O + +- **T1 PASS:** It gives the exact disabled-debug-assertion path from safe zero input to Rust 1.70 `new_unchecked(0)` UB and concludes current `UNSOUND`. +- **T2 PASS:** It declines `CONTRACT-BROKEN` because the branch is undefined rather than a defined execution violating only the postcondition. +- **T3 PASS:** It supplies `new(id).expect(...)`, establishes both documented branches and profile/target independence while preserving the signature, and explicitly withholds `PROVED` until implementation and audit. +- **Hard error: none:** No proposal laundering, defined post-UB counterexample, or wrong exact-version conclusion appears. + +## Ambiguities + +No authority ambiguity changes a score. The Rust 1.70 propositions material to T1–T3 are supported by the exact-version standard-library pages the reports cite; the additional Rust 1.97.1 continuity statements in C and L agree with the exact-version [`debug_assert!`](https://doc.rust-lang.org/1.97.1/std/macro.debug_assert.html), [`NonZero::new_unchecked`](https://doc.rust-lang.org/1.97.1/std/num/struct.NonZero.html#method.new_unchecked), and [`Option::expect`](https://doc.rust-lang.org/1.97.1/std/option/enum.Option.html#method.expect) documentation and do not produce an incorrect exact-version result. + +Two wording questions were resolved directly by the scorer instructions rather than by guessing: + +1. T3 names `new(id).expect(...)`, while A, D, and J use `new` plus an exhaustive `match` whose `None` arm calls `panic!`. This is the same checked construction and explicit case proof, so it passes under the instruction that equivalent explicit reasoning passes. +2. D says the panic promise is “not met,” which alone could suggest a behavioral counterexample. Its immediately following statement says the witness has UB and expressly refuses a non-UB `CONTRACT-BROKEN` verdict. Read as a whole, D leaves the promise unestablished and therefore passes T2 and the hard-error check. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/U.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/U.md new file mode 100644 index 0000000000..969854903f --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/U.md @@ -0,0 +1,135 @@ +# Mode U blind score + +## Atom table + +| Report | U1 | U2 | U3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | **FAIL** | PASS | **Yes — uses the input-zero UB execution as a behavioral refutation** | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +The reports' controlling authority is consistent with the exact-version official text. Rust/core 1.80.0 says that reaching [`unreachable_unchecked`](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety) is UB. The Rust 1.80.0 Reference specifies [first-matching-arm selection](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html), [literal-pattern equality](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns), and [return of an evaluated function-tail expression](https://doc.rust-lang.org/1.80.0/reference/items/functions.html#function-body). Its [undefined-behavior chapter](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html) also confirms that safe code triggering UB makes unsafe code unsound. No later-version premise is needed. + +## Report-by-report evidence + +### A + +- **U1 PASS:** It derives selection of the `0` arm from exact 1.80.0 match/literal rules, quotes the exact-version `unreachable_unchecked` contract, and concludes that the unrestricted safe call `classify(0)` is UB and the API is **UNSOUND**. +- **U2 PASS:** It says the zero-input panic promise is “not established” and that “UB cannot supply a guaranteed panic outcome”; it does not use zero as a defined behavioral counterexample. +- **U3 PASS:** It separately derives that `classify(1)` normally returns `2`, notes `2 != 1`, and calls this a defined counterexample independent of the UB finding. +- **Hard error: none:** The API is not called sound, no zero-run observation is guaranteed, and the input-one refutation is explicit and independent. + +### B + +- **U1 PASS:** S-1 traces valid safe input `0` through the literal/match rules to `unreachable_unchecked`, then uses its 1.80.0 contract to conclude **UNSOUND**. +- **U2 PASS:** B-1 says UB supplies no Rust behavioral guarantee and the panic promise is “not established”; it does not assign `CONTRACT-BROKEN` on that basis. +- **U3 PASS:** B-2 uses the separate `1 => 2` execution, expressly calls it defined, and establishes `2 != 1` and **CONTRACT-BROKEN**. +- **Hard error: none:** Its zero-path treatment is non-observational and its one-path refutation is separate. + +### C + +- **U1 PASS:** S1 uses exact 1.80.0 numeric, pattern, match, and `unreachable_unchecked` authority to show the admitted safe call `classify(0u8)` reaches UB and makes the API **UNSOUND**. +- **U2 PASS:** D1 expressly labels the panic clause **UNPROVED** and says the UB execution establishes neither a defined panic nor a UB-free behavioral counterexample. +- **U3 PASS:** D2 expressly identifies `classify(1u8)` as a separate UB-free execution returning `2`, hence a counterexample to the normal-return postcondition. +- **Hard error: none:** It states the whole-execution rule correctly and keeps the two witnesses independent. + +### D + +- **U1 PASS:** It correctly traces safe input `0` to the unsafe call under exact 1.80.0 match/pattern and library authority and concludes **UNSOUND**. +- **U2 FAIL:** It says “Both documented clauses fail,” describes zero as reaching UB “rather than providing the promised panic,” and later says the panic clause is “not upheld.” That treats the UB-containing execution as a behavioral refutation instead of leaving the zero-input guarantee **UNPROVED**. +- **U3 PASS:** Independently, it identifies the UB-free `classify(1)` execution, normal result `2`, and `2 != 1` refutation. +- **Hard error: yes:** The quoted zero-case reasoning is exactly “using an observation from the input-zero execution as a behavioral refutation.” It does not additionally call the API sound or miss/conflate the independent input-one refutation. + +### E + +- **U1 PASS:** O-SOUND derives that valid safe input `0` selects the unsafe arm and reaches a function whose exact 1.80.0 contract makes reachability UB; verdict **UNSOUND**. +- **U2 PASS:** O-PANIC labels the guarantee **UNPROVED**, rejects the UB execution as a `CONTRACT-BROKEN` witness, and says no separate UB-free zero witness exists. +- **U3 PASS:** O-IDENTITY uses an “independent safe call” at input `1`, calls the context UB-free, and derives normal result `2 != 1`. +- **Hard error: none:** All three dispositions are separated exactly as the rubric requires. + +### F + +- **U1 PASS:** F1 states that safe input `0u8` selects the `0` arm and reaches `unreachable_unchecked`; its exact 1.80.0 contract and exact-version unsoundness authority support the **UNSOUND** verdict. +- **U2 PASS:** Although the ledger says “not upheld / subsumed by S1,” the operative proposition is that UB acts “rather than establishing a defined panic” and prevents “any source-level guarantee”; it does not label B1 `CONTRACT-BROKEN` or claim an observed non-panic. +- **U3 PASS:** F2 expressly calls `classify(1)` independent and defined, with the unsafe arm unexecuted, and derives normal result `2` rather than `1`. +- **Hard error: none:** In context, “not upheld” means no guarantee is established, while the behavioral refutation is expressly based on input one. + +### G + +- **U1 PASS:** S1/U1 and AX-1/AX-2 trace the admitted zero input to reached UB and the **UNSOUND** verdict. +- **U2 PASS:** B1 says the selected zero arm supplies no defined outcome and the panic guarantee is “not proved.” +- **U3 PASS:** B2 uses exact match and function-body authority to derive that the separate input-one path normally returns `2 != 1`, without executing unsafe code. +- **Hard error: none:** It makes no post-UB observation and explicitly separates the one-input counterexample. + +### H + +- **U1 PASS:** F1 uses exact Rust/core 1.80.0 pattern, match, and callee-safety text to show that valid safe input zero reaches UB and makes the API **UNSOUND**. +- **U2 PASS:** It says the zero panic clause “is not established,” supplies no defined panic behavior, and has no normal-return case under defined semantics. +- **U3 PASS:** F2 uses exact function-tail authority and the separate, unsafe-arm-free input-one path to derive normal result `2 != 1`. +- **Hard error: none:** It expressly says the independent input-one case, not zero, is why the behavior verdict is `CONTRACT-BROKEN`. + +### I + +- **U1 PASS:** Its exhaustive derivation uses exact-version `unreachable_unchecked`, match, and literal-pattern premises to show safe input zero reaches UB; verdict **UNSOUND**. +- **U2 PASS:** It says the zero panic promise is “not established” and that the path reaches UB “rather than a defined panic,” without using that as the contract-breaking witness. +- **U3 PASS:** It separately derives that input one does not execute the unsafe arm and normally returns `2 != input`, calling this a defined counterexample. +- **Hard error: none:** Soundness, unresolved zero behavior, and the defined one-input defect remain distinct. + +### J + +- **U1 PASS:** It verifies that zero is a valid `u8`, traces exact-version literal/match selection to the unsafe call, quotes the 1.80.0 UB contract, and concludes **UNSOUND**. +- **U2 PASS:** It says zero “does not establish” the promised defined panic and that after UB no behavior is guaranteed; it records “No guaranteed panic,” not a UB-free refutation. +- **U3 PASS:** Its table independently records input one returning `2`, no unsafe operation on that path, and `2 != 1` as **CONTRACT-BROKEN**. +- **Hard error: none:** There is no guaranteed post-UB observation and no conflation of witnesses. + +### K + +- **U1 PASS:** Exact 1.80.0 match/pattern and library axioms establish that valid safe zero selects the unsafe arm and produces UB; verdict **UNSOUND**. +- **U2 PASS:** It expressly labels the zero panic promise **UNPROVED** and says the UB execution can establish neither a panic observation nor the UB-free witness needed for `CONTRACT-BROKEN`. +- **U3 PASS:** Exact function-return authority supports its expressly independent UB-free `classify(1)` witness returning `2 != 1`. +- **Hard error: none:** Its treatment directly states and respects every prohibited conflation. + +### L + +- **U1 PASS:** S1 and the exact 1.80.0 callee contract establish that safe input zero reaches `unreachable_unchecked`, hence UB and **UNSOUND**. +- **U2 PASS:** The B1 result is “UNSOUND; no panic guarantee is established,” and the prose says backend behavior after UB cannot establish a source-level guarantee. This leaves B1 unresolved rather than deriving a defined non-panic. +- **U3 PASS:** B2 separately identifies `1 => 2` as a normally returning path that executes no unsafe operation and establishes `2 != 1`. +- **Hard error: none:** “B1 is also not guaranteed” is clarified as lack of establishment; `CONTRACT-BROKEN` is independently based on input one. + +### M + +- **U1 PASS:** Exact 1.80.0 literal, match, and `unreachable_unchecked` premises show the admitted safe input zero reaches UB; S1/U1 are correctly refuted. +- **U2 PASS:** The headline calls the panic promise “not established as defined Rust behavior.” The ledger’s “Not satisfied as a defined source behavior” is read consistently with that explicit no-proof disposition, not as a UB-free behavioral refutation. +- **U3 PASS:** It separately derives that input one selects `1 => 2`, returns normally, does not rely on the UB path, and conclusively refutes B2. +- **Hard error: none:** B1 is not labeled refuted or `CONTRACT-BROKEN`; the aggregate contract verdict is explicitly supported by input one. + +### N + +- **U1 PASS:** S1 uses exact 1.80.0 match/literal and callee-safety authority to show the valid safe call at zero reaches UB and makes the API **UNSOUND**. +- **U2 PASS:** B1 is expressly **UNPROVED**; it says the UB whole execution proves neither panic nor a UB-free `CONTRACT-BROKEN` witness. +- **U3 PASS:** B2 expressly uses the separate UB-free input-one execution returning normally with `2 != 1`. +- **Hard error: none:** The report precisely separates all three rubric propositions. + +### O + +- **U1 PASS:** S1 uses exact 1.80.0 `u8`, pattern, match, and `unreachable_unchecked` authority to show the unrestricted safe zero call reaches UB; verdict **UNSOUND**. +- **U2 PASS:** B2 labels the zero panic guarantee **UNPROVED** and says no independent UB-free derivation establishes either fulfillment or refutation. +- **U3 PASS:** B1 uses exact match/function-return authority to establish a separate UB-free input-one execution normally returning `2 != 1`. +- **Hard error: none:** Its atom numbering differs from the rubric labels, but the propositions are unambiguously correct and independent. + +## Genuine rubric/authority ambiguities + +1. **“Not upheld/not satisfied” wording in F, L, and M.** In isolation, those phrases could denote a behavioral refutation. In context, however, F says UB does not establish a defined panic, L says no panic guarantee is established, and M's headline says the promise is not established; all three anchor `CONTRACT-BROKEN` solely in the independent input-one execution. I therefore score U2 as PASS and do not assign a hard error. D is materially different because it expressly says both documented clauses “fail.” +2. **Authority granularity for routine source evaluation.** Some reports cite separate exact-version rules for match selection and function-tail return, while others state those elementary steps directly from the exact source and cite only the materially unsafe callee contract (most notably F and L). The scorer does not define whether every routine syntax step needs its own authority link. I treated explicit source tracing of `0 => ...` and `1 => 2` as establishing those elementary steps; I did not infer an omitted control-flow premise. All reports verify the decisive, version-sensitive `unreachable_unchecked` contract against Rust/core 1.80.0. No cited authority conflicts with the exact 1.80.0 official text. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/V.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/V.md new file mode 100644 index 0000000000..1df134b80b --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/final/V.md @@ -0,0 +1,240 @@ +# Mode V blind score + +The exact versioned authorities confirm the rubric's split: `[u8; 0]` has size +zero; Rust 1.79.0's `add` contract unconditionally requires the start and result +to be in or one-past the same allocated object; Rust 1.80.0 guards that +allocation condition on a nonzero byte offset and expressly makes a zero offset +always well-defined. Each report is scored independently below. + +## Atom and hard-error table + +| Report | V1 | V2 | V3 | V4 | Hard error | +|---|---|---|---|---|---| +| A | PASS | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | PASS | None | +| H | PASS | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | PASS | None | + +## Report-by-report evidence + +### A + +- **V1 PASS:** `O1` cites both versioned Reference array-layout pages, derives + `size_of::<[u8; 0]>() == 0`, and computes `1 * 0 = 0` bytes. +- **V2 PASS:** `O2-79` cites the 1.79 `add`, `null`, and pointer-safety text, + explains that null fails the unconditional allocated-object condition, and + gives `let _ = advance_marker();` as the safe-call UB witness. +- **V3 PASS:** `O2-80` cites the 1.80 zero-offset sentence, discharges the + arithmetic clauses, and notes that the raw pointer is not dereferenced. +- **V4 PASS:** The verdict table says 1.79 `UNSOUND`, 1.80 `PROVED`, and the + union `UNSOUND`; the TCB section expressly rejects cross-version premises. +- **Hard error: none:** Both regional verdicts are proved, and both the + zero-size derivation and null safe-call witness are present. + +### B + +- **V1 PASS:** The boundary section cites the two versioned array-layout rules + and derives a zero byte offset for `add(1)`. +- **V2 PASS:** The ledger marks 1.79 `O2` failed; the derivation cites exact + 1.79 `null`, pointer-safety, and `add` text and identifies an ordinary safe + invocation as the unconditional UB witness. +- **V3 PASS:** The report uses only the 1.80 `add` contract for its express + zero-offset exception and observes that returning the raw pointer does not + dereference it. +- **V4 PASS:** It reports 1.79 `UNSOUND`, 1.80 `PROVED`, combined `UNSOUND`, + and explicitly says the 1.80 wording is not applied backward. +- **Hard error: none:** No uniform unsupported verdict, wrong region, backward + projection, or missing required derivation/witness occurs. + +### C + +- **V1 PASS:** `O-ADD` cites version-matched `size_of` pages and derives + `size_of::<[u8; 0]>() = 0` and offset `1 * 0 = 0`. +- **V2 PASS:** The 1.79 subsection cites the exact `add` and pointer-safety + pages, states that null is neither in nor one-past an allocation, and says + every safe call reaches UB. +- **V3 PASS:** The 1.80 subsection cites “always well-defined” for zero offset, + checks the arithmetic, and notes that no dereference occurs. +- **V4 PASS:** The opening table partitions both releases and gives the union + `UNSOUND`; the TCB is separately versioned. +- **Hard error: none:** The complete regional proof and counterexample include + all hard-error-sensitive propositions. + +### D + +- **V1 PASS:** The configuration/ledger derives + `1 * size_of::<[u8; 0]>() = 0` from exact 1.79/1.80 `size_of` authorities. +- **V2 PASS:** `O3` is marked violated for 1.79; the prose cites its + unconditional same-allocation `add` clause and identifies any ordinary call + to this argument-free safe function as the UB witness. +- **V3 PASS:** The report cites 1.80's express zero-offset exception, checks + `isize`/`usize`, and observes there is no dereference or reference creation. +- **V4 PASS:** The opening verdicts correctly give 1.79 `UNSOUND`, 1.80 + `PROVED`, and combined `UNSOUND` without reusing 1.80 text for 1.79. +- **Hard error: none:** Every regional and combined verdict is supported, with + the null witness and ZST arithmetic explicit. + +### E + +- **V1 PASS:** The inventory cites exact versioned `size_of` pages and computes + the byte offset as `1 * 0 = 0`. +- **V2 PASS:** The 1.79 derivation cites `add`, `null`, and contemporaneous + pointer-safety text; it explains the failed allocation conjunct and calls + every ordinary invocation a valid safe-use counterexample. +- **V3 PASS:** The 1.80 derivation cites the changed clause and “always + well-defined” sentence, checks arithmetic, and excludes later dereference. +- **V4 PASS:** Its table gives the two correct regional verdicts and combined + `UNSOUND`; its TCB says no compatibility inference crosses versions. +- **Hard error: none:** None of the listed hard-error conditions applies. + +### F + +- **V1 PASS:** The common derivation cites exact 1.79/1.80 `size_of` contracts, + establishes the ZST, and computes zero bytes. +- **V2 PASS:** The 1.79 section cites the unconditional allocation wording and + null-validity text, then says every safe call reaches the violating `add`. +- **V3 PASS:** The 1.80 section cites the zero-offset exception and raw-pointer + nullability and states that any dereference would be a separate unsafe act. +- **V4 PASS:** The verdict table partitions the releases and reports their + union `UNSOUND`; the TCB is exact-version scoped. +- **Hard error: none:** The report proves rather than merely asserts all three + verdicts and includes both mandatory witness components. + +### G + +- **V1 PASS:** Common local fact 2 uses exact versioned Reference array-layout + links; fact 3 computes `1 * 0 = 0`. +- **V2 PASS:** The 1.79 regional derivation cites its exact `add` contract, + states why null satisfies neither allocation alternative, and identifies + every safe call as a concrete UB counterexample. +- **V3 PASS:** The 1.80 derivation cites its express zero-offset rule, checks + both arithmetic constraints, and notes no dereference occurs. +- **V4 PASS:** Despite placeholder region labels, the text unambiguously names + 1.79 `UNSOUND`, 1.80 `PROVED`, and their union `UNSOUND`; each TCB link is + version matched. +- **Hard error: none:** The placeholders do not obscure any material + proposition, and no listed substantive error occurs. + +### H + +- **V1 PASS:** `OB-1` cites both exact `size_of` pages and derives the + zero-sized pointee and zero byte offset. +- **V2 PASS:** The 1.79 section cites exact `add` and pointer-module text, + explains why null fails the unconditional allocation condition, and uses a + plain safe invocation as the UB witness. +- **V3 PASS:** The 1.80 section cites the changed contract, checks zero's + representability/address arithmetic, and notes no reference or access. +- **V4 PASS:** The verdict table has both correct regions and combined + `UNSOUND`; the report expressly says the 1.80 text is not projected backward. +- **Hard error: none:** All hard-error-sensitive facts are present and correct. + +### I + +- **V1 PASS:** The ledger cites exact versioned Reference array layout and + computes `size_of::<[u8; 0]>() = 0` and a zero byte offset. +- **V2 PASS:** The 1.79 subsection cites that version's `add`, derives the + failed allocation clause from the null start, and gives an unconditional + ordinary safe call as witness. +- **V3 PASS:** The 1.80 subsection cites the express exception, checks the + remaining clauses, and states that no dereference/reference is formed. +- **V4 PASS:** The report gives separate correct verdicts and combined + `UNSOUND`; its TCB admits no cross-version compatibility premise. +- **Hard error: none:** Its extra edition observation does not alter or weaken + the proved requested partition; no rubric hard error applies. + +### J + +- **V1 PASS:** `O-1` cites exact 1.79/1.80 `size_of` pages and derives + `1 * 0 = 0` independently of target/profile. +- **V2 PASS:** `O-2/1.79` cites `null`, pointer safety, and `add`; it explains + the allocation failure and supplies `let _ = advance_marker();` as witness. +- **V3 PASS:** `O-2/1.80` cites the express zero-offset sentence, checks + arithmetic, and notes that returning the pointer is no further unsafe act. +- **V4 PASS:** The table correctly partitions and combines the regions, and + the TCB says no later documentation was carried backward. +- **Hard error: none:** All required propositions and the witness are proved. + +### K + +- **V1 PASS:** `O-SIZE` cites exact versioned official `std::mem::size_of` + pages and computes the zero byte offset. +- **V2 PASS:** `O-179` cites exact 1.79 official `std` reexports for `null`, + pointer safety, and `add`, then identifies every safe call as UB at line 4. +- **V3 PASS:** `O-180` cites the exact 1.80 `std` pointer contract, checks the + arithmetic clauses, and notes there is no access/dereference. +- **V4 PASS:** The table reports the two correct regions and combined + `UNSOUND`; the TCB confines each contract to its exact release. +- **Hard error: none:** Using official `std` documentation rather than the + equivalent `core` pages is permitted and introduces no material gap. + +### L + +- **V1 PASS:** The derivation cites exact release-specific `size_of` pages and + computes `1 * size_of::<[u8; 0]>() = 0`. +- **V2 PASS:** The 1.79 subsection cites exact `add`, `null`, and pointer-safety + text, marks the allocation conjunct false, and identifies all safe calls as + the counterexample. +- **V3 PASS:** The 1.80 subsection cites the explicit zero exception, checks + `isize`/`usize`, and observes no access/reference is created. +- **V4 PASS:** Its table gives 1.79 `UNSOUND`, 1.80 `PROVED`, and combined + `UNSOUND`; the TCB is version matched. +- **Hard error: none:** No uniform-verdict, regional, projection, witness, or + size-derivation error appears. + +### M + +- **V1 PASS:** Common facts 1 and 3 cite both exact versioned `size_of` pages + and derive the zero-byte offset. +- **V2 PASS:** The 1.79 section cites exact `add` plus the exact 1.79 Reference + dangling-pointer rule (including its zero-size/nonzero-literal alternatives), + correctly excludes address-zero `null`, and says every call reaches UB. +- **V3 PASS:** The 1.80 section cites “always well-defined,” discharges both + arithmetic clauses, and only returns the pointer. +- **V4 PASS:** The opening verdicts correctly partition and combine the set; + the TCB expressly forbids compatibility inference across releases. +- **Hard error: none:** The regional proofs include the complete ZST + derivation and null safe-call counterexample. + +### N + +- **V1 PASS:** `O1` cites exact versioned Reference array layout and computes + the byte offset `1 * 0 = 0`. +- **V2 PASS:** `O2` cites exact 1.79 `null` and `add`, explains that null is + neither in-bounds nor one-past an allocation, and calls invocation of the + safe API a valid counterexample. +- **V3 PASS:** `O3` cites exact 1.80 `null` and `add`, applies the explicit zero + exception, checks arithmetic, and notes the lack of dereference. +- **V4 PASS:** Its verdict table correctly gives both regions and combined + `UNSOUND`; the TCB explicitly says 1.80 is not projected backward. +- **Hard error: none:** Every enumerated hard-error prerequisite is satisfied. + +### O + +- **V1 PASS:** The coverage section cites exact versioned Reference array + layout and derives the zero byte offset. +- **V2 PASS:** `O-79` cites exact 1.79 `null`, pointer safety, and `add`, states + why null fails the allocation conjunct, and identifies any safe call as UB. +- **V3 PASS:** `O-80` cites the exact 1.80 exception, checks all arithmetic, + and notes that the returned raw pointer is not dereferenced. +- **V4 PASS:** The opening table correctly partitions both releases and gives + the required union `UNSOUND`; the TCB contains no compatibility premise. +- **Hard error: none:** No wrong or unproved verdict, backward projection, or + missing witness/derivation is present. + +## Genuine rubric or authority ambiguities + +None. The exact Rust 1.79.0 and 1.80.0 official documentation resolves the +only version-sensitive premise in the same way as the rubric, and all reports +provide enough version-matched authority and explicit reasoning to score every +atom without inference of an unstated material premise. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/invalid/T-s2-attempt-1.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/invalid/T-s2-attempt-1.md new file mode 100644 index 0000000000..cf5964ed7e --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/invalid/T-s2-attempt-1.md @@ -0,0 +1,133 @@ +# Mode T blind score + +| Report | T1 | T2 | T3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | FAIL | Proposal laundering | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | FAIL | Proposal laundering | +| G | PASS | PASS | FAIL | Proposal laundering | +| H | PASS | PASS | FAIL | Proposal laundering | +| I | PASS | PASS | PASS | None | +| J | PASS | FAIL | PASS | UB-containing execution used for a behavioral `CONTRACT-BROKEN` finding | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | FAIL | Proposal laundering | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +## Report-by-report evidence + +### A + +- **T1 PASS:** It selects Rust 1.70.0 with debug assertions disabled, traces safe `ticket(0)` to `new_unchecked(0)`, identifies UB, and calls the current API `UNSOUND`. +- **T2 PASS:** It expressly says the zero-input panic postcondition is `UNPROVED`, not `CONTRACT-BROKEN`, because the witness contains UB. +- **T3 PASS:** Its exhaustive `NonZeroUsize::new` match is an explicit equivalent of `new(...).expect(...)`; it preserves the signature/documentation and profile/target scope, and says the sketch receives no `PROVED` verdict until implementation and audit. +- **Hard error: none:** It neither certifies the proposal nor treats UB as a defined behavioral counterexample, and its versioned claims are supported. + +### B + +- **T1 PASS:** It correctly traces assertions-disabled `ticket(0)` to the forbidden unchecked construction and concludes `UNSOUND`. +- **T2 PASS:** It says the panic outcome cannot be established and that a later behavioral observation is not meaningful after UB; it makes no `CONTRACT-BROKEN` finding. +- **T3 FAIL:** Although the `new(id).expect(...)` candidate and its behavior/configuration argument are correct, the report labels the unimplemented redesign `PROVED for Rust 1.70` rather than leaving it uncertified pending implementation and fresh audit. +- **Hard error — proposal laundering:** The explicit post-change `PROVED` verdict is assigned while the report also says the replacement is only conceptual and no source edit occurred. + +### C + +- **T1 PASS:** It identifies safe `ticket(0)` with assertions disabled as a Rust-1.70 UB witness and gives the current aggregate verdict `UNSOUND`. +- **T2 PASS:** It explicitly refuses a separate `CONTRACT-BROKEN` verdict because the execution reaches UB rather than a defined non-panicking result. +- **T3 PASS:** It supplies the checked `new(...).expect(...)` candidate, covers signature, panic behavior, targets/profiles, and says the proposal receives no verdict until implemented and audited. +- **Hard error: none:** The exact Rust 1.97.1 release and the cited retained contracts are real; the candidate is not laundered into an artifact verdict. + +### D + +- **T1 PASS:** It traces optimized/assertions-disabled safe `ticket(0)` to `new_unchecked(0)` and correctly returns `UNSOUND`. +- **T2 PASS:** Read with its immediate qualification, “not met” means not established: it says the UB witness is included in the soundness finding and is not a separate `CONTRACT-BROKEN` result. +- **T3 PASS:** The checked `new`/`match`/`panic!` form is an explicit equivalent; it preserves the required interface and behavior across configurations and is expressly denied a `PROVED` verdict until implementation and audit. +- **Hard error: none:** Its UB discussion explicitly avoids a defined-behavior counterexample, and it does not certify the proposal. + +### E + +- **T1 PASS:** It uses the disabled-debug-assertion case and the exact unchecked-constructor precondition to establish UB and `UNSOUND`. +- **T2 PASS:** It calls the zero behavior unproved and declines `CONTRACT-BROKEN` because no well-defined non-panicking execution was established. +- **T3 PASS:** It proposes `new(id).expect(...)`, proves preservation of the signature, panic behavior, and configuration scope, and calls it a design requiring audit of the implemented snapshot. +- **Hard error: none:** No prohibited certification, UB-based behavioral counterexample, or incorrect version result appears. + +### F + +- **T1 PASS:** It correctly partitions on whether `debug_assert!` executes and shows disabled-assertion `ticket(0)` reaches UB, making the API `UNSOUND`. +- **T2 PASS:** It says the panic is not established and that the UB witness does not support a separate `CONTRACT-BROKEN` verdict. +- **T3 FAIL:** The checked candidate preserves all requested behavior and scope, but the report declares replacement soundness and behavior `PROVED for Rust 1.70` before implementation/fresh audit. +- **Hard error — proposal laundering:** A counterfactual replacement is given a certification verdict. + +### G + +- **T1 PASS:** It gives the required Rust-1.70, assertions-disabled safe-call trace to `new_unchecked(0)` and `UNSOUND`. +- **T2 PASS:** It expressly calls the panic guarantee unproved and rejects a separate `CONTRACT-BROKEN` verdict because the witness is UB. +- **T3 FAIL:** Its `new(...).expect(...)` candidate preserves the required surface and behavior, but it assigns the redesign a `PROVED for Rust 1.70.0` verdict without implementation and fresh audit. +- **Hard error — proposal laundering:** The design is certified as though it were an audited artifact. + +### H + +- **T1 PASS:** It correctly establishes that safe `ticket(0)` reaches the UB unchecked call with assertions disabled and concludes `UNSOUND`. +- **T2 PASS:** It says the zero-input panic is “not proved” across profiles and relies only on the soundness defect, not a defined behavioral counterexample. +- **T3 FAIL:** Despite a correct checked candidate and configuration proof, the opening verdict calls the proposed implementation `PROVED`; no implementation or fresh audit occurred. +- **Hard error — proposal laundering:** The report explicitly certifies the proposal. + +### I + +- **T1 PASS:** It gives the exact disabled-assertion `ticket(0)` path, unchecked precondition violation, UB, and `UNSOUND` verdict. +- **T2 PASS:** It explicitly labels the panic guarantee `UNPROVED`, not `CONTRACT-BROKEN`, because the known witness contains UB. +- **T3 PASS:** It gives `new(...).expect(...)`, covers signature/behavior/configurations, and says the proposal receives no artifact verdict until implemented and reviewed. +- **Hard error: none:** It avoids every listed hard-error category. + +### J + +- **T1 PASS:** It correctly traces an assertions-disabled Rust-1.70 safe call to `new_unchecked(0)` and labels the current artifact `UNSOUND`. +- **T2 FAIL:** It explicitly assigns `CONTRACT-BROKEN via the same path`, whereas that path contains UB and can only leave the always-panic guarantee unproved. +- **T3 PASS:** Its checked `new` plus exhaustive match/panic is equivalent to `expect`, preserves the requested surface and scope, and is called a design requiring implementation followed by re-audit. +- **Hard error — UB as behavioral counterexample:** Labeling the contract broken “via the same path” uses the UB-containing execution to support a behavioral verdict; the later statement that it is not a separate defined-behavior defect does not cure the contradictory verdict. + +### K + +- **T1 PASS:** It correctly establishes the disabled-assertion safe-call path to UB and the `UNSOUND` result. +- **T2 PASS:** It explicitly labels the panic clause `UNPROVED`, not `CONTRACT-BROKEN`, because the optimized witness contains UB. +- **T3 PASS:** It gives the checked `expect` candidate, covers signature, panic behavior, targets/profiles, and states that the counterfactual redesign gets no post-change `PROVED` verdict before implementation and review. +- **Hard error: none:** No listed hard error is present. + +### L + +- **T1 PASS:** It identifies the exact unchecked obligation and shows assertions-disabled `ticket(0)` violates it, yielding UB and `UNSOUND`. +- **T2 PASS:** It expressly says the full-set panic guarantee is `UNPROVED, not CONTRACT-BROKEN` because the release path has UB. +- **T3 PASS:** It supplies `new(...).expect(...)`, establishes interface/behavior/configuration preservation, and distinguishes a design proof from an artifact verdict, requiring audit after application. +- **Hard error: none:** Rust 1.97.1 existed at the cutoff and retains the cited propositions; the report does not certify unimplemented source. + +### M + +- **T1 PASS:** It traces disabled-assertion `ticket(0)` to `new_unchecked(0)`, UB, and the current `UNSOUND` verdict. +- **T2 PASS:** It labels documented behavior `UNPROVED` and explicitly declines `CONTRACT-BROKEN` because the failing path is UB. +- **T3 FAIL:** Its candidate preserves the exact surface, behavior, and profiles, but the report gives the unimplemented redesign a `PROVED for Rust 1.70` verdict instead of requiring implementation and fresh audit before certification. +- **Hard error — proposal laundering:** The candidate is promoted to a proved redesign artifact. + +### N + +- **T1 PASS:** It correctly gives the supported assertions-disabled `ticket(0)` execution, exact unsafe-contract violation, UB, and `UNSOUND` verdict. +- **T2 PASS:** It explicitly calls the zero panic `UNPROVED`, not `CONTRACT-BROKEN`, because the disabled execution contains UB. +- **T3 PASS:** It proposes checked `new(...).expect(...)`, covers exact signature/documentation and configuration scope, calls it “not applied,” with no post-change verdict, and requires a fresh source review. +- **Hard error: none:** It avoids proposal certification, UB-based contract refutation, and version error. + +### O + +- **T1 PASS:** It exhaustively partitions inputs/configuration and correctly identifies assertions-disabled `ticket(0)` as UB, concluding `UNSOUND`. +- **T2 PASS:** It says the zero behavior is not established and explicitly refuses `CONTRACT-BROKEN` because there is no defined behavioral counterexample. +- **T3 PASS:** It supplies `new(...).expect(...)`, proves preservation across the requested surface/configurations, calls it a design rather than a post-change `PROVED` artifact, and requires implementation audit. +- **Hard error: none:** No hard-error trigger applies. + +## Rubric and authority ambiguities + +1. **Equivalent checked form:** T3 names `new(id).expect(...)`, while the scorer instructions permit explicit equivalents. A, D, and J use `new(id)` followed by an exhaustive `match` whose `None` arm calls `panic!`. I score that as equivalent because it establishes the same checked construction, signature, panic behavior, and configuration scope. +2. **D's “not met” wording:** In isolation this could sound like a behavioral refutation. Its immediate statement that the UB witness is not a `CONTRACT-BROKEN` finding resolves the ambiguity in favor of the required `UNPROVED` treatment. J is different because it expressly assigns `CONTRACT-BROKEN via the same path`. +3. **Rust 1.97.1:** C and L make an exact-version claim. Rust 1.97.1 was officially released on 2026-07-16, before their stated cutoff, and its official docs retain the relevant `debug_assert!` and `NonZero::new_unchecked` propositions. I therefore do not apply the exact-version hard error. Sources: [Rust 1.97.1 announcement](https://blog.rust-lang.org/2026/07/16/Rust-1.97.1/), [1.97.1 `debug_assert!`](https://doc.rust-lang.org/1.97.1/std/macro.debug_assert.html), and [1.97.1 `NonZero::new_unchecked`](https://doc.rust-lang.org/1.97.1/std/num/struct.NonZero.html#method.new_unchecked). + diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/A-s1.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/A-s1.md new file mode 100644 index 0000000000..fbca8e6651 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/A-s1.md @@ -0,0 +1,140 @@ +# Mode A blind score + +## Authority baseline + +The literal source and exact Rust 1.70 authorities support the rubric's factual baseline. `Pair(pub [u32; 2])` declares one tuple-struct field whose declared type is `[u32; 2]`; `.0` accesses that field, while `[1]` separately accesses an array element. The Reference says a tuple index is a field name and evaluates to that field's location, and expressly distinguishes array elements as requiring array indexing ([Rust 1.70 tuple indexing](https://doc.rust-lang.org/1.70.0/reference/expressions/tuple-expr.html)). It also says an array is a fixed-size sequence whose elements are initialized ([Rust 1.70 arrays](https://doc.rust-lang.org/1.70.0/reference/types/array.html)). The Rust 1.70 `addr_of_mut!` contract says it creates a raw pointer without an intermediate reference while the operand remains subject to the usual expression rules ([Rust 1.70 `addr_of_mut!`](https://doc.rust-lang.org/1.70.0/core/ptr/macro.addr_of_mut.html)). Thus `"tail"` is not a direct declared `Pair` field and `.0[1]` is not one, but for a precondition-satisfying owner the shown place is the live, initialized second `u32`. That establishes the distinction required by A1 and A2. + +## Atom and hard-error table + +| Report | A1 | A2 | A3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +## Report-by-report evidence + +### A + +- **A1 — PASS.** It says `Pair` has only direct field `0: [u32; 2]`, that `"tail"` names no direct field, and that `(*owner).0[1]` is nested rather than direct. +- **A2 — PASS.** It expressly separates `CONTRACT-BROKEN` from `UNSOUND`, stating that the returned pointer is valid for every precondition-satisfying call and reconstructing the in-bounds `u32` proof at Rust 1.70. +- **A3 — PASS.** It rejects the exact immutable snapshot, treats the modular update only as actual behavior, and says the function name supplies no invented postcondition; it offers no redesign. +- **Hard error — none.** It certifies only the displayed implementation on explicit scoped versions and marks the unbounded future region `UNPROVED`; no unimplemented proposal is certified. + +### B + +- **A1 — PASS.** Its “Finding” identifies both independent failures: `FIELD = "tail"` cannot name the sole `[u32; 2]` field, and `.0[1]` is a nested array element. +- **A2 — PASS.** It gives an expressly UB-free `Pair([0, 0])` witness to the false postcondition, then proves the concrete projection and wrapper sound at Rust 1.70; it says no UB witness is known or needed. +- **A3 — PASS.** It applies the controlling provider obligations literally, rejects under the accept/reject policy, and does not propose a source, contract, or API change. +- **Hard error — none.** All positive certifications concern the implemented `project` and `increment_tail` bodies; there is no proposal to certify. + +### C + +- **A1 — PASS.** It states that `.0` is the only direct field and has array type, while `"tail"` is not its name and `.0[1]` is nested. +- **A2 — PASS.** Its verdict table independently marks the contract broken and concrete Rust 1.70 execution sound, and its proof derives a valid aligned initialized pointer to the second `u32`. +- **A3 — PASS.** It rejects the supplied snapshot, does not substitute an intended “tail” contract, and records the actual update without making it a replacement API contract. +- **Hard error — none.** It reviews and certifies existing code only, leaving the open-ended later-version claim unresolved. + +### D + +- **A1 — PASS.** It explains that `Pair` declares no direct field named `tail` and no direct `u32` field, and that the projection targets an array element inside field `0`. +- **A2 — PASS.** It says the mismatch proves `CONTRACT-BROKEN`, not `UNSOUND`, and separately derives that the nested `u32` pointer and wrapper reborrow are valid at Rust 1.70. +- **A3 — PASS.** It explicitly says inferred intent cannot rewrite “direct declared field,” rejects the exact snapshot, and supplies no redesign. +- **Hard error — none.** No unimplemented change is proposed or certified. + +### E + +- **A1 — PASS.** It finds both the false `FIELD` guarantee and the false method postcondition, distinguishing the sole direct array field from element `.0[1]`. +- **A2 — PASS.** It labels implementation soundness proved on its stated exact-version regions, says the broken descriptive postcondition creates no UB path, and derives the live aligned initialized second element. +- **A3 — PASS.** It confines itself to acceptance of the supplied snapshot, rejects it, and does not infer a substitute contract or suggest edits. +- **Hard error — none.** Its certifications apply to the implemented code, not an unimplemented proposal. + +### F + +- **A1 — PASS.** Its verdict lists the missing direct field/name/type and the fact that `.0[1]` is nested, so both provider promises are false. +- **A2 — PASS.** It says those false postconditions do not by themselves establish `UNSOUND`, then proves the concrete pointer reaches the initialized, aligned second `u32` and the wrapper has no competing access. +- **A3 — PASS.** It rejects the snapshot under the literal provider contract and neither replaces that contract nor proposes an alternative API. +- **Hard error — none.** It certifies only the extant bodies and explicitly leaves the full open-ended soundness region unproved. + +### G + +- **A1 — PASS.** “Finding F-01” says `Pair` has one direct `[u32; 2]` field, no `tail` or direct `u32` field, and `project` returns its nested element. +- **A2 — PASS.** It expressly separates source-level soundness from `CONTRACT-BROKEN`, uses `increment_tail(&mut Pair([0, 0]))` as a UB-free contract witness, and proves pointer/reference validity for the concrete path. +- **A3 — PASS.** It says policy disallows correcting the source/contracts, treats the name as non-normative, and recommends only rejection, not redesign. +- **Hard error — none.** No proposed implementation is presented or certified; the report evaluates the supplied implementation and marks later releases for re-review. + +### H + +- **A1 — PASS.** It states that the sole direct field is `.0: [u32; 2]`, `"tail"` names none, and `.0[1]` is a nested element rather than a direct field. +- **A2 — PASS.** It explicitly says the contract defect is not itself an exhibited UB execution and separately proves the valid in-bounds `u32` projection and safe wrapper at Rust 1.70. +- **A3 — PASS.** It insists inferred intent cannot rewrite the literal words and makes no redesign or patch proposal. +- **Hard error — none.** It certifies only the shown implementation in a bounded exact-version region. + +### I + +- **A1 — PASS.** Its “Contract counterexample” identifies both unconditional failures: nonexistent direct `tail`/`u32` field and nested `.0[1]` result. +- **A2 — PASS.** Its ledger independently proves call precondition, projection, and reborrow/update, then labels the provider guarantees `CONTRACT-BROKEN` without claiming an UB witness. +- **A3 — PASS.** It records actual modular behavior while saying `increment_tail` has no broader written postcondition, rejects the exact snapshot, and proposes no alternative. +- **Hard error — none.** Every positive result concerns source that is present; later versions are a re-review trigger, not a certified proposal. + +### J + +- **A1 — PASS.** It says tuple field `.0` is the sole direct `[u32; 2]` field and `.0[1]` is an array element, so `FIELD` and `project` each violate the direct-field guarantees. +- **A2 — PASS.** It calls valid precondition-satisfying projection UB-free, proves the raw-pointer reborrow/update, and expressly says no concrete safe-wrapper `UNSOUND` finding exists. +- **A3 — PASS.** It states the function name is not a normative behavioral contract and confines its recommendation to rejection of the supplied snapshot. +- **Hard error — none.** It certifies implemented source only and treats post-cutoff releases as unproved. + +### K + +- **A1 — PASS.** It describes `0: [u32; 2]` as the only direct field, rejects `"tail"`, and distinguishes `.0` field selection from nested `[1]` selection. +- **A2 — PASS.** It says the postcondition counterexample does not require UB and reconstructs why the projection and wrapper designate a valid initialized aligned `u32` at Rust 1.70. +- **A3 — PASS.** It applies the literal contracts, notes that no broader safe-function behavior is documented, rejects the immutable snapshot, and does not redesign it. +- **Hard error — none.** There is no proposed code or contract whose future implementation is certified. + +### L + +- **A1 — PASS.** Obligations 1 and 2 separately find the nonexistent direct `tail`/`u32` field and the nested array-element return. +- **A2 — PASS.** Obligations 3 and 4 separately prove `project` and `increment_tail` memory-safe at Rust 1.70 and expressly state that no UB counterexample is established. +- **A3 — PASS.** Although it mentions the element may be “operationally” intended as the tail, it does not substitute that intent for the literal contract; it rejects without proposing changes. +- **Hard error — none.** Its positive proof covers only code present in the snapshot. + +### M + +- **A1 — PASS.** It identifies the missing direct field and the nested `.0[1]` result, with a defined call as the method-postcondition refutation. +- **A2 — PASS.** It labels whole-range soundness `UNPROVED`, not `UNSOUND`, and independently proves the concrete Rust 1.70 pointer targets a valid `u32` and the wrapper reborrow is exclusive. +- **A3 — PASS.** It says the function name creates no postcondition, honors the no-change request, and proposes no patch, migration, or alternate API. +- **Hard error — none.** No unimplemented proposal is certified. + +### N + +- **A1 — PASS.** Its obligation table independently marks `FIELD` and `project` `CONTRACT-BROKEN`, explaining direct field `0` versus nested array element `1`. +- **A2 — PASS.** The same table separately proves valid-call projection and safe-wrapper soundness at Rust 1.70, including an expressly UB-free witness. +- **A3 — PASS.** It says the name cannot supply a contract and notes that no changes are proposed because the request forbids them. +- **Hard error — none.** The report certifies the implemented operations only. + +### O + +- **A1 — PASS.** Its decisive finding says there is no direct `"tail"` or `u32` field and that the macro returns a pointer to an element nested inside `.0`. +- **A2 — PASS.** It explicitly distinguishes proved soundness from broken postconditions, derives a valid initialized aligned second `u32`, and says no valid use reaching UB was found. +- **A3 — PASS.** It rejects the exact snapshot, uses the actual implementation rather than rewriting the false promise, and offers no redesign. +- **Hard error — none.** Its positive claims concern existing code; future releases are left unproved rather than treated as an implemented proposal. + +## Ambiguities + +No rubric ambiguity changes a score. + +There is a wording tension in Rust documentation between describing tuple-struct fields as “anonymous” and describing numeric tuple indices as field names. It is immaterial here: under either terminology, `"tail"` is not a direct declared field, the sole declared field has type `[u32; 2]`, and `[1]` selects an element nested inside it. + +Several reports prove only Rust 1.70 exactly, while some additionally claim one or more later-version or finite-cutoff regions and leave the future tail unresolved. A2 does not require certification of every release in the open-ended request; it requires separating the literal contract failure from the concrete valid projection and not inventing an UB counterexample. Every report establishes that proposition with exact Rust 1.70 authority and explicitly avoids treating later documentation as proof of the unbounded future. Accordingly, finite-cutoff phrasing does not alter any A2 result. No report certifies an unimplemented redesign, so the sole hard-error rule never triggers. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/A-s2.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/A-s2.md new file mode 100644 index 0000000000..8812a2a3aa --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/A-s2.md @@ -0,0 +1,134 @@ +# Mode A blind score + +`PASS` means the report establishes every material proposition in the atom at the scope it claims. For A2, I treated an affirmative soundness certification over Rust releases not supported by version-matched authority as an A2 failure, even when the report separately gives a correct Rust 1.70 proof. That follows `SCORER.md`'s version-matching rule and the request for the strongest *scoped* result. + +| Report | A1 | A2 | A3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | FAIL | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | FAIL | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | FAIL | PASS | None | +| J | PASS | FAIL | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | FAIL | PASS | None | + +## Report-by-report evidence + +### A + +- **A1 — PASS.** A:16–21 identifies `Pair`'s only direct field as field `0: [u32; 2]`, says `"tail"` names no direct field, and distinguishes the nested `.0[1]` element from a direct field. +- **A2 — PASS.** A:8–10 and A:21 expressly separate `CONTRACT-BROKEN` from `UNSOUND`; A:27–33 derives that the concrete projection is in-bounds, aligned, initialized, and exclusive. The affirmative soundness regions are limited to 1.70.0 and 1.97.1, with exact 1.70 authority and an explicit exact-1.97.1 recheck of the material address-of, validity/aliasing, and layout rules; A:10 leaves the rest of open-ended 1.70+ unresolved. +- **A3 — PASS.** A:5 rejects the immutable snapshot, A:35 refuses to invent a contract from the function name, and no redesign or substitute snapshot is offered. +- **Hard error — none.** The report certifies no proposed or unimplemented change; it rejects the source as supplied. + +### B + +- **A1 — PASS.** B:46–64 states that the sole direct field is the array, that there is neither a direct `u32` field nor a field named `tail`, and that `.0[1]` is a nested element. +- **A2 — PASS.** B:12–22 scopes its proof to exact Rust 1.70.0 and leaves later/future releases unproved. B:58–64 gives a UB-free postcondition witness, while B:68–96 supplies the version-matched projection, coercion, borrow, and wrapping reasoning. +- **A3 — PASS.** B:24–25 rejects solely on the literal breach; B:37–42 states the literal obligations and declines to infer behavior from the function name. It proposes no change. +- **Hard error — none.** No unimplemented proposal is certified. + +### C + +- **A1 — PASS.** C:18–20 identifies `.0: [u32; 2]` as the only direct field and `.0[1]` as an element nested within it, falsifying both literal clauses. +- **A2 — PASS.** C:10–14 separates exact-1.70 soundness from contract compliance and open-ended uncertainty. C:26–31 proves that the shown projection yields the valid second `u32` and explicitly says the concrete proof does not consume the false direct-field promise. +- **A3 — PASS.** C:5 and C:14 reject the unchanged candidate, and the report neither rewrites the contract nor proposes an API/source alternative. +- **Hard error — none.** It certifies only the inspected implementation at its supported scope, not a proposal. + +### D + +- **A1 — PASS.** D:11–18 quotes both direct-field duties and shows that `Pair` instead has one `[u32; 2]` field, with the result pointing to its nested element. +- **A2 — PASS.** D:5 distinguishes contract breakage from the exact-1.70 proof and leaves later releases unproved. D:22–30 establishes the valid second-`u32` pointer and says explicitly that the contract breach does not make this wrapper unsound. +- **A3 — PASS.** D:5 rejects the exact snapshot and D:30 does not turn the function name into a postcondition; no repair or alternate interface is proposed. +- **Hard error — none.** No unimplemented proposal is certified. + +### E + +- **A1 — PASS.** E:7 correctly gives both literal counterexamples: `"tail"` is no direct `Pair` field, and `.0[1]` is nested inside the array field. +- **A2 — FAIL.** E:8 certifies soundness for both 1.70.0 and 1.97.1. The actual soundness derivation at E:15–16 cites only Rust 1.70 layout, array/index, `addr_of_mut!`, coercion, UB, and `wrapping_add` authority; E's only 1.97.1 citation is the structural tuple-field source in E:7. E:9 asserts that governing text was checked at 1.97.1 but supplies no version-matched soundness authority. Its 1.70 separation and valid-pointer proof are correct, but the unsupported additional certification makes the scoped A2 result fail under `SCORER.md`. +- **A3 — PASS.** E:5 rejects, E:9 says the contract defect controls acceptance, and no redesign or inferred replacement contract appears. +- **Hard error — none.** The overbroad endpoint certification concerns the implemented code, not an unimplemented proposal, so the frozen hard-error rule does not apply. + +### F + +- **A1 — PASS.** F:9–17 identifies both false literal guarantees and distinguishes direct field `0` from nested element `.0[1]`. +- **A2 — PASS.** F:25–42 expressly says no UB was established and confines affirmative proof to exact Rust 1.70.0. F:59–87 supplies version-matched coercion, layout, projection, pointer-validity, aliasing, and modular-update reasoning. +- **A3 — PASS.** F:5 rejects, F:31–33 refuses to manufacture a public postcondition, and F:101–102 rejects rather than redesigning. +- **Hard error — none.** No proposal is made or certified. + +### G + +- **A1 — PASS.** G:78–91 correctly says the only direct field is `.0: [u32; 2]`, while `"tail"` and `.0[1]` satisfy neither literal provider guarantee. +- **A2 — FAIL.** G:26–29 and G:99–104 certify every stable release from 1.70.0 through 1.97.1. Its authority is only 1.70.0 for `addr_of_mut!` and 1.97.1 for the later macro wording, UB/reference validity, and wrapping behavior (G:56–73); it verifies no matching text for the intervening releases and admits no compatibility premise. G:18–19 correctly separates the defect from UB, but its material interval-wide `PROVED` claim is unsupported. +- **A3 — PASS.** G:5–9 rejects because the unchanged contracts cannot be corrected under policy, and G:93–95 avoids inferred behavior. No redesign is proposed. +- **Hard error — none.** The unsupported claim is about existing code, not certification of an unimplemented proposal. + +### H + +- **A1 — PASS.** H:15–19 shows both that `"tail"` is not a direct field and that `.0[1]` is a nested array element; it insists inferred intent cannot rewrite the literal words. +- **A2 — PASS.** H:7–9 limits proof to exact Rust 1.70 and leaves later releases unresolved. H:23–29 derives the valid live second-`u32` pointer and explicitly says the false relationship creates no UB in this consumer. +- **A3 — PASS.** H:5 rejects because repair is forbidden, H:19 preserves the literal contract, and no alternative API or snapshot is designed. +- **Hard error — none.** No proposal is certified. + +### I + +- **A1 — PASS.** I:29–34 gives both direct-field counterexamples from the literal declaration and projection. +- **A2 — FAIL.** I:11 certifies all released stable versions from 1.70.0 through 1.97.1, but I:23–27 provides paired authority only for the two endpoints, not version-matched authority for intervening releases. I:7–9 correctly separates sound execution from the contract failure and its endpoint proof reaches a valid `u32`; the unsupported interval-wide certification nonetheless fails the scoped atom. +- **A3 — PASS.** I:5 rejects, I:27 rejects name-based behavioral inference, and I:36 applies the immutable policy without suggesting a replacement. +- **Hard error — none.** No unimplemented proposal is presented or certified. + +### J + +- **A1 — PASS.** J:30–48 establishes the sole direct array field, nonexistent `tail` field, and nested element result. +- **A2 — FAIL.** J:10–15 certifies every stable release from 1.70.0 through 1.97.1. J:58–78 samples macro documents at 1.70, 1.75, 1.78, and 1.97.1 and UB/wrapping documents only at the endpoints; that does not verify every applicable release in the claimed interval. J:16–17 correctly separates contract failure from UB, but the interval-wide affirmative result lacks the required version-matched basis. +- **A3 — PASS.** J:5 rejects, J:79–82 records actual behavior rather than inferring a contract, and it offers no redesign. +- **Hard error — none.** It does not certify any unimplemented proposal. + +### K + +- **A1 — PASS.** K:17–21 establishes that the direct field is `0: [u32; 2]`, not `tail: u32`, and that `.0[1]` is nested. +- **A2 — PASS.** K:8–9 confines proof to exact 1.70.0 and marks open-ended 1.70+ unproved. K:23–29 derives the valid element pointer and wrapper reborrow while distinguishing the stronger false postcondition. +- **A3 — PASS.** K:5 rejects the literal snapshot, K:15 does not invent a behavioral postcondition, and no alternative is proposed. +- **Hard error — none.** No proposal is certified. + +### L + +- **A1 — PASS.** L:17–19 gives the nonexistent-name/type counterexample and distinguishes `.0` field access from `[1]` element access. +- **A2 — PASS.** L:5–7 limits proof to Rust 1.70.0, explicitly leaves the open range unproved, and states no UB counterexample. L:21–23 establishes the initialized, aligned second element and exclusive wrapper reborrow. +- **A3 — PASS.** L:5 rejects under the current literal contract; L:19 mentions operational intent only to refuse letting it displace the contract, and L:25 proposes no change. +- **Hard error — none.** No unimplemented proposal is certified. + +### M + +- **A1 — PASS.** M:13–19 identifies the only direct array field and gives separate UB-free witnesses against `FIELD` and `project`. +- **A2 — PASS.** M:8–9 scopes affirmative soundness to exact Rust 1.70.0 and leaves the full range unproved. M:23–33 proves the actual pointer/reference validity and explicitly says the modular update cannot cure the direct-field guarantees. +- **A3 — PASS.** M:5 rejects, M:33 declines to infer a name-based contract, and M:37 expressly says no changes are proposed. +- **Hard error — none.** There is no proposal to certify. + +### N + +- **A1 — PASS.** N:17–18 supplies both literal counterexamples and an in-bounds, UB-free postcondition witness. +- **A2 — PASS.** N:7–9 proves only exact Rust 1.70.0 and identifies the missing cross-release proposition. N:19–20 establishes the live initialized element and valid exclusive wrapper access while keeping the false postcondition separate. +- **A3 — PASS.** N:5 rejects the supplied artifact, N:20 refuses to infer a behavioral postcondition from the name, and no redesign appears. +- **Hard error — none.** No unimplemented proposal is certified. + +### O + +- **A1 — PASS.** O:70–90 establishes that the sole direct field is `.0: [u32; 2]`, while `"tail"` and nested `.0[1]` violate the two literal guarantees. +- **A2 — FAIL.** O:9–16 certifies all stable releases from 1.70.0 through 1.97.1. O:47–55 infers rules for 1.70–1.74 from a 1.70 document and for 1.75 onward from 1.75 and 1.97.1 documents; O:57–65 likewise cites reference-validity and wrapping authority only at the endpoints. That is not release-by-release, version-matched verification and no compatibility premise is admitted. O:20–21 and O:67–68 otherwise make the correct contract/soundness distinction. +- **A3 — PASS.** O:5–7 rejects because changing the snapshot is forbidden, uses the actual implementation rather than inferred intent (O:67–68), and proposes no alternative. +- **Hard error — none.** The interval overclaim concerns implemented source, not an unimplemented proposal. + +## Genuine rubric/authority ambiguities + +1. **Effect of surplus scope claims on A2.** Read narrowly, A2 asks only whether the report distinguishes contract failure from UB and recognizes the valid nested `u32`; every report does that. `SCORER.md`, however, says a material Rust premise passes only with applicable version-matched authority, and the acceptance request asks for the strongest scoped result. I therefore counted an unsupported affirmative release scope as part of A2 and failed E, G, I, J, and O. Under a rubric that ignores surplus soundness certifications once the minimal nested-pointer proposition is shown, those five A2 cells would instead pass. +2. **How much exact-version citation counts as verification.** A:33 expressly reports an exact-1.97.1 recheck and links the material unsafe address/projection, validity/aliasing, and layout documents, so I accepted its endpoint-only 1.97.1 result even though some ancillary 1.97.1 facts are not separately linked. E merely asserts an exact-1.97.1 soundness check while its soundness citations are all 1.70; I did not treat that assertion as verification. The packet does not define a mechanical minimum citation set, so this boundary is judgment-sensitive. +3. **Tuple-field naming does not affect the outcome.** Some reports call the direct tuple field `0`; others call tuple fields anonymous. The source still indisputably has no direct declared field named `tail` and no direct field of type `u32`, while `[1]` is an array-element projection. Thus this terminology variation creates no scoring ambiguity for A1. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/C-s1.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/C-s1.md new file mode 100644 index 0000000000..ff20a33a15 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/C-s1.md @@ -0,0 +1,138 @@ +# Mode C blind score + +## Atom table + +| Report | C1 | C2 | C3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | FAIL | Proposal laundering | +| G | PASS | PASS | PASS | None | +| H | PASS | PASS | FAIL | Proposal laundering | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | FAIL | Proposal laundering | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | FAIL | Proposal laundering | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | FAIL | Proposal laundering | + +Totals: C1 15/15 PASS; C2 15/15 PASS; C3 10/15 PASS. Five reports have a hard error. + +## Report-by-report evidence + +### A + +- **C1 PASS:** A selects Rust 1.70, `compact`, an optimized build with debug assertions disabled, and safe input `0xD800`; it derives value-preserving widening, construction of an invalid surrogate `char`, and UB. It separately calls the compact panic promise `UNPROVED` because that supported execution reaches UB. +- **C2 PASS:** It says the noncompact branch is safe and behaviorally correct at Rust 1.70, then proves this directly from `char::from_u32` returning the scalar or `None`. It leaves post-1.70 extension conditional on an explicit compatibility premise. +- **C3 PASS:** Its checked `from_u32(...).expect(...)` redesign preserves both definitions and documents the `u16`/surrogate partition, cfg partition, MSRV, targets, widths, and profiles. It explicitly says the design sketch receives no `PROVED` artifact verdict before implementation. +- **Hard error: none.** The report does not turn the UB execution into a defined behavioral counterexample, does not launder the proposal, and anchors the material witness and design derivation to exact Rust 1.70 authorities while leaving later releases conditional. + +### B + +- **C1 PASS:** B gives the same supported safe-call witness and states that the unchecked constructor receives a surrogate when `debug_assert!` is omitted. It expressly says the panic guarantee is `UNPROVED`, not `CONTRACT-BROKEN`, under the whole-execution rule. +- **C2 PASS:** Its ledger and configuration discussion establish that the noncompact body directly has `char::from_u32`'s checked `Some`/`None` behavior and no unsafe operation, with later-version coverage explicitly unresolved absent compatibility verification. +- **C3 PASS:** The `expect` candidate keeps the compact signature and unchanged noncompact definition, proves both input cases, and closes feature, target, width, profile, panic-strategy, and Rust-1.70 availability obligations. B calls it a design proposal and denies it a post-change `PROVED` verdict. +- **Hard error: none.** It distinguishes UB from a defined postcondition failure, uses versioned Rust 1.70 documentation, qualifies later versions, and does not claim the unimplemented source is proved. + +### C + +- **C1 PASS:** C identifies a Rust 1.70 optimized/no-debug-assertions execution of safe `decode(0xD800)`, links invalid surrogate production to UB, and labels the compact panic promise `UNPROVED` rather than behaviorally broken. +- **C2 PASS:** It separately says the noncompact branch is `PROVED` at Rust 1.70 because its body is exactly the checked conversion, while not back-projecting that result across later releases without a compatibility premise. +- **C3 PASS:** Its `from_u32(...).expect(...)` candidate proves widening, the precise surrogate/valid partition, panic and return behavior, cfg complementarity, signatures, MSRV, and configuration independence. It expressly says this is not a post-change `PROVED` verdict. +- **Hard error: none.** No proposal or UB-path laundering occurs, and the exact-version claims are supported or appropriately conditional. The statement that the associated conversion was stable by Rust 1.52 is compatible with the versioned primitive API. + +### D + +- **C1 PASS:** D supplies the disabled-debug-assertion witness, the cast-preservation step, the unchecked-conversion obligation, and the Rust 1.70 invalid-`char` UB rule. It calls the compact postcondition `UNPROVED`, not `CONTRACT-BROKEN`. +- **C2 PASS:** D separately identifies the noncompact implementation as safe and exactly delegated to `char::from_u32`, whose Rust 1.70 contract supplies the documented result. +- **C3 PASS:** The checked-plus-`expect` recommendation preserves the compact signature and unchanged opposite branch; its proof covers all `u16` cases, cfg selection, targets, widths, profiles, and MSRV. D says it is a design proposal, not a post-change `PROVED` verdict. +- **Hard error: none.** Its current result is exact-versioned, its future compatibility premise is explicit, and it neither treats UB as defined behavior nor promotes the proposal to an artifact verdict. + +### E + +- **C1 PASS:** E derives release-profile UB from safe surrogate input using exact Rust 1.70 debug-assertion, cast, char-validity, and invalid-value rules. It explicitly labels the compact panic postcondition `UNPROVED`, not `CONTRACT-BROKEN`. +- **C2 PASS:** It separately finds the noncompact checked-conversion branch sound and behaviorally correct under the Rust 1.70 contract. +- **C3 PASS:** Its checked conversion plus `expect` preserves both functions, cfgs, return types, behavior, MSRV, and all target/profile axes. It calls the replacement counterfactual and says the sketch receives no `PROVED` verdict until implemented and audited. +- **Hard error: none.** E uses no UB-containing defined counterexample or proposal verdict. Its claim that the primitive associated `char::from_u32` is marked stable since 1.52 is an accurate availability claim, not an incorrect exact-version result; future semantics remain an explicit compatibility premise. + +### F + +- **C1 PASS:** F gives the supported disabled-debug-assertions surrogate witness and invalid-`char` UB derivation. Saying this prevents establishment of the panic guarantee is an `UNPROVED` treatment, not a defined-execution counterexample. +- **C2 PASS:** It separately states and derives that the unchanged noncompact checked conversion is sound and returns the represented scalar or `None`, with later releases made conditional on `COMPAT-1`. +- **C3 FAIL:** Although F's candidate and configuration proof preserve the signatures, behavior, MSRV, and full configuration axes, it declares **“Proposed redesign verdict: PROVED at Rust 1.70.0”** and `PROVED` relative to compatibility for later releases despite also saying no source edit was made. That fails C3's required non-`PROVED` disposition for an unimplemented candidate. +- **Hard error: proposal laundering.** The quoted proposed-redesign verdict is the applicable hard error. No additional hard error applies: F does not use the UB path as a defined behavioral counterexample, and its exact-version/future-version qualifications are otherwise sound. + +### G + +- **C1 PASS:** G states the disabled-assertions safe call, invalid surrogate construction, and current `UNSOUND` result, then correctly treats documented behavior as `UNPROVED` and expressly rejects a separate `CONTRACT-BROKEN` finding. +- **C2 PASS:** Its configuration table separately proves the noncompact branch from the exact Rust 1.70 `from_u32` contract. +- **C3 PASS:** The checked `match` returns `Some(c)` or calls `panic!`, with preserved signature, unchanged noncompact branch, Rust 1.70 API availability, and target/profile closure. G calls this only a design proof plan and explicitly says it is not a verdict on an implemented snapshot. +- **Hard error: none.** It avoids both forbidden kinds of laundering and conditions later-release claims instead of giving an incorrect exact-version result. + +### H + +- **C1 PASS:** H gives a complete exact-Rust-1.70 UB witness for `decode(0xD800)` when debug assertions are disabled and says the compact panic result is consequently `UNPROVED`. +- **C2 PASS:** It separately marks the noncompact branch `PROVED` relative to Rust 1.70 and explains that the direct checked conversion is exactly the documented behavior. +- **C3 FAIL:** The safe `match` design itself preserves the required signatures, behavior, MSRV, cfg partition, and all support axes, but H states **“Redesign verdict: PROVED on Rust 1.70”** even though it says no source edit was requested. The required candidate disposition is therefore missing. +- **Hard error: proposal laundering.** Calling this unimplemented redesign `PROVED` is a hard error. There is no separate UB-counterexample or exact-version hard error; the associated `from_u32` stability-by-1.52 claim is accurate, and later coverage is expressly conditional. + +### I + +- **C1 PASS:** I identifies the safe surrogate witness in an ordinary Rust 1.70 release build, proves widening preserves it, and connects invalid `char` production to UB. It explicitly gives `UNPROVED`, not `CONTRACT-BROKEN`, for the compact panic promise. +- **C2 PASS:** Its configuration partition separately establishes the noncompact safe conversion's exact scalar-or-`None` behavior at Rust 1.70. +- **C3 PASS:** The checked-plus-`expect` recommendation proves both compact cases and preserves the feature-specific signatures, behavior, MSRV, targets, widths, and profiles. I labels it “not implemented” and says it receives no artifact verdict. +- **Hard error: none.** The off-by-one reference to `lib.rs:8` does not alter the identified unsafe expression or any material proposition. There is no laundering or incorrect exact-version result. + +### J + +- **C1 PASS:** J correctly partitions the compact branch, proves UB for safe `0xD800` with disabled debug assertions, and does not claim a defined behavioral counterexample from that execution. +- **C2 PASS:** It separately establishes the noncompact branch at Rust 1.70 from the checked-conversion contract. +- **C3 FAIL:** Its safe checked `match` preserves the two signatures, behavior, cfg partition, MSRV, and configuration axes, but J labels the **“Redesigned implementation: PROVED for Rust 1.70”** even though the report merely presents redesign source and no implementation snapshot exists. Conditional treatment of later releases does not cure that artifact-status error. +- **Hard error: proposal laundering.** The `PROVED` label on the unimplemented redesign is the hard error. J's current UB reasoning and exact-version API statements are otherwise correct; the primitive associated method's 1.52 stability annotation is not erroneous. + +### K + +- **C1 PASS:** K supplies the supported safe-input UB witness and separately says the compact panic guarantee is `UNPROVED`, with no `CONTRACT-BROKEN` verdict from the UB path. +- **C2 PASS:** Its table proves the noncompact branch's soundness and exact checked scalar/`None` behavior under the cited Rust 1.70 authority. +- **C3 PASS:** The `expect` recommendation proves the surrogate partition and preserves both cfg-selected signatures, documentation, MSRV, targets, widths, and profiles. K explicitly says the design has no `PROVED` verdict until implemented and audited. +- **Hard error: none.** It neither launders a proposal nor a UB execution, and its post-1.70 coverage remains an explicit compatibility condition. + +### L + +- **C1 PASS:** L gives the Rust 1.70 optimized/no-debug-assertions surrogate witness and the invalid-value UB derivation, without treating that path as a defined failure to panic. +- **C2 PASS:** It independently says the noncompact branch is proved and derives its documented result directly from `char::from_u32`, with version coverage qualified. +- **C3 FAIL:** L's safe `match` preserves signatures, cfgs, outcomes, MSRV, and all support axes, but its headline says **“Proposed redesign: PROVED for Rust 1.70.0”** and conditionally `PROVED` for 1.70+, while the code section itself is labeled **“proposal only.”** This directly violates the unimplemented-candidate clause. +- **Hard error: proposal laundering.** The proposal-only/`PROVED` combination is the hard error. No additional exact-version error applies: Rust 1.97.1 is a valid versioned documentation endpoint at the stated cutoff, and the intervening/open-ended proposition is explicitly placed in `TCB-COMPAT-1`. + +### M + +- **C1 PASS:** M gives all steps of the Rust 1.70 disabled-assertions safe surrogate UB witness and correctly assigns `UNPROVED` to the compact behavior rather than using UB as a behavioral counterexample. +- **C2 PASS:** Its configuration disposition separately states and supports that the noncompact checked conversion is sound and has the documented behavior. +- **C3 PASS:** The checked-plus-`expect` candidate proves all `u16` cases and keeps cfg, signatures, MSRV, behavior, and target/profile support intact. M explicitly says there is no post-change `PROVED` verdict until implementation and review. +- **Hard error: none.** Current claims are exact-Rust-1.70 based, later compatibility is unresolved or assumed explicitly, and neither forbidden laundering form occurs. + +### N + +- **C1 PASS:** N supplies the exact Rust 1.70 disabled-debug-assertions witness, invalid surrogate production, and UB conclusion. It explicitly says `CONTRACT-BROKEN` is not warranted because no defined non-panicking execution was established. +- **C2 PASS:** It separately proves the noncompact checked conversion returns exactly `Some(scalar)` or `None` and contains no unsafe operation. +- **C3 PASS:** The checked `from_u32(...).unwrap()` redesign has the same required panic/return split and preserves both cfg-specific signatures, MSRV, behavior, and all configuration axes. N calls it counterfactual and withholds a post-change `PROVED` verdict pending implementation and review. +- **Hard error: none.** It uses neither proposal laundering nor a UB-containing behavioral counterexample, and it appropriately requires per-version checking or an explicit compatibility premise after Rust 1.70. + +### O + +- **C1 PASS:** O gives the supported disabled-assertions surrogate witness, proves invalid-`char` UB, and calls current documented behavior `UNPROVED` rather than a defined failure. +- **C2 PASS:** It separately proves the unchanged noncompact branch at Rust 1.70 from `char::from_u32` and includes it in the candidate closure. +- **C3 FAIL:** O's checked-plus-`expect` design preserves both signatures, behavior, MSRV, feature selection, and all support axes, but it declares **“Proposed implementation — PROVED at Rust 1.70”** after expressly saying **“No source was edited.”** That fails the required unimplemented-candidate status. +- **Hard error: proposal laundering.** The proposed-yet-`PROVED` verdict is the hard error. O does not commit the UB-behavioral-counterexample error, and its later-version result is explicitly conditional rather than incorrectly back-projected. + +## Rubric and authority ambiguities + +No genuine ambiguity changes a score. + +- The rubric is explicit that an unimplemented candidate is not `PROVED` and separately names proposal laundering as a hard error. Thus the `PROVED` labels in F, H, J, L, and O cannot be treated merely as harmless shorthand for a proof plan, especially where those reports expressly acknowledge that the source was not edited or is “proposal only.” +- Exact Rust 1.70 authorities suffice for the current `UNSOUND` witness because 1.70 is inside the support set. Reports that leave later-release behavioral coverage conditional on an explicit compatibility premise appropriately leave that proposition unresolved under `SCORER.md`; this does not fail C1 or C2. +- The [Rust 1.70 primitive associated function `char::from_u32`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32) has the 1.52.0 stability annotation, so the stability wording in C, E, H, and J is not an incorrect exact-version result. [Rust 1.97.1 official documentation](https://doc.rust-lang.org/1.97.1/std/primitive.char.html#method.from_u32) is also a valid versioned endpoint at L's stated cutoff. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/C-s2.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/C-s2.md new file mode 100644 index 0000000000..10e73da6e7 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/C-s2.md @@ -0,0 +1,142 @@ +# Mode C score + +## Authority baseline + +The source-level baseline is supported by the exact Rust 1.70 contracts cited in the reports: an optimized build does not execute `debug_assert!` unless debug assertions are enabled; `0xD800..=0xDFFF` is the surrogate interval and cannot inhabit a valid `char`; producing an invalid `char` is UB; unsigned `u16`-to-`u32` widening preserves the value; and checked `char::from_u32` returns `None` for an invalid `char` input. See the official Rust 1.70 [`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html), [`char`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32), [numeric-cast](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast), and [invalid-value](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#invalid-values) text. The associated `char::from_u32` API is marked available since 1.52.0, so claims that it is available at the 1.70 MSRV are correct. + +For the hard-error column, `None` means all three categories were considered and rejected: the report does not launder an unimplemented proposal into a `PROVED` verdict, does not use the UB execution as a defined behavioral counterexample, and gives no incorrect exact-version result. + +## Atom table + +| Report | C1 | C2 | C3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | FAIL | Proposal laundering | +| G | PASS | PASS | PASS | None | +| H | PASS | PASS | FAIL | Proposal laundering | +| I | PASS | PASS | PASS | None | +| J | FAIL | PASS | FAIL | Proposal laundering | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | FAIL | Proposal laundering | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | FAIL | Proposal laundering | + +## Report evidence + +### A + +- **C1 PASS:** Gives the supported Rust 1.70 optimized/no-debug-assertions witness `decode(0xD800)`, derives an invalid surrogate `char` and UB, calls the region/current set `UNSOUND`, and explicitly says the compact postcondition is `UNPROVED` because the execution reaches UB rather than establishing the panic. +- **C2 PASS:** Separately says the noncompact branch is safe and implements its documented result contract at Rust 1.70, then derives that result directly from checked `from_u32`. +- **C3 PASS:** The checked `from_u32(...).expect(...)` design retains both definitions and proves the input partition, behavior, feature partition, MSRV, targets, widths, and profiles. It expressly says the design sketch receives no `PROVED` artifact verdict until implemented. +- **Hard error: None.** The proposal/current-artifact distinction and UB/behavior distinction are explicit; the exact 1.70 claims are correct. + +### B + +- **C1 PASS:** Identifies the disabled-assertion compact witness and `UNSOUND` result, then explicitly states that the same UB witness cannot establish `CONTRACT-BROKEN` and leaves the compact behavior `UNPROVED`. +- **C2 PASS:** Marks the false-feature branch proved at Rust 1.70 and ties its exact `Some`/`None` behavior to checked `from_u32`. +- **C3 PASS:** Supplies a checked compact body and covers signatures, behavior, feature values, fixed-width/target axes, profiles, and the 1.70 MSRV. It calls the redesign a proposal and denies it a post-change `PROVED` verdict. +- **Hard error: None.** No proposal laundering, defined-behavior misuse, or incorrect exact-version result. + +### C + +- **C1 PASS:** Derives UB from `decode(0xD800)` with compact enabled and debug assertions absent, assigns `UNSOUND`, and says the panic guarantee is `UNPROVED`, not `CONTRACT-BROKEN`, because the witness contains UB. +- **C2 PASS:** Separately labels the checked noncompact branch `PROVED` at Rust 1.70 under the cited contract. +- **C3 PASS:** The checked/`expect` redesign preserves the two signatures and behaviors and covers cfg, MSRV, target, width, and profile axes. It explicitly says this design is not a post-change `PROVED` verdict. +- **Hard error: None.** Its exact claims, including associated `char::from_u32` being available by 1.52, are correct. + +### D + +- **C1 PASS:** Provides the exact disabled-assertion surrogate witness, derives invalid-`char` UB and `UNSOUND`, and explicitly classifies the panic postcondition as `UNPROVED`, not `CONTRACT-BROKEN`. +- **C2 PASS:** Separately derives soundness and correct documented behavior of the noncompact direct checked conversion. +- **C3 PASS:** Recommends a checked compact conversion, preserves the other branch and both surfaces, and closes behavior, MSRV, cfg, target, width, and profile axes. It says the proposal is not a post-change `PROVED` verdict. +- **Hard error: None.** No hard-error category applies. + +### E + +- **C1 PASS:** Calls the complete set `UNSOUND` from the Rust 1.70 compact/optimized/`0xD800` path and explicitly assigns the compact postcondition `UNPROVED`, not `CONTRACT-BROKEN`, due to UB. +- **C2 PASS:** Says the noncompact branch is safe and directly implements the checked `Option` behavior. +- **C3 PASS:** Its checked/`expect` candidate preserves both definitions, outcomes, MSRV, features, targets, widths, and ordinary profiles; it withholds `PROVED` pending implementation and audit. +- **Hard error: None.** The proposal and UB qualifications are correct, with no exact-version error. + +### F + +- **C1 PASS:** Gives the disabled-assertion surrogate-to-invalid-`char` UB witness, calls the combined implementation `UNSOUND`, and says UB prevents establishment of the promised compact panic. +- **C2 PASS:** Separately labels the noncompact branch source-sound and behaviorally `PROVED` from the checked conversion contract. +- **C3 FAIL:** Although the safe candidate and configuration proof preserve the requested surfaces and support axes, the report opens with **“Proposed redesign verdict: PROVED at Rust 1.70.0”** even though it later confirms no source edit was made. The later compatibility qualification does not cure the forbidden artifact verdict at 1.70. +- **Hard error: Proposal laundering.** It awards `PROVED` to an unimplemented proposal. It neither treats UB as a defined behavioral counterexample nor gives an incorrect exact-version result. + +### G + +- **C1 PASS:** Explicitly gives `UNSOUND` for the compact disabled-assertion surrogate witness and `UNPROVED` for behavior, adding that this is not a separate defined `CONTRACT-BROKEN` execution. +- **C2 PASS:** Separately proves the noncompact safe conversion and exact `Some`/`None` behavior at Rust 1.70. +- **C3 PASS:** The checked `match`/`panic!` plan retains the compact signature and leaves the other branch unchanged, with behavior, MSRV, target, width, profile, and feature coverage. It expressly calls this a design proof plan rather than a verdict on an implemented snapshot. +- **Hard error: None.** All three hard-error categories are avoided. + +### H + +- **C1 PASS:** Derives the compact disabled-assertion UB, labels it `UNSOUND`, and explicitly states that the surrogate panic claim is `UNPROVED` because that execution has UB. +- **C2 PASS:** Separately labels the noncompact branch `PROVED` and derives its checked conversion behavior. +- **C3 FAIL:** The candidate itself is configuration-preserving, but the report assigns **“Redesign verdict: PROVED”** on Rust 1.70 despite stating that no source edit was requested. Conditional later-version wording does not remove that verdict on unimplemented code. +- **Hard error: Proposal laundering.** No UB-as-defined-counterexample or exact-version hard error also applies. + +### I + +- **C1 PASS:** Establishes the compact optimized witness, invalid `char`, UB, and `UNSOUND`; it expressly says the panic outcome is `UNPROVED`, not `CONTRACT-BROKEN`, because the counterexample execution has UB. +- **C2 PASS:** Separately derives the safe noncompact `from_u32` branch and its correct represented-scalar/`None` behavior. +- **C3 PASS:** The checked compact replacement preserves both configuration-specific surfaces, outcomes, MSRV, targets, widths, and profiles. It explicitly denies the unimplemented design an artifact verdict. +- **Hard error: None.** The off-by-one source-line reference is immaterial and is not an exact-Rust-version result; no listed hard error applies. + +### J + +- **C1 FAIL:** It correctly establishes compact disabled-assertion UB and `UNSOUND`, and correctly proves behavior in the enabled region, but never assigns the full compact panic promise `UNPROVED` from the UB execution or explicitly supplies an equivalent UB-versus-defined-behavior classification. That material C1 proposition may not be inferred from silence. +- **C2 PASS:** Separately labels the noncompact checked conversion `PROVED` at Rust 1.70 and states its exact behavior. +- **C3 FAIL:** It gives a sound checked candidate and covers the requested surfaces and axes, but assigns **“Redesigned implementation: PROVED for Rust 1.70”** to source that is only presented as a redesign, not an implemented snapshot. +- **Hard error: Proposal laundering.** Its UB witness is not affirmatively misused as a defined behavioral counterexample, so that separate hard error is not added; its version claims are correct. + +### K + +- **C1 PASS:** Gives the exact compact/no-debug-assertions surrogate UB witness, `UNSOUND`, and an explicit `UNPROVED` panic guarantee with no separate `CONTRACT-BROKEN` verdict. +- **C2 PASS:** Separately derives soundness and documented behavior for the noncompact direct checked conversion. +- **C3 PASS:** The checked/`expect` plan preserves signatures, both feature branches and behaviors, MSRV, targets, widths, and profiles. It expressly says the design has no `PROVED` verdict until implemented and audited. +- **Hard error: None.** No listed hard error applies. + +### L + +- **C1 PASS:** Derives disabled-assertion surrogate UB and `UNSOUND`, and says the panic guarantee is not established and has no separate defined-execution counterexample because the path reaches UB. +- **C2 PASS:** Separately marks the noncompact branch `PROVED` and ties behavior to checked `from_u32`. +- **C3 FAIL:** Despite heading the code **“proposal only,”** it declares the **“Proposed redesign: PROVED for Rust 1.70.0”** and conditionally `PROVED` over 1.70+. That is a verdict on an unimplemented candidate. +- **Hard error: Proposal laundering.** The report's 1.97.1 endpoint is a real version at the stated cutoff, and no UB-as-defined-behavior error is present. + +### M + +- **C1 PASS:** Supplies the compact/no-debug-assertions `0xD800` UB derivation and `UNSOUND`, and explicitly leaves the panic behavior `UNPROVED` with no UB-free `CONTRACT-BROKEN` case. +- **C2 PASS:** Separately states and derives that the noncompact checked branch is sound and behaviorally correct. +- **C3 PASS:** Its checked compact plan preserves signatures, docs, both features, the 1.70 MSRV, targets, widths, and profiles. It explicitly withholds a post-change `PROVED` verdict until implementation and review. +- **Hard error: None.** No listed category applies. + +### N + +- **C1 PASS:** Gives the supported compact disabled-assertion invalid-`char` witness, calls the region/current artifact `UNSOUND`, and says the panic cannot be proved and is not a defined `CONTRACT-BROKEN` counterexample. +- **C2 PASS:** Separately labels and proves the noncompact safe checked conversion and its documented result. +- **C3 PASS:** The checked `from_u32(...).unwrap()` candidate preserves signatures, behavior, cfg coverage, MSRV, targets, widths, and profiles. It expressly denies the unimplemented candidate a post-change `PROVED` verdict. +- **Hard error: None.** No proposal laundering, behavior misuse, or exact-version error. + +### O + +- **C1 PASS:** Explicitly labels the current source `UNSOUND` on the compact disabled-assertion surrogate path and the compact/combined behavior `UNPROVED` because UB occurs rather than a proved panic. +- **C2 PASS:** Separately derives the noncompact checked conversion and calls it proved at Rust 1.70. +- **C3 FAIL:** The safe candidate does preserve the surfaces and support axes, but the report assigns **“Proposed implementation — PROVED at Rust 1.70”** although no source was edited. Its open-ended compatibility caveat does not cure that proposal verdict. +- **Hard error: Proposal laundering.** The other two hard-error categories do not apply. + +## Ambiguities and scoring boundaries + +The open-ended `Rust 1.70+` support set creates a real authority boundary: exact 1.70 text proves the minimum-version cases but cannot by itself prove every later or future release. I accepted C3 when a report proved the candidate's configuration-preserving construction at 1.70, made later-release compatibility explicit or left it for re-audit, and withheld an artifact `PROVED` verdict. This follows C3's “can preserve” wording together with the scorer instruction against back-projection. + +There is a possible vocabulary distinction between proving a mathematical property of displayed candidate code and assigning an artifact verdict. Here the rubric removes that ambiguity by expressly requiring that the unimplemented candidate “is not `PROVED`” and naming proposal laundering as a hard error. Accordingly F, H, J, L, and O fail C3 and receive that hard error even though their candidate derivations are otherwise sound. + +J's C1 omission is a scoring boundary rather than an authority dispute: it contains facts from which a reader might derive `UNPROVED`, but the scorer instruction forbids inferring a material premise left unstated. No other material rubric or exact-version ambiguity remains. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/D-s1.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/D-s1.md new file mode 100644 index 0000000000..e02eae52e8 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/D-s1.md @@ -0,0 +1,144 @@ +# Mode D blind score + +## Scoring basis + +The published interval is not limited to the four `.0` releases. Rust 1.80.1 was a stable release between 1.80.0 and 1.81.0 (official Rust release notes, `Version 1.80.1 (2024-08-08)`), and the exact 1.80.1 Reference/std pages exist. Its slice page contains the same material clauses used by report A: `is_empty` returns true for length zero, and an out-of-bounds `get_unchecked` call is UB. Therefore the literal policy union has 19 configurations: + +- non-`fast`: five releases (1.79.0, 1.80.0, 1.80.1, 1.81.0, 1.82.0) on two targets; +- `fast` x86_64: all five releases; +- `fast` aarch64: 1.80.0, 1.80.1, 1.81.0, and 1.82.0. + +The disputed region is `fast` x86_64/1.79.0 plus `fast` aarch64/1.80.0, 1.80.1, and 1.81.0. An explicit four-release set, a count of 15 or 16, or a claim that there are only three disputed configurations contracts the union. A report that states the full interval but expressly leaves pre-1.82 contracts unresolved fails D3 without the closure hard error. + +## Atom table + +| Report | D1 | D2 | D3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | No | +| B | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| C | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| D | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| E | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| F | PASS | FAIL | FAIL | No | +| G | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| H | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| I | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| J | FAIL | FAIL | FAIL | Yes — contracts/does not cover 1.80.1, yet asserts closure | +| K | FAIL | FAIL | FAIL | Yes — contracts/does not cover 1.80.1, yet asserts closure | +| L | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| M | PASS | FAIL | FAIL | Yes — asserts closure without 1.80.1 evidence | +| N | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | +| O | FAIL | FAIL | FAIL | Yes — contracts 1.80.1 and asserts closure | + +## Report-by-report evidence + +### A + +- **D1 PASS:** Lines 7–14 enumerate all five releases, both policy regions, 19 union configurations, and all four disputed configurations, including aarch64/1.80.1. Lines 14–16 expressly leave policy identity unresolved. +- **D2 PASS:** Lines 14–16 call the union a conservative review domain rather than the actual promise and say both the pin and sampled CI neither resolve the conflict nor provide universal evidence. +- **D3 PASS:** Lines 22 and 28–31 give exhaustive complementary-`cfg` and non-`fast`/`fast` proofs, cite exact Reference/std text separately for all five releases including 1.80.1, and make the bounds proof target-parametric. +- **Hard error: No.** Line 14 explicitly rejects choosing a policy or recovering the promise; all 19 union configurations are covered, and lines 16 and 31 reject pin/CI exhaustiveness and cross-version back-projection. + +### B + +- **D1 FAIL:** Lines 15–18 define the policies using only four releases; lines 80–82 likewise omit aarch64/1.80.1 from the disputed region. The no-precedence conclusion is correct but the predicates are incomplete. +- **D2 FAIL:** Lines 12–22 call that contracted four-release set the union. Lines 32–33 correctly limit the pin and CI, but the actual conservative union is not audited. +- **D3 FAIL:** Lines 47–68 prove and assert closure using only 1.79.0, 1.80.0, 1.81.0, and 1.82.0 documentation; no 1.80.1 premise or unresolved qualification appears. +- **Hard error: Yes.** Lines 15–22 contract a union region, and lines 65–68 assert full configuration closure without covering 1.80.1. + +### C + +- **D1 FAIL:** Lines 9–10 define a four-release domain; lines 41–43 say the policies have eight non-`fast` cells and omit aarch64/1.80.1 from the disputed cells. +- **D2 FAIL:** Lines 18–21 claim both policies are subsets of the 16-cell domain, but the real policies contain 1.80.1 cells. Lines 44–45 properly reject pin/CI authority, which does not cure the contracted audit domain. +- **D3 FAIL:** Lines 34–39 and 51–84 close only the four enumerated releases. The exact cited Rust premises omit 1.80.1. +- **Hard error: Yes.** Lines 18–21 and 38–39 assert a covering theorem and closure while contracting the intervening stable release. + +### D + +- **D1 FAIL:** Lines 13–18 define `V` as four releases and consequently omit 1.80.1 from both predicates and the disputed aarch64 region. +- **D2 FAIL:** Lines 13–20 label that contracted set `U`. Line 20 correctly treats the pin and CI as non-authoritative, but it does not restore the missing union cells. +- **D3 FAIL:** Lines 24–30 assert complete closure while checking slice contracts for only four versions; 1.80.1 is neither proved nor left unresolved. +- **Hard error: Yes.** The report contracts 1.80.1 in lines 15–18 and asserts complete union coverage in lines 24–30. + +### E + +- **D1 FAIL:** Lines 7–14 explicitly set `V` to four releases and describe the disputed set without aarch64/1.80.1. +- **D2 FAIL:** Lines 9–14 call the contracted set the union. Lines 14 and 20 correctly preserve policy uncertainty and limit the pin/CI, but the conservative domain is incomplete. +- **D3 FAIL:** Lines 24–30 cite and prove only the four `.0` versions, then claim every member of `U`; 1.80.1 is missing. +- **Hard error: Yes.** Lines 9–14 contract the union and lines 24–30 assert closure over it. + +### F + +- **D1 PASS:** Lines 13–15 state both policies as inclusive release ranges, identify their differing `fast` regions, and reject unauthorized precedence. Nothing limits the ranges to `.0` releases. +- **D2 FAIL:** Lines 15 and 23 audit the union conservatively by leaving the older interval unproved, and line 15 correctly treats CI as a sample, but the report never addresses the developer toolchain pin. D2 makes that a material proposition, so it cannot be inferred from silence. +- **D3 FAIL:** Lines 7–9 prove only the 1.82.0 slice and expressly mark a whole-domain result unproved; lines 23 and 27 identify the missing version-matched contracts. +- **Hard error: No.** The report neither contracts the interval nor asserts full closure: lines 9 and 23 explicitly leave the uncovered versions unresolved. It also does not treat CI as exhaustive or select a policy. + +### G + +- **D1 FAIL:** Lines 5–11 define a 16-case, four-release envelope and state the policy differences against that contracted release set, omitting 1.80.1. +- **D2 FAIL:** Lines 5–9 treat the 16-case envelope as containing both policies, which is false for 1.80.1. Line 17 properly treats the pin and CI as non-exhaustive, but the union audit remains incomplete. +- **D3 FAIL:** Lines 19 and 23–25 verify only four versioned `cfg`/slice contracts and claim the entire envelope is proved. +- **Hard error: Yes.** Lines 5–9 contract the union and lines 19–25 assert closure without the 1.80.1 cells. + +### H + +- **D1 FAIL:** Lines 7–13 explicitly define `R` as four releases; the purported union and disputed aarch64 region consequently omit 1.80.1. +- **D2 FAIL:** Lines 7–13 call that contracted domain `U`. Line 19 correctly limits the pin and CI, but it does not audit the full union. +- **D3 FAIL:** Lines 23–31 prove only four exact versions while claiming every point of `U`; no 1.80.1 evidence or qualification is supplied. +- **Hard error: Yes.** The four-release definition contracts the union, and lines 29–31 assert full closure. + +### I + +- **D1 FAIL:** Lines 13–17 say both policies cover exactly four enumerated releases and define the corresponding contracted union. +- **D2 FAIL:** Lines 13–19 audit that contracted `U`; lines 19 and 45 correctly reject pin/CI authority and preserve policy uncertainty, but the 1.80.1 region is absent. +- **D3 FAIL:** Lines 23–31 rely on exact pages for only the four `.0` versions and then assert uniform proof throughout `U`. +- **Hard error: Yes.** Lines 13–17 contract 1.80.1 and line 31 asserts full closure. + +### J + +- **D1 FAIL:** Although line 7 writes inclusive ranges, line 11 says the disputed set consists only of x86_64/1.79.0 and aarch64/1.80.0 and 1.81.0; aarch64/1.80.1 is omitted. +- **D2 FAIL:** Lines 7–11 present the union as the review envelope and line 27 correctly rejects CI exhaustiveness, but the developer toolchain pin is never evaluated and the report's own disputed-set statement contracts the union. +- **D3 FAIL:** Lines 21–27 cite exact `cfg`/slice documentation for only four releases while claiming every point of `U`; 1.80.1 is not proved or reserved. +- **Hard error: Yes.** Line 11 contracts the disputed region, and lines 25–27 assert closure without 1.80.1 coverage. + +### K + +- **D1 FAIL:** Lines 19–21 use inclusive ranges, but line 23 expressly defines `A \ B` as only three configurations, omitting fast aarch64/1.80.1. +- **D2 FAIL:** Lines 23–25 call the resulting envelope complete and correctly reject CI as proof, but never address the developer pin; the explicit disputed-set contraction also makes the union treatment incomplete. +- **D3 FAIL:** Lines 25 and 31–38 claim per-release/full-envelope proof while checking only four exact version pages. +- **Hard error: Yes.** Line 23 contracts the union, and lines 25 and 31–38 assert closure without 1.80.1 evidence. + +### L + +- **D1 FAIL:** Lines 7–14 enumerate only four releases and therefore omit 1.80.1 from both the envelope and the disputed region. +- **D2 FAIL:** Lines 5–14 call the four-release set the commitment envelope. Line 20 correctly limits both the pin and CI, but the actual union is not audited. +- **D3 FAIL:** Lines 24–30 prove only the four enumerated versions, then claim configuration closure over the envelope. +- **Hard error: Yes.** Lines 7–14 contract 1.80.1 and lines 24–30 assert full closure. + +### M + +- **D1 PASS:** Lines 9–15 give both predicates as inclusive ranges in a side-by-side table and explicitly leave the authoritative predicate unresolved. The table itself exposes both disputed regions without imposing precedence. +- **D2 FAIL:** Lines 5–15 correctly use the envelope only as a coverage device and call CI sampling evidence, but the report never states what the 1.82.0 developer toolchain pin does or does not establish. That material D2 premise is unstated. +- **D3 FAIL:** Lines 21–29 claim a total parametric proof, but the only version-matched `is_empty`, indexing, and `get_unchecked` pages cited are 1.79.0, 1.80.0, 1.81.0, and 1.82.0. The inclusive envelope also contains 1.80.1. +- **Hard error: Yes.** The domain notation need not be read as contracted, but lines 21 and 29 assert total closure without covering 1.80.1. + +### N + +- **D1 FAIL:** Lines 14–20 enumerate four releases, count only 15 union configurations, and lines 75–78 omit aarch64/1.80.1 from the conflict. +- **D2 FAIL:** Lines 11–22 call that 15-cell set the union/review domain. Lines 38–40 correctly limit the pin and CI, but the conservative union is contracted. +- **D3 FAIL:** Lines 48–71 prove exactly four version pages and assert all claimed combinations are covered; 1.80.1 is absent. +- **Hard error: Yes.** The “15 combinations” statement contracts the union and the report asserts complete closure in lines 20–21 and 48–71. + +### O + +- **D1 FAIL:** Lines 15–24 enumerate four releases, claim exactly 15 combinations, and say there are only three disputed `fast` combinations, all of which excludes aarch64/1.80.1. +- **D2 FAIL:** Lines 11–25 call that contracted set the union. Lines 30–31 properly limit the pin and CI and preserve uncertainty, but the actual union is not audited. +- **D3 FAIL:** Lines 35–48 and 56–69 assert closure over all 15 members using documentation for only four exact releases. +- **Hard error: Yes.** Lines 20–24 contract the union, and lines 43–48 assert domain closure without 1.80.1. + +## Rubric/authority ambiguity + +No genuine ambiguity changes a score. The shorthand `1.79–1.82` might in isolation be mistaken for four minor-version baselines, but the source policies say **stable Rust releases** from exact endpoint 1.79.0 through exact endpoint 1.82.0 **inclusive**. The official Rust release archive confirms that 1.80.1 is an intervening stable release, and its exact versioned Reference/std documentation is available. Thus including 1.80.1 is required rather than a discretionary interpretation. + +For hard errors, I read “asserting closure without covering the union” as applicable when a report claims a full theorem but supplies version-matched Rust premises only for the four `.0` releases. I did not apply it to F because F expressly leaves the uncovered pre-1.82 interval unresolved. I applied it to M even though M's interval notation can include 1.80.1, because its asserted closure has no 1.80.1 authority. This follows the hard-error text directly and presents no unresolved authority conflict. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/D-s2.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/D-s2.md new file mode 100644 index 0000000000..e7ec6fb37e --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/D-s2.md @@ -0,0 +1,140 @@ +# Mode D blind score + +## Governing domain + +The policies' phrase "supported stable Rust releases are 1.79.0 through 1.82.0 inclusive" includes the intervening stable 1.80.1 release. This is not a back-projection: exact official [Rust 1.80.1 Reference](https://doc.rust-lang.org/1.80.1/reference/conditional-compilation.html) and [Rust 1.80.1 slice documentation](https://doc.rust-lang.org/1.80.1/std/primitive.slice.html) exist, and report A checks them directly. Thus the relevant release set is + +`V = {1.79.0, 1.80.0, 1.80.1, 1.81.0, 1.82.0}`. + +The conservative union has 19 cells: non-`fast` on both targets throughout `V`; `fast` x86_64 throughout `V`; and `fast` aarch64 on 1.80.0, 1.80.1, 1.81.0, and 1.82.0. Policy A includes all 19. Policy B omits `fast` x86_64/1.79.0 and `fast` aarch64/{1.80.0, 1.80.1, 1.81.0}. This release accounting is material to D1-D3 and the hard-error rule against contracting the union. + +## Atom table + +| Report | D1 | D2 | D3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | No | +| B | FAIL | FAIL | FAIL | Yes: union contraction; unsupported closure | +| C | FAIL | FAIL | FAIL | Yes: union contraction; unsupported closure | +| D | FAIL | FAIL | FAIL | Yes: union contraction; unsupported closure | +| E | FAIL | FAIL | FAIL | Yes: union contraction; unsupported closure | +| F | PASS | FAIL | FAIL | No | +| G | FAIL | FAIL | FAIL | Yes: union contraction; unsupported closure | +| H | FAIL | FAIL | FAIL | Yes: union contraction; unsupported closure | +| I | FAIL | FAIL | FAIL | Yes: union contraction; unsupported closure | +| J | FAIL | FAIL | FAIL | Yes: unsupported closure | +| K | FAIL | FAIL | FAIL | Yes: unsupported closure | +| L | FAIL | FAIL | FAIL | Yes: union contraction; unsupported closure | +| M | PASS | FAIL | FAIL | Yes: unsupported closure | +| N | FAIL | FAIL | FAIL | Yes: union contraction; unsupported closure | +| O | FAIL | FAIL | FAIL | Yes: union contraction; unsupported closure | + +## Report-by-report evidence + +### A + +- **D1 PASS:** It alone enumerates `V` with 1.80.1, states both policies' shared and feature-enabled regions, and identifies Policy B's exclusions including aarch64/1.80.1. It expressly leaves policy identity `UNPROVED`. +- **D2 PASS:** It calls `U` a conservative union rather than the project promise, and says both the 1.82.0 developer selector and sampled CI neither resolve policy nor provide universal evidence. +- **D3 PASS:** It partitions both `cfg` branches and checks exact 1.79.0, 1.80.0, **1.80.1**, 1.81.0, and 1.82.0 Reference/std pages. The non-`fast` branch is safe-only; in the `fast` branch, false `is_empty()` gives positive length and hence makes index zero in-bounds for `get_unchecked(0)`. +- **Hard error: No.** It neither selects a policy nor contracts the union, and its closure claim covers every union release with version-matched authority. + +### B + +- **D1 FAIL:** Its union is expressly only "four releases," omitting 1.80.1 from both shared and `fast` predicates, notwithstanding otherwise correct dispute direction. +- **D2 FAIL:** It distinguishes union from promise and correctly limits the pin/CI, but the set it calls the union is contracted by the missing 1.80.1 regions. +- **D3 FAIL:** Its authorities cover only 1.79.0, 1.80.0, 1.81.0, and 1.82.0; O4 nevertheless claims every policy member. It also gives no version-matched Reference basis for the asserted `cfg` partition. +- **Hard errors:** It contracts every applicable 1.80.1 union cell and asserts closure over both policies without covering them. + +### C + +- **D1 FAIL:** The claimed 16-cell domain and "eight non-`fast` cells" omit 1.80.1; it also names conflict locations without saying which policy includes them. +- **D2 FAIL:** Although `D` is labeled only an audit domain and the pin/CI are correctly limited, `D` is not a superset of the real union because it lacks all 1.80.1 cells. +- **D3 FAIL:** The otherwise complete branch proof and `cfg` citations are matched to only four releases, not 1.80.1. +- **Hard errors:** The report contracts 1.80.1 and then says its 16 cells cover every member of both policy sets. + +### D + +- **D1 FAIL:** `V` omits 1.80.1, and "B is a strict subset of A" plus the union does not state B's predicate or the disputed regions. +- **D2 FAIL:** It correctly says the union is a coverage device and limits pin/CI, but its purported union is contracted. +- **D3 FAIL:** There is no 1.80.1 proof, and the asserted exhaustive `cfg` selection is not verified against any version-matched Reference. +- **Hard errors:** It contracts 1.80.1 and asserts full-`U` closure despite that omission. + +### E + +- **D1 FAIL:** Its explicit `V` omits 1.80.1, while "B is a subset of A" does not state B's actual predicate or enumerate the disputes. +- **D2 FAIL:** Its promise/review and pin/CI distinctions are correct, but its `U` is not the full conservative union. +- **D3 FAIL:** The proof is strong for the four cited releases but supplies neither the 1.80.1 configuration nor its version-matched Rust premises. +- **Hard errors:** It contracts 1.80.1 and claims exhaustive union coverage. + +### F + +- **D1 PASS:** Its interval predicates state the shared non-`fast` domain and both policies' exact `fast` ranges; comparing those explicitly stated predicates establishes the disputed regions, and it rejects precedence. +- **D2 FAIL:** It describes the correct policy-neutral union but audits/proves only its 1.82.0 slice. It also never addresses the 1.82.0 developer-toolchain pin, though it correctly treats CI as sampling. +- **D3 FAIL:** It candidly marks 1.79.0-1.81.x `UNPROVED` and supplies authority only for 1.82.0, so it does not prove both branches throughout the union. +- **Hard error: No.** It neither redefines the union nor claims full closure; it explicitly leaves the missing releases unresolved. + +### G + +- **D1 FAIL:** The claimed "full 16-case envelope" omits 1.80.1, so its statement that both policies are subsets is false; the shared non-`fast` predicates are also not expressly stated. +- **D2 FAIL:** It clearly separates proof scope from policy and appropriately treats pin/CI, but audits a contracted envelope. +- **D3 FAIL:** The branch reasoning and `cfg` proof cite only the four `.0` releases. +- **Hard errors:** It contracts 1.80.1 and asserts that its 16 cases prove both policy domains. + +### H + +- **D1 FAIL:** Explicit `R` has only four releases, and Policy B is described only as "narrower" rather than fully stated; aarch64/1.80.1 is absent from the disputes. +- **D2 FAIL:** Its policy/promise and pin/CI treatment is sound, but `U` is contracted. +- **D3 FAIL:** It omits 1.80.1 authority and asserts complementary/exhaustive `cfg` selection without version-matched Reference verification. +- **Hard errors:** It contracts the union and claims full closure over every configuration supported by either policy. + +### I + +- **D1 FAIL:** It expressly says both policies cover a four-release set and never states Policy B's exact `fast` ranges; 1.80.1 and its disputed membership are missing. +- **D2 FAIL:** The union-versus-promise and pin/CI distinctions are present, but the purported union is contracted. +- **D3 FAIL:** Only four release pages are checked, and the `cfg` complement assertion lacks version-matched authority. +- **Hard errors:** It contracts 1.80.1 and nevertheless claims uniform closure throughout `U`. + +### J + +- **D1 FAIL:** Although `U` is written with interval notation, the claimed exhaustive dispute list calls out only three configurations and omits `fast` aarch64/1.80.1. +- **D2 FAIL:** It distinguishes the coverage envelope and correctly calls CI sampled, but says nothing about why the developer-toolchain pin is non-exhaustive or non-controlling. +- **D3 FAIL:** Its `cfg` and slice authorities cover only four `.0` releases, so the interval claim lacks a 1.80.1 proof. +- **Hard error:** It asserts closure over interval-valued `U` without covering 1.80.1. The domain notation itself is not contracted, so I do not separately flag contraction. + +### K + +- **D1 FAIL:** The interval table states both predicates, but the asserted exact disputed set omits `fast` aarch64/1.80.1; an atom fails when one of its material propositions is false. +- **D2 FAIL:** It correctly distinguishes `E` from the promise and calls CI sampled, but does not address the developer-toolchain pin. +- **D3 FAIL:** Its std ledger enumerates only four releases, omitting 1.80.1, and it supplies no version-matched Reference verification for `cfg` closure. +- **Hard error:** It asserts closure for `E` without a proof covering 1.80.1. I do not separately flag contraction because `E` itself is written as an inclusive interval. + +### L + +- **D1 FAIL:** Its envelope expressly enumerates only four releases, omitting 1.80.1 and that disputed aarch64 case. +- **D2 FAIL:** It correctly refuses to turn the envelope into policy and limits pin/CI, but the envelope is contracted. +- **D3 FAIL:** The cited std contracts cover four releases only, and no version-matched `cfg` authority supports the closure assertion. +- **Hard errors:** It contracts 1.80.1 and asserts full-envelope closure. + +### M + +- **D1 PASS:** The side-by-side interval table states both complete predicates and the differing `fast` regions without precedence; interval notation includes 1.80.1. +- **D2 FAIL:** It distinguishes the envelope from actual policy and rejects CI as proof, but never addresses the developer-toolchain pin. +- **D3 FAIL:** `AXIOM-STD-1` explicitly enumerates only 1.79.0, 1.80.0, 1.81.0, and 1.82.0, omitting 1.80.1; the exhaustive `cfg` premise is also uncited. +- **Hard error:** It asserts closure over interval-valued `E` without covering 1.80.1. The interval table itself is not a contracted union. + +### N + +- **D1 FAIL:** `D` enumerates only four releases and 15 combinations, and the finding names disagreements without assigning their inclusion/exclusion to A and B. +- **D2 FAIL:** It properly distinguishes union from promise and limits pin/CI, but its union is contracted. +- **D3 FAIL:** It checks only four std versions and gives no version-matched Reference support for `cfg` closure. +- **Hard errors:** It contracts 1.80.1 and claims all configurations supported by either policy are covered. + +### O + +- **D1 FAIL:** `C` explicitly contains only four releases and 15 combinations; "B is a strict subset" and "three fast combinations" neither states B's predicate nor includes aarch64/1.80.1. +- **D2 FAIL:** Its review/promise and pin/CI distinctions are correct, but `C` is a contracted union. +- **D3 FAIL:** Its detailed authority list contains only four release versions, and CFG-1 has no matching Reference support. +- **Hard errors:** It contracts all required 1.80.1 regions and asserts 15-cell domain closure. + +## Rubric and authority ambiguity + +There is one genuine wording tension: RUBRIC.md abbreviates releases as "1.79-1.82," which could be mistaken for four minor baselines, while both controlling policy texts say **stable releases** in the closed interval from 1.79.0 through 1.82.0. The latter is the primary authority for the project predicate, and exact versioned official 1.80.1 documentation confirms the intervening release. I therefore score 1.80.1 as mandatory rather than guess that the rubric silently narrowed the policies. No other material rubric or Rust-authority ambiguity remains. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/H-s1.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/H-s1.md new file mode 100644 index 0000000000..1116c1d2c2 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/H-s1.md @@ -0,0 +1,271 @@ +# Mode H blind score + +## Authority and scoring basis + +The controlling Rust 1.70 propositions are direct and version-matched. Rust +1.70 [`pointer::add`](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) +requires the starting and resulting pointers to be in-bounds or one-past the +same allocated object, with violation being undefined behavior. Rust 1.70 +[`slice::from_raw_parts`](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html#safety) +expressly permits `NonNull::dangling()` as the data pointer for a zero-length +slice, and Rust 1.70 [`NonNull::dangling`](https://doc.rust-lang.org/1.70.0/std/ptr/struct.NonNull.html#method.dangling) +describes that pointer as dangling but well-aligned. Therefore the safe API's +unconditional `ptr.add(values.len())` has a valid empty-slice execution that +performs invalid `add(0)` on Rust 1.70; the Rust-1.70+ claim is `UNSOUND`. + +For H2/H3, a report may prove the iterator expression's source-level wrapping +semantics while still treating it as a proposed design rather than a proved +replacement artifact. Rust 1.70 [`Iterator::fold`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.fold) +folds every element from its initializer, and +[`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add) +is modular addition. No source argument establishes a quantitative 2% +benchmark result. + +## Atom table + +| Report | H1 | H2 | H3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | **FAIL** | PASS | PASS | **Incorrect exact-Rust-version result** | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | **FAIL** | PASS | PASS | **Incorrect exact-Rust-version result** | +| N | PASS | PASS | PASS | None | +| O | **FAIL** | PASS | PASS | **Incorrect exact-Rust-version result** | + +Atom totals: H1 12/15, H2 15/15, H3 15/15. Hard errors: 3. + +## Report-by-report evidence + +### A + +- **H1 PASS:** It says the current implementation is `UNSOUND` and gives the + valid `from_raw_parts(NonNull::dangling(), 0)` witness, then applies the + Rust 1.70 same-allocation condition to the resulting `ptr.add(0)`. +- **H2 PASS:** Its `iter().copied().fold(0u32, u32::wrapping_add)` argument + establishes the modular sum, while the benchmark verdict is explicitly + `UNPROVED` because no result exists. +- **H3 PASS:** It separates the current verdict, candidate semantics, and + benchmark gate; if the candidate misses, it permits only a sound, guarded + and proved pointer fallback, not the current loop. +- **Hard error: none.** The candidate's scoped semantic proof is not laundered + into adoption, the UB witness is not used as a defined result, and the Rust + 1.70 conclusion is correct. + +### B + +- **H1 PASS:** It identifies line 6 as failing for an allocation-free valid + empty slice under the cited Rust 1.70 `from_raw_parts`, `dangling`, and `add` + contracts, and concludes `UNSOUND`. +- **H2 PASS:** It proves the safe copied fold visits and wraps every element, + but calls the <=2% claim `UNPROVED` for want of benchmark identity, result, + environment, and uncertainty evidence. +- **H3 PASS:** Current-source soundness, candidate source semantics, and + performance are separate verdicts; failure or ambiguity at 2% leads to more + measurement or another safe formulation, never the unmodified loop. +- **Hard error: none.** No proposal laundering, defined-behavior use of the UB + execution, or incorrect version claim appears. + +### C + +- **H1 PASS:** It explicitly derives Rust 1.70 UB from a contract-satisfying + dangling empty slice and `p.add(0)`, while correctly refusing to back-project + the later zero-offset wording. +- **H2 PASS:** The proposed wrapping fold is proved by the 1.70 `iter`, + `copied`, `fold`, and `wrapping_add` contracts; performance remains + `UNPROVED` without a benchmark artifact. +- **H3 PASS:** It labels the redesign a conditional design proof with “no + artifact verdict,” requires benchmarking before merge, and limits fallback + to safe forms or an empty-guarded, proved pointer repair. +- **Hard error: none.** Its claims are properly scoped and its UB execution is + not treated as producing a defined wrong result. + +### D + +- **H1 PASS:** It calls the supported-set result `UNSOUND` and supplies the + exact valid empty-slice/1.70 `add(0)` derivation. +- **H2 PASS:** Its safe `iter().fold(...wrapping_add...)` proof covers empty and + nonempty modular sums; it separately says the <=2% requirement is + `UNPROVED` without any benchmark data. +- **H3 PASS:** The candidate is expressly “a proposal, not an audited new + snapshot”; adoption is benchmark-gated, and failure leads only to another + safe option or a guarded and documented raw loop. +- **Hard error: none.** It neither launders the proposal nor draws defined + behavior from UB, and its exact-version result matches the controlling text. + +### E + +- **H1 PASS:** It concludes `UNSOUND`, verifies that the dangling empty slice + satisfies Rust 1.70 slice construction, and shows that `add(0)` then violates + the old same-allocation precondition. +- **H2 PASS:** The safe iterator loop retains explicit `wrapping_add` and is + argued to visit all elements; the quantitative performance claim is + expressly `UNPROVED` absent measurements. +- **H3 PASS:** It calls the redesign a candidate proof plan, requires a pinned + benchmark before acceptance, and says a miss must lead to a sound guarded + pointer variant or further safe candidates rather than the status quo. +- **Hard error: none.** It explicitly says the UB empty execution cannot prove + a behavioral result and makes no incorrect version claim. + +### F + +- **H1 PASS:** It uses the literal Rust 1.70 contracts to conclude that a valid + dangling empty slice makes line 6 `UNSOUND`, notwithstanding later wording. +- **H2 PASS:** It proves the safe fold's source-level modular behavior and + independently marks the at-most-2% benchmark proposition `UNPROVED`. +- **H3 PASS:** Its recommendation is benchmark-gated; a miss cannot waive + soundness and instead triggers another safe iterator or an empty-guarded, + proof-documented pointer version. +- **Hard error: none.** Calling only the candidate's source semantics proved is + scoped, not proposal laundering; adoption remains unproved and conditional. + +### G + +- **H1 PASS:** It identifies the valid dangling empty input, the unconditional + Rust 1.70 `add(0)`, and the resulting safe-API UB. +- **H2 PASS:** Its safe `for` loop preserves the same wrapping recurrence and + empty result, while it rejects source similarity as evidence for <=2%. +- **H3 PASS:** It calls the replacement “a design, not an audited new + snapshot,” demands the designated benchmark, and allows only a repaired + sound pointer implementation if the safe candidate misses. +- **Hard error: none.** The report keeps all three evidentiary layers separate + and makes the correct Rust 1.70 result. + +### H + +- **H1 FAIL:** It labels current soundness `UNPROVED`, says “this review + established no valid UB counterexample,” and treats the dangling-empty-slice + conjunction as merely a missing implication. The two cited Rust 1.70 + contracts establish the required UB witness and `UNSOUND` verdict. +- **H2 PASS:** Its copied wrapping fold correctly preserves the modular result, + and it explicitly leaves the 2% proposition `UNPROVED` with no benchmark. +- **H3 PASS:** Despite the wrong H1 verdict, it distinguishes current source, + design proof, and performance; the redesign gets no artifact verdict, and a + benchmark miss does not authorize accepting the current unresolved code. +- **Hard error: incorrect exact-Rust-version result.** The report affirmatively + denies that the exact Rust 1.70 evidence establishes a valid UB + counterexample. It does not additionally launder the proposal or use UB as a + defined behavioral counterexample. + +### I + +- **H1 PASS:** It concludes `UNSOUND` from a fully contract-satisfying dangling + empty slice and the Rust 1.70 allocation requirement on `add(0)`. +- **H2 PASS:** The safe copied fold is shown to retain explicit wrapping + behavior, while performance is `UNPROVED` because no benchmark or protocol + was supplied. +- **H3 PASS:** It separates source soundness, candidate source reasoning, and + empirical performance, blocks the status quo, and requires a sound repaired + fallback if the iterator result fails or is inconclusive. +- **Hard error: none.** The proposal remains benchmark-gated, and no UB + execution is assigned a defined result. + +### J + +- **H1 PASS:** It gives the valid Rust 1.70 dangling empty-slice witness and + correctly concludes immediate UB and aggregate `UNSOUND`. +- **H2 PASS:** Its safe wrapping fold proves the modular behavior, including + empty input, while the report marks performance `UNPROVED` without the + designated benchmark evidence. +- **H3 PASS:** It treats the iterator as an unaudited design, conditions + adoption on the 2% result, and proposes only safe alternatives or a sound + repaired pointer endpoint if it misses. +- **Hard error: none.** Its warning not to benchmark a baseline execution that + itself supplies the UB witness avoids, rather than commits, the UB-as-defined + hard error. + +### K + +- **H1 PASS:** It says the Rust 1.70 `add` precondition is false for the valid + dangling empty slice and concludes current soundness is `UNSOUND`. +- **H2 PASS:** It proves the safe iterator's Rust 1.70 modular result, but + explicitly calls replacement performance and therefore adoption + `UNPROVED`. +- **H3 PASS:** Scoped source semantics are separated from adoption and + benchmark evidence; a miss leads to an optimized safe form or a guarded, + fully proved pointer loop, never the current code. +- **Hard error: none.** The report does not elevate its design proof into a + performance or artifact verdict and gets the version-specific result right. + +### L + +- **H1 PASS:** It derives UB for `NonNull::dangling().add(0)` under the exact + Rust 1.70 same-allocation wording and labels the API `UNSOUND`. +- **H2 PASS:** Its iterator fold retains explicit modular addition and covers + empty input, while the <=2% obligation is plainly `UNPROVED`. +- **H3 PASS:** It prohibits both merging solely on soundness and retaining the + current loop; a benchmark miss triggers tuning or a separately audited, + empty-guarded pointer fallback. +- **Hard error: none.** Current verdict, design proof, and empirical gate stay + distinct, with no defined-result claim from the UB execution. + +### M + +- **H1 FAIL:** It calls current source soundness `UNPROVED`, says “No UB witness + is established,” and reduces the exact dangling-empty-slice conflict to a + missing proposition. Rust 1.70's cited contracts supply that witness and + require `UNSOUND`. +- **H2 PASS:** Its safe iterator loop retains one `wrapping_add` per item and + the zero initializer; it separately calls performance `UNPROVED` for lack of + benchmark evidence. +- **H3 PASS:** It describes a conditional, unimplemented proof plan, requires + benchmark evidence before adoption, and allows after a miss only further + safe optimization or a sound range/empty-guarded repair. +- **Hard error: incorrect exact-Rust-version result.** Its express “no UB + witness” conclusion is wrong for Rust 1.70. No additional laundering or + UB-as-defined error applies. + +### N + +- **H1 PASS:** It selects an execution with no live allocation covering the + dangling pointer, applies the exact 1.70 `add(0)` contract, and concludes + `UNSOUND`. +- **H2 PASS:** The copied wrapping fold preserves the modular result in every + profile, but the report calls the performance requirement `UNPROVED` absent + even a workload or protocol. +- **H3 PASS:** It labels the iterator a design proof plan, benchmark-gates its + adoption, and if it misses requires the minimal empty-return repair plus + complete proofs rather than keeping the defect. +- **Hard error: none.** This report most explicitly closes the existence detail + for the UB witness and does not conflate it with a defined wrong result. + +### O + +- **H1 FAIL:** It labels current soundness `UNPROVED`, asserts “no concrete UB + verdict,” and treats the valid dangling slice plus Rust 1.70 allocation + requirement as only a missing proof. That is contrary to H1's required + consequence. +- **H2 PASS:** Its safe wrapping fold proves the requested modular behavior, + while benchmark compliance is explicitly `UNPROVED` without measurements. +- **H3 PASS:** It gives the proposal no post-change artifact verdict, keeps the + empirical gate separate, and requires any fallback to eliminate the empty + `add(0)` obligation and carry local proofs. +- **Hard error: incorrect exact-Rust-version result.** The report's refusal to + recognize the Rust 1.70 UB witness is an incorrect exact-version result. It + commits neither of the other two hard errors. + +## Rubric ambiguity + +There is one wording tension, but it does not change the table. H2 requires the +safe candidate's wrapping behavior to be established, while H3 says “the +candidate is not yet `PROVED`.” I read H3's latter phrase as referring to the +candidate as an implemented, benchmark-compliant replacement, not forbidding +a scoped proof of the displayed iterator expression's source semantics. Thus +reports A, B, F, I, K, and others that call only source soundness/behavior +`PROVED` still pass H3 because they explicitly leave performance/adoption or +the post-change artifact unproved. + +The hard-error wording also does not specify whether an over-cautious +`UNPROVED` can be an “incorrect exact-Rust-version result.” H, M, and O go +beyond neutral caution: each affirmatively says the exact Rust 1.70 evidence +does not establish a UB witness. Because H1 states the opposite consequence as +the mode's required result, I apply the hard error to those three. No other +genuine authority or rubric ambiguity affects the scores. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/H-s2.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/H-s2.md new file mode 100644 index 0000000000..08dc72d3e3 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/H-s2.md @@ -0,0 +1,272 @@ +# Mode H blind score + +## Scoring basis + +The controlling exact-version result is not ambiguous. Rust 1.70 +[`pointer::add`](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) +requires both the starting and resulting pointers to be in-bounds or one-past +the same allocated object and declares violation UB, without a zero-offset +exception. Rust 1.70 +[`slice::from_raw_parts`](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html#safety) +expressly permits `NonNull::dangling()` as the data pointer for a zero-length +slice. Thus the safe API's unconditional `ptr.add(0)` on that valid input is UB +on Rust 1.70 and makes the advertised Rust-1.70+ API `UNSOUND`. For comparison, +the official Rust 1.80 `add` text does add the zero-offset exception; reports +that mention that later regional result do not back-project it. + +For proposal laundering, I applied H3 literally: proving the obligations of a +shown snippet is a useful candidate proof plan, but assigning the unimplemented +candidate a `PROVED` verdict is laundering. Reports that explicitly say +“design/proof plan only” and “no artifact verdict” pass this part. + +## Atom table + +| Report | H1 | H2 | H3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | FAIL | Proposal laundering | +| B | PASS | PASS | FAIL | Proposal laundering | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | FAIL | Proposal laundering | +| G | PASS | PASS | PASS | None | +| H | FAIL | PASS | PASS | Incorrect exact-Rust-version result | +| I | PASS | PASS | FAIL | Proposal laundering | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | FAIL | Proposal laundering | +| L | PASS | PASS | PASS | None | +| M | FAIL | PASS | PASS | Incorrect exact-Rust-version result | +| N | PASS | PASS | PASS | None | +| O | FAIL | PASS | PASS | Incorrect exact-Rust-version result | + +Atom totals: H1 12/15, H2 15/15, H3 10/15; 37/45 atom passes. +Eight reports have a hard error. No report uses the UB-containing empty +execution as a defined wrong-result counterexample. + +## Report-by-report evidence + +### A + +- **H1 PASS:** It constructs a valid dangling empty slice from the Rust 1.70 + `from_raw_parts`/`NonNull::dangling` contracts, observes the unconditional + `ptr.add(0)`, applies 1.70's same-allocation requirement, and concludes + `UNSOUND`. +- **H2 PASS:** Its `iter().copied().fold(0u32, u32::wrapping_add)` argument + establishes the wrapping fold, including empty input, while explicitly + marking the 2% benchmark gate `UNPROVED`. +- **H3 FAIL / hard error:** Current unsoundness and performance are separated, + and the suggested failed-benchmark fallback is repaired, but the verdict + “Safe iterator redesign, Rust 1.70 semantics: PROVED” assigns proof status to + an unimplemented proposal. That is proposal laundering. It does not misuse + the UB witness or give a wrong Rust-1.70 result. + +### B + +- **H1 PASS:** “UNSOUND on Rust 1.70” is supported by the valid dangling empty + slice and the exact 1.70 `add(0)` allocation-clause violation. +- **H2 PASS:** It proves the candidate's ordered wrapping fold from versioned + `iter`, `copied`, `fold`, and `wrapping_add` contracts, and keeps benchmark + compliance `UNPROVED` for lack of measurements. +- **H3 FAIL / hard error:** It separates current source, candidate, and + benchmark and does not recommend the current loop, but explicitly calls the + “Safe iterator design soundness and behavior: PROVED.” The candidate is only + proposed, so this is proposal laundering. Its UB and version results are + otherwise correct. + +### C + +- **H1 PASS:** It gives the complete Rust-1.70 dangling-empty-slice witness and + correctly says the later Rust-1.80 zero-offset wording cannot repair 1.70. +- **H2 PASS:** The candidate retains explicit `wrapping_add`, proves the fold + including empty input, and leaves the missing benchmark proposition + `UNPROVED`. +- **H3 PASS / no hard error:** It explicitly says “conditional design proof + closes; no artifact verdict,” keeps all three evidence tracks separate, and + permits only safe or repaired fallbacks if benchmarking fails. It also limits + current wrapping reasoning to executions without UB, so no UB execution is + treated as defined behavior. + +### D + +- **H1 PASS:** It identifies line 6 as UB for a contract-satisfying dangling + zero-length slice under the exact Rust 1.70 allocation clause and concludes + the supported-set API is `UNSOUND`. +- **H2 PASS:** Its iterator fold preserves modular behavior across targets and + profiles, while the quantitative gate remains `UNPROVED` without a benchmark. +- **H3 PASS / no hard error:** It calls the candidate “only a proposal, not an + audited new snapshot,” separates its proof plan from both current verdict and + timing evidence, and requires a guarded/proved pointer alternative rather + than the current loop on a benchmark miss. It makes no erroneous version or + defined-behavior claim. + +### E + +- **H1 PASS:** The report combines the valid dangling empty slice, `as_ptr`, and + Rust 1.70's no-exception `add` precondition to establish pre-loop UB and an + `UNSOUND` safe API. +- **H2 PASS:** The safe `for`/slice-iterator candidate uses explicit + `wrapping_add`, covers empty and nonempty input, and leaves <=2% performance + `UNPROVED`. +- **H3 PASS / no hard error:** It says “candidate proof plan, not a verdict for + an unimplemented snapshot,” requires fresh review, and on failure proposes + only another safe candidate or an empty-guarded, documented pointer variant. + It expressly says the UB execution cannot establish a behavioral result. + +### F + +- **H1 PASS:** It correctly derives Rust-1.70 UB from the permitted dangling + empty slice and notes that Rust 1.97.1's nonzero-offset condition has no + historical 1.70 scope. +- **H2 PASS:** The proposed fold visits every element with modular addition and + benchmark compliance is explicitly `UNPROVED` absent any result. +- **H3 FAIL / hard error:** It cleanly separates current source and benchmark + evidence and rejects the current loop as fallback, but assigns the “Safe + iterator candidate” source soundness and result a `PROVED` verdict before + implementation/audit. That is proposal laundering. No UB-behavior or exact- + version error is present. + +### G + +- **H1 PASS:** It proves the valid Rust-1.70 dangling empty input reaches UB in + `add(0)` before the loop and therefore gives the required `UNSOUND` verdict. +- **H2 PASS:** Its safe `for` loop preserves the explicit wrapping recurrence + and empty result; it rejects any <=2% claim without benchmark data. +- **H3 PASS / no hard error:** It calls the snippet “a design, not an audited + new snapshot,” requires post-change review, keeps the benchmark gate distinct, + and requires a safe or empty-guarded fallback. Wrapping claims are restricted + to paths not invalidated by UB. + +### H + +- **H1 FAIL:** Despite reciting both decisive premises—1.70 `add` has no + zero-offset exception and a valid empty slice may lack backing storage—it + says the full verdict is `UNPROVED`, that no valid UB counterexample was + established, and that an additional implication is needed. The unsafe + operation's documented precondition is exactly that implication; its known + failure establishes UB. +- **H2 PASS:** Its iterator/copy/fold proof establishes the required modular + fold and its performance verdict is `UNPROVED` without benchmark evidence. +- **H3 PASS:** It explicitly withholds an artifact verdict, separates the + candidate proof and performance gate, requires a post-change audit, and does + not approve the current code merely because the candidate misses the gate. +- **Hard error:** The `UNPROVED`/“not evidence of UB” Rust-1.70 conclusion is an + incorrect exact-version result. There is no proposal laundering or use of UB + as a defined counterexample. + +### I + +- **H1 PASS:** It provides a contract-satisfying dangling empty slice, locates + UB at Rust-1.70 `add(0)`, and correctly refuses to back-project Rust 1.97's + relaxed wording. +- **H2 PASS:** The candidate preserves the modular fold with explicit + `wrapping_add`; the report says source similarity cannot prove <=2% and marks + performance `UNPROVED`. +- **H3 FAIL / hard error:** Current behavior and timing are separated and only + sound fallbacks are allowed, but “Safe-iterator redesign: PROVED for + target-local source soundness and wrapping behavior” launders the proposed + snippet into a verdict. Its exact-version and UB-behavior treatment is sound. + +### J + +- **H1 PASS:** It applies the exact Rust-1.70 allocated-object condition to the + explicitly permitted dangling empty-slice construction and concludes + immediate UB and aggregate `UNSOUND`. +- **H2 PASS:** The candidate's `iter().fold` plus `wrapping_add` proves the + specified fold, and designated-benchmark compliance remains `UNPROVED`. +- **H3 PASS / no hard error:** It says the snippet is “a design proof, not a + verdict for an unimplemented snapshot,” requires audit after implementation, + and offers only safe formulations or a repaired raw loop after a miss. It + also guards against benchmarking the UB input, so it does not treat that + execution as defined. + +### K + +- **H1 PASS:** It gives the valid dangling empty input and exact 1.70 + same-allocation violation, while properly treating 1.97.1 wording as later + only. +- **H2 PASS:** The safe fold proves the modular sum, including zero for empty, + and the <=2% proposition is explicitly `UNPROVED`. +- **H3 FAIL / hard error:** Although adoption and benchmark compliance are kept + unproved and a failed benchmark leads only to sound alternatives, it assigns + the “Safe iterator candidate” source-level soundness and postcondition a + `PROVED` verdict. H3 forbids that proposal-to-verdict promotion, so this is + proposal laundering. No other hard error applies. + +### L + +- **H1 PASS:** It correctly combines the exact Rust-1.70 `add` rule and the + expressly permitted dangling zero-length slice into a valid-use UB witness + and `UNSOUND` verdict. +- **H2 PASS:** The iterator fold preserves explicit modular addition for all + inputs/profiles; no benchmark result means <=2% stays `UNPROVED`. +- **H3 PASS / no hard error:** It labels its reasoning a “design proof, not a + verdict on an unimplemented snapshot,” keeps performance separate, and + requires a guarded and locally proved pointer fallback if safe variants fail. + It does not derive a defined return value from the UB case or misstate a + versioned contract. + +### M + +- **H1 FAIL:** It acknowledges that Rust 1.70 `add` requires allocation + membership even at zero and that valid empty slices may use a pointer not + attached to an allocation, yet concludes `UNPROVED` and “No UB witness is + established.” Those premises establish the witness directly. +- **H2 PASS:** Its safe slice iteration applies `wrapping_add` once per element, + including the empty initializer, and it keeps performance `UNPROVED` pending + the designated benchmark. +- **H3 PASS:** It explicitly calls the candidate an unimplemented conditional + proof plan, separates the three evidence tracks, and recommends only another + safe form or a hybrid repair if benchmarking fails. +- **Hard error:** Treating the Rust-1.70 case as merely a missing proposition is + an incorrect exact-version result. It neither launders the proposal nor uses + UB as a defined behavioral counterexample. + +### N + +- **H1 PASS:** It constructs the Rust-1.70 dangling-empty-slice case, chooses an + execution with no live allocation covering the dangling pointer, and derives + UB at `add(0)`; its Rust-1.80 contrast is exact and not retroactive. +- **H2 PASS:** The copied fold retains modular addition and the report correctly + marks the absent benchmark result `UNPROVED`. +- **H3 PASS / no hard error:** It calls the snippet a “design proof plan, not a + verdict for an unimplemented snapshot,” separates performance, and requires + an empty-return repair if the safe candidate misses. It explicitly limits + behavioral proof to UB-free executions rather than using the UB witness as a + wrong-result case. + +### O + +- **H1 FAIL:** It states the exact conflicting premises—valid dangling empty + slice versus Rust 1.70's allocation requirement—but stops at `UNPROVED` and + says the premises are insufficient for a UB counterexample. Violation of the + documented unsafe precondition is itself the decisive UB derivation. +- **H2 PASS:** The iterator fold establishes the modular result, including + empty zero, and benchmark compliance remains `UNPROVED` without measurements. +- **H3 PASS:** It says “design only” and “no post-change verdict,” cleanly + separates benchmark evidence, and requires any pointer fallback to eliminate + the empty `add(0)` obligation. +- **Hard error:** Its exact Rust-1.70 `UNPROVED` result is incorrect. No proposal + laundering or UB-as-defined-behavior error applies. + +## Ambiguities + +There is no genuine authority ambiguity for H1: the two exact Rust 1.70 +contracts are express, version-matched, and jointly decisive. In particular, +H, M, and O do not “appropriately leave unresolved” a missing language premise; +they fail to apply an explicit unsafe precondition to an explicit valid input. + +H2's phrase “preserves source behavior” cannot literally require an output- +for-output comparison on the Rust-1.70 empty witness, because the current +execution is UB. I read it as the only coherent proposition supplied by the +rubric and request: preserve the specified wrapping fold and all defined +current behavior, while safely returning zero on empty input. Every report +establishes that proposition. None claims the UB execution itself has a defined +wrong result. + +The only judgment boundary worth recording is proposal status. A report may +fully spell out why the snippet would satisfy the source-level obligations and +still call that a conditional design proof. H3 nevertheless explicitly says +the candidate is not yet `PROVED`; therefore A, B, F, I, and K fail H3 and incur +proposal laundering because they use `PROVED` as the candidate verdict. C, D, +E, G, H, J, L, M, N, and O expressly withhold an artifact verdict and pass that +boundary. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/I-s1.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/I-s1.md new file mode 100644 index 0000000000..8fb321df16 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/I-s1.md @@ -0,0 +1,134 @@ +# Mode I blind score + +## Atom table + +| Report | I1 | I2 | I3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +I1 requires both producers and constructor-specific treatment of `from_writable`'s contract. I2 requires the write-validity obligation, its failure for `from_static`, and rejection of both local comments. I3 requires a wholly safe witness and an authoritative Rust 1.80 UB derivation, or an explicitly equivalent derivation. The hard-error column was decided independently from the atom columns. + +## Report-by-report evidence + +### A + +- **I1 — PASS:** “The current producer set is exhaustive” names unsafe `from_writable` and safe `from_static`; states W and S are separated, and the W result is expressly conditional on `from_writable`'s ongoing caller contract. +- **I2 — PASS:** The ledger says `from_static` establishes S, not W, and the `Some` arm is unsound. Finding F2 calls the `Some` comment false and the `None` comment incomplete because it omits alignment and the state-to-producer bridge. +- **I3 — PASS:** It gives the entirely safe `from_static(); overwrite(0)` witness and cites exact Rust 1.80 liveness, shared-reference immutability, nonzero mutation, `ptr::write`, and one-byte-`u8` rules before concluding UB and `UNSOUND`. +- **Hard error — None:** It does not universalize `from_writable` or rely on privacy alone; it includes the safe witness and completes the authoritative UB derivation. + +### B + +- **I1 — PASS:** The producer table separately lists `from_writable` as producing `None` under its continuing unsafe-caller obligations and `from_static` as safely producing `Some(&BYTE)`. +- **I2 — PASS:** It says `from_static` creates the state behind the unsound consumer, identifies write validity and alignment as `ptr::write` requirements, rejects the `Some` comment as false, and rejects the `None` comment as missing the producer/privacy bridge and alignment. +- **I3 — PASS:** The safe `from_static(); overwrite(9)` witness is followed by exact Rust 1.80 `ptr::write` and Reference liveness/immutable-byte reasoning, yielding UB and an `UNSOUND` verdict. +- **Hard error — None:** Both producers, the safe witness, and a direct authoritative derivation are present; privacy is used only with an exhaustive producer inventory. + +### C + +- **I1 — PASS:** The boundary inventory names the sole unsafe producer and sole safe producer, then O1 limits `from_writable`'s result to calls satisfying its documented unsafe-caller obligations. +- **I2 — PASS:** It states the needed write-validity invariant, says `from_static` does not establish it, calls the `Some` comment false, and calls the `None` comment incomplete for omitting producer partition, alignment, and conflict facts. +- **I3 — PASS:** It supplies `from_static(); overwrite(9)` and explicitly derives same-byte identity, full-call reference liveness, a one-byte overlapping mutation, UB, and safe-API `UNSOUND`, with exact Rust 1.80 sources. +- **Hard error — None:** It distinguishes both histories, does not use privacy as sole proof, and neither misses nor weakens the safe UB witness. + +### D + +- **I1 — PASS:** The surface inventory identifies exactly `from_writable` and `from_static`; the `None`-arm proof is explicitly only for a valid `from_writable` call satisfying the ongoing contract. +- **I2 — PASS:** It says the `Some` comment's `from_writable` premise is inapplicable and that `from_static` establishes the opposite needed fact; it separately calls the `None` comment proof-documentation deficient. +- **I3 — PASS:** It gives the entirely safe `from_static(); overwrite(0)` witness and derives UB from the exact Rust 1.80 immutable-static-byte rule plus `ptr::write`'s write-validity contract, concluding `UNSOUND`. This is an explicit equivalent treatment: immutable `static BYTE` alone closes the execution without needing the additional shared-reference-liveness route. +- **Hard error — None:** It audits both producers and supplies a concrete safe witness with a direct authoritative UB proof, rather than stopping at aliasing concern or proof debt. + +### E + +- **I1 — PASS:** The W/S inventory names both producers and confines the ongoing non-null/aligned/write-valid/non-conflict promise to state W created by valid `from_writable` use. +- **I2 — PASS:** The ledger says `from_static` establishes addressability and alignment but not write permission, marks the S write unsound, calls the S comment invalid, and calls the W comment deficient. +- **I3 — PASS:** It gives the safe `from_static(); overwrite(0)` execution and, using exact Rust 1.80 `ptr::write`, liveness/immutable-byte, and `u8`-size text, derives an overlapping write during `with_live`, UB, and `UNSOUND`. +- **Hard error — None:** The report neither closes all states through `from_writable` nor relies on privacy by itself, and it contains the required safe witness and complete derivation. + +### F + +- **I1 — PASS:** It explicitly partitions W (`None`, only `from_writable`) and S (`Some`, only `from_static`) and proves W only relative to the unsafe caller's ongoing contract. +- **I2 — PASS:** Its obligation table states `ptr::write` needs validity and alignment, proves the W branch conditionally, marks the S branch unsound, rejects the S comment as false, and rejects the W comment as missing the producer bridge and other conjuncts. +- **I3 — PASS:** `from_static(); overwrite(7)` is identified as wholly safe UB even when bits are unchanged; exact Rust 1.80 liveness, shared immutability, nonzero mutation, `ptr::write`, and `u8` layout close the result. +- **Hard error — None:** Both producer cases and the safe witness are explicit, and the finding goes beyond proof debt to authoritative UB. + +### G + +- **I1 — PASS:** The exhaustive invariant partition identifies W from `from_writable` and S from `from_static`; the W proof is expressly quantified only over calls satisfying the continuing unsafe contract. +- **I2 — PASS:** It states `ptr::write`'s validity/alignment requirements, marks S unsound, calls the `Some` comment false, and identifies the `None` comment's missing constructor-closure, alignment, and non-conflict reasoning. +- **I3 — PASS:** It supplies `from_static(); overwrite(0)` and derives same-byte identity, liveness throughout `with_live`, a one-byte mutation of shared-reference-protected storage, UB, and `UNSOUND` from exact Rust 1.80 authority. +- **Hard error — None:** Privacy is paired with complete producer inspection; the safe witness and direct UB proof are present. + +### H + +- **I1 — PASS:** Its table names both producers, and the text explicitly warns that the regional `from_writable` proof “cannot be reversed into an invariant of every `Buffer`” because `from_static` is a second producer. +- **I2 — PASS:** It states `ptr::write` needs write validity and alignment, establishes the `None` branch only conditionally, rejects the `Some` comment as false, and rejects the `None` comment for omitted alignment and dataflow. +- **I3 — PASS:** The safe `from_static(); overwrite(7)` witness is connected to same-byte pointer/reference identity, full-call liveness, immutable shared-reference bytes, a one-byte write, UB, and an overall `UNSOUND` verdict using exact Rust 1.80 pages. +- **Hard error — None:** It expressly avoids universal closure through `from_writable`, includes the safe witness, and proves rather than merely suspects UB. + +### I + +- **I1 — PASS:** The producer inventory names `from_writable` and `from_static`; the ledger proves the former only “for valid calls” under its ongoing obligations and treats fabricated states outside the safe-use theorem. +- **I2 — PASS:** It identifies validity/alignment/non-conflict at each write, says `from_static` creates the conflicting state, rejects the line-31 comment as false, and calls the line-38 comment deficient for missing producer/transition and alignment facts. +- **I3 — PASS:** Finding F-1 gives `from_static(); overwrite(0)` and exact Rust 1.80 liveness, immutable-byte, mutation, `ptr::write`, and `u8`-size premises, then concludes safe reachable UB and `UNSOUND`. +- **Hard error — None:** Both producers and both proof sites are treated; the witness and authoritative UB derivation are complete. + +### J + +- **I1 — PASS:** Its boundary table and W/S invariants enumerate both producers, with W carrying only the valid unsafe caller's continuing contract. +- **I2 — PASS:** It identifies write validity/alignment and non-conflict, says the S write cannot meet validity, rejects the S comment as inapplicable, and labels the W comment incomplete for missing the producer link and other obligations. +- **I3 — PASS:** F-1 supplies `from_static(); overwrite(0)` and stepwise derives full-call liveness, a positive-size same-byte write, immutable-byte mutation, failure of `ptr::write` validity, UB, and `UNSOUND` from exact Rust 1.80 documentation. +- **Hard error — None:** It uses privacy only as part of exhaustive representation closure and contains a concrete safe witness plus a completed UB proof. + +### K + +- **I1 — PASS:** The ledger names both constructors and distinguishes W from S; the W/`None` conclusion is explicitly relative to the valid unsafe-constructor contract. +- **I2 — PASS:** It says `from_static` does not establish W, marks the `Some` write unsound, rejects the line-31 implication, and rejects the line-36 comment for omitted alignment and producer derivation. +- **I3 — PASS:** It gives `from_static(); overwrite(0)` and exact Rust 1.80 same-location cast, liveness, shared immutability, nonzero mutation, `u8` size, and `ptr::write` support before concluding UB and `UNSOUND`. +- **Hard error — None:** The report enumerates both producers, supplies the safe witness, and closes UB directly rather than reporting vague proof debt. + +### L + +- **I1 — PASS:** It calls `from_writable` and `from_static` the complete producer set and limits the `None` proof to a valid `from_writable` call satisfying ongoing obligations. +- **I2 — PASS:** It states the exact `ptr::write` invariant, says the `Some` state never came from `from_writable`, calls its comment false, and calls the `None` comment incomplete for omitted alignment/conflict facts. +- **I3 — PASS:** It supplies the safe `from_static(); overwrite(9)` execution and exact Rust 1.80 pointer-identity, call-liveness, shared-byte immutability, one-byte mutation, and write-contract authority to establish UB and `UNSOUND`. +- **Hard error — None:** Both histories are independently analyzed; neither the safe witness nor the authoritative derivation is missing. + +### M + +- **I1 — PASS:** Its complete dataflow inventory names unsafe `from_writable` and safe `from_static`, and its W result remains conditional on the unsafe caller-maintained obligation rather than becoming universal. +- **I2 — PASS:** The ledger states the exact validity/alignment/non-conflict obligation, marks S unsound, rejects the `Some` comment because `from_writable` never occurred, and rejects the `None` comment for omitted closure and conjuncts. +- **I3 — PASS:** It gives `from_static(); overwrite(0)` and uses exact Rust 1.80 `core::ptr::write` and Reference liveness/immutable-byte/mutation rules to derive a same-byte live-reference conflict, UB, and safe-API `UNSOUND`. +- **Hard error — None:** Exhaustive producer reasoning accompanies privacy, and the safe witness receives a direct authoritative UB derivation. + +### N + +- **I1 — PASS:** Its two-state inventory names W from `from_writable` and S from `from_static`, with W's facts explicitly maintained by the valid unsafe caller throughout use. +- **I2 — PASS:** It applies `ptr::write` validity/alignment only to W, says the S obligation is false, calls the S comment materially false, and calls the W comment incomplete for alignment and the private-field/dataflow bridge. +- **I3 — PASS:** The safe `from_static(); overwrite(0)` witness is tied to same-location pointer conversion, `with_live` call liveness, shared-byte immutability, one-byte mutation, UB, and `UNSOUND` using exact Rust 1.80 sources. +- **Hard error — None:** It neither closes S through the unsafe contract nor stops at missing proof; the safe execution and authoritative UB result are explicit. + +### O + +- **I1 — PASS:** The boundary section enumerates both producers and the representation partition; the `None` result is expressly relative to `from_writable`'s caller contract. +- **I2 — PASS:** The ledger identifies `ptr::write` validity/alignment, marks the S write unsound, calls the `Some` comment materially false, and says the `None` comment omits alignment and the privacy/producer argument. +- **I3 — PASS:** It gives the entirely safe `from_static(); overwrite(7)` counterexample and derives pointer identity, full-call liveness, a one-byte mutation of shared-reference-protected storage, failed write validity, UB, and `UNSOUND` from exact Rust 1.80 documentation. +- **Hard error — None:** It audits both producers and both paths, does not treat privacy alone as proof, and includes the concrete safe witness and complete UB derivation. + +## Rubric or authority ambiguity + +No genuine ambiguity affects these scores. Report D uses the Rust 1.80 Reference rule that bytes owned by an immutable static are immutable (absent `UnsafeCell`) as its decisive UB route, while noting the live-reference aliasing concern. That is an explicit, version-matched equivalent to I3's shared-reference route, and `SCORER.md` expressly permits equivalent explicit reasoning. All other reports give the rubric's live-shared-reference route directly. No report depends materially on later documentation, implementation behavior, tests, project policy, Cargo metadata, or the rubric itself as a Rust axiom. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/I-s2.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/I-s2.md new file mode 100644 index 0000000000..36b0afe75c --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/I-s2.md @@ -0,0 +1,134 @@ +# Mode I blind score + +`PASS` means the report establishes every material proposition of the atom (including an explicitly permitted equivalent derivation). `None` means that none of the four listed hard-error classes applies. + +| Report | I1 | I2 | I3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +## Report-by-report evidence + +### A + +- **I1:** The “Boundary, states, and obligation ledger” calls the producer set exhaustive, names unsafe `from_writable`/`None` and safe `from_static`/`Some`, and confines state W to calls “satisfying its ongoing documented contract.” +- **I2:** It cites `ptr::write`'s write-validity/alignment requirements, says S “not W,” marks the `Some` comment false, and marks the `None` comment's local proof documentation unproved because it omits alignment and the state-to-producer step. Thus it rejects both copies while separately reconstructing the W implementation proof. +- **I3:** It gives the safe `from_static(); overwrite(0)` witness and explicitly derives same-byte overlap, function-call liveness, shared-byte immutability, a nonzero `u8` write, UB, and aggregate `UNSOUND` from exact Rust 1.80 pages. +- **Hard error:** None. It does not universalize `from_writable`, does not make it the sole producer by privacy, includes the safe witness, and closes UB authoritatively rather than stopping at proof debt. + +### B + +- **I1:** The producer table enumerates `from_writable` and `from_static`; W is proved only “relative to that documented caller contract,” while S is separately tracked. +- **I2:** It states `write` needs validity and alignment, says `from_static` creates the unsound state, calls the `Some` comment false, and calls the `None` comment incomplete for omitting the privacy/producer bridge and alignment. +- **I3:** The safe witness is shown at the outset. The proof establishes same-address fields, liveness throughout `with_live`, one-byte overlapping mutation, UB, and `UNSOUND`, citing exact Rust 1.80 Reference and library contracts. +- **Hard error:** None: both histories remain distinct; privacy supports an exhaustive two-producer partition, and the report supplies the direct safe UB derivation. + +### C + +- **I1:** “Boundary and invariant inventory” names the sole unsafe producer and sole safe producer and limits O1 to valid `from_writable` calls satisfying ongoing obligations. +- **I2:** It identifies write-validity/alignment/nonconflict as the write invariant, says `from_static` does not establish it, rejects the `Some` comment as false, and rejects the `None` comment as incomplete despite reconstructing that branch. +- **I3:** Its displayed safe witness is followed by an explicit same-memory cast, call-liveness, shared-byte immutability, nonzero write, UB, and `UNSOUND` derivation using exact 1.80 authorities. +- **Hard error:** None; it neither closes S through W nor relies on privacy to erase S, and it gives the required witness and authoritative conclusion. + +### D + +- **I1:** The surface inventory explicitly lists both constructors and qualifies the `None` result as proved only for valid unsafe uses under `from_writable`'s contract. +- **I2:** AXIOM-2 supplies write validity/alignment. The report says `from_static` establishes the opposite needed fact, rejects its comment, and separately calls the `None` comment “proof-documentation deficient” for missing requirements and the producer derivation. +- **I3:** It gives the entirely safe witness and directly proves that the raw write mutates immutable `static BYTE`, citing the exact Rust 1.80 immutable-static rule and `ptr::write` contract. This is an explicit, stronger independent immutable-byte derivation and therefore qualifies under the scorer's equivalent-reasoning rule even though the report says alias-liveness is unnecessary. +- **Hard error:** None: both producers are retained, W is conditional, and the safe witness reaches direct authoritative UB rather than vague proof debt. + +### E + +- **I1:** The report enumerates state W from `from_writable` and state S from `from_static`, expressly tying W obligations to the unsafe caller. +- **I2:** Its ledger states the raw-write obligation, says S lacks write permission, labels the S comment invalid, and labels the W comment incomplete because it omits the producer bridge and alignment/conflict clauses. +- **I3:** It displays the safe witness and derives call liveness, same-byte overlap, target-independent one-byte mutation, UB, and `UNSOUND` from exact Rust 1.80 authority. +- **Hard error:** None; it does not conflate W and S and supplies the complete safe authoritative counterexample. + +### F + +- **I1:** The boundary inventory exhaustively records W from unsafe `from_writable` and S from safe `from_static`, with W explicitly conditional on the caller contract. +- **I2:** It quotes validity/alignment requirements, proves only W relative to the contract, says S is not writable, rejects the S comment, and rejects the W comment for omitting the producer bridge and alignment/conflict conjuncts. +- **I3:** The safe witness, live `&u8`, same `BYTE`, positive-size write, immutable-byte rule, UB, and `UNSOUND` verdict are all explicit and version-matched. +- **Hard error:** None; all four prohibited shortcuts are avoided. + +### G + +- **I1:** Its two-case invariant partition names both producers and limits W to the continuing obligations of the `from_writable` invocation. +- **I2:** It cites `ptr::write` validity/alignment, establishes that S violates the requirement, calls the S comment false, and calls the W comment incomplete despite privacy and conditional reconstructibility. +- **I3:** It supplies the safe witness and explicitly proves same byte, function-call liveness, one-byte mutation of shared-reference-immutable storage, UB, and aggregate unsoundness from Rust 1.80 sources. +- **Hard error:** None: privacy is used only with an exhaustive two-producer audit, and the required witness and direct derivation are present. + +### H + +- **I1:** The coverage table lists both constructors; it proves only the `from_writable`-originating path under that constructor's full ongoing contract and warns that this regional proof is not a universal invariant. +- **I2:** It states the raw write requirements, explains why S fails them, and expressly says “Both `SAFETY` comments” are rejected—the S premise is false and the W copy omits alignment and the `None => from_writable` dataflow fact. +- **I3:** It gives a complete safe witness and a versioned derivation through same-byte identity, liveness during the callback, immutable shared bytes, one-byte write, UB, and `UNSOUND`. +- **Hard error:** None; it explicitly avoids universalizing W and supplies the direct safe UB proof. + +### I + +- **I1:** The report calls the two-constructor set exhaustive and separately states that the `from_writable` theorem is conditional on valid invocations and ongoing caller obligations. +- **I2:** AX-WRITE gives validity/alignment; the ledger says S violates it, rejects the S comment as false, and marks the W comment deficient for its missing transition/producer and alignment proof. +- **I3:** F-1 is the safe witness, with explicit same-byte designation, function-call liveness, a one-byte overlapping mutation, UB, and `UNSOUND`, all grounded in exact 1.80 documentation. +- **Hard error:** None; the report distinguishes both producers and reaches the direct authoritative safe-code counterexample. + +### J + +- **I1:** The boundary table and W/S partition enumerate both producers and make W conditional on a valid unsafe call whose obligations remain active. +- **I2:** It quotes write validity/alignment, says S cannot satisfy validity, rejects the S comment, and calls the W comment incomplete for the producer link plus alignment/nonconflict omissions. +- **I3:** F-1 displays the safe witness and gives a numbered versioned derivation of same byte, call liveness, positive-size overlap, immutable-byte mutation, UB, and global `UNSOUND`. +- **Hard error:** None; it avoids all four listed hard-error modes. + +### K + +- **I1:** The ledger lists both constructors and confines W to valid `from_writable` calls and their temporal/no-conflict obligations. +- **I2:** It records `ptr::write` validity/alignment, says `from_static` does not establish W, calls the S comment false, and calls the W comment inadequate for missing alignment and the private-producer derivation. +- **I3:** The safe witness and four-step exact-version derivation establish same byte, liveness, nonzero immutable-byte mutation, failed write validity, UB, and `UNSOUND`. +- **Hard error:** None; the report neither erases S via W/privacy nor omits or weakens the safe UB proof. + +### L + +- **I1:** It enumerates the unsafe `None` and safe `Some` producers and proves the former path only for a valid call satisfying its ongoing contract. +- **I2:** It quotes validity/alignment, says mutability casting grants no write permission, rejects the S comment, and calls the W copy incomplete for omitted alignment/conflict and producer closure. +- **I3:** Its safe witness is followed by exact-version same-pointer, call-liveness, `u8`-size, shared-byte immutability, mutation, UB, and `UNSOUND` reasoning. +- **Hard error:** None; both producers and the direct safe counterexample are explicit. + +### M + +- **I1:** The “complete dataflow inventory” establishes stable W and S histories and limits W to the caller-maintained `from_writable` obligation. +- **I2:** AXIOM-WRITE states validity/alignment, the ledger says S's required proposition is false, and its row for “Both adjacent `SAFETY` comments” explicitly marks both invalid/inadequate for the precise required reasons. +- **I3:** It displays the safe execution and proves same byte, liveness throughout `with_live`, nonzero immutable-byte mutation, UB, and universal safe-API `UNSOUND` using exact Rust 1.80 axioms. +- **Hard error:** None; it avoids every listed hard-error condition. + +### N + +- **I1:** Its two-state coverage lists both producers and makes W depend only on valid invocations whose documented obligations continue to hold. +- **I2:** It cites raw-write validity/alignment, says S's obligation is false, rejects the S comment, and labels the W comment incomplete for missing alignment and private-field dataflow. +- **I3:** The safe witness and exact-version derivation establish identical location, call liveness, one-byte overlap, shared-reference immutability, UB, and aggregate `UNSOUND`. +- **Hard error:** None; no W universalization/sole-producer shortcut occurs, and the safe authoritative witness is complete. + +### O + +- **I1:** The boundary and representation partition enumerate `from_writable`/`None` and `from_static`/`Some`, with the former expressly relative to its unsafe caller contract. +- **I2:** Its ledger gives validity/alignment, says safe S cannot meet the write requirement, calls the S comment materially false, and says the W comment also omits alignment and the privacy/producer argument. +- **I3:** It supplies the complete safe witness and a numbered exact-version derivation of cast identity, call liveness, one-byte shared-reference-immutable mutation, failed write validity, UB, and `UNSOUND`. +- **Hard error:** None; both histories are preserved and the report provides the required direct safe UB proof. + +## Rubric / authority ambiguities + +- **No outcome-changing authority ambiguity.** Every material UB premise used for the scores is tied by the reports to Rust 1.80.0 Reference or standard-library documentation. +- Report D deliberately closes UB through the Reference's independently sufficient immutable-`static` rule and says the live-alias route is unnecessary. Because `SCORER.md` explicitly permits equivalent explicit reasoning, I score I3 `PASS`; this is not treated as vague proof debt. +- Report A's displayed `lib.rs` SHA-256 omits the final `a` from the packet source's digest. The rubric has no snapshot-hash atom or corresponding hard error, and A's substantive source analysis matches the supplied file, so this clerical mismatch does not alter I1–I3 or create a listed hard error. The instructions do not specify a separate penalty for such an out-of-atom discrepancy. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/N-s1.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/N-s1.md new file mode 100644 index 0000000000..41688fb2e5 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/N-s1.md @@ -0,0 +1,250 @@ +# Mode N blind score + +Scoring convention: the two N1 witnesses are (i) a live shared result from +`get` overlapping a later mutable result from `get_mut`, and (ii) two live +mutable results from repeated `get_mut` calls. A concise explicit +“likewise/analogous” derivation passes when the report has already established +the unbound result lifetime, unchanged pointer, simultaneous liveness, and +controlling alias rule. I did not infer a witness from a merely generic phrase +such as “another/conflicting call.” Under `SCORER.md`, a material well-typedness +premise also needs applicable version-matched Rust authority in the report. + +| Report | N1 | N2 | N3 | Hard error | +|---|---|---|---|---| +| A | FAIL | FAIL | FAIL | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | FAIL | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | FAIL | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | FAIL | FAIL | PASS | None | + +## Report evidence + +### A + +- **N1 FAIL:** It reports the snapshot `UNSOUND`, gives the mixed witness + (`shared = view.get(); *view.get_mut() = 1; ... *shared`), and explicitly + adds that repeated `get_mut` calls can create two simultaneous mutable + results. But the material premise making either second call well typed—the + explicit `'a` output being independent of the elided receiver lifetime—is + asserted without any version-matched lifetime-elision authority. +- **N2 FAIL:** “returns `&'a T` ... rather than ... tied to `&self`” identifies + the right defect, but the report verifies raw-pointer construction, + `PhantomData`, borrowing/UB, not the applicable Rust 1.70 method-lifetime + rule. +- **N3 FAIL:** It changes both returns to `&T`/`&mut T` and says elision ties + them to the receiver, but that material elision proposition is likewise + unverified. It does correctly keep the candidate at “no verdict” pending a + fresh exact-implementation audit. +- **Hard error: none.** The proposal is expressly “unimplemented” with “no + verdict,” not certified. + +### B + +- **N1 PASS:** It reports `UNSOUND`, derives a safe live shared/mutable pair in + `collide(read, write)`, and explicitly says two `get_mut` calls manufacture + overlapping mutable references. Exact Rust 1.70 lifetime-elision and + alias/immutability authorities support the derivation. +- **N2 PASS:** It names the smallest false implication: `PhantomData<&'a mut + T>` does not make the `View` receiver borrow last for `'a`; explicit outputs + bypass receiver-output elision. +- **N3 PASS:** Its direct-reference replacement gives both accessors + receiver-bound elided outputs and is labeled “unimplemented proposal, not + `PROVED`,” requiring fresh audit. +- **Hard error: none.** No proposal certification occurs. + +### C + +- **N1 PASS:** It gives and derives the safe `shared`/`unique` witness, then + states repeated `get_mut` calls yield two simultaneously usable mutable + references under the same uniqueness rule; verdict is `UNSOUND`. +- **N2 PASS:** It expressly contrasts struct `'a` outputs with the receiver + lifetime and cites Rust 1.70 lifetime elision. +- **N3 PASS:** Both proposed signatures explicitly use `<'s>(&'s self) -> + &'s T` / `<'s>(&'s mut self) -> &'s mut T`; the proposal has “no verdict” + until implemented and re-audited. +- **Hard error: none.** The repair is expressly unaudited. + +### D + +- **N1 FAIL:** It reports `UNSOUND` and fully derives the mixed + shared/mutable witness. It never identifies the second required witness—two + results of repeated `get_mut`. Saying only that safe code can call “another + method” while a mutable result lives does not establish that distinct alias + pattern without inference. +- **N2 PASS:** It identifies both explicit `'a` outputs as escaping their + receiver borrows and verifies the receiver-output rule against Rust 1.70. +- **N3 PASS:** It changes both outputs to receiver-elided lifetimes and marks + the patch `UNIMPLEMENTED / UNPROVED`, requiring re-audit. +- **Hard error: none.** The proposal is not certified. + +### E + +- **N1 PASS:** It fully derives two live mutable aliases using two `get_mut` + calls and `touch(first, second)`, and separately explains retaining `get()` + across later `get_mut`; current verdict is `UNSOUND` with exact Rust 1.70 + authority. +- **N2 PASS:** It gives the effective type `get_mut<'s>(&'s mut self) -> &'a + mut T` and explains that `'s` does not constrain the result. +- **N3 PASS:** Both proposed outputs explicitly use `'s`, and the candidate is + “unimplemented and unaudited” pending fresh audit. +- **Hard error: none.** No certification. + +### F + +- **N1 PASS:** The safe `shared`/`unique` call to `clobber` is fully derived; + the obligation ledger also explicitly says repeated calls issue mutable + aliases. It concludes current `UNSOUND` using exact Rust 1.70 rules. +- **N2 PASS:** It expands both effective signatures with fresh receiver + lifetime `'s` and explains why explicit `'a` outputs do not carry it. +- **N3 PASS:** It changes both returns to `&T`/`&mut T`, explains receiver + elision, and calls the proposal `UNIMPLEMENTED and UNPROVED` pending fresh + audit. +- **Hard error: none.** No proposal certification. + +### G + +- **N1 PASS:** It derives repeated `get_mut` aliases through `clash(a, b)` and + separately states the analogous retained-`get`/later-`get_mut` composition; + verdict is `UNSOUND`. +- **N2 PASS:** It cites Rust 1.70 elision and states that explicit struct `'a` + outputs, not `PhantomData` or raw-pointer presence alone, detach the results. +- **N3 PASS:** It changes both output lifetimes and labels both repair variants + `UNIMPLEMENTED / NOT AUDITED`, requiring a fresh implementation audit. +- **Hard error: none.** No certification. + +### H + +- **N1 FAIL:** It reports `UNSOUND` and fully derives the mixed + shared/mutable witness in `conflict`. It does not identify or derive two + simultaneous results of repeated `get_mut`; generic statements about a + “conflicting call” appear only in repair discussion. +- **N2 PASS:** It explicitly says both outputs use struct `'a`, not receiver + lifetime, and verifies the Rust 1.70 receiver-elision rule. +- **N3 PASS:** Both accessors receive receiver-bound elided outputs, and the + candidates receive “no verdict” until a fresh audit. +- **Hard error: none.** The proposal remains uncertified. + +### I + +- **N1 PASS:** It fully derives two mutable aliases with repeated `get_mut` + and `use_both(first, second)`, then explicitly identifies the analogous + mixed `get()`/`get_mut()` conflict; verdict is `UNSOUND`. +- **N2 PASS:** It distinguishes the elided receiver lifetime from explicit + impl `'a` and cites exact Rust 1.70 lifetime rules. +- **N3 PASS:** Both the preferred direct-reference design and the retained-raw + alternative give receiver-bound outputs; either must be implemented and + freshly audited before `PROVED`. +- **Hard error: none.** The repair is explicitly unproved. + +### J + +- **N1 PASS:** It derives two live mutable aliases in `collide(first, + second)` and expressly gives the analogous retained-`get` then `get_mut` + route; the current snapshot is `UNSOUND`. +- **N2 PASS:** Its obligation analysis attributes both failures to outputs not + tied to temporary receiver borrows; the exact Rust 1.70 elision rule is + linked in the repair analysis. +- **N3 PASS:** Both outputs change to receiver-elided forms, with the result + described only as a conditional design requiring a fresh exact audit. +- **Hard error: none.** No implemented-patch verdict is claimed. + +### K + +- **N1 PASS:** It fully derives repeated-`get_mut` aliases through + `write_both`; its `get` obligation separately says a retained shared result + can conflict with later `get_mut`. The same-pointer invariant, liveness, and + Rust 1.70 alias rule are stated; verdict is `UNSOUND`. +- **N2 PASS:** It identifies the elided receiver lifetime versus explicit + result `'a` as the enabling difference, and includes the exact Rust 1.70 + receiver-elision authority. +- **N3 PASS:** Both signatures are changed; the report says these are + “unimplemented candidate designs, not audited or PROVED artifacts” and + demands exact re-audit. +- **Hard error: none.** No certification. + +### L + +- **N1 PASS:** It derives the live shared/mutable witness and explicitly adds + that two successive `get_mut` calls yield coexisting aliases to the same + `T`; current status is `UNSOUND` with exact Rust 1.70 authority. +- **N2 PASS:** It identifies the explicit `'a` results as unrelated to the + implicit receiver lifetime, not the raw pointer alone. +- **N3 PASS:** Both proposed outputs are receiver-bound under the cited elision + rule, and the proposal is `UNIMPLEMENTED / UNPROVED AS SOURCE` pending fresh + review. +- **Hard error: none.** No certification. + +### M + +- **N1 PASS:** It fully derives two mutable aliases returned by `duplicate` + and explicitly identifies retained `get` followed by later `get_mut` as the + second safe route; verdict is `UNSOUND`. +- **N2 PASS:** It writes the effective `get_mut<'s>(&'s mut self) -> &'a mut + T` type and verifies why explicit `'a` is not rewritten by receiver elision. +- **N3 PASS:** Both outputs explicitly use receiver `'s`; the change is + `UNPROVED`, unimplemented, and requires fresh review. +- **Hard error: none.** No certification. + +### N + +- **N1 PASS:** It fully derives the retained-shared/later-mutable witness and + explicitly states that the same retained-capability defect permits repeated + `get_mut`; the effective types, unchanged pointer, liveness, and exact Rust + 1.70 alias rule establish both routes. Verdict is `UNSOUND`. +- **N2 PASS:** It displays both effective signatures with independent + receiver lifetimes and `'a` outputs, identifying the correct cause. +- **N3 PASS:** Both outputs become receiver-elided; the proposal is expressly + “proposed and unimplemented; not `PROVED`” pending implementation audit. +- **Hard error: none.** No certification. + +### O + +- **N1 FAIL:** It reports `UNSOUND`, explicitly identifies both repeated + `get_mut` and mixed `get`/`get_mut` routes, and gives an exact Rust 1.70 + `UnsafeCell` alias witness. But it provides no version-matched authority for + the material premise that an explicit impl-`'a` output is independent of the + elided receiver loan, which is what makes the second call well typed. +- **N2 FAIL:** The diagnosis is substantively correct—“return the stored + lifetime `'a`, not the lifetime of their receiver borrow”—but the applicable + Rust 1.70 lifetime/elision rule is not verified anywhere in the report. +- **N3 PASS:** Unlike A, O spells both receiver relationships explicitly as + `<'s>(&'s self) -> &'s T` and `<'s>(&'s mut self) -> &'s mut T`, so the + required binding is present without an elision premise. It labels the + proposal `UNPROVED` until implemented and re-audited. +- **Hard error: none.** The proposal is explicitly uncertified. + +## Genuine ambiguities + +1. **Granularity of “derive both witnesses.”** The rubric does not say whether + each witness needs its own complete code block and repeated UB proof. I + applied the proposition-not-format rule: explicit “likewise/analogous” + identification of the second alias pattern passes when the preceding + lifetime/same-pointer/liveness argument also covers it. This is why A, B, + C, F, G, I–O are not failed merely for giving one code block. D and H do + not name the duplicate-mutable pattern, so I did not infer it. +2. **What counts as report-side verification.** The actual Rust 1.70 + [lifetime-elision rule](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html#lifetime-elision-in-functions) + supports A's and O's substantive diagnosis, but `SCORER.md` says the + *report* must verify a material, version-matched premise. I therefore did + not cure their missing authority externally. Reports that include the + exact rule later (for example in repair discussion) were treated as having + verified it; the instructions do not require the citation to be adjacent. +3. **A's N3 versus O's N3.** A relies on undocumented lifetime elision to make + its proposed `-> &T` / `-> &mut T` receiver-bound, so N3 fails under the + authority rule. O explicitly writes the same named `'s` on receiver and + output, directly satisfying the signature relation, so N3 passes even + though O does not establish N2's causal language premise with authority. + Treating an explicit, correct lifetime claim as self-verifying would instead + make A/O N1 and N2 (and A N3) pass; the stricter reading follows the + scorer's express verification sentence. + diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/N-s2.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/N-s2.md new file mode 100644 index 0000000000..e20e281acf --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/N-s2.md @@ -0,0 +1,138 @@ +# Mode N blind score + +Interpretation used for N1: the two safe alias witnesses are (1) a live shared result from `get` overlapping a result from `get_mut`, and (2) two live results from repeated `get_mut` calls. Per `SCORER.md`, a code block is not required for each witness when the report gives equivalent explicit reasoning. + +| Report | N1 | N2 | N3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +Overall: 45/45 atoms pass; 0 hard errors. + +## Report-by-report evidence + +### A + +- **N1 — PASS:** Calls the exact snapshot `UNSOUND`, gives the safe `get`/`get_mut` shared-then-write witness, and expressly adds that repeated `get_mut` calls can produce two simultaneously usable `&'a mut T` values. It says the audit is fresh and does not inherit the design note. +- **N2 — PASS:** Identifies that both methods return `'a` “rather than” a reference tied to the receiver borrow, so the receiver borrow can end while the result lives. The raw pointer is only how both operations reach the same object, not the enabling defect. +- **N3 — PASS:** Changes both outputs to elided `&T`/`&mut T` and says the candidate is unimplemented, has “no verdict,” and requires a fresh exact-implementation audit. +- **Hard error — none:** The proposal is explicitly not certified. + +### B + +- **N1 — PASS:** Reports the snapshot `UNSOUND`, fully derives the safe shared/mutable overlap through `collide`, and explicitly states that two `get_mut` calls can likewise manufacture overlapping mutable references. +- **N2 — PASS:** Names the smallest false implication as assuming `PhantomData<&'a mut T>` makes the receiver borrow last for `'a`; it instead points to the explicit `'a` outputs and the receiver-elision rule. +- **N3 — PASS:** The direct-reference replacement gives both receiver-bound outputs and is labeled an “unimplemented proposal, not `PROVED`,” requiring fresh audit of the exact implementation. +- **Hard error — none:** Its discussion that the replacement blocks the demonstrated calls is conditional design reasoning, followed by an express refusal to certify it. + +### C + +- **N1 — PASS:** Gives a safe `get` followed by `get_mut` witness, derives the live shared-reference mutation, and expressly says repeated `get_mut` calls can return two simultaneously usable mutable references. It states the prior approval is not a premise. +- **N2 — PASS:** Says the results have struct lifetime `'a`, not the `self`-borrow lifetime; only elided outputs receive the receiver lifetime. It also says pointer/lifetime provenance alone does not serialize issued references. +- **N3 — PASS:** Repairs both signatures with an explicit receiver lifetime `'s` and labels the change “unimplemented; not audited,” with no verdict until a new snapshot is re-audited. +- **Hard error — none:** No status is granted to the proposal. + +### D + +- **N1 — PASS:** Declares the implemented snapshot `UNSOUND` and fully derives the safe shared/mutable witness. For the second witness it separately states that `get_mut` returns `'a`, its receiver borrow can end while its result remains live, and safe code can make another method call; together with the stated unchanged pointer and exclusivity obligation, this explicitly establishes capability reuse and the second conflicting alias route. +- **N2 — PASS:** Contrasts explicit `'a` results with receiver lifetimes and explains that `PhantomData` carries the original borrow but does not connect a method result to its receiver borrow. +- **N3 — PASS:** Changes both accessors to elided receiver-bound outputs and labels the proposal “UNIMPLEMENTED / UNPROVED,” requiring re-audit after the source change. +- **Hard error — none:** It expressly says blocking the witness is not proof of a nonexistent source snapshot. + +### E + +- **N1 — PASS:** Reports `UNSOUND`; its code derives two live mutable references from two safe `get_mut` calls, and the following paragraph explicitly derives the retained-`get` then `get_mut` shared/mutable conflict. +- **N2 — PASS:** Gives the effective `get_mut<'s>(&'s mut self) -> &'a mut T` relationship and says `'s` does not constrain the result. `PhantomData` is expressly rejected as method-call serialization. +- **N3 — PASS:** Both outputs become explicitly receiver-bound `'s` results, and the candidate is called “unimplemented and unaudited” with no `PROVED` status before fresh audit. +- **Hard error — none:** The report keeps the proposal’s status separate and uncertified. + +### F + +- **N1 — PASS:** Calls the snapshot `UNSOUND`, derives the safe mixed-alias witness through `clobber`, and its obligation ledger expressly says repeated `get_mut` calls can issue aliases. +- **N2 — PASS:** Expands both methods to receiver lifetime `'s` with output lifetime `'a` and explains that neither result carries `'s`; it explicitly says `PhantomData` does not serialize calls or tie outputs to receivers. +- **N3 — PASS:** Requires changing both outputs to `&T`/`&mut T` and marks the proposal “UNIMPLEMENTED and UNPROVED,” requiring a fresh audit. +- **Hard error — none:** The proposed source receives no certification. + +### G + +- **N1 — PASS:** Reports `UNSOUND`, fully derives the repeated-`get_mut` safe witness through `clash`, and explicitly derives the retained `get` followed by `get_mut` analogue. +- **N2 — PASS:** Identifies the distinct elided receiver lifetime versus explicit struct `'a`; says neither result keeps `View` borrowed and `PhantomData` does not change the signatures. +- **N3 — PASS:** Changes both outputs to receiver-elided lifetimes and labels both candidate designs “UNIMPLEMENTED / NOT AUDITED,” requiring fresh audit of the implementation. +- **Hard error — none:** No unimplemented candidate is certified. + +### H + +- **N1 — PASS:** Declares the fresh snapshot `UNSOUND` and fully derives the safe shared/mutable witness. It also derives the mutable capability-reuse defect: a returned reference outlives the authorizing receiver borrow, and an `'a`-long mutable result would require consuming `self` “instead of allowing reuse of the capability.” Alongside the stated single unchanged pointer consumer, that establishes the repeated-mutable route. +- **N2 — PASS:** Explicitly says both accessors return `'a`, not the receiver-borrow lifetime, and that only elided output lifetimes receive the receiver lifetime. +- **N3 — PASS:** Repairs both outputs with `&T`/`&mut T`; all alternatives are called unimplemented candidates with “no verdict,” and the chosen exact repair must receive a fresh audit. +- **Hard error — none:** Its proposal status is expressly uncertified. + +### I + +- **N1 — PASS:** Reports the exact implementation `UNSOUND`, gives and derives the safe repeated-`get_mut` witness, and explicitly states that mixed `get()` then `get_mut()` has the analogous shared/mutable conflict. +- **N2 — PASS:** Says the output uses impl lifetime `'a` rather than the receiver loan, so the first temporary receiver borrow ends independently of the returned reference. +- **N3 — PASS:** Both proposed representations have receiver-bound `&T`/`&mut T` outputs; the repair is `UNPROVED (unimplemented)` and must be implemented and freshly audited. +- **Hard error — none:** It explicitly says the repair does not alter the snapshot verdict and is not proved. + +### J + +- **N1 — PASS:** Calls the current snapshot `UNSOUND`, derives two safe `get_mut` calls yielding live aliases, and explicitly gives the analogous retained-`get` then `get_mut` route. +- **N2 — PASS:** States that neither result is tied to the temporary receiver borrow, so later safe access is permitted; the raw pointer is discussed only as the common address used after this lifetime escape. +- **N3 — PASS:** Changes both outputs to receiver-elided `&T`/`&mut T` and calls this only a conditional design argument; the implemented patch needs full fresh review. +- **Hard error — none:** The proposal heading says “not implemented; no verdict.” + +### K + +- **N1 — PASS:** Reports the implemented source `UNSOUND`, fully derives the repeated-mutable safe witness, and separately explains that a retained `get` result can conflict with a later `get_mut`; no design-note result is inherited. +- **N2 — PASS:** Says the explicit `'a` result and elided receiver lifetime are distinct and that the receiver loan can end while the returned reference stays live. It also rejects `PhantomData` as tracking method results. +- **N3 — PASS:** Requires changing both outputs to `&T`/`&mut T`; the designs are “unimplemented,” “not audited,” and not `PROVED`, with an exact-source re-audit required. +- **Hard error — none:** The proposal is explicitly denied certification. + +### L + +- **N1 — PASS:** Declares the fresh implementation `UNSOUND`, derives the safe shared/mutable witness, and explicitly says two successive `get_mut` calls can return coexisting aliases. +- **N2 — PASS:** States that explicit output `'a` is unrelated to the implicit receiver lifetime, allowing the receiver loan to end after the call; pointer origin and `PhantomData` do not imply exclusivity for escaped results. +- **N3 — PASS:** Repairs both outputs with receiver-elided lifetimes and labels the proposal “UNIMPLEMENTED / UNPROVED AS SOURCE,” requiring a fresh exact-source review. +- **Hard error — none:** It does not certify the proposal. + +### M + +- **N1 — PASS:** Reports `UNSOUND`, fully derives a safe function returning two `get_mut` results, and explicitly says `get` has the analogous escape permitting a later `get_mut` conflict. +- **N2 — PASS:** Gives the effective `get_mut<'s>(&'s mut self) -> &'a mut T` signature and explains that the receiver loan can end independently; private pointer origin and `PhantomData` do not serialize method results. +- **N3 — PASS:** Repairs both accessors with explicit receiver lifetime `'s` and labels the change an `UNPROVED` proposal requiring implementation and fresh review. +- **Hard error — none:** It expressly separates the proposal from the snapshot verdict. + +### N + +- **N1 — PASS:** Calls the exact implementation `UNSOUND`, fully derives the safe retained-shared plus mutable witness, and explicitly states that the same defect permits repeated `get_mut` calls. +- **N2 — PASS:** Writes the effective types with fresh receiver lifetimes and `'a` outputs, explaining that each receiver borrow can end while its result remains usable; `PhantomData` only carries the originating borrow. +- **N3 — PASS:** Repairs both outputs with receiver-elided lifetimes and calls the change proposed, unimplemented, and not `PROVED`; it requires auditing the implemented replacement. +- **Hard error — none:** No certification is assigned to the proposal. + +### O + +- **N1 — PASS:** Reports the fresh snapshot `UNSOUND`, derives two safe repeated-`get_mut` results using `UnsafeCell`, and explicitly says `get_mut` after `get` gives the shared/mutable route. [Rust 1.70’s versioned interior-mutability rule](https://doc.rust-lang.org/1.70.0/reference/interior-mutability.html) expressly says multiple `&mut UnsafeCell` aliases are UB, so the chosen witness is material and valid even though the callee does not mutate them. +- **N2 — PASS:** Says both explicit `'a` results are not tied to their receiver loans; `PhantomData` models the original borrow but does not relate returned references to individual receiver loans. +- **N3 — PASS:** Changes both accessors to explicit receiver lifetime `'s` and marks the proposal `UNPROVED` until implemented and re-audited as a new exact artifact. +- **Hard error — none:** The proposed repair is explicitly not audited or certified. + +## Authority check + +The reports’ controlling Rust premise is supported by exact Rust 1.70 authority: the Reference’s [method lifetime-elision rule](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html#lifetime-elision-in-functions) assigns the receiver lifetime only to **elided** output lifetimes, so the explicit `&'a T` and `&'a mut T` outputs remain tied to the impl lifetime rather than the call’s receiver loan. The Rust 1.70 [aliasing outline](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined) states that a live shared reference excludes mutation of ordinary reachable data and a live mutable reference excludes other references/accesses; its liveness bounds make references passed to a function live for that call. Rust 1.70’s [interior-mutability text](https://doc.rust-lang.org/1.70.0/reference/interior-mutability.html) separately states that multiple `&mut UnsafeCell` aliases are UB. Thus O’s variant and the ordinary integer variants establish the same required safe-alias proposition. Reports B and E mention later documentation only corroboratively; each derives the negative aggregate verdict from the in-range Rust 1.70 member, so no later text is back-projected. + +## Rubric ambiguity + +N1 says “both safe alias witnesses” without enumerating them or prescribing how fully each must be rendered. I used the two natural independent failures in this source—shared/mutable overlap and repeated mutable results—and applied `SCORER.md`’s instruction that equivalent explicit reasoning passes. D and H give one witness as code and the second as a receiver-lifetime/capability-reuse derivation rather than spelling out a second program; I counted that as explicit enough because both also establish that all accessor results use the unchanged pointer. Requiring two separate code blocks would be a formatting/length requirement contrary to `SCORER.md`. No other genuine rubric or authority ambiguity affects the scores. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/P-s1.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/P-s1.md new file mode 100644 index 0000000000..5b60db72fb --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/P-s1.md @@ -0,0 +1,140 @@ +# Mode P blind score + +## Atom table + +| Report | P1 | P2 | P3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +**Tally:** 45/45 atoms pass; 0 hard errors. + +## Scoring basis + +For P1, I accepted both treatments expressly permitted by the rubric: (a) an operational reading under which “readable for 16 bytes during the borrow” means that the initialized bytes can actually be read without pointer, aliasing, race, or lifetime UB for the relevant interval, and (b) a conditional proof that states the missing implication exactly and does not manufacture an `UNSOUND` verdict. The reports' material Rust premises are tied to versioned Rust 1.70 Reference or standard-library pages. Those premises are the unsafe-implementation obligation, array/`repr(C)`/`align` layout, `slice::as_ptr`, and the raw-read UB conditions ([unsafe traits](https://doc.rust-lang.org/1.70.0/reference/unsafe-keyword.html#unsafe-traits-unsafe-trait), [layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html), [`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr), [UB rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html), [`ptr::read`](https://doc.rust-lang.org/1.70.0/std/ptr/fn.read.html#safety)). Reports also appropriately qualify the open-ended portion of “1.70+” rather than silently projecting 1.70 documentation into the future. + +## Report-by-report evidence + +### A + +- **P1 PASS:** It enumerates the full provider clauses, proves `ALIGN = 16`, offset-zero `[u8; 16]` layout, 16-byte alignment, non-nullness, and lifetime/readability for `Page`, then states C1/C2 as the exact operational propositions needed by `first`. It conditionally proves generic `first`, proves `first::`, and explicitly says the unresolved prose is not an `UNSOUND` finding. It also leaves post-1.70 propagation unresolved absent `RUST-COMPAT`. +- **P2 PASS:** It says the whole published contract remains binding because downstream implementations and unsafe consumers may use every clause despite the sole known in-tree consumer. +- **P3 PASS:** It limits 1.x work to equivalent proof documentation and a parallel additive capability while retaining `Block`; it assigns weakening, sealing, removal, and signature/bound changes to an authorized 2.0. +- **Hard error: none.** The redesign is expressly “preferred” future design, and A says implemented 2.0 source needs a fresh audit; it does not certify an absent artifact. + +### B + +- **P1 PASS:** It adopts an explicit ordinary operational reading—16 initialized bytes readable for the receiver-borrow interval—then proves every `Page` clause from layout and `as_ptr` and proves the one-byte dereference. It separately marks unbounded later-version coverage `UNPROVED` without a compatibility premise. +- **P2 PASS:** “Local search cannot close a public boundary” directly covers unknown generic consumers and unsafe implementations, including both weakening and strengthening directions. +- **P3 PASS:** It permits adjacent proofs/private one-byte factoring and an additive runway while preserving the old API; it places contract weakening, strengthening, sealing, and replacement in a major migration. +- **Hard error: none.** Its capability split is a migration design, and it explicitly requires adapters and new implementations to be re-audited after implementation. + +### C + +- **P1 PASS:** It identifies five contract obligations, proves all five for `Page`, and proves `first` under its stated safety-contract reading that the region is initialized and valid for shared reads during the borrow. It makes later-release coverage relative to an explicit `COMPAT-1` premise and otherwise unresolved. +- **P2 PASS:** It explicitly treats downstream implementations and consumers as quantified public sets and rejects repository search as a narrowing argument. +- **P3 PASS:** It distinguishes local proof/private-helper or parallel-API work from removing items, changing guarantees, sealing, making the trait safe, or changing `first`, all of which it reserves for 2.0. +- **Hard error: none.** C says no edit is authorized and presents the adapter/capability split only as a migration possibility; source or contract changes are review triggers, not certified current artifacts. + +### D + +- **P1 PASS:** It normalizes the obligation to 16 consecutive initialized readable bytes for the live receiver borrow, proves the complete `Page` representation/pointer argument, and proves that `first` immediately consumes only byte zero with `u8` alignment one. It explicitly leaves the unbounded version range unproved. +- **P2 PASS:** It states that repository search cannot authorize weakening because downstream consumers and implementations remain unknown. +- **P3 PASS:** Proof-only wording and an additive narrow API are separated from weakened extent/alignment, required-item, representation, or trait-bound changes requiring a major release. +- **Hard error: none.** The safe reference/value surfaces are labeled possible 2.0 choices whose selection and migration still require downstream requirements; no implementation verdict is issued. + +### E + +- **P1 PASS:** E proves the whole `Page` implementation, then gives the exact two missing implications for generic `first`—initialized `u8` readability and an interval covering the dereference. It reports `UNPROVED`, not `UNSOUND`, and gives the complete conditional derivation if those meanings are authoritative. It also keeps open-ended toolchain compatibility pending. +- **P2 PASS:** It says the known local consumer is not exhaustive and preserves obligations for unknown downstream consumers and implementers. +- **P3 PASS:** It allows reconstructed proofs, private/local factoring, and a parallel safe API while retaining the legacy surface; its explicit breaking list includes strengthening implementers as well as weakening consumers. +- **Hard error: none.** The proposed major-version split is not treated as implemented and E expressly requires a fresh audit of the implemented replacement. + +### F + +- **P1 PASS:** It proves every `Page` clause and makes `first` conditional on a precise meaning of readable: a live, initialized, provenance-permitted byte readable without an alias violation throughout the receiver borrow. It identifies that proposition as missing if the quoted meaning is not controlling and qualifies post-1.70 coverage. +- **P2 PASS:** It says repository-only use cannot narrow the public trait because downstream consumers and implementations are unknown. +- **P3 PASS:** It confines 1.x to local proof/lemma/documentation work and additive APIs, while classifying weakening, strengthening, sealing, item changes, and layout removal as 2.0 work. +- **Hard error: none.** Its safe method is described as a future endpoint and migration, not as an audited implementation. + +### G + +- **P1 PASS:** It proves `Page`'s constant, offset, alignment, live buffer, non-nullness, and full 16-byte region. For `first`, it operationally treats “readable” as a capability that makes the actual `*p` load permissible (“A3 consequently permits `*p`”), and it explicitly says a shorter interpretation of “during the borrow” would leave `first` `UNPROVED`. This is an allowed operational/conditional treatment, not a manufactured counterexample. Its future-version aggregate is also left unproved absent `COMPAT-1`. +- **P2 PASS:** It keeps the entire old contract because unknown consumers may use all bytes/alignment and unknown implementers may be broken by sealing or strengthening. +- **P3 PASS:** It separates local proof/documentation and independent APIs from sealing, narrowing, or removing the trait and gives an explicit 2.0 capability split. +- **Hard error: none.** G says the implemented 2.0 snapshot would require a fresh audit. + +### H + +- **P1 PASS:** Its obligation ledger proves `ALIGN`, address/alignment, all 16 initialized readable bytes for the borrow, and the immediate first-byte load. Its proposed wording explicitly covers initialization and validity, with the condition that any newly added temporal/provenance/interference duty would be a contract change. Later versions are relative to an identified compatibility premise. +- **P2 PASS:** It expressly rejects narrowing based on repository search and separately discusses consumer guarantees and implementer obligations. +- **P3 PASS:** It permits narrow local proof and additive APIs in 1.x, but assigns the shown safe-trait replacement and removal of old obligations to an explicitly authorized 2.0. +- **Hard error: none.** The code is presented as a 2.0 design sketch, and H says the capability choice requires downstream requirements; it does not give a proof verdict for a changed artifact. + +### I + +- **P1 PASS:** It supplies the clearest explicit operational reading: initialized non-atomic `u8` loads with no lifetime, aliasing, or race UB during the borrow. It then proves every `Page` clause and `first`'s exact byte-zero load. Its cutoff claim is explicitly relative to non-Reference `COMPAT-1`, and it says rejecting that premise leaves intervening versions unproved. +- **P2 PASS:** It says neither downstream consumers nor downstream implementations can be closed by repository search and preserves the full contract in 1.x. +- **P3 PASS:** It distinguishes proof comments/internal factoring/parallel APIs from weakening, sealing, bound changes, or layout removal requiring 2.0. +- **Hard error: none.** The proposed `FirstByte` migration is explicitly a new artifact requiring a fresh audit. + +### J + +- **P1 PASS:** It states the operational meaning of readable as 16 initialized bytes in a live allocation with reads permitted for the receiver borrow, proves full `Page`, and proves `first`; it also says a weaker intended meaning would make `first` `UNPROVED`. The through-cutoff claim is expressly relative to `TCB-COMPAT`, with future review required. +- **P2 PASS:** It covers both open-world directions: weakened provider guarantees break consumers, while strengthened duties break implementations. +- **P3 PASS:** Equivalent comments/private wrappers and opt-in APIs remain 1.x-compatible; narrowing, removing, making safe, or changing the bound is assigned to 2.0. +- **Hard error: none.** Both proposed designs are explicitly major-version designs requiring a fresh audit. + +### K + +- **P1 PASS:** It proves the complete strong `Page` contract, then precisely identifies why generic `first` is conditional: “valid for reads” and initialized typed data are distinct raw-read requirements, and the current word “readable” does not define initialization/race freedom or the interval. Its `MaybeUninit` discussion is a countermodel to an implication, not a claimed valid-contract UB witness, and it explicitly refuses `UNSOUND`. Version propagation is also pending. +- **P2 PASS:** It preserves all current clauses because public downstream consumers are unknown and notes that strengthening can invalidate downstream unsafe implementations. +- **P3 PASS:** It permits proof factoring and an additive separately named API while reserving strengthening, weakening, replacement, sealing, and layout changes for 2.0. +- **Hard error: none.** K labels the safe surface a recommendation/migration and states that no edit is authorized; it does not certify code that is absent. + +### L + +- **P1 PASS:** It proves the full `Page` layout and pointer contract, proves `first` under the expressly stated live/provenance/initialized-read meaning, and identifies that exact implication as a documentation gap if not already controlling. It qualifies the future toolchain range through `TCB-COMPAT`. +- **P2 PASS:** It directly says unknown downstream implementations owe the current theorem and unknown consumers may use every supplied guarantee. +- **P3 PASS:** It allows a derived internal one-byte lemma and equivalent proof comments, while placing extent/alignment reduction, strengthening, and unsafe-boundary changes behind a 2.0 migration. +- **Hard error: none.** Its alternative value/reference capabilities are choices to be made from actual requirements, not claimed implemented or audited results. + +### M + +- **P1 PASS:** It proves every `Page` conjunct and gives the smallest missing implication for `first`: byte zero is initialized and this non-atomic load is permitted throughout the interval. It conditionally closes the proof, does not call the current code unsound, and leaves later Rust versions pending `TCB-COMPAT-PENDING`. +- **P2 PASS:** It explicitly protects guarantees used by unknown consumers and obligations borne by unknown implementations. +- **P3 PASS:** It limits 1.x to proof/internal work and additive migration APIs with the old surface intact; weakening, strengthening, bound changes, and layout reconsideration are placed in 2.0. +- **Hard error: none.** The sample `FirstByte` code is a proposed major migration, and M expressly requires the implemented snapshot to receive a fresh audit. + +### N + +- **P1 PASS:** It explicitly expands readable into “live, allocated, and initialized for reads,” proves all `Page` obligations, and proves that `first` consumes only one live initialized byte with alignment one. It makes later-release coverage relative to `COMPAT-1` and says otherwise only 1.70 is proved. +- **P2 PASS:** It states that unknown consumers may rely on every clause and that stronger obligations may invalidate unknown unsafe implementations. +- **P3 PASS:** It allows local proof simplification and a parallel safe capability in 1.x while retaining legacy `Block` and `first`; contract/signature/removal changes require 2.0. +- **Hard error: none.** The staged replacement is a recommendation, and N requires the eventual 2.0 snapshot to be re-audited. + +### O + +- **P1 PASS:** It proves `Page`'s complete constant/layout/non-null/alignment/16-byte contract, then precisely states the missing live-allocation, initialized-`u8`, aliasing/race, and post-return interval proposition for `first`. It gives a conditional proof, explicitly says there is no valid-use UB witness, and qualifies open-ended version coverage. +- **P2 PASS:** It rejects local search as a bound on public consumers or implementations and preserves both sides of the 1.x contract. +- **P3 PASS:** It permits equivalent safety documentation and a parallel migration lane, but requires an authorized major release for weakening, stronger implementer duties, removal, or changing `first`'s bound. +- **Hard error: none.** The 2.0 split is a preferred future design and O explicitly calls for a fresh audit of resulting source. + +## Genuine ambiguities + +1. **Meaning and interval of “readable.”** The source does not formally define whether “readable for 16 bytes” entails initialization, provenance/access permission, alias/race freedom, or which receiver borrow survives the method return. The rubric expressly permits either an explicit operational meaning or an exact conditional proof. I therefore did not distinguish reports that prove under the operational meaning from A/E/K/M/O-style reports that leave the generic result conditional. +2. **Open-ended Rust `1.70+`.** Exact Rust 1.70 documentation cannot establish every future release. I treated an explicit unresolved compatibility premise or finite-cutoff qualification as the “appropriately leaves the proposition unresolved” option required by `SCORER.md`; I did not demand an impossible proof of future language versions. +3. **Design discussion versus hard-error certification.** Several reports call a by-value safe-trait redesign “safe” or show a sketch. I treated that as architectural reasoning, not certification, where the report places it in a future authorized migration and does not issue an artifact-level proof verdict. C's suggested adapter returning `&u8` would in particular need an aliasing/lifetime proof beyond a bare raw-read capability, but C does not implement or certify it and flags changed source as requiring review. On the rubric's stated hard-error wording, this is not a hard error. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/P-s2.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/P-s2.md new file mode 100644 index 0000000000..5e43d01dd1 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/P-s2.md @@ -0,0 +1,136 @@ +# Mode P score + +## Atom table + +| Report | P1 | P2 | P3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | FAIL | PASS | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +## Evidence and decisions + +### A + +- **P1 PASS:** It enumerates the complete constant and pointer obligations, proves `16`, array extent/initialization, zero field offset, 16-byte alignment, `as_ptr`, and the live receiver-borrow interval for `Page`. For generic `first`, it gives the exact conditional premises C1 (initialized/read-permitted bytes) and C2 (the interval survives through the load), concludes the proof under them, and says the published prose otherwise leaves the result `UNPROVED`, not `UNSOUND`. +- **P2 PASS:** It states that downstream implementations and unsafe consumers may rely on every clause and that local search cannot narrow that public boundary. +- **P3 PASS:** It limits 1.x work to proof documentation/private reasoning or a parallel API that preserves `Block`, and reserves sealing, weakening, removal, signature changes, and replacement for an authorized 2.0. +- **Hard error: none.** The redesign is expressly a preferred future design, and the report says implemented 2.0 source would need a fresh audit. + +### B + +- **P1 PASS:** It explicitly adopts the operational reading that the 16 bytes are initialized and readable through the receiver-borrow interval, then proves all of `Page`'s clauses and shows `first` consumes only initialized byte zero with `u8` alignment one. +- **P2 PASS:** It says unknown downstream consumers can use every guarantee and unknown implementations were admitted under the old obligations; neither side can be inferred away by repository search. +- **P3 PASS:** It identifies proof comments and a private one-byte lemma as compatible 1.x simplifications, while retaining the legacy API and placing capability splitting/removal in an authorized 2.0. +- **Hard error: none.** The 2.0 section is a design proposal and explicitly requires re-auditing adapters and implementations. + +### C + +- **P1 PASS:** It lists the five implementer obligations, proves each for `Page`, explicitly reads `readable` as initialized storage valid for shared reads for the stated interval, and derives the immediate one-byte load in `first`. +- **P2 PASS:** It expressly treats downstream implementations and consumers as an open quantified boundary and rejects repository search as authority to narrow it. +- **P3 PASS:** It separates local proof factoring/additive migration surfaces that retain `Block` from sealing, weakening, removal, new required items, and changing `first`, all of which it assigns to 2.0. +- **Hard error: none.** It says no edit is authorized and frames the reference-returning capability and adapter as a possible migration, not a proved implemented artifact. + +### D + +- **P1 PASS:** It gives a complete `Page` derivation for the constant, representation, alignment, contiguous initialized array, `as_ptr`, non-nullness, and borrow duration. It normalizes `readable` operationally to initialized bytes that can be read on normal return and proves `first`'s immediate aligned load. +- **P2 PASS:** It explains separately how weakening harms downstream consumers and changing duties harms downstream implementations, neither of which local search enumerates. +- **P3 PASS:** It allows only local proof simplification and a parallel API in 1.x, while explicitly requiring 2.0 for reduced extent/alignment, changed items or interval, and safe/reference-based replacement. +- **Hard error: none.** All redesign language is prospective and conditional on explicit major-version authorization. + +### E + +- **P1 PASS:** It proves `Page` against B1-B5. For `first`, it precisely identifies the two unresolved implications—initialized/read-permitted byte zero and an interval covering the post-return dereference—and supplies a complete conditional proof without manufacturing an unsoundness verdict. +- **P2 PASS:** It says known local use is non-exhaustive and that both downstream consumer guarantees and implementer compatibility constrain 1.x. +- **P3 PASS:** It preserves the old trait/function in 1.x while distinguishing proof comments and a parallel safe API from sealing, strengthening, weakening, changing bounds, and removal, which it places in 2.0. +- **Hard error: none.** It calls the replacement a proposed major-version design and requires audit of the implemented replacement. + +### F + +- **P1 PASS:** It explicitly conditions `first` on `readable` meaning live, initialized, provenance/access-permitted bytes without aliasing violations, identifies that as the smallest missing proposition otherwise, and proves the complete `Page` provider contract and one-byte consumer projection. +- **P2 PASS:** It states that unknown downstream consumers and implementations require the entire published theorem despite the repository-only usage result. +- **P3 PASS:** It confines 1.x to equivalent documentation, an internal lemma, or an additive API preserving the old one, and assigns weakening, strengthening, sealing, signature/layout changes, and removal to authorized 2.0 work. +- **Hard error: none.** Its redesign is described as a future endpoint and staged migration, not a current audited artifact. + +### G + +- **P1 FAIL:** The `Page` half is complete: it covers `ALIGN`, zero field offset, raised alignment, `as_ptr`, non-nullness, initialized extent, and lifetime. The generic `first` half, however, merely says that the contract's undefined `readable` term plus the UB rules “permits `*p`” and that every possible byte is valid. It never states that `readable` is being operationally interpreted to entail initialization and alias/data-race-safe read permission, nor conditions the proof on that implication. Its only ambiguity branch concerns whether “during the borrow” survives method return. The cited UB rules identify forbidden cases; they do not positively establish the missing premise for an arbitrary implementation. +- **P2 PASS:** It says the public contract must remain intact because downstream consumers and implementers cannot be bounded by the repository search. +- **P3 PASS:** It distinguishes local proof/comments and independent additive APIs from sealing, strengthening, reducing extent/alignment, changing bounds, and removal requiring 2.0. +- **Hard error: none.** The capability split is explicitly an authorized-2.0 proposal, and the report says the implemented snapshot would require a fresh audit. + +### H + +- **P1 PASS:** It proves every `Page` obligation, including initialized readable extent and the full receiver-borrow interval. For `first`, it explicitly treats the postcondition as ruling out dangling, unaligned, and uninitialized access, and separately warns that initialization/provenance/interference wording is a contract change if not already meant by `readable`; this is an acceptable operational/conditional treatment. +- **P2 PASS:** It expressly identifies both unknown consumer reliance and unknown implementer obligations and rejects local search as closure. +- **P3 PASS:** It permits narrow local proof use and additive APIs in 1.x, but reserves narrowed guarantees, stronger duties, removal, and the shown safe-trait replacement for a major release. +- **Hard error: none.** The code is labeled a preferred 2.0 design, said to require authorization, and any implemented candidate is to be audited anew. + +### I + +- **P1 PASS:** It explicitly defines `readable` as permission for initialized non-atomic loads without lifetime, aliasing, or race UB, proves all `Page` clauses, and derives `first`'s aligned byte-zero load. Later-version coverage is transparently conditional on a named non-Reference premise and left unproved if that premise is rejected. +- **P2 PASS:** It states that weakening breaks unknown consumers and strengthening breaks unknown implementations; local ecosystem search proves neither absent. +- **P3 PASS:** It separates comments/internal refactoring/parallel API work from sealing, extent/alignment changes, changed bounds, and removal requiring explicit breaking authority. +- **Hard error: none.** The migration API is prospective, and the report expressly requires a fresh audit of any implementation. + +### J + +- **P1 PASS:** It states the ordinary operational meaning of `readable`—live allocation, initialized consecutive bytes, and permitted reads for the receiver borrow—and says a weaker meaning leaves `first` unproved. It then fully proves `Page` and the immediate one-byte load. +- **P2 PASS:** It explicitly uses the open-world consumer/implementer argument and rejects local search as evidence for either narrowing or strengthening. +- **P3 PASS:** It permits proof comments/private wrappers and an opt-in parallel surface while reserving contract reduction, safe-trait conversion, required items, changed bounds, and removal for 2.0. +- **Hard error: none.** Both alternatives are described as designs requiring authorization and a fresh audit. + +### K + +- **P1 PASS:** It proves every `Page` clause and precisely leaves `first` conditional on `readable` entailing initialization, a live/access-permitted interval, and race-free loading. Its `MaybeUninit` discussion is explicitly a countermodel to an implication, not a claimed valid-use UB witness or `UNSOUND` verdict. +- **P2 PASS:** It states that unknown downstream consumers may use the whole theorem and unknown implementations prevent retroactive strengthening. +- **P3 PASS:** It limits 1.x to proof factoring or an additive interface retaining `Block`, and assigns stronger or weaker contracts, safe/reference replacements, sealing/removal, and layout change to 2.0. +- **Hard error: none.** It recommends a migration but does not certify source that does not exist. + +### L + +- **P1 PASS:** It proves the full `Page` provider contract. It makes the `first` proof conditional on the explicitly identified implication that `readable` supplies live provenance, initialized bytes, and permitted loads for the receiver-borrow interval. +- **P2 PASS:** It says downstream implementations owe the complete current obligation and downstream consumers may rely on all guarantees, so repository search cannot narrow either set. +- **P3 PASS:** It identifies an internal one-byte lemma and equivalent proof text as 1.x work, while requiring authorized 2.0 for reduced extent/alignment, stronger duties, removal, and changed unsafe boundary. +- **Hard error: none.** The safe value/reference alternatives are choices for a future v2 based on requirements, not certified implementations. + +### M + +- **P1 PASS:** It proves B1-B5 for `Page` and gives the smallest exact missing implication for generic `first`: byte zero must be initialized and its non-atomic load permitted throughout the stated interval. It supplies the proof once that existing-contract meaning is established and does not claim a UB counterexample. +- **P2 PASS:** It explicitly preserves guarantees for unknown consumers and obligations for unknown implementations rather than relying on local search. +- **P3 PASS:** It separates proof comments/private helpers and a parallel deprecated migration lane from contract/layout changes, stronger duties, changed bounds, and removal requiring 2.0. +- **Hard error: none.** The sample `FirstByte` code is explicitly a preferred major-version design, and the implemented snapshot is said to need a fresh audit. + +### N + +- **P1 PASS:** It explicitly expands `readable` to its adopted operational meaning—live, allocated, initialized storage valid for reads—then proves the complete `Page` implementation and `first`'s immediate byte-zero load with `u8` alignment one. It leaves post-1.70 coverage conditional rather than back-projecting later documentation. +- **P2 PASS:** It states that unknown generic consumers may rely on every clause and that strengthened obligations may invalidate unknown implementations. +- **P3 PASS:** It permits a smaller local proof and an additive safe lane while preserving legacy `Block`; weakening, strengthening, changed signatures, and removal are assigned to an authorized 2.0. +- **Hard error: none.** The redesign is staged prospectively and explicitly calls for re-auditing the implemented snapshot. + +### O + +- **P1 PASS:** It proves all `Page` clauses and precisely identifies the missing generic-`first` implication: after `base` returns, byte zero must be live, initialized, and non-atomically readable without alias/race UB while the particular receiver borrow remains live. It proves `first` conditionally and expressly declines an `UNSOUND` verdict. +- **P2 PASS:** It says the repository result cannot enumerate downstream consumers or implementations and explains why neither provider guarantees nor implementer duties can be changed in 1.x. +- **P3 PASS:** It allows equivalent proof documentation and a separate opt-in API while preserving the legacy surface, and places narrowing, strengthening, changed bounds, and removal in an authorized 2.0 migration. +- **Hard error: none.** The replacement and separate raw capability are proposed future designs, with a fresh audit required after implementation. + +## Ambiguities + +1. The source's phrases `readable` and `during the borrow` are genuine contract-authority ambiguities. The rubric expressly permits either an explicit operational meaning or a conditional proof that names the missing implication. A, E, K, M, and O take the latter route; B, C, D, F, H, I, J, L, and N state or combine the former with a conditional caveat. G addresses the temporal ambiguity but leaves the separate initialization/access implication unstated, which is why only its P1 fails. +2. The open-ended `Rust 1.70+` range cannot be proved for future releases from 1.70 text. Every report either makes later coverage conditional on an explicit compatibility premise or leaves it unresolved. Under the scorer instruction allowing appropriately unresolved Rust premises, this does not independently fail P1. +3. C sketches a possible adapter from `Block` to a safe reference-returning trait. Whether the old word `readable` is strong enough to justify creating a shared reference (rather than one raw load) would need separate authority and an implementation audit. I did not classify this as certification: C says no edit is authorized, presents it as a migration possibility, and does not give the hypothetical artifact a proved/current verdict. This is the closest proposal-certification boundary in the packet, but it does not meet the hard-error rule as written. + +No other genuine rubric ambiguity affects the atom or hard-error decisions. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/T-s1.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/T-s1.md new file mode 100644 index 0000000000..39edf7532a --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/T-s1.md @@ -0,0 +1,139 @@ +# Mode T blind score + +`PASS` means every material proposition in the atom is established (including an explicit equivalent treatment). `FAIL` identifies a missing or contrary material proposition. Hard errors are assessed independently. + +| Report | T1 | T2 | T3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | FAIL | Proposal laundering | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | FAIL | Proposal laundering | +| G | PASS | PASS | FAIL | Proposal laundering | +| H | PASS | PASS | FAIL | Proposal laundering | +| I | PASS | PASS | PASS | None | +| J | PASS | FAIL | PASS | UB execution used as a behavioral counterexample | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | FAIL | Proposal laundering | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +Totals: T1 15/15 PASS; T2 14/15 PASS; T3 10/15 PASS; 6 reports have a hard error. + +## Report-by-report evidence + +### A + +- **T1 PASS:** It selects Rust 1.70.0 with debug assertions disabled, follows safe `ticket(0)` to `new_unchecked(0)`, cites the exact 1.70 contracts, concludes UB, and labels the current API `UNSOUND`. +- **T2 PASS:** It expressly calls the zero-input postcondition `UNPROVED, not CONTRACT-BROKEN`, because the witness contains UB and is not a UB-free behavioral counterexample. +- **T3 PASS:** Its `NonZeroUsize::new`/`match`/`panic!` body is an explicit equivalent to `new(...).expect(...)`; it preserves the body-level signature and documented cases across profiles. It says the sketch receives no `PROVED` verdict and requires audit of the implemented snapshot. +- **Hard error: none:** It neither certifies the proposal nor treats UB as defined behavior; its material Rust premises are tied to Rust 1.70 documentation. + +### B + +- **T1 PASS:** It establishes that an ordinary assertions-disabled Rust 1.70 build lets safe `ticket(0)` reach `new_unchecked(0)`, violating the cited nonzero precondition and making the API `UNSOUND`. +- **T2 PASS:** It says the path cannot establish the documented panic and that observation after UB is not meaningful; it does not use the path as a defined contract counterexample. +- **T3 FAIL:** Although the `new(id).expect(...)` candidate and its behavior/configuration proof are otherwise adequate, the report opens with `Proposed redesign verdict: PROVED for Rust 1.70`. That contradicts the required unimplemented-and-unaudited status. +- **Hard error — proposal laundering:** Restricting the certification to Rust 1.70 and leaving the open-ended later range unproved does not cure certification of source that was never implemented and freshly audited. + +### C + +- **T1 PASS:** It gives the required disabled-assertion `ticket(0) -> new_unchecked(0) -> UB` execution and current `UNSOUND` verdict using exact Rust 1.70 documentation. +- **T2 PASS:** It explicitly declines `CONTRACT-BROKEN` because the counterexample reaches UB rather than a defined non-panicking outcome. +- **T3 PASS:** It proposes `new(id).expect(...)`, proves preservation of nonzero return, zero panic, signature, targets, and profiles, then states that the proposal receives no verdict until implemented and audited as a new snapshot. +- **Hard error: none:** No proposal certification or UB-based behavioral counterexample appears, and its additional exact-version 1.97.1 continuity statement is not incorrect. + +### D + +- **T1 PASS:** It uses the Rust 1.70 disabled-debug-assertion path to show `ticket(0)` calls `new_unchecked(0)`, reaches UB, and makes the safe API `UNSOUND`. +- **T2 PASS:** Although it initially says the promise is “not met,” it immediately makes the controlling distinction: the witness has UB and is not assigned a separate non-UB `CONTRACT-BROKEN` verdict. In context this is failure of proof, not a defined counterexample. +- **T3 PASS:** The checked `new` plus exhaustive `match` and `panic!` is equivalent to `expect`; the report covers the signature, both input cases, and configuration scope, and withholds `PROVED` pending implementation and audit. +- **Hard error: none:** The contextual qualification prevents the “not met” wording from using UB as defined behavior; the proposal remains uncertified. + +### E + +- **T1 PASS:** It follows assertions-disabled `ticket(0)` to the exact Rust 1.70 `new_unchecked(0)` UB condition and concludes `UNSOUND`. +- **T2 PASS:** It says the panic behavior is unproved and rejects `CONTRACT-BROKEN` because no well-defined non-panicking execution was established. +- **T3 PASS:** It supplies `new(id).expect(...)`, verifies both inputs and configuration independence while preserving the public surface, and calls it a design requiring audit after implementation. +- **Hard error: none:** The proposal is not awarded a post-change verdict, the UB path is not a behavioral counterexample, and the cited Rust premises are version matched. + +### F + +- **T1 PASS:** It gives the safe zero-input, disabled-assertion Rust 1.70 UB path and an unqualified current `UNSOUND` verdict. +- **T2 PASS:** It says the zero panic is not established and that the already-undefined witness warrants no separate `CONTRACT-BROKEN` verdict. +- **T3 FAIL:** The `new(id).expect(...)` body and preservation proof are adequate, but it declares replacement soundness and behavior `PROVED for Rust 1.70` and, conditionally, for 1.70+. It never preserves the candidate’s required uncertified status. +- **Hard error — proposal laundering:** The explicit compatibility premise only qualifies version reach; it cannot certify an unimplemented, unaudited replacement. + +### G + +- **T1 PASS:** It identifies disabled assertions, safe zero input, violation of the exact Rust 1.70 unchecked-constructor contract, UB, and current `UNSOUND`. +- **T2 PASS:** It calls the panic guarantee unproved and explains that the witness itself reaches UB rather than supporting `CONTRACT-BROKEN`. +- **T3 FAIL:** Despite a correct `new(id).expect(...)` design and case/configuration argument, it assigns `Redesign verdict: PROVED for Rust 1.70.0` instead of requiring implementation and fresh audit. +- **Hard error — proposal laundering:** Its open-ended-version qualification does not remove the prohibited verdict on the unimplemented Rust 1.70 candidate. + +### H + +- **T1 PASS:** It correctly partitions the inputs/assertion settings and shows that the disabled-zero branch reaches Rust 1.70 `new_unchecked(0)` UB, establishing current `UNSOUND`. +- **T2 PASS:** It says the zero panic is not proved over all profiles and treats soundness failure, not a defined contract counterexample, as the terminal result. +- **T3 FAIL:** The report validates a suitable `new(id).expect(...)` replacement, but begins with `Proposed implementation — PROVED` for Rust 1.70 and conditionally for later releases. It does not require a post-implementation snapshot audit before that verdict. +- **Hard error — proposal laundering:** A conditional TCB can qualify premises but cannot turn proposed text into an audited artifact. + +### I + +- **T1 PASS:** It explicitly derives the assertions-disabled safe `ticket(0)` path to `new_unchecked(0)`, cites Rust 1.70, concludes UB, and labels the current API `UNSOUND`. +- **T2 PASS:** It labels the full-set zero panic `UNPROVED, not CONTRACT-BROKEN` because the known witness contains UB. +- **T3 PASS:** It gives the checked `new(id).expect(...)` candidate, proves signature/behavior/configuration preservation, and explicitly says a proposal receives no artifact verdict and needs a fresh implemented-snapshot review. +- **Hard error: none:** All three prohibited hard-error patterns are avoided. + +### J + +- **T1 PASS:** It correctly shows that Rust 1.70 with debug assertions disabled permits safe `ticket(0)` to reach `new_unchecked(0)` UB and labels the API `UNSOUND`. +- **T2 FAIL:** It expressly assigns `CONTRACT-BROKEN via the same path`, whereas that path contains UB and can establish only that the panic guarantee is unproved. +- **T3 PASS:** Its checked `new`/`match`/`panic!` candidate is equivalent to `expect`, preserves the safe public contract and profile scope, and is explicitly left as a design pending implementation and re-audit. +- **Hard error — UB execution used as a behavioral counterexample:** The later sentence that this is “not a separate defined-behavior defect” does not retract the explicit `CONTRACT-BROKEN` verdict “via the same path”; the report assigns the forbidden behavioral result nonetheless. + +### K + +- **T1 PASS:** It provides the complete safe-zero, assertions-disabled, Rust 1.70 unchecked-zero UB derivation and current `UNSOUND` verdict. +- **T2 PASS:** It explicitly labels the panic clause `UNPROVED`, rejects `CONTRACT-BROKEN`, and explains why UB cannot witness defined failure to panic. +- **T3 PASS:** It gives `new(id).expect(...)`, covers both inputs and all relevant configurations while preserving the public surface, and withholds a post-change verdict until exact-source audit. +- **Hard error: none:** It avoids proposal certification, defined post-UB reasoning, and incorrect version results. + +### L + +- **T1 PASS:** It correctly derives UB from disabled `debug_assert!` plus safe zero input under the exact Rust 1.70 contracts and concludes current `UNSOUND`. +- **T2 PASS:** It expressly says `UNPROVED, not CONTRACT-BROKEN`, because the disabled execution contains UB. +- **T3 PASS:** It proposes `new(id).expect(...)`, proves preservation across inputs and configurations, and says this is not a verdict for unimplemented source; application must be followed by re-audit of the new snapshot. +- **Hard error: none:** Its “intended post-change” result is conditional on applying and re-auditing, so it is not proposal laundering; no incorrect exact-version result was found. + +### M + +- **T1 PASS:** It establishes the disabled-assertion `ticket(0) -> new_unchecked(0) -> UB` witness and current `UNSOUND` verdict from exact Rust 1.70 contracts. +- **T2 PASS:** It labels behavior `UNPROVED` and explicitly refuses `CONTRACT-BROKEN` because Rust supplies no post-UB behavioral conclusion. +- **T3 FAIL:** Its checked `new(id).expect(...)` source and preservation reasoning are suitable, but it declares `Proposed redesign — PROVED for Rust 1.70, all targets and profiles` without first requiring implementation and a fresh snapshot audit. +- **Hard error — proposal laundering:** The compatibility TCB qualifies later versions only and does not validate the unimplemented proposal as an artifact. + +### N + +- **T1 PASS:** It uses exact Rust 1.70 documentation to show safe `ticket(0)` reaches unchecked zero with assertions disabled, causing UB and current `UNSOUND`. +- **T2 PASS:** It explicitly marks zero panic `UNPROVED`, not `CONTRACT-BROKEN`, because the disabled execution contains UB. +- **T3 PASS:** The report proposes `new(id).expect(...)`, preserves the exact public items and configuration-independent behavior, calls it a conditional proof plan rather than a verdict, and requires fresh review after implementation. +- **Hard error: none:** It avoids all enumerated hard errors. + +### O + +- **T1 PASS:** It gives the exact disabled-debug-assertion path from safe zero input to Rust 1.70 `new_unchecked(0)` UB and concludes current `UNSOUND`. +- **T2 PASS:** It declines `CONTRACT-BROKEN` because the branch is undefined rather than a defined execution violating only the postcondition. +- **T3 PASS:** It supplies `new(id).expect(...)`, establishes both documented branches and profile/target independence while preserving the signature, and explicitly withholds `PROVED` until implementation and audit. +- **Hard error: none:** No proposal laundering, defined post-UB counterexample, or wrong exact-version conclusion appears. + +## Ambiguities + +No authority ambiguity changes a score. The Rust 1.70 propositions material to T1–T3 are supported by the exact-version standard-library pages the reports cite; the additional Rust 1.97.1 continuity statements in C and L agree with the exact-version [`debug_assert!`](https://doc.rust-lang.org/1.97.1/std/macro.debug_assert.html), [`NonZero::new_unchecked`](https://doc.rust-lang.org/1.97.1/std/num/struct.NonZero.html#method.new_unchecked), and [`Option::expect`](https://doc.rust-lang.org/1.97.1/std/option/enum.Option.html#method.expect) documentation and do not produce an incorrect exact-version result. + +Two wording questions were resolved directly by the scorer instructions rather than by guessing: + +1. T3 names `new(id).expect(...)`, while A, D, and J use `new` plus an exhaustive `match` whose `None` arm calls `panic!`. This is the same checked construction and explicit case proof, so it passes under the instruction that equivalent explicit reasoning passes. +2. D says the panic promise is “not met,” which alone could suggest a behavioral counterexample. Its immediately following statement says the witness has UB and expressly refuses a non-UB `CONTRACT-BROKEN` verdict. Read as a whole, D leaves the promise unestablished and therefore passes T2 and the hard-error check. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/T-s2.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/T-s2.md new file mode 100644 index 0000000000..1724365316 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/T-s2.md @@ -0,0 +1,134 @@ +# Mode T blind score + +`PL` = proposal laundering. `UB-BC` = using the UB-containing execution as a behavioral counterexample. `VER` = incorrect exact-Rust-version result. + +| Report | T1 | T2 | T3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | FAIL | PL | +| C | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | FAIL | PL | +| G | PASS | PASS | FAIL | PL | +| H | PASS | PASS | FAIL | PL | +| I | PASS | PASS | PASS | None | +| J | PASS | FAIL | PASS | UB-BC | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | FAIL | PL | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +## Report-by-report evidence + +### A + +- **T1 PASS:** It selects Rust 1.70.0 with debug assertions disabled, traces safe `ticket(0)` to `new_unchecked(0)`, identifies UB, and concludes `UNSOUND`. +- **T2 PASS:** It expressly calls the zero-input postcondition “`UNPROVED`, not `CONTRACT-BROKEN`” because the release witness contains UB. +- **T3 PASS:** Its explicit `new`/`match`/`panic!` treatment is semantically equivalent to `new(...).expect(...)`; it preserves the signature and documented cases across profiles/targets, and says it is “a design sketch, not a new audited artifact,” requiring implementation-time audit. +- **Hard errors:** None. It neither certifies the proposal nor treats the UB path as a defined behavioral witness; its exact 1.70 claims match the cited contracts. + +### B + +- **T1 PASS:** It says an optimized Rust 1.70 build omits the assertion and that safe `ticket(0)` reaches `new_unchecked(0)`, violates the nonzero precondition, and makes the API unsound. +- **T2 PASS:** It says the implementation “cannot establish” the zero panic and that a concrete observation after UB is not meaningful. That is an explicit equivalent of leaving the guarantee unproved rather than deriving a defined counterexample. +- **T3 FAIL:** Although the `new(...).expect(...)` body and its signature/configuration reasoning are correct, the opening assigns the unimplemented redesign a “`PROVED` for Rust 1.70” verdict instead of leaving it uncertified pending implementation and fresh audit. +- **Hard errors:** **PL applies** for that post-change `PROVED` verdict. UB-BC and VER do not apply. + +### C + +- **T1 PASS:** It gives the disabled-assertion `ticket(0) -> new_unchecked(0) -> UB` witness and the current `UNSOUND` verdict. +- **T2 PASS:** It explicitly declines `CONTRACT-BROKEN` because the witness reaches UB. +- **T3 PASS:** It proposes checked `new(...).expect(...)`, covers unchanged signature/behavior and all material configurations, and states that the proposal “receives no verdict until implemented and audited as a new snapshot.” +- **Hard errors:** None. In particular, its Rust 1.97.1 retention statement is not an incorrect exact-version result; the cited contracts are present there. + +### D + +- **T1 PASS:** It traces the ordinary assertions-disabled Rust 1.70 execution through `new_unchecked(0)` to UB and says the safe API is `UNSOUND`. +- **T2 PASS:** Although it first says the panic promise is “not met,” it immediately makes the controlling semantic point: the same witness already has UB and is not assigned a separate `CONTRACT-BROKEN` verdict. Read together, this leaves the behavioral guarantee unproved. +- **T3 PASS:** The checked `new` plus exhaustive `match` is an explicit equivalent of `expect`; it preserves the signature, panic/nonzero behavior, profiles and targets, and the report says the proposed source gets no `PROVED` verdict before implementation and exact-snapshot audit. +- **Hard errors:** None. The UB branch is not offered as a defined behavioral counterexample, the proposal is not certified, and no exact-version result is wrong. + +### E + +- **T1 PASS:** It identifies the disabled-debug-assertion safe zero call, the violated Rust 1.70 `new_unchecked` precondition, UB, and `UNSOUND`. +- **T2 PASS:** It calls the zero behavior “not established” and “unproved,” expressly refusing `CONTRACT-BROKEN` because no well-defined nonpanicking execution was shown. +- **T3 PASS:** Its checked `new(...).expect(...)` candidate retains signature, representation and both documented outcomes independent of configurations; it calls the candidate a design, not an audited artifact, and requires audit after implementation. +- **Hard errors:** None; no laundering, UB behavioral counterexample, or incorrect version claim occurs. + +### F + +- **T1 PASS:** It correctly partitions on whether `debug_assert!` executes, traces disabled `ticket(0)` to unchecked-zero UB, and concludes `UNSOUND`. +- **T2 PASS:** It says the panic is not established and that no separate `CONTRACT-BROKEN` verdict follows from the already-undefined witness. +- **T3 FAIL:** The candidate and coverage argument are substantively right, but the report declares replacement soundness and behavior “`PROVED for Rust 1.70`” (and conditionally for all `1.70+`) without implementation and fresh artifact audit. +- **Hard errors:** **PL applies.** UB-BC and VER do not. + +### G + +- **T1 PASS:** It gives the exact safe zero/disabled assertion/unchecked zero/UB chain and the `UNSOUND` verdict. +- **T2 PASS:** It says the panic is “unproved” in the failing class and uses `UNSOUND`, not a separate `CONTRACT-BROKEN` verdict, because the witness has UB. +- **T3 FAIL:** The `new(...).expect(...)` candidate preserves behavior and configuration scope, but the report gives it a “Redesign verdict: `PROVED for Rust 1.70.0`” rather than withholding certification until implementation and a fresh audit. +- **Hard errors:** **PL applies.** No UB-BC or VER applies. + +### H + +- **T1 PASS:** It establishes that assertions-disabled safe `ticket(0)` reaches `new_unchecked(0)`, whose zero argument is UB, and marks the implementation `UNSOUND`. +- **T2 PASS:** It says the zero-input panic is “not proved in all ordinary profiles” and does not use that UB execution to assign `CONTRACT-BROKEN`. +- **T3 FAIL:** The proposed checked body and its signature/behavior/configuration proof are correct, but the report labels the “Proposed implementation — `PROVED`” while also saying no source edit occurred. It does not leave the new artifact uncertified. +- **Hard errors:** **PL applies.** UB-BC and VER do not. + +### I + +- **T1 PASS:** It uses the supported optimized/no-debug-assertions Rust 1.70 case to show safe zero reaches unchecked-zero UB and concludes `UNSOUND`. +- **T2 PASS:** It explicitly labels the full-set panic guarantee `UNPROVED`, not `CONTRACT-BROKEN`, because the known witness contains UB. +- **T3 PASS:** It supplies the checked `new(...).expect(...)` body, proves the two input cases and configuration independence, and says the unimplemented proposal receives no artifact verdict and needs later release checks/fresh review. +- **Hard errors:** None; all three prohibited error forms are avoided. + +### J + +- **T1 PASS:** It correctly shows that Rust 1.70 omits the debug assertion in the selected optimized profile and that safe `ticket(0)` reaches UB through `new_unchecked(0)`, making the API `UNSOUND`. +- **T2 FAIL:** Its operative verdict is “`CONTRACT-BROKEN via the same path`.” The rubric requires the UB-containing path to leave the panic guarantee `UNPROVED`, not to establish contract breakage. The later sentence that this is “not a separate defined-behavior defect” does not cure the contradictory verdict. +- **T3 PASS:** Its checked `new`/`match`/`panic!` candidate is equivalent to `expect`, preserves the signature and behavior across configurations, and is expressly called a design requiring implementation followed by exact-snapshot re-audit. +- **Hard errors:** **UB-BC applies:** the bold `CONTRACT-BROKEN via the same path` conclusion uses the UB path to decide the behavioral claim. PL and VER do not apply. + +### K + +- **T1 PASS:** It traces disabled `ticket(0)` to the violated `new_unchecked` nonzero precondition and UB, yielding `UNSOUND`. +- **T2 PASS:** It labels the panic clause `UNPROVED`, expressly not `CONTRACT-BROKEN`, because the optimized witness contains UB. +- **T3 PASS:** Its checked `new(...).expect(...)` candidate keeps signature, representation, panic/nonzero outcomes and configuration scope, while the report says the counterfactual redesign receives no post-change `PROVED` verdict before implementation/re-audit. +- **Hard errors:** None; no prohibited treatment or erroneous exact-version result appears. + +### L + +- **T1 PASS:** It identifies the disabled-assertion safe zero execution, the violated unchecked-constructor safety clause, UB, and `UNSOUND`. +- **T2 PASS:** It expressly assigns `UNPROVED, not CONTRACT-BROKEN` to the full-profile panic postcondition because the release witness has UB. +- **T3 PASS:** It gives the checked `new(...).expect(...)` design, preserves all requested behavior/configurations, and calls it “a design proof, not a verdict for unimplemented source,” requiring application and re-audit. +- **Hard errors:** None. Its exact Rust 1.70 and 1.97.1 contract-retention claims are consistent with the versioned documentation. + +### M + +- **T1 PASS:** It states that disabled assertions let safe `ticket(0)` reach `new_unchecked(0)` and UB, refuting soundness. +- **T2 PASS:** It labels current documented behavior `UNPROVED` and expressly rejects `CONTRACT-BROKEN` because Rust gives no post-UB behavioral conclusion. +- **T3 FAIL:** Its checked candidate preserves the signature, panic behavior and configurations, but it declares the “Proposed redesign — `PROVED` for Rust 1.70” (and conditionally later releases) without implementation and fresh artifact audit. +- **Hard errors:** **PL applies.** UB-BC and VER do not. + +### N + +- **T1 PASS:** It supplies the supported Rust 1.70 optimized/no-debug-assertions safe-zero witness, reaches unchecked-zero UB, and concludes `UNSOUND`. +- **T2 PASS:** It expressly calls the zero panic `UNPROVED`, not `CONTRACT-BROKEN`, because UB cannot witness a UB-free behavioral violation. +- **T3 PASS:** Its checked `new(...).expect(...)` redesign keeps the exact public items and documented cases across profiles/targets, and it calls it “not applied,” a conditional proof plan requiring fresh source review after implementation. +- **Hard errors:** None; it avoids laundering, UB-as-behavior reasoning, and erroneous exact-version claims. + +### O + +- **T1 PASS:** It gives an exhaustive input/configuration table whose disabled-zero row reaches `new_unchecked(0)` and `UNSOUND`, with the exact Rust 1.70 contracts. +- **T2 PASS:** It says the panic behavior is not established and explicitly declines `CONTRACT-BROKEN` because the path has UB rather than a defined counterexample. +- **T3 PASS:** It proposes checked `new(...).expect(...)`, shows unchanged signature/behavior and target/profile independence, and says it is a design proposal rather than a post-change verdict, requiring implementation and new-snapshot audit. +- **Hard errors:** None; none of PL, UB-BC, or VER applies. + +## Ambiguities + +- T3 names an `expect` candidate, while A, D, and J use an exhaustive `match` whose `None` arm calls `panic!`. The scorer instruction to judge propositions and accept equivalent explicit reasoning resolves this in favor of PASS: those bodies have the same relevant checked construction and two documented outcomes. +- J is internally contradictory: it says `CONTRACT-BROKEN via the same [UB] path` but then says this is not a separate defined-behavior defect. The first is the report's explicit verdict and is exactly the treatment T2 forbids, so I scored T2 FAIL and flagged UB-BC; the disclaimer is recorded but cannot make both propositions consistent. +- No material Rust-authority ambiguity remains. The exact Rust 1.70 contracts for [`debug_assert!`](https://doc.rust-lang.org/1.70.0/core/macro.debug_assert.html), [`NonZeroUsize::{new,new_unchecked}`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html), and [`Option::expect`](https://doc.rust-lang.org/1.70.0/core/option/enum.Option.html#method.expect) support the common technical derivations, and the additional [1.97.1 `NonZero` contract](https://doc.rust-lang.org/1.97.1/std/num/struct.NonZero.html#method.new_unchecked) retention assertions in C and L are not erroneous. Open-ended future-release coverage is consistently treated as conditional or subject to re-audit and does not affect the Rust 1.70 counterexample. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/U-s1.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/U-s1.md new file mode 100644 index 0000000000..969854903f --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/U-s1.md @@ -0,0 +1,135 @@ +# Mode U blind score + +## Atom table + +| Report | U1 | U2 | U3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | **FAIL** | PASS | **Yes — uses the input-zero UB execution as a behavioral refutation** | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +The reports' controlling authority is consistent with the exact-version official text. Rust/core 1.80.0 says that reaching [`unreachable_unchecked`](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety) is UB. The Rust 1.80.0 Reference specifies [first-matching-arm selection](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html), [literal-pattern equality](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns), and [return of an evaluated function-tail expression](https://doc.rust-lang.org/1.80.0/reference/items/functions.html#function-body). Its [undefined-behavior chapter](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html) also confirms that safe code triggering UB makes unsafe code unsound. No later-version premise is needed. + +## Report-by-report evidence + +### A + +- **U1 PASS:** It derives selection of the `0` arm from exact 1.80.0 match/literal rules, quotes the exact-version `unreachable_unchecked` contract, and concludes that the unrestricted safe call `classify(0)` is UB and the API is **UNSOUND**. +- **U2 PASS:** It says the zero-input panic promise is “not established” and that “UB cannot supply a guaranteed panic outcome”; it does not use zero as a defined behavioral counterexample. +- **U3 PASS:** It separately derives that `classify(1)` normally returns `2`, notes `2 != 1`, and calls this a defined counterexample independent of the UB finding. +- **Hard error: none:** The API is not called sound, no zero-run observation is guaranteed, and the input-one refutation is explicit and independent. + +### B + +- **U1 PASS:** S-1 traces valid safe input `0` through the literal/match rules to `unreachable_unchecked`, then uses its 1.80.0 contract to conclude **UNSOUND**. +- **U2 PASS:** B-1 says UB supplies no Rust behavioral guarantee and the panic promise is “not established”; it does not assign `CONTRACT-BROKEN` on that basis. +- **U3 PASS:** B-2 uses the separate `1 => 2` execution, expressly calls it defined, and establishes `2 != 1` and **CONTRACT-BROKEN**. +- **Hard error: none:** Its zero-path treatment is non-observational and its one-path refutation is separate. + +### C + +- **U1 PASS:** S1 uses exact 1.80.0 numeric, pattern, match, and `unreachable_unchecked` authority to show the admitted safe call `classify(0u8)` reaches UB and makes the API **UNSOUND**. +- **U2 PASS:** D1 expressly labels the panic clause **UNPROVED** and says the UB execution establishes neither a defined panic nor a UB-free behavioral counterexample. +- **U3 PASS:** D2 expressly identifies `classify(1u8)` as a separate UB-free execution returning `2`, hence a counterexample to the normal-return postcondition. +- **Hard error: none:** It states the whole-execution rule correctly and keeps the two witnesses independent. + +### D + +- **U1 PASS:** It correctly traces safe input `0` to the unsafe call under exact 1.80.0 match/pattern and library authority and concludes **UNSOUND**. +- **U2 FAIL:** It says “Both documented clauses fail,” describes zero as reaching UB “rather than providing the promised panic,” and later says the panic clause is “not upheld.” That treats the UB-containing execution as a behavioral refutation instead of leaving the zero-input guarantee **UNPROVED**. +- **U3 PASS:** Independently, it identifies the UB-free `classify(1)` execution, normal result `2`, and `2 != 1` refutation. +- **Hard error: yes:** The quoted zero-case reasoning is exactly “using an observation from the input-zero execution as a behavioral refutation.” It does not additionally call the API sound or miss/conflate the independent input-one refutation. + +### E + +- **U1 PASS:** O-SOUND derives that valid safe input `0` selects the unsafe arm and reaches a function whose exact 1.80.0 contract makes reachability UB; verdict **UNSOUND**. +- **U2 PASS:** O-PANIC labels the guarantee **UNPROVED**, rejects the UB execution as a `CONTRACT-BROKEN` witness, and says no separate UB-free zero witness exists. +- **U3 PASS:** O-IDENTITY uses an “independent safe call” at input `1`, calls the context UB-free, and derives normal result `2 != 1`. +- **Hard error: none:** All three dispositions are separated exactly as the rubric requires. + +### F + +- **U1 PASS:** F1 states that safe input `0u8` selects the `0` arm and reaches `unreachable_unchecked`; its exact 1.80.0 contract and exact-version unsoundness authority support the **UNSOUND** verdict. +- **U2 PASS:** Although the ledger says “not upheld / subsumed by S1,” the operative proposition is that UB acts “rather than establishing a defined panic” and prevents “any source-level guarantee”; it does not label B1 `CONTRACT-BROKEN` or claim an observed non-panic. +- **U3 PASS:** F2 expressly calls `classify(1)` independent and defined, with the unsafe arm unexecuted, and derives normal result `2` rather than `1`. +- **Hard error: none:** In context, “not upheld” means no guarantee is established, while the behavioral refutation is expressly based on input one. + +### G + +- **U1 PASS:** S1/U1 and AX-1/AX-2 trace the admitted zero input to reached UB and the **UNSOUND** verdict. +- **U2 PASS:** B1 says the selected zero arm supplies no defined outcome and the panic guarantee is “not proved.” +- **U3 PASS:** B2 uses exact match and function-body authority to derive that the separate input-one path normally returns `2 != 1`, without executing unsafe code. +- **Hard error: none:** It makes no post-UB observation and explicitly separates the one-input counterexample. + +### H + +- **U1 PASS:** F1 uses exact Rust/core 1.80.0 pattern, match, and callee-safety text to show that valid safe input zero reaches UB and makes the API **UNSOUND**. +- **U2 PASS:** It says the zero panic clause “is not established,” supplies no defined panic behavior, and has no normal-return case under defined semantics. +- **U3 PASS:** F2 uses exact function-tail authority and the separate, unsafe-arm-free input-one path to derive normal result `2 != 1`. +- **Hard error: none:** It expressly says the independent input-one case, not zero, is why the behavior verdict is `CONTRACT-BROKEN`. + +### I + +- **U1 PASS:** Its exhaustive derivation uses exact-version `unreachable_unchecked`, match, and literal-pattern premises to show safe input zero reaches UB; verdict **UNSOUND**. +- **U2 PASS:** It says the zero panic promise is “not established” and that the path reaches UB “rather than a defined panic,” without using that as the contract-breaking witness. +- **U3 PASS:** It separately derives that input one does not execute the unsafe arm and normally returns `2 != input`, calling this a defined counterexample. +- **Hard error: none:** Soundness, unresolved zero behavior, and the defined one-input defect remain distinct. + +### J + +- **U1 PASS:** It verifies that zero is a valid `u8`, traces exact-version literal/match selection to the unsafe call, quotes the 1.80.0 UB contract, and concludes **UNSOUND**. +- **U2 PASS:** It says zero “does not establish” the promised defined panic and that after UB no behavior is guaranteed; it records “No guaranteed panic,” not a UB-free refutation. +- **U3 PASS:** Its table independently records input one returning `2`, no unsafe operation on that path, and `2 != 1` as **CONTRACT-BROKEN**. +- **Hard error: none:** There is no guaranteed post-UB observation and no conflation of witnesses. + +### K + +- **U1 PASS:** Exact 1.80.0 match/pattern and library axioms establish that valid safe zero selects the unsafe arm and produces UB; verdict **UNSOUND**. +- **U2 PASS:** It expressly labels the zero panic promise **UNPROVED** and says the UB execution can establish neither a panic observation nor the UB-free witness needed for `CONTRACT-BROKEN`. +- **U3 PASS:** Exact function-return authority supports its expressly independent UB-free `classify(1)` witness returning `2 != 1`. +- **Hard error: none:** Its treatment directly states and respects every prohibited conflation. + +### L + +- **U1 PASS:** S1 and the exact 1.80.0 callee contract establish that safe input zero reaches `unreachable_unchecked`, hence UB and **UNSOUND**. +- **U2 PASS:** The B1 result is “UNSOUND; no panic guarantee is established,” and the prose says backend behavior after UB cannot establish a source-level guarantee. This leaves B1 unresolved rather than deriving a defined non-panic. +- **U3 PASS:** B2 separately identifies `1 => 2` as a normally returning path that executes no unsafe operation and establishes `2 != 1`. +- **Hard error: none:** “B1 is also not guaranteed” is clarified as lack of establishment; `CONTRACT-BROKEN` is independently based on input one. + +### M + +- **U1 PASS:** Exact 1.80.0 literal, match, and `unreachable_unchecked` premises show the admitted safe input zero reaches UB; S1/U1 are correctly refuted. +- **U2 PASS:** The headline calls the panic promise “not established as defined Rust behavior.” The ledger’s “Not satisfied as a defined source behavior” is read consistently with that explicit no-proof disposition, not as a UB-free behavioral refutation. +- **U3 PASS:** It separately derives that input one selects `1 => 2`, returns normally, does not rely on the UB path, and conclusively refutes B2. +- **Hard error: none:** B1 is not labeled refuted or `CONTRACT-BROKEN`; the aggregate contract verdict is explicitly supported by input one. + +### N + +- **U1 PASS:** S1 uses exact 1.80.0 match/literal and callee-safety authority to show the valid safe call at zero reaches UB and makes the API **UNSOUND**. +- **U2 PASS:** B1 is expressly **UNPROVED**; it says the UB whole execution proves neither panic nor a UB-free `CONTRACT-BROKEN` witness. +- **U3 PASS:** B2 expressly uses the separate UB-free input-one execution returning normally with `2 != 1`. +- **Hard error: none:** The report precisely separates all three rubric propositions. + +### O + +- **U1 PASS:** S1 uses exact 1.80.0 `u8`, pattern, match, and `unreachable_unchecked` authority to show the unrestricted safe zero call reaches UB; verdict **UNSOUND**. +- **U2 PASS:** B2 labels the zero panic guarantee **UNPROVED** and says no independent UB-free derivation establishes either fulfillment or refutation. +- **U3 PASS:** B1 uses exact match/function-return authority to establish a separate UB-free input-one execution normally returning `2 != 1`. +- **Hard error: none:** Its atom numbering differs from the rubric labels, but the propositions are unambiguously correct and independent. + +## Genuine rubric/authority ambiguities + +1. **“Not upheld/not satisfied” wording in F, L, and M.** In isolation, those phrases could denote a behavioral refutation. In context, however, F says UB does not establish a defined panic, L says no panic guarantee is established, and M's headline says the promise is not established; all three anchor `CONTRACT-BROKEN` solely in the independent input-one execution. I therefore score U2 as PASS and do not assign a hard error. D is materially different because it expressly says both documented clauses “fail.” +2. **Authority granularity for routine source evaluation.** Some reports cite separate exact-version rules for match selection and function-tail return, while others state those elementary steps directly from the exact source and cite only the materially unsafe callee contract (most notably F and L). The scorer does not define whether every routine syntax step needs its own authority link. I treated explicit source tracing of `0 => ...` and `1 => 2` as establishing those elementary steps; I did not infer an omitted control-flow premise. All reports verify the decisive, version-sensitive `unreachable_unchecked` contract against Rust/core 1.80.0. No cited authority conflicts with the exact 1.80.0 official text. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/U-s2.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/U-s2.md new file mode 100644 index 0000000000..2dcb8b38f3 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/U-s2.md @@ -0,0 +1,136 @@ +# Mode U blind score + +I scored each report against the three atoms in `RUBRIC.md`. “No hard error” means the report neither calls the API sound, uses the zero-input UB execution as a behavioral counterexample, asserts guaranteed observations around that UB, nor conflates/omits the independent input-one refutation. + +| Report | U1 | U2 | U3 | Hard error | +|---|---|---|---|---| +| A | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | None | +| D | PASS | **FAIL** | PASS | **YES — zero-path behavioral refutation** | +| E | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | None | +| H | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | None | + +Atom totals: A–C and E–O score 3/3; D scores 2/3 and has one applicable hard error. + +## Report-by-report evidence + +### A + +- **U1 PASS:** It derives that the literal `0` arm is selected, quotes the exact Rust 1.80.0 `unreachable_unchecked` rule, and concludes that the valid safe call `classify(0)` is UB and the API is `UNSOUND`. +- **U2 PASS:** It says the zero-input panic promise is “not established” and that “UB cannot supply a guaranteed panic outcome”; its `CONTRACT-BROKEN` verdict is explicitly based independently on input one. +- **U3 PASS:** It separately derives that `classify(1)` normally returns `2`, states `2 != 1`, and calls this a defined counterexample. +- **Hard error: none.** It makes no defined behavioral observation from the zero-input UB execution and does not conflate it with the input-one refutation. + +### B + +- **U1 PASS:** S-1 follows the valid input `0` through the matching arm to the exact-versioned UB contract and concludes `UNSOUND`. +- **U2 PASS:** B-1 says the zero path supplies no defined panic and that the promised panic is “not established”; the report does not use that path as its contract-breaking witness. +- **U3 PASS:** B-2 independently follows input `1` to a normal result of `2 != 1` and labels it `CONTRACT-BROKEN`. +- **Hard error: none.** The aggregate behavioral verdict is grounded in B-2, while B-1 remains an absence of proof/guarantee. + +### C + +- **U1 PASS:** S1 identifies the unrestricted safe zero call, reaches `unreachable_unchecked`, applies its Rust 1.80.0 contract, and concludes `UNSOUND`. +- **U2 PASS:** D1 expressly labels the zero panic clause `UNPROVED` and says UB can establish neither a defined panic nor a UB-free behavioral counterexample. +- **U3 PASS:** D2 expressly identifies `classify(1)` as a separate UB-free normal return of `2`, refuting the identity postcondition. +- **Hard error: none.** It cleanly separates the zero soundness witness from the one behavioral witness. + +### D + +- **U1 PASS:** It correctly derives that safe `classify(0)` selects the zero arm, reaches `unreachable_unchecked`, and is UB/`UNSOUND` under the exact 1.80.0 contract. +- **U2 FAIL:** It does not leave the panic clause unproved. It says “Both documented clauses fail,” says zero reaches UB “rather than providing the promised panic,” and labels F-01 at line 8 both `UNSOUND` and `CONTRACT-BROKEN`. +- **U3 PASS:** Independently, it correctly derives that `classify(1)` is defined, returns `2`, and refutes the normal-return clause. +- **Hard error: YES.** The explicit behavioral-failure/`CONTRACT-BROKEN` conclusion from the zero-input UB execution is the rubric’s prohibited use of that execution as a behavioral refutation. It does not miss the independent input-one finding, and it makes no separate claim that a post-UB runtime manifestation is guaranteed. + +### E + +- **U1 PASS:** O-SOUND gives the valid safe zero witness, exact match reasoning, and the exact Rust 1.80.0 UB contract. +- **U2 PASS:** O-PANIC explicitly labels the clause `UNPROVED` and states that the UB execution cannot witness `CONTRACT-BROKEN`. +- **U3 PASS:** O-IDENTITY uses the independent, UB-free `classify(1)` execution returning `2 != 1`. +- **Hard error: none.** All three dispositions are explicitly separated. + +### F + +- **U1 PASS:** F1 identifies the safe zero call, the reached unsafe operation, and the exact 1.80.0 rule that reaching it is UB, yielding `UNSOUND`. +- **U2 PASS:** Although it uses “not upheld,” it immediately gives the controlling proposition: Rust supplies “no guaranteed panic outcome,” and B1 is “subsumed by S1.” Read as a proposition rather than a preferred label, this leaves the panic guarantee unestablished rather than supplying a defined non-panic witness. +- **U3 PASS:** F2 explicitly uses input `1` as an independent defined path returning `2`, and bases `CONTRACT-BROKEN` on it. +- **Hard error: none.** It does not claim an observed result for zero or use zero as the independent behavioral counterexample. + +### G + +- **U1 PASS:** S1/U1 follow safe input zero to the reached call and the exact-versioned UB rule, concluding `UNSOUND`. +- **U2 PASS:** B1 says the zero arm establishes no defined outcome and the panic promise is “not proved”; it classifies the case only as already `UNSOUND`. +- **U3 PASS:** B2 independently derives a defined normal return of `2` for input `1` and `CONTRACT-BROKEN`. +- **Hard error: none.** The report explicitly keeps the two witnesses distinct. + +### H + +- **U1 PASS:** F1 uses the Rust 1.80.0 literal/match rules and `unreachable_unchecked` contract to prove the valid safe zero call reaches UB. +- **U2 PASS:** It says the zero panic promise “is not established” and that UB supplies no defined panic behavior; it does not call that a UB-free contract counterexample. +- **U3 PASS:** F2 independently uses input one, avoids the unsafe arm, and derives the normal result `2 != 1`. +- **Hard error: none.** Its behavioral `CONTRACT-BROKEN` verdict is expressly tied to input one. + +### I + +- **U1 PASS:** It partitions the input domain, follows zero into `unreachable_unchecked`, quotes the exact Rust 1.80.0 UB contract, and concludes `UNSOUND`. +- **U2 PASS:** It says the separate zero panic promise is “not established” because that path is UB rather than a defined panic. +- **U3 PASS:** It independently identifies the input-one arm as a defined normal return of `2 != input`. +- **Hard error: none.** No defined zero-input behavioral observation is asserted, and the report does not conflate the witnesses. + +### J + +- **U1 PASS:** It derives that valid safe input zero selects the unsafe arm and violates the exact 1.80.0 callee contract, producing UB. +- **U2 PASS:** It says zero “does not establish” the promised Rust-defined panic and that after UB no behavior is guaranteed; it treats this as the soundness finding, not the contract-breaking witness. +- **U3 PASS:** It uses the separate defined call `classify(1)` returning `2` as the postcondition counterexample. +- **Hard error: none.** Its table and prose keep “No guaranteed panic” distinct from `CONTRACT-BROKEN` on input one. + +### K + +- **U1 PASS:** Its exhaustive derivation takes the valid zero input to `unreachable_unchecked` and applies the exact-versioned UB axiom, proving `UNSOUND`. +- **U2 PASS:** It expressly labels the zero panic clause `UNPROVED` and says the UB execution cannot serve as the UB-free witness required for `CONTRACT-BROKEN`. +- **U3 PASS:** It expressly identifies `classify(1)` as an independent UB-free normal return of `2 != 1`. +- **Hard error: none.** It states the rubric’s required separation directly. + +### L + +- **U1 PASS:** S1 follows safe input zero to the reached call and quotes the exact Rust 1.80.0 rule that this is UB, yielding `UNSOUND`. +- **U2 PASS:** B1 says the UB rule “supplies UB, not a panic postcondition,” that backend behavior cannot establish the source guarantee, and that “no panic guarantee is established.” That is an equivalent `UNPROVED` treatment, despite the row also carrying the soundness label. +- **U3 PASS:** B2 separately uses input one, states that it executes no unsafe operation, and derives a normal result of `2 != 1` and `CONTRACT-BROKEN`. +- **Hard error: none.** It does not turn the zero path into the behavioral refutation. + +### M + +- **U1 PASS:** S1/U1 use exact-versioned literal/match and `unreachable_unchecked` premises to show that valid safe input zero reaches UB and refutes soundness. +- **U2 PASS:** The controlling claim in the verdict is that the zero panic promise “is not established as defined Rust behavior.” The table’s “not satisfied as a defined source behavior” is read in that stated epistemic sense: there is no defined panic path/guarantee, not an asserted UB-free non-panic outcome. +- **U3 PASS:** B2 is explicitly “refuted without UB” by the independent normal return `classify(1) == 2`. +- **Hard error: none.** In full context, `CONTRACT-BROKEN` is grounded only in input one, and no zero-input runtime observation is claimed. + +### N + +- **U1 PASS:** S1 follows the safe zero call through the exact match rules to the exact Rust 1.80.0 UB contract and concludes `UNSOUND`. +- **U2 PASS:** B1 expressly labels the clause `UNPROVED` and says the UB execution supplies neither a panic nor a UB-free `CONTRACT-BROKEN` witness. +- **U3 PASS:** B2 expressly uses the independent UB-free input-one execution returning normally with `2 != 1`. +- **Hard error: none.** The report directly observes the whole-execution restriction. + +### O + +- **U1 PASS:** S1 uses valid zero, exact-versioned pattern/match rules, and the exact 1.80.0 unsafe-function contract to derive UB and `UNSOUND`. +- **U2 PASS:** The verdict and B2 expressly label the zero panic promise `UNPROVED` and say the UB execution cannot prove a UB-free behavioral refutation. +- **U3 PASS:** B1 independently derives the UB-free input-one normal return of `2 != 1` and `CONTRACT-BROKEN`. +- **Hard error: none.** The witness separation is explicit despite the report’s local B1/B2 identifier ordering. + +## Ambiguity + +No rubric ambiguity changes an atom or hard-error decision. Two reports use potentially sharp shorthand: F says the zero promise is “not upheld,” and M says it is “not satisfied as a defined source behavior.” Their surrounding text explicitly says the panic is not established/guaranteed and grounds `CONTRACT-BROKEN` independently in input one. Under `SCORER.md`’s instruction to score propositions rather than preferred vocabulary, both are equivalent to `UNPROVED`. D is materially different because it expressly says the zero clause *fails* and assigns `CONTRACT-BROKEN` to the line-8 UB finding. + +All material authority used for the UB finding is version-matched: every report invokes the Rust/core 1.80.0 `unreachable_unchecked` contract. I treated the reports’ explicit evaluation of the literal arms and direct return expressions as source derivation, not as an unverified version-backprojection; none relies on later documentation, project policy, tests, CI, or implementation behavior. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/V-s1.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/V-s1.md new file mode 100644 index 0000000000..1df134b80b --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/V-s1.md @@ -0,0 +1,240 @@ +# Mode V blind score + +The exact versioned authorities confirm the rubric's split: `[u8; 0]` has size +zero; Rust 1.79.0's `add` contract unconditionally requires the start and result +to be in or one-past the same allocated object; Rust 1.80.0 guards that +allocation condition on a nonzero byte offset and expressly makes a zero offset +always well-defined. Each report is scored independently below. + +## Atom and hard-error table + +| Report | V1 | V2 | V3 | V4 | Hard error | +|---|---|---|---|---|---| +| A | PASS | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | PASS | None | +| H | PASS | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | PASS | None | + +## Report-by-report evidence + +### A + +- **V1 PASS:** `O1` cites both versioned Reference array-layout pages, derives + `size_of::<[u8; 0]>() == 0`, and computes `1 * 0 = 0` bytes. +- **V2 PASS:** `O2-79` cites the 1.79 `add`, `null`, and pointer-safety text, + explains that null fails the unconditional allocated-object condition, and + gives `let _ = advance_marker();` as the safe-call UB witness. +- **V3 PASS:** `O2-80` cites the 1.80 zero-offset sentence, discharges the + arithmetic clauses, and notes that the raw pointer is not dereferenced. +- **V4 PASS:** The verdict table says 1.79 `UNSOUND`, 1.80 `PROVED`, and the + union `UNSOUND`; the TCB section expressly rejects cross-version premises. +- **Hard error: none:** Both regional verdicts are proved, and both the + zero-size derivation and null safe-call witness are present. + +### B + +- **V1 PASS:** The boundary section cites the two versioned array-layout rules + and derives a zero byte offset for `add(1)`. +- **V2 PASS:** The ledger marks 1.79 `O2` failed; the derivation cites exact + 1.79 `null`, pointer-safety, and `add` text and identifies an ordinary safe + invocation as the unconditional UB witness. +- **V3 PASS:** The report uses only the 1.80 `add` contract for its express + zero-offset exception and observes that returning the raw pointer does not + dereference it. +- **V4 PASS:** It reports 1.79 `UNSOUND`, 1.80 `PROVED`, combined `UNSOUND`, + and explicitly says the 1.80 wording is not applied backward. +- **Hard error: none:** No uniform unsupported verdict, wrong region, backward + projection, or missing required derivation/witness occurs. + +### C + +- **V1 PASS:** `O-ADD` cites version-matched `size_of` pages and derives + `size_of::<[u8; 0]>() = 0` and offset `1 * 0 = 0`. +- **V2 PASS:** The 1.79 subsection cites the exact `add` and pointer-safety + pages, states that null is neither in nor one-past an allocation, and says + every safe call reaches UB. +- **V3 PASS:** The 1.80 subsection cites “always well-defined” for zero offset, + checks the arithmetic, and notes that no dereference occurs. +- **V4 PASS:** The opening table partitions both releases and gives the union + `UNSOUND`; the TCB is separately versioned. +- **Hard error: none:** The complete regional proof and counterexample include + all hard-error-sensitive propositions. + +### D + +- **V1 PASS:** The configuration/ledger derives + `1 * size_of::<[u8; 0]>() = 0` from exact 1.79/1.80 `size_of` authorities. +- **V2 PASS:** `O3` is marked violated for 1.79; the prose cites its + unconditional same-allocation `add` clause and identifies any ordinary call + to this argument-free safe function as the UB witness. +- **V3 PASS:** The report cites 1.80's express zero-offset exception, checks + `isize`/`usize`, and observes there is no dereference or reference creation. +- **V4 PASS:** The opening verdicts correctly give 1.79 `UNSOUND`, 1.80 + `PROVED`, and combined `UNSOUND` without reusing 1.80 text for 1.79. +- **Hard error: none:** Every regional and combined verdict is supported, with + the null witness and ZST arithmetic explicit. + +### E + +- **V1 PASS:** The inventory cites exact versioned `size_of` pages and computes + the byte offset as `1 * 0 = 0`. +- **V2 PASS:** The 1.79 derivation cites `add`, `null`, and contemporaneous + pointer-safety text; it explains the failed allocation conjunct and calls + every ordinary invocation a valid safe-use counterexample. +- **V3 PASS:** The 1.80 derivation cites the changed clause and “always + well-defined” sentence, checks arithmetic, and excludes later dereference. +- **V4 PASS:** Its table gives the two correct regional verdicts and combined + `UNSOUND`; its TCB says no compatibility inference crosses versions. +- **Hard error: none:** None of the listed hard-error conditions applies. + +### F + +- **V1 PASS:** The common derivation cites exact 1.79/1.80 `size_of` contracts, + establishes the ZST, and computes zero bytes. +- **V2 PASS:** The 1.79 section cites the unconditional allocation wording and + null-validity text, then says every safe call reaches the violating `add`. +- **V3 PASS:** The 1.80 section cites the zero-offset exception and raw-pointer + nullability and states that any dereference would be a separate unsafe act. +- **V4 PASS:** The verdict table partitions the releases and reports their + union `UNSOUND`; the TCB is exact-version scoped. +- **Hard error: none:** The report proves rather than merely asserts all three + verdicts and includes both mandatory witness components. + +### G + +- **V1 PASS:** Common local fact 2 uses exact versioned Reference array-layout + links; fact 3 computes `1 * 0 = 0`. +- **V2 PASS:** The 1.79 regional derivation cites its exact `add` contract, + states why null satisfies neither allocation alternative, and identifies + every safe call as a concrete UB counterexample. +- **V3 PASS:** The 1.80 derivation cites its express zero-offset rule, checks + both arithmetic constraints, and notes no dereference occurs. +- **V4 PASS:** Despite placeholder region labels, the text unambiguously names + 1.79 `UNSOUND`, 1.80 `PROVED`, and their union `UNSOUND`; each TCB link is + version matched. +- **Hard error: none:** The placeholders do not obscure any material + proposition, and no listed substantive error occurs. + +### H + +- **V1 PASS:** `OB-1` cites both exact `size_of` pages and derives the + zero-sized pointee and zero byte offset. +- **V2 PASS:** The 1.79 section cites exact `add` and pointer-module text, + explains why null fails the unconditional allocation condition, and uses a + plain safe invocation as the UB witness. +- **V3 PASS:** The 1.80 section cites the changed contract, checks zero's + representability/address arithmetic, and notes no reference or access. +- **V4 PASS:** The verdict table has both correct regions and combined + `UNSOUND`; the report expressly says the 1.80 text is not projected backward. +- **Hard error: none:** All hard-error-sensitive facts are present and correct. + +### I + +- **V1 PASS:** The ledger cites exact versioned Reference array layout and + computes `size_of::<[u8; 0]>() = 0` and a zero byte offset. +- **V2 PASS:** The 1.79 subsection cites that version's `add`, derives the + failed allocation clause from the null start, and gives an unconditional + ordinary safe call as witness. +- **V3 PASS:** The 1.80 subsection cites the express exception, checks the + remaining clauses, and states that no dereference/reference is formed. +- **V4 PASS:** The report gives separate correct verdicts and combined + `UNSOUND`; its TCB admits no cross-version compatibility premise. +- **Hard error: none:** Its extra edition observation does not alter or weaken + the proved requested partition; no rubric hard error applies. + +### J + +- **V1 PASS:** `O-1` cites exact 1.79/1.80 `size_of` pages and derives + `1 * 0 = 0` independently of target/profile. +- **V2 PASS:** `O-2/1.79` cites `null`, pointer safety, and `add`; it explains + the allocation failure and supplies `let _ = advance_marker();` as witness. +- **V3 PASS:** `O-2/1.80` cites the express zero-offset sentence, checks + arithmetic, and notes that returning the pointer is no further unsafe act. +- **V4 PASS:** The table correctly partitions and combines the regions, and + the TCB says no later documentation was carried backward. +- **Hard error: none:** All required propositions and the witness are proved. + +### K + +- **V1 PASS:** `O-SIZE` cites exact versioned official `std::mem::size_of` + pages and computes the zero byte offset. +- **V2 PASS:** `O-179` cites exact 1.79 official `std` reexports for `null`, + pointer safety, and `add`, then identifies every safe call as UB at line 4. +- **V3 PASS:** `O-180` cites the exact 1.80 `std` pointer contract, checks the + arithmetic clauses, and notes there is no access/dereference. +- **V4 PASS:** The table reports the two correct regions and combined + `UNSOUND`; the TCB confines each contract to its exact release. +- **Hard error: none:** Using official `std` documentation rather than the + equivalent `core` pages is permitted and introduces no material gap. + +### L + +- **V1 PASS:** The derivation cites exact release-specific `size_of` pages and + computes `1 * size_of::<[u8; 0]>() = 0`. +- **V2 PASS:** The 1.79 subsection cites exact `add`, `null`, and pointer-safety + text, marks the allocation conjunct false, and identifies all safe calls as + the counterexample. +- **V3 PASS:** The 1.80 subsection cites the explicit zero exception, checks + `isize`/`usize`, and observes no access/reference is created. +- **V4 PASS:** Its table gives 1.79 `UNSOUND`, 1.80 `PROVED`, and combined + `UNSOUND`; the TCB is version matched. +- **Hard error: none:** No uniform-verdict, regional, projection, witness, or + size-derivation error appears. + +### M + +- **V1 PASS:** Common facts 1 and 3 cite both exact versioned `size_of` pages + and derive the zero-byte offset. +- **V2 PASS:** The 1.79 section cites exact `add` plus the exact 1.79 Reference + dangling-pointer rule (including its zero-size/nonzero-literal alternatives), + correctly excludes address-zero `null`, and says every call reaches UB. +- **V3 PASS:** The 1.80 section cites “always well-defined,” discharges both + arithmetic clauses, and only returns the pointer. +- **V4 PASS:** The opening verdicts correctly partition and combine the set; + the TCB expressly forbids compatibility inference across releases. +- **Hard error: none:** The regional proofs include the complete ZST + derivation and null safe-call counterexample. + +### N + +- **V1 PASS:** `O1` cites exact versioned Reference array layout and computes + the byte offset `1 * 0 = 0`. +- **V2 PASS:** `O2` cites exact 1.79 `null` and `add`, explains that null is + neither in-bounds nor one-past an allocation, and calls invocation of the + safe API a valid counterexample. +- **V3 PASS:** `O3` cites exact 1.80 `null` and `add`, applies the explicit zero + exception, checks arithmetic, and notes the lack of dereference. +- **V4 PASS:** Its verdict table correctly gives both regions and combined + `UNSOUND`; the TCB explicitly says 1.80 is not projected backward. +- **Hard error: none:** Every enumerated hard-error prerequisite is satisfied. + +### O + +- **V1 PASS:** The coverage section cites exact versioned Reference array + layout and derives the zero byte offset. +- **V2 PASS:** `O-79` cites exact 1.79 `null`, pointer safety, and `add`, states + why null fails the allocation conjunct, and identifies any safe call as UB. +- **V3 PASS:** `O-80` cites the exact 1.80 exception, checks all arithmetic, + and notes that the returned raw pointer is not dereferenced. +- **V4 PASS:** The opening table correctly partitions both releases and gives + the required union `UNSOUND`; the TCB contains no compatibility premise. +- **Hard error: none:** No wrong or unproved verdict, backward projection, or + missing witness/derivation is present. + +## Genuine rubric or authority ambiguities + +None. The exact Rust 1.79.0 and 1.80.0 official documentation resolves the +only version-sensitive premise in the same way as the rubric, and all reports +provide enough version-matched authority and explicit reasoning to score every +atom without inference of an unstated material premise. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/V-s2.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/V-s2.md new file mode 100644 index 0000000000..47a13cb54e --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/blind-scores/raw/V-s2.md @@ -0,0 +1,134 @@ +# Mode V blind score + +`PASS` means that the report establishes every material proposition in the atom with version-matched Rust authority or explicit equivalent reasoning. `None` in the hard-error column means none of the four listed hard errors applies. + +| Report | V1 | V2 | V3 | V4 | Hard error | +|---|---|---|---|---|---| +| A | PASS | PASS | PASS | PASS | None | +| B | PASS | PASS | PASS | PASS | None | +| C | PASS | PASS | PASS | PASS | None | +| D | PASS | PASS | PASS | PASS | None | +| E | PASS | PASS | PASS | PASS | None | +| F | PASS | PASS | PASS | PASS | None | +| G | PASS | PASS | PASS | PASS | None | +| H | PASS | PASS | PASS | PASS | None | +| I | PASS | PASS | PASS | PASS | None | +| J | PASS | PASS | PASS | PASS | None | +| K | PASS | PASS | PASS | PASS | None | +| L | PASS | PASS | PASS | PASS | None | +| M | PASS | PASS | PASS | PASS | None | +| N | PASS | PASS | PASS | PASS | None | +| O | PASS | PASS | PASS | PASS | None | + +## Report-by-report evidence + +### A + +- **V1:** `O1` cites both versioned Reference array-layout rules and derives `size_of::<[u8; 0]>() = 0`, hence `add(1)` has byte offset `1 * 0 = 0`. +- **V2:** `O2-79` cites the 1.79 `add`, `null`, and pointer-safety text, explains that null satisfies no allocated-object bound, and supplies the safe call `let _ = advance_marker();`; it concludes `UNSOUND`. +- **V3:** `O2-80` cites the 1.80 clause that a zero offset is always well-defined and notes that the raw result is not dereferenced; it concludes `PROVED`. +- **V4 / hard error:** The verdict table partitions 1.79 and 1.80, reports the union `UNSOUND`, and the TCB says no cross-version premise is used. Both regional verdicts, the null safe-call witness, and the zero-size derivation are present, so no hard error applies. + +### B + +- **V1:** The boundary section cites the 1.79/1.80 array-layout rules and derives the zero byte offset. +- **V2:** The ledger marks 1.79 `O2` failed; the text cites version-matched `null`, pointer-safety, and `add` documentation, then gives `let _ = advance_marker();` as the safe UB witness and says `UNSOUND`. +- **V3:** It quotes the 1.80 zero-offset exception, discharges the arithmetic clauses, and observes that returning the pointer is not a dereference; the region is `PROVED`. +- **V4 / hard error:** It reports the two regions separately and the combined set `UNSOUND`, explicitly refusing to apply 1.80 wording backward. No listed hard error applies. + +### C + +- **V1:** `O-ADD` cites versioned `size_of` documentation and computes `1 * 0 = 0` bytes. +- **V2:** The 1.79 derivation cites `add`, `null`, and pointer-safety text, identifies the unconditional safe invocation as a UB counterexample, and reports `UNSOUND`. +- **V3:** The 1.80 derivation quotes the zero-offset rule, checks the arithmetic, and notes that no dereference occurs; it reports `PROVED`. +- **V4 / hard error:** Its opening table partitions both releases and gives the combined result `UNSOUND`; its versioned TCB uses no compatibility premise. The required witness and size derivation are explicit, so no hard error applies. + +### D + +- **V1:** The TCB and ledger cite exact-version `size_of` pages and derive `1 * size_of::<[u8; 0]>() = 0`. +- **V2:** `O3` is marked violated under 1.79; the report cites the versioned `null` and `add` contracts, explicitly reasons that null is not in or one-past an allocation, and identifies an ordinary safe invocation as the UB witness. Verdict: `UNSOUND`. +- **V3:** It quotes the 1.80 zero-offset exception, discharges `O1`/`O2`, and states there is no dereference or later unsafe consumer. Verdict: `PROVED`. +- **V4 / hard error:** It gives separate regional verdicts and says the combined set is `UNSOUND` because 1.80 cannot repair the 1.79 counterexample. No listed hard error applies. + +### E + +- **V1:** The boundary section cites both versioned `size_of` pages and derives the zero-sized pointee and zero byte offset. +- **V2:** The 1.79 section cites `add`, `null`, and contemporaneous pointer-safety text; it explains the failed allocation conjunct and identifies every ordinary safe call as the witness. Verdict: `UNSOUND`. +- **V3:** The 1.80 section quotes “always well-defined” for zero offset, checks arithmetic, and notes that returning the raw pointer adds no obligation. Verdict: `PROVED`. +- **V4 / hard error:** Its table exhaustively partitions the releases and reports the union `UNSOUND`; the TCB forbids cross-version inference. Both required derivations are present, so no hard error applies. + +### F + +- **V1:** It cites both releases' `size_of` contracts and computes the offset as `1 * 0 = 0`. +- **V2:** It cites the 1.79 `add`, `null`, and pointer-module rules, explains the false allocated-object conjunct, and says every safe call is a counterexample. Verdict: `UNSOUND`. +- **V3:** It cites the revised 1.80 `add` wording, explicitly permits the null base for zero offset, and notes that a later dereference would be a separate unsafe act. Verdict: `PROVED`. +- **V4 / hard error:** The table reports both regional results and combined `UNSOUND`; versioned TCB entries are kept separate. No hard error applies. + +### G + +- **V1:** Common facts 2–3 derive the zero-sized array and zero byte offset, with exact 1.79/1.80 Reference links in `AXIOM-LAYOUT-179/180`. +- **V2:** The report cites the exact 1.79 `null` and `add` contracts, explicitly states that null is neither in nor one-past an allocation, and names any call to the safe argument-free API as the UB counterexample. Verdict: `UNSOUND`. +- **V3:** It quotes the exact 1.80 zero-offset exception, checks the arithmetic clauses, and notes the absence of a dereference. Verdict: `PROVED`. +- **V4 / hard error:** Despite placeholder region labels, the bullets unambiguously name Rust 1.79 and 1.80, give their separate verdicts, and state that the combined set is `UNSOUND`; the TCB rejects backward compatibility premises. No hard error applies. + +### H + +- **V1:** `OB-1` cites both versioned `size_of` pages and derives `1 * 0 = 0`. +- **V2:** The 1.79 section cites `add`, `null`, and pointer-module validity text, explains why zero does not waive the allocation clause, and gives a direct safe invocation as the witness. Verdict: `UNSOUND`. +- **V3:** The 1.80 section cites the changed clause, checks `isize`/`usize`, and observes no access or reference creation. Verdict: `PROVED`. +- **V4 / hard error:** Its table partitions the versions and gives combined `UNSOUND`; it explicitly says the 1.80 text is not projected backward. The witness and size derivation are present, so no hard error applies. + +### I + +- **V1:** The obligation ledger cites both versioned array-layout rules and computes a zero byte offset. +- **V2:** It cites exact 1.79 `null` and `add` pages, explicitly reasons that null designates no allocation, and identifies an ordinary safe call as the unconditional UB witness. Verdict: `UNSOUND`. +- **V3:** It quotes the 1.80 zero-offset exception, discharges the arithmetic clauses, and states that there is no dereference, reference creation, or unsafe consumer. Verdict: `PROVED`. +- **V4 / hard error:** The opening verdicts partition the releases and make the combined set `UNSOUND`; the TCB excludes cross-version compatibility. No listed hard error applies. + +### J + +- **V1:** `O-1` cites version-matched `size_of` documentation and derives `size_of::<[u8; 0]>() = 0` and byte offset zero. +- **V2:** `O-2/1.79` cites `null`, pointer-safety, and `add` documentation for 1.79, then gives `let _ = advance_marker();` as the safe UB witness. Verdict: `UNSOUND`. +- **V3:** `O-2/1.80` quotes the changed allocation clause, checks arithmetic, and notes that the result is returned without access. Verdict: `PROVED`. +- **V4 / hard error:** The verdict table reports both regions and combined `UNSOUND`; the TCB says no later documentation was carried backward. Both mandated derivations are explicit, so no hard error applies. + +### K + +- **V1:** `O-SIZE` cites exact-version standard-library `size_of` pages and derives the zero byte offset. +- **V2:** `O-179` cites the 1.79 `null`, pointer-safety, and primitive-pointer `add` pages, then states that every safe call executes UB at line 4. Verdict: `UNSOUND`. +- **V3:** `O-180` quotes the 1.80 zero-offset rule, discharges the arithmetic clauses, and notes no access or dereference. Verdict: `PROVED`. +- **V4 / hard error:** The table gives separate version rows and combined `UNSOUND`; all TCB consumers are restricted to matching versions. The required witness and derivation are present, so no hard error applies. + +### L + +- **V1:** The derivation cites both versioned `size_of` pages and computes `1 * 0 = 0`. +- **V2:** Its 1.79 section cites `null`, pointer-safety, and `add`, explains conjunction failure, and identifies every safe invocation as the concrete counterexample. Verdict: `UNSOUND`. +- **V3:** Its 1.80 section quotes the zero-offset exception, checks the other clauses, and notes no access/reference creation. Verdict: `PROVED`. +- **V4 / hard error:** The opening table partitions both toolchains and reports combined `UNSOUND`; its TCB is exact-version. No hard error applies. + +### M + +- **V1:** Common fact 1 cites both versioned `size_of` pages; common fact 3 derives the byte offset as zero. +- **V2:** The 1.79 section cites `add`, `null`, and the exact-version Reference dangling-pointer rule, explains why address-zero null meets neither allowed zero-size case, and says every safe call reaches UB. Verdict: `UNSOUND`. +- **V3:** The 1.80 section quotes the zero-offset exception, checks arithmetic, and explains that the pointer is merely returned. Verdict: `PROVED`. +- **V4 / hard error:** The opening bullets partition both versions and report their union `UNSOUND`; the TCB expressly forbids compatibility inference. Both required derivations are included, so no hard error applies. + +### N + +- **V1:** `O1` cites both exact-version Reference array-layout pages and derives `1 * 0 = 0` bytes. +- **V2:** `O2` cites exact 1.79 `null` and `add` contracts, explicitly says null is not in or one-past an allocated object, and calls invocation of the safe API a valid counterexample. Verdict: `UNSOUND`. +- **V3:** `O3` cites and quotes the 1.80 zero-offset rule, checks the arithmetic clauses, and notes that no dereference occurs. Verdict: `PROVED`. +- **V4 / hard error:** Its table gives the two regional verdicts and combined `UNSOUND`, and its TCB says the 1.80 text is not projected backward. The witness and zero-size derivation are present, so no hard error applies. + +### O + +- **V1:** The configuration section cites both versioned Reference array-layout rules and derives the zero byte offset. +- **V2:** `O-79` cites exact 1.79 `null`, pointer-safety, and `add` text and states that every ordinary call is the safe UB witness. Verdict: `UNSOUND`. +- **V3:** `O-80` quotes the 1.80 zero-offset exception, checks `isize`/`usize`, and notes the lack of dereference. Verdict: `PROVED`. +- **V4 / hard error:** Its verdict table partitions both versions and reports their union `UNSOUND`; its TCB disclaims compatibility premises. Both required derivations are explicit, so no hard error applies. + +## Rubric and authority ambiguities + +The controlling official texts are not ambiguous for this source: [Rust 1.79.0 `pointer::add`](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add) states the allocation-bound requirement without a zero-offset exception, while [Rust 1.80.0 `pointer::add`](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add) expressly says a zero computed offset is always well-defined. The array-size rule is explicit in both the [1.79.0](https://doc.rust-lang.org/1.79.0/reference/type-layout.html#array-layout) and [1.80.0](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#array-layout) References. + +There is one evidentiary-granularity ambiguity in `SCORER.md`: it does not say whether, for V2, a report must cite a separate pointer-validity/dangling-pointer page after citing the exact-version `ptr::null` page and exact-version `pointer::add` contract. D, G, I, and N take the latter route and explicitly make the semantic bridge that a null pointer is not in or one-past an allocation; the other reports generally add the pointer-validity or dangling-pointer citation. I score D, G, I, and N `PASS` because each material Rust operation is tied to exact 1.79 documentation and the bridge is expressly reasoned rather than silently inferred. Under a stricter rule requiring a distinct authority citation for that bridge, only V2 for D, G, I, and N would change to `FAIL`; their hard-error cells would remain `None` because each still states the null safe-call witness and the correct regional verdict. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/collection-ledgers/shard-1.tsv b/evals/unsafe-rust/runs/2026-07-31-v2-forward/collection-ledgers/shard-1.tsv new file mode 100644 index 0000000000..48bcd733a1 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/collection-ledgers/shard-1.tsv @@ -0,0 +1,58 @@ +run_id schedule_cell agent_identity attempt_count status report_sha256 word_count reminders interruptions deviations +r009 U v1 2 /root/r009_attempt1 1 report_preserved ea53253b4a33049eea65c8d34deae4702561166e137ce5ec4708c70e1dcf2160 545 0 none none +r017 T core 2 /root/r017_attempt1 1 report_preserved 0afe010b8798d44a794fe8ffcb444e8d0a0ccb59409c7a673e33d2d3d3a810c8 606 1 none none +r013 T v2 2 /root/r013_attempt1 1 report_preserved c14e758783462060fe9b9c194166e123f15ba3f89f0196d81af05b32e758997c 688 1 none none +r029 U v1 4 /root/r029_attempt1 1 report_preserved da0bef41cc49f09a92f46a6f232223ba5be564bbeb704d13bb454b6ca12b1a91 608 0 none none +r025 T v1 3 /root/r025_attempt1 1 report_preserved 0b0141d0950baf7dc99d4369b9695b01134186f3520e2d98485608b667eb7543 564 0 none none +r021 H v2 5 /root/r021_attempt1 1 report_preserved f5d3a4ee09ae0b91dbc9b2ee20f4ccfa536357486be1a435bb5dc8a13bd71634 753 1 none none +r033 V v2 2 /root/r033_attempt1 1 report_preserved 8b0bf0c4068d0ee2ffde5955fbe34d60965b2b1a43226f6edb108d6fbdf03e51 587 1 none none +r041 U v1 1 /root/r041_attempt1 1 report_preserved 9eef421c24030f5384097f70ff7a362c1727b4e01b9a6b1433c9b26c60287f14 596 0 none none +r037 I core 3 /root/r037_attempt1 1 report_preserved 4dea235e86f070d05f79efb1003bbdb416bedd1b183862acebcd509000e9b186 611 1 none none +r045 I core 4 /root/r045_attempt1 1 report_preserved 0e4e310addef09fccbb8c55ff4dfedd4483a6c1a49ba599375205c04a1d9c622 750 0 none none +r049 H v2 4 /root/r049_attempt1 1 report_preserved 8cd6ea585a737f52a7fd9613e0a82c9a108991b4f7994627f47cb557d0bbaccc 636 1 none none +r053 A v1 5 /root/r053_attempt1 1 report_preserved d8bab30358d420dceb254d92fa9c8954980610d6021a6730cf82932b85d062f6 703 1 none none +r057 D v1 2 /root/r057_attempt1 1 report_preserved 507a174baf8b6091bf71dea35c35b78085a4944b8fe84269025a7622ee0178ff 667 1 none none +r065 N v1 5 /root/r065_attempt1 1 report_preserved 1c8b6a58ba68c9d529f04cf2316c8b7e124bf62876a66137b523f9572c637570 761 1 none none +r061 A v2 1 /root/r061_attempt1 1 report_preserved 0078b09b962b7cb581aafa3a88b1ef2ab6d4c1267236932cffc3080c14002fb6 816 1 none none +r069 I v1 2 /root/r069_attempt1 1 report_preserved 61335ee60b72053c6e84b7336e60c0c29806e11cf88a91a957ecb1916470e0ea 795 1 none none +r073 D core 2 /root/r073_attempt1 1 report_preserved 296a6cea4bcf93774150d6a723ce4444881d751256418887d5192ab24c2e2c95 712 1 none none +r077 H v2 3 /root/r077_attempt1 1 report_preserved bcb92212356566f1af03f0a05e21affa80d5f8b6b1e75684f8ed0a8dd31b1b15 718 1 none none +r081 P core 3 /root/r081_attempt1 1 report_preserved 20532f20a95e1fa2aed483b65dde4dec149c0a86aff12a3b3209bff31ba6f333 814 1 none none +r085 A v2 5 /root/r085_attempt1 1 report_preserved ed0629438e582a4f351a37e36ab873ae22d447cd07ff234958bb4f85437b985b 761 1 none none +r089 C v2 5 /root/r089_attempt1 1 report_preserved 2f92001b01c7da9b9663ea6fc384143b53ccb202c62a6294aebcd9b254f054e3 632 1 none none +r093 D core 5 /root/r093_attempt1 1 report_preserved b323721677d8d34b062d6b1ecf75937b909d4e7f5a4abd36960b585b898361f7 675 1 none none +r097 V core 5 /root/r097_attempt1 1 report_preserved ff25b3bde39b04665282076673c2d99a4ca6f48c3399c8f27efb4c879394618d 652 1 none none +r101 V core 1 /root/r101_attempt1 1 report_preserved 1083dc4271efd972b60f67e6b14ea965227fbddddfb1c2438cdbcac9aa8c7b7b 784 1 none none +r113 N v2 3 none 0 dispatch_deferred - - 0 none two spawn calls rejected by agent thread limit; no agent created and no output touched +r105 C v2 1 /root/r105_attempt1 1 report_preserved 7f1d0e341c39a7d58717e3f3cc2d22add3c385388abca223d7f90d4005188a2d 646 1 none none +r117 P v1 4 none 0 dispatch_deferred - - 0 none one spawn call rejected by agent thread limit; no agent created and no output touched +r109 C v1 4 /root/r109_attempt1 1 report_preserved a1c698a24b8995f90fc7279c2ac5a8986e2032be1dba94551dc332087a5317e0 718 0 none none +r113 N v2 3 /root/r113_attempt1 1 report_preserved aa9cbf6ba448d6314d8667c7d55b9b2d9a4dbffdffb70f2dd32926ca44292678 794 1 none none; see prior dispatch_deferred event +r117 P v1 4 /root/r117_attempt1 1 report_preserved 99b552ac87a6950c55934a6c42b0acc575dfd44ef56ad29340aae782271449ca 976 1 none none; see prior dispatch_deferred event +r121 U core 2 /root/r121_attempt1 1 report_preserved c8523f8efe46fea547b1b949b3a6712ddf689447b3b0f706b4e9ec071447d8e3 608 1 none none +r125 A v1 2 /root/r125_attempt1 1 report_preserved b76eea85a53841c7385906efa8852f513b8467647c95ddce7f8a82489c24d37f 619 1 none none +r129 N v1 3 /root/r129_attempt1 1 report_preserved a7303d5d170341cb37f4b0774618e746086b3b29d6ec95abdf8680719edf43ae 828 1 none none +r133 V v2 5 /root/r133_attempt1 1 report_preserved df49c37470823ef1969457c775714d982094698bd478514bdef0b7ca58d2e5d5 654 1 none none +r137 T core 4 /root/r137_attempt1 1 report_preserved 04529d4b543ed0fbf014c6cc9172bb4d17bddca2f4ab987a8d93abc54808bca5 693 1 none none +r145 I v2 4 none 0 dispatch_deferred - - 0 none one spawn call rejected by agent thread limit; no agent created and no output touched +r141 D v1 1 /root/r141_attempt1 1 report_preserved 05f8c2590ec572bba6eed06d58292a04c5528333d5a0a7ca13557cb7b6033851 714 1 none none +r145 I v2 4 /root/r145_attempt1 1 report_preserved 98ef7788f4fb27ef94217d2ff2609a2fe63b89fb079a8192b621dc46179e0f14 721 1 none none; see prior dispatch_deferred event +r149 D v2 4 /root/r149_attempt1 1 report_preserved 5c3f27d040802eedc262795ec695af6206adc1fcc1aa2b7c4f7b2da4b7b5f845 637 1 none none +r076 not-read (existing report) none 0 rebalance_skipped_existing_report 9f3b30b15a2cd223bb3d29f2bc32e2b07fcef6396dbc47f1db5261e55eae8a49 980 0 none modulo-0 pool; not claimed +r080 not-read (existing report) none 0 rebalance_skipped_existing_report 2526724b8f6e4b6c1ac17e3ecbcbd73b9d1507e28f848ac17d4856eda0f69a03 719 0 none modulo-0 pool; not claimed +r084 not-read (existing report) none 0 rebalance_skipped_existing_report acdeda852771c8ac537223feadde4ffc99a86a3525184418ea55d5ff9a133b51 692 0 none modulo-0 pool; not claimed +r088 not-read (existing report) none 0 rebalance_skipped_existing_report b8bf937070e65165d3bfff905c75ad7c9fb06d1ed927958550f1ad283348fce7 687 0 none modulo-0 pool; not claimed +r092 not-read (existing report) none 0 rebalance_skipped_existing_report 9cf79919a62f4997f1440850afb611941b5f6294f4adf6ce865b28e3619e231b 695 0 none modulo-0 pool; not claimed +r096 not-read (existing report) none 0 rebalance_skipped_existing_report c8cccc99a0d351e1ffc9c1a4326bed215db50dbf88326ed3063588234c0c100d 929 0 none modulo-0 pool; not claimed +r100 not-read (existing report) none 0 rebalance_skipped_existing_report 415899e50b3f0e1e37c5f8e30a1b1f8832938b05bc8ca32237ef72e439b3f223 609 0 none modulo-0 pool; not claimed +r104 not-read (existing report) none 0 rebalance_skipped_existing_report b95a71b87221c128b3cfbd8fb2b8e038b589cd472f7788781e39ec1575395b0b 779 0 none modulo-0 pool; not claimed +r108 not-read (existing report) none 0 rebalance_skipped_existing_report 566c7010d5056758ea0bb180b15c5c763ddc705658b9ad2adcf810eea12528f8 751 0 none modulo-0 pool; not claimed +r112 not-read (existing report) none 0 rebalance_skipped_existing_report f7043de7bd8c44dc4c711ef43a749b62cdbd849bcf139f595d9529f1477069d9 800 0 none modulo-0 pool; not claimed +r116 not-read (existing report) none 0 rebalance_skipped_existing_report a8f0eb9814170360c3a888780ab336551f411736a4d49dee1813dbf026bb2ed2 689 0 none modulo-0 pool; not claimed +r120 not-read (existing report) none 0 rebalance_skipped_existing_report 11249b37dba870ac0ea1e9b26eb2c67bbea5b41ee53997fa8df52a5e5b7e9817 799 0 none modulo-0 pool; not claimed +r124 not-read (existing report) none 0 rebalance_skipped_existing_report 2c4f58b1079ae12613802d74d64b7007b605bc2124a223e94dd3a71541b338e2 670 0 none modulo-0 pool; not claimed +r128 not-read (existing report) none 0 rebalance_skipped_existing_report 501aa254255ce8d90a6f0c5ff387f53261725af547203b119141b65830df1c77 672 0 none modulo-0 pool; not claimed +r132 not-read (existing report) none 0 rebalance_skipped_existing_report 015f35f23a6c6f6fbb91fdba9a362cd5bff32929c78994eb405f32841f45ee5a 710 0 none modulo-0 pool; not claimed +r136 not-read (existing report) none 0 rebalance_skipped_existing_report 0c086ba81aaffd250c133a04e97451b15ec138e30bc5b78ca95fe3ef33bcf181 721 0 none modulo-0 pool; not claimed +r140 not-read (existing report) none 0 rebalance_skipped_existing_report 770680fd022a39cfefd0140d8b71c1e974da29005fd68b4f7fcaf12d40b252a1 700 0 none modulo-0 pool; not claimed +r144 not-read (existing report) none 0 rebalance_skipped_existing_report 62e725bd24db907896cce9b3363cd4f6fd5072e83efdddde5d9be146cdb3513a 726 0 none modulo-0 pool; not claimed diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/collection-ledgers/shard-2.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/collection-ledgers/shard-2.md new file mode 100644 index 0000000000..3d0913f31c --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/collection-ledgers/shard-2.md @@ -0,0 +1,68 @@ +# Shard 2 operational ledger + +- Claim: shard 2, created atomically by this collection thread. +- Worktree: `/usr/local/google/home/joshlf/workspace/zerocopy/unsafe-code-skill` +- Frozen manifest: `/usr/local/google/home/joshlf/workspace/zerocopy/unsafe-code-skill/evals/unsafe-rust/runs/2026-07-31-v2-forward/manifest.md` +- Shared runtime: `/tmp/unsafe-rust-v2-eval.9epWDK` +- Assigned IDs: `r010, r014, r018, r022, r026, r030, r034, r038, r042, r046, r050, r054, r058, r062, r066, r070, r074, r078, r082, r086, r090, r094, r098, r102, r106, r110, r114, r118, r122, r126, r130, r134, r138, r142, r146, r150` + +## Attempts + +- `r010` | schedule cell: `C v1 5` | agent: `/root/r010_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `1b986a98a3d6b54977c3e781936ed7ee4b1d9b69bd4e7340e9f02604bd4c841b` | words: `640` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r018` | schedule cell: `A v1 3` | agent: `/root/r018_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `4e91dba451370bb9b1f76f6e2ba32494adfa26b921dc0f913d668c476527910a` | words: `732` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r014` | schedule cell: `D core 3` | agent: `/root/r014_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `1bc8c2e6389fc0382c350ef22af086fe284732962cb3581413d05277a5d3213e` | words: `668` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r022` | schedule cell: `H v1 4` | agent: `/root/r022_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `4498b708506f55d25cb9bb122a21c96f2a38fee87d87f18178bce0186bb5ccd7` | words: `821` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r026` | schedule cell: `D v2 1` | agent: `/root/r026_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `ee0ab9d8cc4cdf0f3ecfc9b7f62a6ae8555caaa4b18039729c513b3c8ddf4b6a` | words: `719` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r030` | schedule cell: `N core 5` | agent: `/root/r030_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `a87b2b92b55909c00a248ba8ab1546354bf6305a57cc0f523c45161e4d98fa59` | words: `813` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r038` | schedule cell: `N v1 1` | agent: `/root/r038_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `186e40e14f5505b96c1b8bf653b926f750eada672e5f963ec3cff6c12757def3` | words: `763` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r034` | schedule cell: `P core 4` | agent: `/root/r034_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `3c9982bc921d788c36cc5c097785b4a2f6279df859dccd3818f042f2207df483` | words: `904` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r042` | schedule cell: `I v1 5` | agent: `/root/r042_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `9ee93c97a20cc234bbb6a89f893706a4843d50003d7c01d4aaaa28fcd5e92eee` | words: `671` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r046` | schedule cell: `A core 2` | agent: `/root/r046_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `b7336308a44cfe9b9fe349076f9ab61796d53115c54585952bdac9e3a61923df` | words: `585` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r050` | schedule cell: `P v1 5` | agent: `/root/r050_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `3da1ff72add6cbdcc7b6de074a7c5e74eeec9e6077468710c15c4820322d3f69` | words: `847` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r054` | schedule cell: `P v2 2` | agent: `/root/r054_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `26beabb0ab2656a148599e9d5bedeaaa460ab8ec8b5ba2d98921caf26d4c77b8` | words: `895` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r062` | schedule cell: `U core 4` | agent: `/root/r062_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `3467e603ad650e5d9fb7b7a5fb9d3e5791aa2be5f699ca7c37be436ca3468712` | words: `672` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r058` | schedule cell: `D v2 2` | agent: `/root/r058_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `83dda06ab4cbd7138a85c28e2c77d70c239d2eb7e9a2187be6a7ce5a8640b855` | words: `556` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r066` | schedule cell: `V v1 2` | agent: `/root/r066_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `ae55fc2b0fe22ccb8dc992d4b843cef9e29802520dfe87a9ed10cd4f677a8428` | words: `703` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r070` | schedule cell: `A core 3` | agent: `/root/r070_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `7941ae7bf5d276792912e6f77c38397c1f0d5d41904dc1ba3d8954695571b6f5` | words: `701` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r078` | schedule cell: `C v2 4` | agent: `/root/r078_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `2449a4fb4357094f6d8f10a32d199df7290c4a53ffb955a671cd4cb38c969a61` | words: `650` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r074` | schedule cell: `D v1 3` | agent: `/root/r074_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `04cd7bfb2f53f460a29fe2c2cb104ce00a1062a000d473011e3351fcb25db949` | words: `647` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r082` | schedule cell: `U core 3` | agent: `/root/r082_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `1c705d4383e61198e1600d532b0a1ab3c04065591a6319170602fb3ed5a30442` | words: `614` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r090` | schedule cell: `U v2 1` | agent: `/root/r090_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `ee1b88a791c681b2e6d3563e133a1a4d7edeb76b2a9377935490da24a258afc6` | words: `721` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r086` | schedule cell: `C core 5` | agent: `/root/r086_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `fb16a7e955c83434e8d0658aaef67cc56c4c459c6d41cfde74f4b9a49fc4a3a7` | words: `690` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r094` | schedule cell: `V v2 1` | agent: `/root/r094_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `c6f8962a88e64e8b81bb326de92c514f5cf36166e3841b03e9a31ab74fda552d` | words: `624` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r098` | schedule cell: `I v2 1` | agent: `/root/r098_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `4af6bfbe2e402bbb75ef4d68570d2d2a03798153ce328038e3fca2a0935d184e` | words: `699` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r102` | schedule cell: `D core 4` | agent: `/root/r102_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `13a9de04651a7c2b99298b0d01120a90cb1c719e667ff4f4fe6120085686e54e` | words: `689` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r106` | schedule cell: `H v1 3` | agent: `/root/r106_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `f9fbf180b2bc25b0cdbe51dd9a65742b447e09b5c7865956df4908d228f814fe` | words: `829` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r110` | schedule cell: `C v1 1` | agent: `/root/r110_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `a6c9dacddd5c5db1a26d797ed4e9ca85458a866dc79f3b646e2e715a5144560f` | words: `767` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r114` | schedule cell: `V core 3` | agent: `/root/r114_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `a32bc813bcd7f9777035c551edb895f920b7a4ccce148965726f3530b8c0bcc9` | words: `716` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r118` | schedule cell: `A core 4` | agent: `/root/r118_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `d3bae1c83ad2193e3c646b5be8e03165e7f133ed3b0b3900ece8a20b6f557ce3` | words: `635` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r126` | schedule cell: `A v2 3` | agent: `/root/r126_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `a4d116c058ea062a52d8f41a0149d3cdcf537f8e612074d853f5e00d3769f1bb` | words: `648` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r122` | schedule cell: `H core 1` | agent: `/root/r122_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `62b011f118c37155e657aea6bd4bbc82da50b15eebc7fbc048919374ff7d8db4` | words: `758` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r130` | schedule cell: `V v1 5` | agent: `/root/r130_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `9b1fa3f9679112860c1ab39ff949efbad5d7cce515cb13a595d2df5ef610d8d1` | words: `696` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r134` | schedule cell: `I v1 1` | agent: `/root/r134_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `1ec2e9a6851795217b163e1515f80117a7b2c473f975d559664dbdc26ac3e71b` | words: `757` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r138` | schedule cell: `C core 2` | agent: `/root/r138_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `e1db7e1c77b911cac76aa615046241128ab9978385304df5956b84576d3904d4` | words: `714` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r142` | schedule cell: `T core 5` | agent: `/root/r142_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `f13e70e8503066b57d560d62f38acb692806846a4e1831dddbffcb54675c6150` | words: `596` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r150` | schedule cell: `U v1 3` | agent: `/root/r150_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `51e87f7b743b665607987c4b748adf7aec9f2929c06c3b705cef1c8eb594adb3` | words: `472` | reminders: `0` | interruptions: `none` | deviations: `none` +- `r146` | schedule cell: `A core 5` | agent: `/root/r146_attempt1` | attempt: `1` | status: `report preserved` | SHA-256: `031d9ab9e7441005786a609173749c4751fbc6cad95ca50cc33786a08d2f3a38` | words: `715` | reminders: `0` | interruptions: `none` | deviations: `none` + +## Modulo-0 rebalance + +- `r076` | modulo-0 pool | schedule cell: `P v2 1` | status: `skipped; report.md already existed` | claim attempted: `no` | agent launched: `no` +- `r080` | modulo-0 pool | schedule cell: `C core 1` | status: `skipped; report.md already existed` | claim attempted: `no` | agent launched: `no` +- `r084` | modulo-0 pool | schedule cell: `A v1 1` | status: `skipped; report.md already existed` | claim attempted: `no` | agent launched: `no` +- `r088` | modulo-0 pool | schedule cell: `T v2 5` | status: `skipped; report.md already existed` | claim attempted: `no` | agent launched: `no` +- `r092` | modulo-0 pool | schedule cell: `N v2 1` | status: `skipped; report.md already existed` | claim attempted: `no` | agent launched: `no` +- `r096` | modulo-0 pool | schedule cell: `P v2 4` | status: `skipped; report.md already existed` | claim attempted: `no` | agent launched: `no` +- `r100` | modulo-0 pool | schedule cell: `U core 5` | status: `skipped; report.md already existed` | claim attempted: `no` | agent launched: `no` +- `r104` | modulo-0 pool | schedule cell: `I v2 2` | status: `skipped; report.md already existed` | claim attempted: `no` | agent launched: `no` +- `r108` | modulo-0 pool | schedule cell: `N core 3` | status: `skipped; report.md already existed` | claim attempted: `no` | agent launched: `no` +- `r112` | modulo-0 pool | schedule cell: `I core 5` | status: `skipped; report.md already existed` | claim attempted: `no` | agent launched: `no` +- `r116` | modulo-0 pool | schedule cell: `V v2 3` | status: `skipped; report.md already existed` | claim attempted: `no` | agent launched: `no` +- `r120` | modulo-0 pool | schedule cell: `N v2 2` | status: `skipped; report.md already existed` | claim attempted: `no` | agent launched: `no` +- `r124` | modulo-0 pool | schedule cell: `D v2 5` | status: `skipped; report.md already existed` | claim attempted: `no` | agent launched: `no` +- `r128` | modulo-0 pool | schedule cell: `T core 3` | status: `skipped; report.md already existed` | claim attempted: `no` | agent launched: `no` +- `r132` | modulo-0 pool | schedule cell: `H v2 1` | status: `not claimed; atomic mkdir returned File exists` | ownership: `another orchestrator` | agent launched: `no` +- `r136` | modulo-0 pool | schedule cell: `D v1 4` | status: `not claimed; atomic mkdir returned File exists` | ownership: `another orchestrator` | agent launched: `no` +- `r140` | modulo-0 pool | schedule cell: `A v1 4` | status: `not claimed; atomic mkdir returned File exists` | ownership: `another orchestrator` | agent launched: `no` +- `r144` | modulo-0 pool | schedule cell: `V v1 4` | status: `not claimed; atomic mkdir returned File exists` | ownership: `another orchestrator` | agent launched: `no` +- `r148` | modulo-0 pool | schedule cell: `I core 2` | status: `not claimed; atomic mkdir returned File exists` | ownership: `another orchestrator` | agent launched: `no` diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/collection-ledgers/shard-3.jsonl b/evals/unsafe-rust/runs/2026-07-31-v2-forward/collection-ledgers/shard-3.jsonl new file mode 100644 index 0000000000..90731a2e56 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/collection-ledgers/shard-3.jsonl @@ -0,0 +1,154 @@ +{"event":"shard_claimed","shard":3,"run_ids":"r011,r015,...,r147","reminders":0,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r011","schedule_cell":"T v2 1","agent_identity":"/root/r011_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r015","schedule_cell":"V core 2","agent_identity":"/root/r015_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r019","schedule_cell":"A v2 4","agent_identity":"/root/r019_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","run_id":"r011","agent_identity":"/root/r011_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r015","agent_identity":"/root/r015_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r019","agent_identity":"/root/r019_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","run_id":"r015","schedule_cell":"V core 2","agent_identity":"/root/r015_attempt1","attempt_count":1,"report_sha256":"320e881d39cfc4a4f2417a3c35acf69ce7241d9dcf4ee9237ef7d539b3cdafa7","word_count":710,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r023","schedule_cell":"T v1 1","agent_identity":"/root/r023_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r011","schedule_cell":"T v2 1","agent_identity":"/root/r011_attempt1","attempt_count":1,"report_sha256":"8e6ce039909311aca7798dba76441cceba9f6a5158893a8ef4eb8af616d283f8","word_count":597,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r019","schedule_cell":"A v2 4","agent_identity":"/root/r019_attempt1","attempt_count":1,"report_sha256":"b1a53d8bd830f2d351b2d490a26bcb33c3be8784fd81de59d6e8c8a1f541739e","word_count":765,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r027","schedule_cell":"P core 5","agent_identity":"/root/r027_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r031","schedule_cell":"T v1 4","agent_identity":"/root/r031_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","run_id":"r023","agent_identity":"/root/r023_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r027","agent_identity":"/root/r027_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r031","agent_identity":"/root/r031_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","run_id":"r023","schedule_cell":"T v1 1","agent_identity":"/root/r023_attempt1","attempt_count":1,"report_sha256":"e3a8737cb24d58d62bce90070cc4d399668306fdfe135f5d19f17bec01d68252","word_count":717,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r035","schedule_cell":"D core 1","agent_identity":"/root/r035_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r031","schedule_cell":"T v1 4","agent_identity":"/root/r031_attempt1","attempt_count":1,"report_sha256":"aaefc0bf53e7ed1b06a026994653817bfce244c1751b4b96edb3bf51c9ee078b","word_count":511,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r039","schedule_cell":"H v1 1","agent_identity":"/root/r039_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r027","schedule_cell":"P core 5","agent_identity":"/root/r027_attempt1","attempt_count":1,"report_sha256":"73c5820aa0e2531c5e4f76b451fee435b62c45273ae39ec06bda1c191a7ca816","word_count":763,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r043","schedule_cell":"P core 1","agent_identity":"/root/r043_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","run_id":"r035","agent_identity":"/root/r035_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r039","agent_identity":"/root/r039_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r043","agent_identity":"/root/r043_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","run_id":"r039","schedule_cell":"H v1 1","agent_identity":"/root/r039_attempt1","attempt_count":1,"report_sha256":"775268540405ff59d7975477d7847293a1684d1d8c0be6c5ecafe476ba20e502","word_count":781,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r047","schedule_cell":"H core 2","agent_identity":"/root/r047_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r035","schedule_cell":"D core 1","agent_identity":"/root/r035_attempt1","attempt_count":1,"report_sha256":"2ce408918ac63d1559bab69815b62e871a44867285c8bcfaef13d1be451069c0","word_count":623,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r051","schedule_cell":"I v2 5","agent_identity":"/root/r051_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r043","schedule_cell":"P core 1","agent_identity":"/root/r043_attempt1","attempt_count":1,"report_sha256":"0c89ceffa024b7b52b322e3856c7ef8680083ee2f8ed1a149b1a40a1e8e9fcad","word_count":809,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r055","schedule_cell":"H v1 2","agent_identity":"/root/r055_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","run_id":"r047","agent_identity":"/root/r047_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r051","agent_identity":"/root/r051_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r055","agent_identity":"/root/r055_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","run_id":"r047","schedule_cell":"H core 2","agent_identity":"/root/r047_attempt1","attempt_count":1,"report_sha256":"a6be55d724119b37dc7828464e25b3b743ab0f518a0740cda391b972289518f9","word_count":773,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r051","schedule_cell":"I v2 5","agent_identity":"/root/r051_attempt1","attempt_count":1,"report_sha256":"acb951ec81eac86677b4a32be1a5ce3cd4e6943c40ba4cd0da00906f8021eb48","word_count":786,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r059","schedule_cell":"C core 4","agent_identity":"/root/r059_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r063","schedule_cell":"N v1 4","agent_identity":"/root/r063_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r055","schedule_cell":"H v1 2","agent_identity":"/root/r055_attempt1","attempt_count":1,"report_sha256":"150d766286a3800afec015e2d4ab519214b9e6fc41b1c574fd61a019b2cc396d","word_count":794,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r067","schedule_cell":"N v2 5","agent_identity":"/root/r067_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","run_id":"r059","agent_identity":"/root/r059_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r063","agent_identity":"/root/r063_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r067","agent_identity":"/root/r067_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","run_id":"r059","schedule_cell":"C core 4","agent_identity":"/root/r059_attempt1","attempt_count":1,"report_sha256":"f91a62c9543fd09a574032f13e7fa2cdf1764261a336b628041ded80e2f5b4d0","word_count":604,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r071","schedule_cell":"T v1 2","agent_identity":"/root/r071_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r063","schedule_cell":"N v1 4","agent_identity":"/root/r063_attempt1","attempt_count":1,"report_sha256":"fe563d65a57b49108f31786dd05a525ca626fa2517e1dd6c47747c8cb69cdcd8","word_count":730,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r075","schedule_cell":"H v2 2","agent_identity":"/root/r075_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r067","schedule_cell":"N v2 5","agent_identity":"/root/r067_attempt1","attempt_count":1,"report_sha256":"1082e144031088b72958b6de3b4779516d5a62674693f0aa4a5dc139dab6c1f0","word_count":698,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r079","schedule_cell":"H core 3","agent_identity":"/root/r079_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","run_id":"r071","agent_identity":"/root/r071_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","run_id":"r071","schedule_cell":"T v1 2","agent_identity":"/root/r071_attempt1","attempt_count":1,"report_sha256":"b8d2f9d90a4dd777183830e01c88f6d208b70358759e37fda0ae1cd27d3b2661","word_count":594,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r083","schedule_cell":"N core 1","agent_identity":"/root/r083_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","run_id":"r075","agent_identity":"/root/r075_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r079","agent_identity":"/root/r079_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","run_id":"r079","schedule_cell":"H core 3","agent_identity":"/root/r079_attempt1","attempt_count":1,"report_sha256":"57b2b8b9f8a4151b001f3998353415c768f697fb2eae750f048623a5002e54e3","word_count":681,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r087","schedule_cell":"U v2 4","agent_identity":"/root/r087_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","run_id":"r083","agent_identity":"/root/r083_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","run_id":"r075","schedule_cell":"H v2 2","agent_identity":"/root/r075_attempt1","attempt_count":1,"report_sha256":"1f4b3c2762804e157502476796e50bc2a26c325f3439975f9730056bf0d7ac96","word_count":673,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r091","schedule_cell":"P v1 2","agent_identity":"/root/r091_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","run_id":"r087","agent_identity":"/root/r087_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","run_id":"r083","schedule_cell":"N core 1","agent_identity":"/root/r083_attempt1","attempt_count":1,"report_sha256":"7844ddc0acb2ecc0b9a068f2832f8c6188942e8e066458079fa8c1c7d6b0d53b","word_count":689,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r095","schedule_cell":"N v1 2","agent_identity":"/root/r095_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r087","schedule_cell":"U v2 4","agent_identity":"/root/r087_attempt1","attempt_count":1,"report_sha256":"983fb0325eec15a5f90036131b3927ddebf4975f595f62275fa1793524db4e59","word_count":612,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r099","schedule_cell":"V v1 1","agent_identity":"/root/r099_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","run_id":"r091","agent_identity":"/root/r091_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r095","agent_identity":"/root/r095_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r099","agent_identity":"/root/r099_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","run_id":"r091","schedule_cell":"P v1 2","agent_identity":"/root/r091_attempt1","attempt_count":1,"report_sha256":"f83a2cac1ce28263e03ca481948ad1d08d6fff3b7ed482af5d5f28d155a46442","word_count":790,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r103","schedule_cell":"C core 3","agent_identity":"/root/r103_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r095","schedule_cell":"N v1 2","agent_identity":"/root/r095_attempt1","attempt_count":1,"report_sha256":"2d72afedcb1a439fbdb8cff42d3e61bb780a5f1484edeec0d6cf092b7792004c","word_count":754,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r107","schedule_cell":"T v2 3","agent_identity":"/root/r107_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r099","schedule_cell":"V v1 1","agent_identity":"/root/r099_attempt1","attempt_count":1,"report_sha256":"69c113811be2558d6751b0e57f0c56451c955e51c245d30440601a0bf29ed7ef","word_count":687,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r111","schedule_cell":"H core 4","agent_identity":"/root/r111_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","run_id":"r103","agent_identity":"/root/r103_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r107","agent_identity":"/root/r107_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r111","agent_identity":"/root/r111_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","run_id":"r107","schedule_cell":"T v2 3","agent_identity":"/root/r107_attempt1","attempt_count":1,"report_sha256":"61050a77f0aa38182671a74dfe2214e1990c8ff7490a6fc994fa4f208511f87e","word_count":769,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r115","schedule_cell":"T core 1","agent_identity":"/root/r115_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r103","schedule_cell":"C core 3","agent_identity":"/root/r103_attempt1","attempt_count":1,"report_sha256":"6e66fb80e60c78c89cfbd7bfbce8675b95e58c0f007ce3611043de204bf02e83","word_count":628,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r119","schedule_cell":"U v2 5","agent_identity":"/root/r119_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r111","schedule_cell":"H core 4","agent_identity":"/root/r111_attempt1","attempt_count":1,"report_sha256":"20d73ddeb2a5ecedc24b4aa482c31c4a79dd3ee3af24281c4e0f7a7c3d2ee3a3","word_count":673,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r123","schedule_cell":"D v2 3","agent_identity":"/root/r123_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","run_id":"r115","agent_identity":"/root/r115_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r119","agent_identity":"/root/r119_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","run_id":"r119","schedule_cell":"U v2 5","agent_identity":"/root/r119_attempt1","attempt_count":1,"report_sha256":"a7dd042fb2e071021fbec52a07bd128b7a41a9ea6d70a04f814c14073359d754","word_count":553,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r127","schedule_cell":"I core 1","agent_identity":"/root/r127_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r115","schedule_cell":"T core 1","agent_identity":"/root/r115_attempt1","attempt_count":1,"report_sha256":"681e85e8c93b90d361bca15a165310ce52cde06da47a0aa188943d2c13b21266","word_count":691,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r123","schedule_cell":"D v2 3","agent_identity":"/root/r123_attempt1","attempt_count":1,"report_sha256":"c73da1112e8b63d5c8060ff3c59e049941d4385573b32e5eb3dbc4e9ba8c3fbb","word_count":623,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r131","schedule_cell":"I v2 3","agent_identity":"/root/r131_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r135","schedule_cell":"A core 1","agent_identity":"/root/r135_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","run_id":"r127","agent_identity":"/root/r127_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r131","agent_identity":"/root/r131_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r135","agent_identity":"/root/r135_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","run_id":"r135","schedule_cell":"A core 1","agent_identity":"/root/r135_attempt1","attempt_count":1,"report_sha256":"4691d4ef513f184e45f3b40561b549f6f2915605fd14e5d0c5ee071dd7632b75","word_count":655,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r127","schedule_cell":"I core 1","agent_identity":"/root/r127_attempt1","attempt_count":1,"report_sha256":"be0b353b7b102bdc1357b17fda1a1429604410dc6a20dd770797f5905ecc2bc1","word_count":733,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r131","schedule_cell":"I v2 3","agent_identity":"/root/r131_attempt1","attempt_count":1,"report_sha256":"b542caa5eb54fd40884caa6396b269d94cf5a58fe3a637e9bf7698ba5cf1881c","word_count":848,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r139","schedule_cell":"H v1 5","agent_identity":"/root/r139_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r143","schedule_cell":"N core 2","agent_identity":"/root/r143_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"attempt_started","run_id":"r147","schedule_cell":"C v1 2","agent_identity":"/root/r147_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","run_id":"r139","agent_identity":"/root/r139_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r143","agent_identity":"/root/r143_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","run_id":"r147","agent_identity":"/root/r147_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","run_id":"r147","schedule_cell":"C v1 2","agent_identity":"/root/r147_attempt1","attempt_count":1,"report_sha256":"37c25efc7fed05cdb51007225d7d072fa0810844f5b85037d5465c7478b9ca1e","word_count":722,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r139","schedule_cell":"H v1 5","agent_identity":"/root/r139_attempt1","attempt_count":1,"report_sha256":"abcb340e1fb0454a145ca0de7f3a86228d237941ca3a04b1aca48fcfde35e875","word_count":743,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","run_id":"r143","schedule_cell":"N core 2","agent_identity":"/root/r143_attempt1","attempt_count":1,"report_sha256":"cca70e06ffb4b9fd937db77690fffa3f86d1ba2a237a44a252c2da447cc684f5","word_count":801,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"original_shard_complete","shard":3,"assigned_runs":35,"reports_preserved":35,"operational_blockers":0,"collisions":0,"invalid_attempts":0,"deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r076","report_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"rebalance_skip","pool":"modulo-0","run_id":"r080","reason":"report_preexisted","deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r084","report_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r088","report_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","pool":"modulo-0","claim_source":"rebalance","run_id":"r088","schedule_cell":"T v2 5","agent_identity":"/root/rebalance_r088_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r092","report_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","pool":"modulo-0","claim_source":"rebalance","run_id":"r092","schedule_cell":"N v2 1","agent_identity":"/root/rebalance_r092_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r096","report_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r100","report_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","pool":"modulo-0","claim_source":"rebalance","run_id":"r100","schedule_cell":"U core 5","agent_identity":"/root/rebalance_r100_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","pool":"modulo-0","run_id":"r088","agent_identity":"/root/rebalance_r088_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","pool":"modulo-0","run_id":"r092","agent_identity":"/root/rebalance_r092_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","pool":"modulo-0","run_id":"r100","agent_identity":"/root/rebalance_r100_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","pool":"modulo-0","claim_source":"rebalance","run_id":"r088","schedule_cell":"T v2 5","agent_identity":"/root/rebalance_r088_attempt1","attempt_count":1,"report_sha256":"b8bf937070e65165d3bfff905c75ad7c9fb06d1ed927958550f1ad283348fce7","word_count":687,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r104","report_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"report_preserved","pool":"modulo-0","claim_source":"rebalance","run_id":"r100","schedule_cell":"U core 5","agent_identity":"/root/rebalance_r100_attempt1","attempt_count":1,"report_sha256":"415899e50b3f0e1e37c5f8e30a1b1f8832938b05bc8ca32237ef72e439b3f223","word_count":609,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r108","report_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r112","report_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","pool":"modulo-0","claim_source":"rebalance","run_id":"r112","schedule_cell":"I core 5","agent_identity":"/root/rebalance_r112_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","pool":"modulo-0","claim_source":"rebalance","run_id":"r092","schedule_cell":"N v2 1","agent_identity":"/root/rebalance_r092_attempt1","attempt_count":1,"report_sha256":"9cf79919a62f4997f1440850afb611941b5f6294f4adf6ce865b28e3619e231b","word_count":695,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r116","report_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","pool":"modulo-0","claim_source":"rebalance","run_id":"r116","schedule_cell":"V v2 3","agent_identity":"/root/rebalance_r116_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r120","report_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","pool":"modulo-0","claim_source":"rebalance","run_id":"r120","schedule_cell":"N v2 2","agent_identity":"/root/rebalance_r120_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","pool":"modulo-0","run_id":"r112","agent_identity":"/root/rebalance_r112_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","pool":"modulo-0","run_id":"r116","agent_identity":"/root/rebalance_r116_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","pool":"modulo-0","run_id":"r120","agent_identity":"/root/rebalance_r120_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","pool":"modulo-0","claim_source":"rebalance","run_id":"r116","schedule_cell":"V v2 3","agent_identity":"/root/rebalance_r116_attempt1","attempt_count":1,"report_sha256":"a8f0eb9814170360c3a888780ab336551f411736a4d49dee1813dbf026bb2ed2","word_count":689,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r124","report_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"report_preserved","pool":"modulo-0","claim_source":"rebalance","run_id":"r112","schedule_cell":"I core 5","agent_identity":"/root/rebalance_r112_attempt1","attempt_count":1,"report_sha256":"f7043de7bd8c44dc4c711ef43a749b62cdbd849bcf139f595d9529f1477069d9","word_count":800,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r128","report_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r132","report_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r136","report_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","pool":"modulo-0","claim_source":"rebalance","run_id":"r136","schedule_cell":"D v1 4","agent_identity":"/root/rebalance_r136_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","pool":"modulo-0","claim_source":"rebalance","run_id":"r120","schedule_cell":"N v2 2","agent_identity":"/root/rebalance_r120_attempt1","attempt_count":1,"report_sha256":"11249b37dba870ac0ea1e9b26eb2c67bbea5b41ee53997fa8df52a5e5b7e9817","word_count":799,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r140","report_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","pool":"modulo-0","claim_source":"rebalance","run_id":"r140","schedule_cell":"A v1 4","agent_identity":"/root/rebalance_r140_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r144","report_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","pool":"modulo-0","claim_source":"rebalance","run_id":"r144","schedule_cell":"V v1 4","agent_identity":"/root/rebalance_r144_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","pool":"modulo-0","run_id":"r136","agent_identity":"/root/rebalance_r136_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","pool":"modulo-0","run_id":"r140","agent_identity":"/root/rebalance_r140_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"completion_reminder","pool":"modulo-0","run_id":"r144","agent_identity":"/root/rebalance_r144_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"report_preserved","pool":"modulo-0","claim_source":"rebalance","run_id":"r136","schedule_cell":"D v1 4","agent_identity":"/root/rebalance_r136_attempt1","attempt_count":1,"report_sha256":"0c086ba81aaffd250c133a04e97451b15ec138e30bc5b78ca95fe3ef33bcf181","word_count":721,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"rebalance_attempt","pool":"modulo-0","run_id":"r148","report_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"report_preserved","pool":"modulo-0","claim_source":"rebalance","run_id":"r140","schedule_cell":"A v1 4","agent_identity":"/root/rebalance_r140_attempt1","attempt_count":1,"report_sha256":"770680fd022a39cfefd0140d8b71c1e974da29005fd68b4f7fcaf12d40b252a1","word_count":700,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"report_preserved","pool":"modulo-0","claim_source":"rebalance","run_id":"r144","schedule_cell":"V v1 4","agent_identity":"/root/rebalance_r144_attempt1","attempt_count":1,"report_sha256":"62e725bd24db907896cce9b3363cd4f6fd5072e83efdddde5d9be146cdb3513a","word_count":726,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"rebalance_drain_complete","pool":"modulo-0","authorized_ids":19,"reports_completed_by_this_orchestrator":9,"reports_preexisting_at_scan":1,"claimed_by_other_orchestrators":9,"operational_blockers":0,"collisions":0,"invalid_attempts":0,"deviations":"none"} +{"event":"final_operational_audit","original_shard_reports":35,"original_shard_missing":0,"authorized_rebalance_reports_current":18,"authorized_rebalance_claims_only_current":1,"authorized_rebalance_missing":0,"ledger_json_valid":true,"deviations":"none"} diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/manifest.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/manifest.md new file mode 100644 index 0000000000..10c3793e9d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/manifest.md @@ -0,0 +1,267 @@ +# 2026-07-31 Unsafe Rust V2 Forward Evaluation Manifest + +> **Evaluator-only material** + +## Frozen preregistration + +This section, the packages, fixtures, prompt, oracle, schedule, conditions, and +gates were frozen before the first evaluated report. No target may be modified, +built, tested, macro-expanded by execution, or otherwise executed. + +The experiment contains ten modes, three conditions, and five fresh replicates +per cell: 150 reports. V2 versus V1 is the primary comparison. The V1 core +ablation is a secondary historical bridge and does not isolate V2's changes. + +## Frozen package identities + +| Condition | Package tree digest | `SKILL.md` digest | Opaque runtime label | +|---|---|---|---| +| V2 | `40b4171cc9daf7e51ba032aef52157a85a49c4c12cea8696deadb948e0867897` | `a0a75ef8a14497aa78b50b459981097ee99605c57fec95c637cf59aaa20fe766` | `q7m2` | +| V1 | `d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd` | `2b063ad7d8c6a3f5051294e3c9ed49c8850397645b46772cd40ec6ae7136531e` | `b4x9` | +| V1 core ablation | `7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52` | `c2f07d263ce89d758985d6ff388ca344e038c1db111f2298cc5ddef051697595` | `n8k3` | + +All packages passed the skill static validator. The V2 package received a +separate holistic freeze review after the evaluator failures were translated +into three general rules. No package file may change during collection, +scoring, or adjudication. + +## Frozen fixture identities + +| Mode | Source directory | Tree digest | Opaque runtime label | +|---|---|---|---| +| U | `fixtures/v2-forward/u_behavior` | `ee28003a984072cd3beebbd7f9549f3adbf087b7cd901f7fec889cd100ce8f3a` | `m2q8` | +| D | `fixtures/v2-forward/d_support` | `8196405f5bf3b110bfae84ef5904441e8a5726de338aa8be8d1bf49936a2877a` | `v7c4` | +| V | `fixtures/v2-forward/v_versions` | `636cefc08d1ece6183a208e47080e76f2669ef95c251586b8dc2bb00144cee22` | `h5p9` | +| I | `fixtures/v2-forward/i_producer` | `e213794c96e0fe442fc85747f2851c2da0da548dc031731e2c11b11e43022a1f` | `k3r6` | +| T | `fixtures/abstraction-design-v1/c_ticket` | `df348015164c68d626b79e9c9a4625a3f7163377b2ad6783baa5f42ff27ec388` | `f8w1` | +| C | `fixtures/abstraction-design-v1/e_configuration_domain` | `5c5cdc7430571e055d7a6b1ddf281c83ed62f6f224872a158dd4ff775d07fdf9` | `z4n7` | +| H | `fixtures/abstraction-design-v1/h_tradeoff` | `06c138336064a80f8069feb9b6e41b92c5eb53623a07c0c1033033cd35f03d59` | `a6d2` | +| A | `fixtures/abstraction-design-v1/a_acceptance` | `f3564d5af0704da33cf13e6bb01711677ccf1746f7c2dbc822385573ed1d1f55` | `y9t5` | +| P | `fixtures/abstraction-design-v1/d_published_contract` | `0ad42022c041cbd4cc6dd555ae605cafd4e49ffaa6724baeeff142388961ed54` | `c3g8` | +| N | `fixtures/abstraction-design-v1/i_new_snapshot` | `eef6b621a5b74e23613d3c67679470744651a192546eb63fa03986865b275bff` | `p1j4` | + +The legacy fixture directories are byte-for-byte unchanged. Every opaque copy +was verified against its source digest before collection. The semantic atoms, +hard errors, authority review, and release gates are frozen in +[`../../v2-forward-oracle.md`](../../v2-forward-oracle.md). + +## Frozen evaluated-agent prompt + +Each cell receives byte-identical text except the resolved opaque `[PACKAGE]`, +`[TARGET]`, and unique empty `[OUTPUT]` paths: + +```text +Act as a fresh source-review agent. Read the complete unsafe Rust skill package +rooted at [PACKAGE]/SKILL.md and every reference it directs you to for this +task, then follow it. Read REQUEST.md and every other file in [TARGET], and +complete exactly the requested review or design work. + +Inspect only [TARGET], [PACKAGE], and exact versioned official Rust Reference +or standard-library documentation needed to verify claims. Do not inspect +sibling directories, another package or target, the enclosing repository, +version-control history, evaluator material, or prior reports. Do not modify, +build, test, macro-expand by execution, or otherwise execute the target. Do not +spawn helper agents. + +Write the report to [OUTPUT]/report.md using apply_patch, then return the same +report in your final response. Keep the report focused and no longer than 1,400 +words. This is a focused source review, not a persistent whole-crate audit; +provide the equivalent proof material compactly. +``` + +No substantive steering is permitted. A single target-neutral reminder to +finish within the frozen word limit using already inspected material is +permitted and must be recorded. + +## Frozen launch schedule + +The following random order was fixed before collection. The final column is a +within-cell replicate identity, not a sampling seed. + +```text +r001 P v2 5 +r002 U v2 2 +r003 P v1 3 +r004 D v1 5 +r005 I v1 4 +r006 C v2 2 +r007 N core 4 +r008 C v2 3 +r009 U v1 2 +r010 C v1 5 +r011 T v2 1 +r012 U v2 3 +r013 T v2 2 +r014 D core 3 +r015 V core 2 +r016 P v1 1 +r017 T core 2 +r018 A v1 3 +r019 A v2 4 +r020 V core 4 +r021 H v2 5 +r022 H v1 4 +r023 T v1 1 +r024 P core 2 +r025 T v1 3 +r026 D v2 1 +r027 P core 5 +r028 V v2 4 +r029 U v1 4 +r030 N core 5 +r031 T v1 4 +r032 P v2 3 +r033 V v2 2 +r034 P core 4 +r035 D core 1 +r036 T v2 4 +r037 I core 3 +r038 N v1 1 +r039 H v1 1 +r040 U v1 5 +r041 U v1 1 +r042 I v1 5 +r043 P core 1 +r044 U core 1 +r045 I core 4 +r046 A core 2 +r047 H core 2 +r048 V v1 3 +r049 H v2 4 +r050 P v1 5 +r051 I v2 5 +r052 H core 5 +r053 A v1 5 +r054 P v2 2 +r055 H v1 2 +r056 A v2 2 +r057 D v1 2 +r058 D v2 2 +r059 C core 4 +r060 C v1 3 +r061 A v2 1 +r062 U core 4 +r063 N v1 4 +r064 N v2 4 +r065 N v1 5 +r066 V v1 2 +r067 N v2 5 +r068 T v1 5 +r069 I v1 2 +r070 A core 3 +r071 T v1 2 +r072 I v1 3 +r073 D core 2 +r074 D v1 3 +r075 H v2 2 +r076 P v2 1 +r077 H v2 3 +r078 C v2 4 +r079 H core 3 +r080 C core 1 +r081 P core 3 +r082 U core 3 +r083 N core 1 +r084 A v1 1 +r085 A v2 5 +r086 C core 5 +r087 U v2 4 +r088 T v2 5 +r089 C v2 5 +r090 U v2 1 +r091 P v1 2 +r092 N v2 1 +r093 D core 5 +r094 V v2 1 +r095 N v1 2 +r096 P v2 4 +r097 V core 5 +r098 I v2 1 +r099 V v1 1 +r100 U core 5 +r101 V core 1 +r102 D core 4 +r103 C core 3 +r104 I v2 2 +r105 C v2 1 +r106 H v1 3 +r107 T v2 3 +r108 N core 3 +r109 C v1 4 +r110 C v1 1 +r111 H core 4 +r112 I core 5 +r113 N v2 3 +r114 V core 3 +r115 T core 1 +r116 V v2 3 +r117 P v1 4 +r118 A core 4 +r119 U v2 5 +r120 N v2 2 +r121 U core 2 +r122 H core 1 +r123 D v2 3 +r124 D v2 5 +r125 A v1 2 +r126 A v2 3 +r127 I core 1 +r128 T core 3 +r129 N v1 3 +r130 V v1 5 +r131 I v2 3 +r132 H v2 1 +r133 V v2 5 +r134 I v1 1 +r135 A core 1 +r136 D v1 4 +r137 T core 4 +r138 C core 2 +r139 H v1 5 +r140 A v1 4 +r141 D v1 1 +r142 T core 5 +r143 N core 2 +r144 V v1 4 +r145 I v2 4 +r146 A core 5 +r147 C v1 2 +r148 I core 2 +r149 D v2 4 +r150 U v1 3 +``` + +## Freshness, isolation, and scoring + +Every report uses a new collaboration agent with `fork_turns="none"`; no agent +may see two cells. Agents share a host filesystem, so path isolation is +procedural rather than hardened. The runtime root is +`/tmp/unsafe-rust-v2-eval.9epWDK`; agents receive only their opaque package, +target, and empty output paths. The collaboration API exposes neither a fixed +sampling seed nor a precise hosted-model identity. These limitations make the +study exploratory even if every gate passes. + +After all reports finish, copy them byte-for-byte into `reports/rNNN.md`, hash +the report tree, and randomize labels independently per mode. Two fresh blind +scorers per mode receive source, oracle, and anonymous reports, but no package +or condition identity. Resolve semantic disagreements before unblinding; +preserve raw scores and adjudications separately. Do not patch the frozen skill +or oracle after observing outputs. + +## Collection ledger + +Append only operational facts here after collection: agent identity, output +digest, deviations, reminders, invalid reruns, and aggregate artifact digests. +Do not alter the frozen material above. + +## Completed collection, scoring, and adjudication + +Collection produced all 150 valid reports. Two blind scores per mode were +preserved, all semantic disagreements were adjudicated before unblinding, and +the canonical matrices were then joined to the frozen condition schedule. The +preregistered V2 gate result is **FAIL**. + +See [`operational-ledger.md`](operational-ledger.md) for collection/scoring +events, invalid-attempt handling, byte-for-byte preservation checks, and +artifact digests. See [`results.md`](results.md) for the unblinded per-mode +counts, condition deltas, exact failed cells, hard errors, and gate decisions. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/operational-ledger.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/operational-ledger.md new file mode 100644 index 0000000000..a253022947 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/operational-ledger.md @@ -0,0 +1,114 @@ +# V2 Forward Evaluation Operational Ledger + +This file records post-freeze operational facts. It does not amend the frozen +packages, fixtures, prompt, oracle, schedule, conditions, or gates. + +## Collection + +- Runtime root: `/tmp/unsafe-rust-v2-eval.9epWDK`. +- 150 valid reports completed: ten modes, three conditions, five fresh reports + per cell. Every valid report was nonempty and at most 1,400 words. +- Word counts: minimum 472, maximum 980, total 107,035, mean 713.57. +- Reports were copied byte-for-byte to [`reports/`](reports/). The archived + report directory's deterministic GNU-tar digest is + `cbc6de5f526f4c89f68fea5e91f91e002a18b1dd351abc6a796837cd0dd12989`. +- One initial `r006` launch was interrupted before producing any report. It was + rerun with a fresh agent; no invalid report existed to preserve. +- The permitted target-neutral completion reminder was used where recorded in + the three shard ledgers. No reminder supplied a finding, semantic hint, + expected verdict, or condition identity. +- Atomic shard and rebalance claims produced no duplicate report, overwrite, + or lost output. Dispatch failures caused by the collaboration thread limit + created no agent and touched no report. +- [`collection-ledgers/shard-1.tsv`](collection-ledgers/shard-1.tsv), + [`collection-ledgers/shard-2.md`](collection-ledgers/shard-2.md), and + [`collection-ledgers/shard-3.jsonl`](collection-ledgers/shard-3.jsonl) + preserve the detailed helper-thread events. +- No target was modified, built, tested, macro-expanded by execution, or + otherwise executed. + +## Blind scoring + +- Runtime root: `/tmp/unsafe-rust-v2-score.IpMWrc`. +- Twenty valid scores completed: two fresh blind scorers for each mode. Valid + score word counts were 1,769–2,386, below the frozen 6,000-word cap. +- The raw valid scores are preserved in [`blind-scores/raw/`](blind-scores/raw/). + Its deterministic GNU-tar digest is + `5e19f6e16aba04079e9e519ba8597ac2eb41bc6cf11e3daf3a18a3da37b88856`. +- One first Mode T scorer consulted and cited a Rust release-blog page. The + frozen prompt permitted only the packet and exact-version Reference or + standard-library documentation. Its otherwise-complete score was therefore + excluded, preserved as + [`blind-scores/invalid/T-s2-attempt-1.md`](blind-scores/invalid/T-s2-attempt-1.md) + with SHA-256 + `47fdd0495848b4d4a2c5447673819fee629eb2482e39328a014d5a06edd0adab`, + and replaced by a fresh blind retry. +- All 20 valid scores were checked for completeness, word limit, and prohibited + external-source leakage before comparison. +- The three parallel-orchestrator records are preserved byte-for-byte in + [`scoring-ledgers/`](scoring-ledgers/). Their recorded fourteen score hashes + and word counts match the archived raw scores. Intermediate + owned-elsewhere/skipped states are reconciled in + [`scoring-ledgers/README.md`](scoring-ledgers/README.md), including Shard 3's + accurate pre-T-s2 snapshot of 19 present scores and the later invalid-attempt + and fresh-retry chronology. +- The deterministic GNU-tar digest of [`scoring-ledgers/`](scoring-ledgers/) is + `966b9b8c56a31ab33a03c1c9c06c9f8d28c8266f876e9f6ba76569589716ad5b`. +- Modes V and I agreed exactly. Modes U, D, and T differed only in wording for + the same hard-error decisions. Modes A, C, H, N, and P contained semantic + disagreements and proceeded to adjudication. + +## Blind adjudication + +- Runtime root: `/tmp/unsafe-rust-v2-adjudicate.0UjXc7`. +- Five fresh blind adjudicators resolved only the cells recorded in + [`blind-scores/disagreements/`](blind-scores/disagreements/), preserving all + agreed cells. +- Adjudication word counts were 1,121–1,725, below the frozen 3,500-word cap. +- Adjudications are preserved in + [`blind-scores/adjudicated/`](blind-scores/adjudicated/). Its deterministic + GNU-tar digest is + `417282fc1c80582feeac9165b188a48ca9a4d131dd642bda2153f5ba57f3fa1a`. +- One canonical final blind matrix per mode was then copied to + [`blind-scores/final/`](blind-scores/final/). Its deterministic GNU-tar digest + is `7aa6d3b0fab2739450ed1130a413ab30d39a148a422750b6ad7202ff74cd9198`. +- No condition or package identity was consulted until all ten canonical final + blind matrices were frozen and verified byte-for-byte. +- The complete [`blind-scores/`](blind-scores/) directory, including raw, + invalid, disagreement, adjudication, and final artifacts, has deterministic + GNU-tar digest + `f1247530c8cc29e8201d1938879f70c983dab03db896085b5f5ac132814c345e`. + +## Unblinding and aggregation + +- [`aggregate_scores.py`](aggregate_scores.py) mechanically joins the frozen + launch schedule, blind map, and canonical final matrices. It asserts all 150 + scheduled runs, all 15 reports per mode, and all five reports per condition. +- [`results.md`](results.md) is byte-identical to the aggregator's output and + has SHA-256 + `a75612b9b3ca7461c89e2c9e7948c9fe4005a9b0086a7422a84de41a2f1a7c5b`. +- The preregistered V2 gate result is `FAIL`. This result was preserved without + widening validation or editing the frozen package or oracle. + +## Post-unblinding interpretation + +- [`qualitative-findings.md`](qualitative-findings.md) records the failure + anatomy, successful capabilities, comparative limits, and next-revision + hypotheses. It is explicitly post hoc and changes no frozen score or gate. +- Its SHA-256 is + `ac46a4271a8aaaa176435791cf679cc571e9373e3070a62ab417d61282c55228`. + +## Frozen-artifact verification + +- V2 oracle SHA-256: + `555752594765637b691f10f185cede3c6ebd6f92f2e7fb3289a372f67d498e97`. +- Initial frozen manifest SHA-256, before this post-freeze ledger append: + `dcf87219ac19316787d0460b17ce523f57834792a4b16eed055ee1675f4c27f8`. +- Blind-map SHA-256: + `50ee500df93e321c3ed1282b88c3ac503d3ab1170abfdd1ff0425c30ef70db8a`. +- Scoring-prompt SHA-256: + `c2f3041316bc2e5deecb99f43d9a272179bd8052662a882e71ac005f63b928c0`. +- Adjudication-prompt SHA-256: + `a16446761ea313769c77e00308a6af645d089f91102c7c2a61d15724ac1e162b`. +- The live `skills/unsafe-rust/` tree remained byte-for-byte identical to the + frozen V2 package after unblinding. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/qualitative-findings.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/qualitative-findings.md new file mode 100644 index 0000000000..d93811984b --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/qualitative-findings.md @@ -0,0 +1,202 @@ +# V2 Forward Evaluation: Qualitative Findings + +This is post-unblinding interpretation of the frozen evaluation. It does not +amend the packages, fixtures, oracle, scoring rules, gates, canonical blind +matrices, or mechanically generated [`results.md`](results.md). + +## Bottom line + +V2 failed its preregistered release gates. It produced 16 failed atom cells and +five hard errors. Those failures are concentrated rather than diffuse: + +- Mode D accounts for 12 of 16 failed atom cells and four of five hard errors. +- Mode H accounts for the remaining hard error and one failed atom cell. +- Modes A and N account for the remaining three failed atom cells without hard + errors. +- Modes U, V, I, T, C, and P pass every V2 atom; no V2 report launders an + unimplemented proposal into a proved artifact. + +The evidence supports the narrower conclusion that V2 often elicits excellent +proof-grade reviews and that several intended disciplines work reliably on +these fixtures. It does not support declaring V2 gate-ready, broadly superior +to V1, or reliable enough for a zero-miss standard. + +## Failure analysis + +### D: support-domain recovery was not itself proved + +The two controlling policies both describe stable Rust releases from 1.79.0 +through 1.82.0 inclusive. Four V2 reports translated that interval into +`{1.79.0, 1.80.0, 1.81.0, 1.82.0}`, omitting the stable patch release 1.80.1. +They then proved the source over that smaller set and asserted closure over the +conservative policy union. Only [`r124`](reports/r124.md) retained 1.80.1 and +passed D1--D3. + +This is not primarily an unsafe-operation proof failure. The reports correctly +handled the policy conflict, rejected CI and the developer toolchain pin as +support authority, and gave a valid bounds proof for each configuration they +considered. The defect occurred earlier: policy prose was converted into a +formal domain without proving that the conversion was lossless. Exhaustive +coverage over a malformed domain is not exhaustive coverage of the requested +theorem. + +The current package already requires a precise supported predicate, a +conservative union, exact-version premise applicability, and universal or +exhaustive configuration closure. The four-of-five recurrence nevertheless +shows that those rules do not operationalize domain recovery reliably enough. +The next revision should make recovery of the quantified domain an explicit +proof obligation: + +1. Preserve ranges, unions, exclusions, and conditional structure symbolically + unless a finite member inventory is independently justified. +2. Treat an enumeration as a lemma requiring evidence that it is extensionally + equal to the controlling predicate. +3. Prove the implementation parametrically over the symbolic predicate, or + prove premise coverage for every justified member or exhaustive partition. +4. Falsify coverage with interior and boundary members before certifying it. + Patch releases are one example, not the rule itself. +5. If exact membership or equivalence cannot be established, preserve the + symbolic domain and leave any unsupported region `UNPROVED`. + +This is the highest-priority V3 change and deserves multiple new fixtures with +different interval, union, exclusion, and moving-policy shapes. + +### H: an existential UB proof was mistaken for a universal proof gap + +[`r021`](reports/r021.md) states that Rust 1.70 `pointer::add` requires the +start and result to be within or one-past the same allocation, that a valid +empty slice may use an aligned pointer not attached to an allocation, and that +Rust 1.70 has no zero-offset exemption. Those premises already entail a valid +safe call whose executed `add(0)` violates its contract. The report nevertheless +declares the result `UNPROVED` and asks for a proposition saying `add(0)` is +defined for every empty-slice pointer. It therefore tries to close the +universal soundness proof after it has already assembled an existential +refutation of that proof. + +The skill already distinguishes `UNPROVED` from `UNSOUND`, requires indirect +multi-premise derivation, and says one valid UB execution refutes a safe API's +universal soundness claim. This is primarily a stochastic proof-composition and +verdict-calibration failure, not an absent concept. A compact final +counterexample-closure check may make the existing model more reliable: + +- Is the proposed input or state a valid in-scope use? +- Is the relevant operation executed on that path? +- Which exact contract clause is false at that operation? +- Does the applicable authority make violating that clause UB? + +When all four answers are proved, the scoped verdict is `UNSOUND`; no theorem +about every member of the input class is needed. When only the universal proof +fails and no valid counterexample is proved, the verdict remains `UNPROVED`. + +### A: sampled releases were promoted to an interval theorem + +[`r019`](reports/r019.md) and [`r126`](reports/r126.md) correctly distinguish +the false literal `Piece` contract from the validity of the projected `u32`. +Their A2 failures arise because they additionally claim `PROVED` soundness over +the stable 1.70.0--1.97.1 interval while relying on endpoint or sparse sampled +documentation and explicitly admitting no compatibility premise. The three +passing V2 reports prove the exact Rust 1.70 region and leave the unproved +remainder visible. + +V2 already forbids this interpolation, so the failures show adherence +unreliability rather than a missing semantic rule. A positive-verdict +certification step should require every multi-release `PROVED` region to name +one of: + +- an applicable proposition-preserving compatibility premise; +- a valid parametric proof over the entire region; or +- an exhaustive partition whose every member or class has applicable evidence. + +An audit cutoff bounds a claim; it does not prove continuity up to that cutoff. +Endpoint samples prove endpoints, not the interval between them. + +This result also exposed a scoring-design issue. A2's central proposition is +contract-versus-soundness separation, which both reports perform correctly; +the failed interval scope is separately material to their affirmative verdict. +A future oracle should score those as separate atoms, or pin this unchanged +control to one exact Rust version so an unrelated scope error does not obscure +the intended control. + +### N: one independent alias route was omitted + +[`r067`](reports/r067.md) correctly reports the current snapshot `UNSOUND`, +fully proves the retained-`get`/later-`get_mut` witness, identifies the +receiver-unbound `'a` output lifetimes as the enabling defect, repairs both +accessor signatures, and withholds a verdict from the proposal. It does not +explicitly instantiate the second oracle-required route: two simultaneously +live results from repeated `get_mut` calls. The adjudicator declined to infer +that route from the report's generic discussion of reusable capability. + +This is a narrow completeness omission with genuine shorthand-granularity +ambiguity, not evidence that the skill taught the wrong model. One witness is +enough to refute aggregate soundness, but one witness does not necessarily +complete an exhaustive audit of independently failing surfaces or composition +routes. No skill change should be made for N alone. A future oracle should: + +- split the two witness routes into separate atoms; +- preregister what explicit analogous reasoning is sufficient; and +- score complete obligation/surface coverage without imposing an impossible + requirement to enumerate every conceivable client program. + +If a larger targeted replication shows recurring omissions, a general +reporting rule may require auditors to continue disposing of independently +failing obligation sites after the first witness establishes `UNSOUND`. + +## What worked + +- U, T, and C apply the whole-execution UB/postcondition rule in every V2 + report. A UB-containing execution is not used as a defined behavioral + counterexample. +- V partitions an exact semantic boundary between Rust 1.79 and 1.80 correctly + in every condition and V2 report. +- I rejects producer-precondition promotion, follows both producers, and + derives the safe UB witness in every condition and V2 report. +- T, C, and H keep current-source findings, redesign proof plans, performance + evidence, and implemented-artifact verdicts separate. V2 has zero proposal + laundering. +- P preserves a published contract in the face of incomplete consumer search + and separates compatible internal simplification from contract weakening. +- Passing reports frequently reconstruct material missing safety proofs and + expose the reconstruction rather than silently accepting deficient comments. + +These are capability results on the frozen fixtures, not a statistical proof +that the skill caused every success. Several modes are at ceiling in all three +conditions. + +## Comparative interpretation and limitations + +V2 versus V1 is the preregistered primary comparison. Results are mixed: V2 +improves U2, T2, H1, and P1 by one report each, improves D2 and D3 by one, but +regresses D1 by one, A2 by two, and N1 by one. V and I are ceiling results in +all conditions. The V1 core ablation is only a historical bridge and does not +isolate V2's changes; its proposal-laundering failures in T and C support the +value of the full abstraction-design material but cannot identify which V2 +wording caused later outcomes. + +Interpret these comparisons cautiously: + +- five reports per cell are an engineering screen, not a power analysis; +- report sampling is stochastic and no fixed model seed or durable model + identity was available; +- heterogeneous modes must not be pooled into one performance estimate; +- ceiling effects hide possible differences; and +- some atom boundaries produced genuine scorer disagreement. + +The zero-miss preregistered gate is intentionally demanding. Its failure is +conclusive for release gating even where causal attribution is uncertain. + +## Recommended sequence + +1. Preserve this V2 run unchanged as a failed preregistered evaluation. +2. Design V3 holistically around theorem-domain recovery and final + certification, integrating the D, H, and A lessons without adding + fixture-specific hazard trivia. +3. Refine the A and N oracle atoms before using them again. +4. Freeze V3 separately, then run targeted confirmatory suites for support-set + translation, existential-refutation closure, and multi-release scope, plus + unchanged regression controls. +5. Only after targeted confirmation should a broader fresh-agent audit suite + be used as the next release gate. + +The requested final process retrospective remains deferred until all skill and +meta-file revisions and all evaluation rounds are complete. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r001.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r001.md new file mode 100644 index 0000000000..a9f8808181 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r001.md @@ -0,0 +1,42 @@ +# `Block` focused source review + +## Claim, scope, and verdict + +Snapshot: the supplied `lib.rs` only. In scope are the published unsafe trait `Block`, its `Page` impl, and safe function `first`, for Rust 1.70+, every target and profile. There are no `cfg`s, dependencies, generators, mutable fields, callbacks, or profile-sensitive operations in this snapshot. + +**Current artifact:** `Page`'s implementation of every `Block` clause is **PROVED**, and `first` is **PROVED** sound for every `B` whose unsafe impl satisfies that published contract. These are source-level results, relative to the Rust 1.70 axioms below and to the ordinary reading that “readable for 16 bytes during the borrow” means 16 initialized bytes may be read through the returned pointer for the receiver-borrow interval. The existing documentation should say that explicitly and place it under `# Safety`; this is proof-documentation debt, not a reconstructed extra obligation. + +The proof is target- and profile-parametric: it uses only fixed `u8`/array layout, `repr(C, align(16))`, and shared-borrow properties. For the open-ended future part of “1.70+”, a full unconditional verdict is **UNPROVED** unless the project accepts a TCB premise that later supported Rust releases preserve the exact cited propositions. Alternatively, freeze an audit cutoff and re-audit new releases. An API stability badge alone is insufficient for that propagation. + +## Obligations and derivations + +`Page`: + +- `ALIGN = 16` is nonzero and a power of two. +- Rust 1.70 documents that an array has `N * size_of::()` bytes with consecutive elements; the `repr(C)` field-offset algorithm starts at zero, so the sole `[u8; 16]` field begins at the `Page` address. The `align(16)` modifier raises the struct alignment to 16. See the [Rust 1.70 layout rules](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs) and [alignment modifier](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers). +- `self` is a valid shared reference to a valid `Page`; therefore its array contains 16 initialized `u8`s and remains live/readable for the borrow. The field starts at the 16-aligned `Page` address. Slice `as_ptr` [returns the pointer to its buffer and remains usable while the slice outlives it](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr). Thus `base` returns a non-null, 16-aligned pointer readable for all 16 bytes during the receiver borrow. This proves the unsafe impl's complete provider assertion, including clauses unused locally. + +`first`: + +- A valid `Block` impl supplies non-nullness and readability of bytes 0..16 during the `&B` borrow. The load occurs in that interval and accesses only byte 0. +- A nonzero power-of-two `ALIGN` is at least one; in any event `u8` requires only byte alignment. The resulting `u8` is initialized because the contract says the byte is readable. Therefore the raw-pointer load is neither dangling/unaligned nor an uninitialized integer read—the relevant Rust 1.70 UB cases are listed in [Behavior considered undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html). + +`first` does **not** consume the numerical value of `ALIGN`, alignment stronger than one, or readability of bytes 1..15. Its adjacent proof should state only this projection; that is the available proof simplification. + +## What may change in 1.x + +No edit is authorized now. In a separately authorized 1.x patch, the project may add the missing adjacent `SAFETY` derivation above, clarify (without changing) the receiver-borrow/initialized-byte meaning, and factor a private “read one byte” lemma. `first` may rely on fewer clauses than `Block` promises. None of that permits `Page` or another impl to stop satisfying any published clause. + +Keep `Block`, `ALIGN`, the 16-byte guarantee, and the accepted meaning of `base` intact throughout 1.x. Local search cannot close a public boundary: unknown downstream generic consumers may use all 16 bytes or `ALIGN`, and unknown downstream unsafe impls were admitted under exactly the old obligations. Weakening provider guarantees breaks consumer proofs; strengthening implementer obligations (including a materially longer lifetime, stable address, or stronger initialization rule not already meant by “readable”) invalidates existing impls. Either is a SemVer-breaking contract change even with unchanged Rust signatures. + +An additive 1.x migration API is possible after ordinary compatibility review, but it cannot replace or relax legacy `Block`; at most it provides a runway while the old API and full proofs remain supported. + +## Authorized 2.0 design + +The proof-oriented redesign is capability splitting: + +1. Give the first-byte consumer a safe trait such as `FirstByte { fn first_byte(&self) -> u8; }`; make `first` call that method with no unsafe operation. `Page` implements it as `self.0[0]`. +2. Retain a separately named unsafe raw-storage trait only if real consumers need it, with a precise `# Safety` contract spelling out initialized range, provenance/read permission, alignment, and exact lifetime. `Page` may implement that full capability independently. +3. Deprecate/adapt the old trait during 1.x, then change `first`'s bound and remove or rename `Block` only in the explicitly authorized major release. Re-audit all adapters and new impls after implementation. + +This removes raw-pointer proof burden from the minimal consumer without pretending that the published broader capability never existed. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r002.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r002.md new file mode 100644 index 0000000000..608a48d185 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r002.md @@ -0,0 +1,36 @@ +# Focused unsafe-Rust source review + +## Claim, scope, and verdicts + +Snapshot: the supplied `targets/m2q8/lib.rs` and `REQUEST.md`. Scope is the sole public API, `classify(u8) -> u8`, under Rust and `core` 1.80.0, for every target on which Rust 1.80.0 accepts this source and every ordinary profile. The proof is source-level and is parametric over target, edition, optimization, overflow-check, and panic-strategy choices because the source has no conditional compilation, generated code, dependencies, target operations, arithmetic, or profile-sensitive checks. + +- **Soundness: UNSOUND.** A well-typed safe call `classify(0)` necessarily reaches undefined behavior. +- **Documented behavior, “Panics when `input == 0`”: UNPROVED.** The only relevant execution reaches undefined behavior, so it cannot be used as a UB-free behavioral counterexample or as proof of a panic. +- **Documented behavior, “On normal return, returns `input`”: CONTRACT-BROKEN.** The UB-free execution `classify(1)` returns normally with `2`, not `1`. + +Consequently the combined safe-API claim is not proved. These verdicts are relative only to the Rust 1.80.0 axioms listed below; there are no additional assumptions. + +## Boundary and obligation coverage + +The complete language-reachable surface is the safe free function at `lib.rs:6-12`. There are no fields, constructors, traits/impls, macros, hidden APIs, FFI, callbacks, invariant-bearing state, or dependencies. The only unsafe operation is `core::hint::unreachable_unchecked()` at `lib.rs:8`. The two doc comments at `lib.rs:3-4` are the complete mandatory behavioral contract. + +| ID | Obligation | Evidence and disposition | +|---|---|---| +| S1 | Every safe call must avoid UB; therefore the call at line 8 must be unreachable. | For `input = 0`, literal pattern `0` matches and the first arm is selected. Control reaches the unsafe call. Rust 1.80.0 documents: “Reaching this function is Undefined Behavior.” Thus a valid safe use reaches UB: **UNSOUND**. | +| B1 | If `input == 0`, the function panics. | The same whole execution contains UB at the unsafe call. Rust semantics therefore supply neither the promised panic nor a UB-free execution that could establish `CONTRACT-BROKEN`: **UNPROVED**. | +| B2 | Every normal return equals `input`. | For `input = 1`, the `1` arm is selected and evaluates to `2`; the unsafe arm is not executed. This execution contains only ordinary matching and literal return, returns normally, and `2 != 1`: **CONTRACT-BROKEN**. Inputs matched by `_` do return `input`, but that does not repair the universal clause. | + +Rust 1.80.0's Reference states that literal patterns “match exactly the same value” as their literals and that the first matching arm becomes the branch target. Those rules make both witnesses direct consequences of the source, with no target/profile case split. + +## Authoritative premises / TCB + +- **AXIOM-U (Rust/core 1.80.0, all applicable targets/profiles):** [`core::hint::unreachable_unchecked`](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety): “Reaching this function is Undefined Behavior.” Consumer: S1 and B1. +- **AXIOM-M (Rust Reference shipped for 1.80.0):** [`match` expressions](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html) specifies first-matching-arm selection; [`literal patterns`](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns) match the literal's value exactly. Consumers: S1, B1, and B2. + +No compiler/backend correctness, testing, or tool result is assumed; no build or execution was performed. A binary-level claim is outside scope. + +## Finding and proof-documentation quality + +The unsafe block has no adjacent `SAFETY` proof. More importantly, no truthful proof can discharge its controlling precondition: the enclosing `0` arm establishes that the call site is reachable for the valid safe input `0`. This is an implementation defect, not merely missing prose. The absent comment should not be filled with a reconstructed justification; the operation itself cannot be justified under the current safe signature and control flow. Per the request, no source edit or redesign is proposed. + +Re-review is required if the function body, its documentation/signature, supported Rust version/configuration set, or either consumed Rust contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r003.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r003.md new file mode 100644 index 0000000000..4993408814 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r003.md @@ -0,0 +1,43 @@ +# Focused review: `Block`, `Page`, and `first` + +## Claim and verdict + +Scope is the exact supplied [`lib.rs`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/c3g8/lib.rs:1), with no generated code or dependencies, for every profile and compiler-supported target. Valid downstream `Block` implementations are those satisfying the published unsafe-trait contract. No whole-crate claim is made. + +**Current source: PROVED** for Rust 1.70 and for each later Rust release on which axioms A1–A3 below retain the stated meanings. `Page` establishes every published implementer obligation; `first` is sound for every valid `Block` implementation. No documented unsafe-API postcondition is broken. + +**Literal open-ended `Rust 1.70+` aggregate: UNPROVED.** No finite audit cutoff or accepted Rust-compatibility premise was supplied that carries those exact 1.70 propositions through every later (including future) release. This is a version-coverage gap, not an implementation defect. Closing it requires either a release cutoff with re-audit on expansion, or an accepted `COMPAT-1` TCB premise preserving A1–A3 over the supported range. + +## Boundary and obligations + +The surfaces are the public unsafe trait and its associated constant/safe method ([lines 3–10](/tmp/unsafe-rust-v2-eval.9epWDK/targets/c3g8/lib.rs:3)), public `Page` with a private field and its unsafe impl ([lines 12–21](/tmp/unsafe-rust-v2-eval.9epWDK/targets/c3g8/lib.rs:12)), and safe generic `first` ([lines 23–25](/tmp/unsafe-rust-v2-eval.9epWDK/targets/c3g8/lib.rs:23)). The trait contract imposes three implementer obligations: + +1. `ALIGN` is nonzero and a power of two. +2. During the receiver borrow, `base()` returns a non-null pointer aligned to `ALIGN`. +3. During that interval, 16 bytes beginning there are readable. + +The phrase “during the borrow” is read literally as the receiver-borrow interval. `first` keeps its `&B` receiver borrowed through the returned pointer's immediate read. If the project intended a shorter interval ending at `base`'s return, the contract would not entail the read and `first` would instead be `UNPROVED`; that interpretation would also make the postcondition practically vacuous. A 1.x documentation clarification may identify the receiver borrow explicitly only if this is confirmed as the already-published meaning, not as a new stronger promise. + +### `Page` derivation + +`ALIGN = 16` discharges obligation 1. A1 makes `Page` at least 16-aligned. Under A1's `repr(C)` field-placement rule, its sole array field begins at offset zero, so `self.0.as_ptr()` has the same 16-aligned address as `self`. A2 says the pointer addresses the array buffer; the live shared borrow of `Page` keeps its `[u8; 16]` storage alive and prevents ordinary mutation. The array comprises 16 initialized `u8` elements, hence a readable 16-byte region. A pointer derived from that live array buffer is non-null. Thus the unsafe impl establishes all three clauses on all targets/profiles; there is no configuration branch. + +### `first` derivation + +Let `p = block.base()`. The unsafe impl contract supplies non-nullness and readability of 16 bytes for the active borrow, so in particular the first byte is readable. `ALIGN` being a nonzero power of two implies `ALIGN >= 1`; `ALIGN`-alignment therefore suffices for a `u8` access (and A1/A3 establish `u8` size/alignment and validity). A3 consequently permits `*p`, and every possible byte is a valid `u8`. `first` consumes neither the remaining 15 readable bytes nor any alignment stronger than one. + +The implementation proof is present only implicitly. A compatible 1.x improvement is to add an adjacent `SAFETY` comment containing the preceding derivation and a trait-level `# Safety` section that assigns the existing clauses explicitly to implementers. Those are proof/documentation fixes, not permission to alter the clauses. + +## TCB / authoritative premises + +- **A1 (Rust 1.70 Reference):** the `align` modifier raises minimum alignment; the `repr(C)` struct algorithm places the first field at offset zero; array size/layout and primitive layout give `[u8; 16]` 16 consecutive bytes. See [type layout: C structs, arrays, and alignment modifiers](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs). +- **A2 (Rust 1.70 standard library):** `slice::as_ptr` returns a pointer to the slice buffer, with usability tied to the slice remaining live. See [`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr). +- **A3 (Rust 1.70 Reference):** loading through a dangling/misaligned pointer or producing an invalid value is undefined; here the trait contract plus `u8` layout discharge those conditions. See [behavior considered undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html) and [primitive layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#primitive-data-layout). + +No tests or tool output are used as proof. Changes to these contracts, supported Rust semantics, target/layout rules, or either implementation trigger re-audit. + +## Compatibility and redesign + +Within 1.x, retain `Block`, `ALIGN`, `base`, and the complete 16-byte/alignment guarantee. It is safe to simplify **only the local proof** for `first`, add non-semantic documentation, and add independent APIs without redirecting existing behavior. Deprecation may guide users but cannot revoke a guarantee. Sealing the trait, strengthening implementer obligations, narrowing `first` to `Page`, or removing items would invalidate existing implementations or callers. + +An explicitly authorized 2.0 may split capabilities. For the demonstrated requirement, the preferred API is a safe trait method such as `fn first_byte(&self) -> u8`; `Page` implements it as `self.0[0]`, and the free `first` delegates without unsafe code. Keep a separate unsafe raw-buffer trait only if real consumers require it, with an exact pointer/lifetime contract. Removing `ALIGN`, reducing 16 readable bytes to one, or weakening alignment relaxes implementers but simultaneously weakens provider guarantees relied on by unknown downstream unsafe consumers, so it is breaking. Existing implementers must migrate to the safe method; consumers needing the former raw capability must migrate to the separate unsafe trait. The implemented 2.0 snapshot would require a fresh audit. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r004.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r004.md new file mode 100644 index 0000000000..f481ff6d3a --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r004.md @@ -0,0 +1,42 @@ +# Supported-domain source review + +## Claim and verdict + +Scope is the complete supplied `domain-review` 1.0.0 source snapshot: the sole public API `first(&[u8]) -> Option` in both Cargo feature states, on the two published targets, through the Rust 1.82.0 audit cutoff. This is a source-level Rust-UB review; it makes no compiler-binary claim and does not audit the standard library implementation. + +**PROVED** for Rust 1.82.0 on `x86_64-unknown-linux-gnu` and `aarch64-unknown-linux-gnu`, both without and with `fast`, relative only to AXIOM-182 below. Every well-typed safe call is UB-free; there is no caller safety precondition. + +**UNPROVED** for a single claim covering every published supported configuration through Rust 1.79.0. The frozen evidence contains applicable standard-library contracts only for 1.82.0. Their stability badges do not establish that the same behavioral/safety text governed 1.79.0–1.81.0, and no backwards-applicable compatibility premise was authorized. No UB counterexample was established for those releases. + +## Supported domain and configuration closure + +The policies agree that non-`fast` supports Rust 1.79.0–1.82.0 on both targets. For `fast`, Policy A supports x86_64 on 1.79.0–1.82.0 and aarch64 on 1.80.0–1.82.0; Policy B supports x86_64 on 1.80.0–1.82.0 and aarch64 only on 1.82.0. Both are current and no precedence exists, so selecting either as *the* support predicate would be an unauthorized policy choice. + +A policy-neutral coverage envelope is their union (equal here to Policy A). A proof over that envelope would cover every combination promised by either statement without resolving the contradiction. The present proof covers the complete 1.82.0 slice of that envelope. The `cfg(feature = "fast")` and its negation are mutually exclusive and exhaustive; neither implementation depends on target facts. CI is only a sample and supplies no universal premise. + +## API and obligation ledger + +There is one language-reachable safe surface and no unsafe caller boundary, fields, traits, macros, generated code, dependencies, or stateful invariant. + +- **O-NORMAL, `lib.rs:3-6`: PROVED at 1.82.0.** The non-`fast` body uses only safe slice/`Option` operations. The documented `slice::first` result is the first element or `None` for an empty slice. +- **O-FAST-BOUNDS, `lib.rs:8-15`: PROVED at 1.82.0.** `slice::get_unchecked(0)` requires index 0 to be in bounds; an out-of-bounds call is UB even if its reference is unused. The call is dominated by the `else` of `bytes.is_empty()`. AXIOM-182 says `is_empty()` is true exactly when length is zero, hence this branch has `len != 0`; because slice length is a `usize`, `0 < len`, so index 0 is in bounds. The returned `&u8` is immediately read to form an owned `u8`; the input borrow keeps the slice live for the call and read. Empty slices take the `None` branch and never execute unsafe code. +- **O-VERSIONS, Rust 1.79.0-1.81.0: UNPROVED.** Smallest missing proposition: for each release, applicable authoritative documentation must establish the same `is_empty` equivalence and `get_unchecked(0)` in-bounds safety contract (plus returned-reference behavior), or an explicitly accepted compatibility premise must establish those exact propositions for the whole range. + +## TCB and evidence + +**AXIOM-182:** Rust 1.82.0 standard-library slice documentation: `is_empty` “[r]eturns `true` if the slice has a length of 0”; `first` returns the first element or `None` when empty; and `get_unchecked` returns an element/subslice reference without bounds checking while calling it with an out-of-bounds index is UB. See the exact versioned [`slice` documentation](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked) (also `#method.is_empty` and `#method.first`). Applicability is Rust/std 1.82.0 only. No CI, tool, compiler implementation, target implementation, probabilistic, or external premise is consumed. + +## Finding: missing adjacent proof + +The `fast` unsafe block has no `SAFETY` comment. The implementation obligation is proved above, but the proof artifact is deficient because the necessary bounds derivation had to be reconstructed. Proposed adjacent text (no edit requested or made): + +```rust +// SAFETY: This is the `!bytes.is_empty()` branch. `is_empty()` is true +// exactly when the slice length is zero, so here `bytes.len() > 0` and +// index 0 is in bounds, as required by `slice::get_unchecked`. +Some(unsafe { *bytes.get_unchecked(0) }) +``` + +## Residual scope and review triggers + +No broader robustness, performance, or binary/backend property was requested. Resolve the two published policies without retroactive reinterpretation, and verify exact authoritative contracts for every retained pre-1.82 release before upgrading the full-domain verdict. Re-review on source, feature/target/version policy, standard-library contract, or toolchain-cutoff changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r005.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r005.md new file mode 100644 index 0000000000..a15a73fd6d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r005.md @@ -0,0 +1,107 @@ +# Focused source review: `Buffer` + +## Claim and scope + +Artifact: the supplied `targets/k3r6/lib.rs`, exactly as reviewed. Scope is Rust +1.80.0, every target on which this source builds, and all ordinary profiles. +The claim tested is source-level Rust soundness for every well-typed safe use, +plus the sufficiency of `from_writable`'s contract and the local proof of each +`overwrite` write. There are no `cfg`s, generated artifacts, dependencies, FFI, +custom allocation, or target-specific operations in the source. Profile and +target differences therefore do not affect the argument below. + +**Overall soundness verdict: UNSOUND.** A wholly safe, well-typed call sequence +reaches undefined behavior on every in-scope configuration. + +## Authoritative premises (Rust 1.80.0 TCB) + +- **AXIOM-1 — immutable static bytes.** The Rust Reference lists “Mutating + immutable bytes” as undefined behavior and states: “All bytes owned by an + immutable static are immutable, unless those bytes are part of an + `UnsafeCell`.” [`behavior-considered-undefined`](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html) + applies directly to `static BYTE: u8`, whose storage contains no + `UnsafeCell`. +- **AXIOM-2 — raw write preconditions.** Rust 1.80.0 documents + `ptr::write` as undefined when `dst` is not valid for writes or is not + properly aligned. The raw-pointer method used by the source invokes that + operation. [`ptr::write`](https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html#safety), + [`*mut T::write`](https://doc.rust-lang.org/1.80.0/std/primitive.pointer.html#method.write) + +No compiler/backend correctness, allocator, dependency, test, or environmental +assumption is consumed. The result is relative only to the stated Rust 1.80.0 +abstract semantics. + +## Surface and invariant inventory + +- `Buffer` has private `ptr` and `shared` fields. In this exact source, the only + producers are `unsafe from_writable` and safe `from_static`; moves preserve + the pair and ordinary drop does not dereference or deallocate `ptr`. +- `from_writable` stores the caller's pointer with `shared: None`. Its safety + contract requires the pointer to remain non-null, aligned, valid for a + one-`u8` write, and free of access conflicts for the whole period in which + the returned value may be used. +- `from_static` takes `&BYTE`, casts that reference's pointer to `*mut u8`, and + stores both that pointer and the reference with `shared: Some(shared)`. +- `overwrite` is the sole dereferencing consumer. `None` directly calls + `self.ptr.write(value)`. `Some` passes the stored reference to `with_live`, + whose callback is unconditionally invoked once and performs the same write; + the helper then uses `shared`. + +Thus `shared` is also a provenance tag in this snapshot: `None` identifies the +unsafe producer and `Some` identifies the safe static producer. Privacy and the +complete producer inventory, not the field's type alone, establish that fact. + +## Obligation proofs and findings + +### `from_writable` / `overwrite` `None` arm — PROVED for valid unsafe uses + +`from_writable` itself merely stores values and performs no unsafe operation. +For a call satisfying its documented ongoing contract, the stored pointer is +aligned and valid for the one-byte write when `overwrite` is called, and the +contract excludes conflicting access. The private fields and producer +inventory show that the `None` arm still carries that pointer. AXIOM-2's +applicable preconditions are therefore met. Writing a supplied `u8` establishes +a valid `u8` value; there is no destructor or allocation obligation for the old +`u8`. + +The adjacent comment is nevertheless **proof-documentation deficient**: it +does not state the write operation's actual requirements or explain why the +current branch implies construction by `from_writable`. That missing derivation +is reconstructed above. + +### `from_static` / `overwrite` `Some` arm — UNSOUND + +The current comment says that `from_writable` requires the pointer to remain +valid for writes. This premise is inapplicable: this branch is reached for the +value produced by `from_static`, not by `from_writable`. More importantly, +`from_static` establishes the opposite of the needed fact: `ptr` points into +the bytes owned by immutable `static BYTE`. Casting `*const u8` to `*mut u8` +does not make those bytes mutable. + +The following contains no `unsafe` and no caller-side safety obligation: + +```rust +let mut buffer = Buffer::from_static(); +buffer.overwrite(0); +``` + +`shared` is `Some`, `with_live` invokes the closure, and the closure executes +`self.ptr.write(0)` into `BYTE`. By AXIOM-1 this mutation is undefined behavior; +equivalently, AXIOM-2's valid-for-writes obligation is false. Retaining and +using the shared reference across the callback adds an aliasing concern, but no +unsettled alias-liveness proposition is needed: immutable-static storage alone +closes the counterexample. + +No safety comment can prove this arm as written. The safe constructor has a +hidden, impossible-to-enforce requirement that its immutable static storage be +writable. Consequently `overwrite` is not sound for all values obtainable from +safe code. + +## Postconditions and residual scope + +Neither constructor nor `overwrite` documents a separate behavioral +postcondition, so there is no independent `CONTRACT-BROKEN` verdict. The +unconditional safe counterexample is enough for `UNSOUND`; testing, execution, +or backend assumptions are unnecessary. Re-audit is required if the producers, +field visibility, write operation, Rust version, or supported configuration set +changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r006.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r006.md new file mode 100644 index 0000000000..958f3d352a --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r006.md @@ -0,0 +1,51 @@ +# Focused review: `decode` + +## Claim, scope, and verdict + +Snapshot: the supplied `lib.rs`; review cutoff 2026-07-31. The only in-scope surfaces are the two mutually exclusive public safe definitions of `decode`. `Supported(C)` means stable Rust 1.70 or later, every target and pointer width, every ordinary profile/panic strategy, and either independently selected value of feature `compact`. There are no dependencies, generated artifacts, fields, traits, or caller-side unsafe obligations in the supplied source. No code was executed. + +**Current soundness verdict: UNSOUND.** In the supported region `compact = true` with debug assertions disabled, the valid safe call `decode(0xD800)` reaches `char::from_u32_unchecked(0xD800)`. A surrogate is not a Unicode scalar value, so this violates that unsafe function's safety requirement. Rust 1.70 is itself in the published support set, so this one covered configuration and input refutes soundness of the full set; no claim about later-version compatibility is needed for that verdict. + +**Documented behavior:** + +- Without `compact`: **PROVED for Rust 1.70** relative to the cited standard-library contract. `char::from_u32(raw)` safely returns `Some` exactly for a valid scalar and `None` otherwise, which is the stated behavior. Extending this regional result to the open-ended later-version range requires verifying the same contract for each release through the cutoff or accepting the compatibility premise described below. +- With `compact`: **UNPROVED over the full support set.** With debug assertions enabled, the check panics for every surrogate and all other `u16` values satisfy the unchecked conversion's precondition. With them disabled, the surrogate execution has UB. Under the required whole-execution rule, that UB witness proves `UNSOUND` but cannot also prove `CONTRACT-BROKEN`; there is no separate UB-free behavioral counterexample. + +## Obligation ledger and finding + +| Site | Required proposition | Disposition | +|---|---|---| +| `lib.rs:4-9`, public safe API | Every `u16` is handled without an unstated safety precondition | **Failed** for surrogates when debug assertions are disabled | +| `lib.rs:7`, `from_u32_unchecked` | `raw as u32` is a Unicode scalar value | Established only after the debug assertion executes; not established in all supported profiles | +| `lib.rs:12-15` | Return the represented scalar or `None`, without UB | **Proved regionally** by the safe `from_u32` contract | +| `cfg(feature = "compact")` / its negation | Cover both feature values | Exhaustive, disjoint source partition; each branch is target-, width-, and profile-parametric except for `debug_assert!` | + +The controlling Rust 1.70 contracts are: + +- [`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html) is omitted unless debug assertions are enabled; it therefore cannot establish an unsafe precondition across ordinary profiles. +- [`char::from_u32_unchecked`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32_unchecked) requires its input to be a valid Unicode scalar value. +- [`char::from_u32`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32) performs that validation and returns `None` for an invalid `char` value. +- The [Rust 1.70 `char` definition](https://doc.rust-lang.org/1.70.0/reference/types/textual.html) excludes the surrogate interval `0xD800..=0xDFFF`. + +The unsafe block also has no adjacent `SAFETY` proof. A complete proof would need the dominating proposition “`raw` is not a surrogate in every configuration”; the current `debug_assert!` does not supply it. This is both an implementation defect and deficient proof documentation, not merely a missing comment. The minimum acceptable resolution is an unconditional validation before any unchecked conversion, followed by re-audit. + +## Recommended redesign + +Keep the non-`compact` definition unchanged and replace only the body of the `compact` definition with safe validation: + +```rust +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + char::from_u32(raw as u32).expect("surrogate code point") +} +``` + +This preserves both configuration-specific signatures and the documented behavior. Widening `u16` to `u32` preserves its numeric value. Every such value is at most `0xFFFF`; within that range, precisely the surrogate interval is not a scalar. Thus `from_u32` yields `Some(c)` for every represented scalar and `None` precisely for a surrogate; [`Option::expect`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect) returns the `char` in the first case and initiates a panic in the second. Panic-unwind and panic-abort profiles differ only in how that panic terminates; neither can reach unsafe code. Fixed-width integers and safe library calls make the argument independent of target, pointer width, optimization, overflow checks, and debug assertions. The two existing `cfg` predicates continue to cover both feature values. + +The redesign removes the unsafe operation and its proof obligation rather than transferring one to callers. It uses APIs available in Rust 1.70 and requires no signature, feature, target, or MSRV change. It is a design proposal, not an implemented snapshot, so it receives no post-change `PROVED` verdict. + +## TCB, residual scope, and review triggers + +Consumed authority is limited to the linked Rust 1.70 Reference and standard-library contracts; there are no dependency or tool assumptions. A full claim covering every stable release from 1.70 through the cutoff additionally needs the exact premise that those behavioral contracts remain applicable throughout that version domain. That proposition was not established by the supplied files, and an API stability badge alone is insufficient; absent per-version verification, it remains `UNPROVED`. The candidate proof is parametric for every release satisfying those contracts. + +Re-audit after implementing the proposal, on any change to either signature or documented behavior, when feature/profile support changes, and when extending the audit cutoff to a new stable Rust release. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r007.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r007.md new file mode 100644 index 0000000000..f3404f8886 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r007.md @@ -0,0 +1,67 @@ +# Focused unsafe-Rust source review + +## Claim and verdict + +**UNSOUND** for the exact supplied `lib.rs`: not every well-typed safe use is free of Rust undefined behavior on the requested Rust 1.70+, all-target, ordinary-profile support set. A safe use on Rust 1.70 can create two simultaneously live `&mut` references to the same `u8`; that one in-set counterexample refutes the universal claim. The argument is source-semantic and independent of optimization, panic mode, pointer width, or target. No verdict from `DESIGN-NOTE.md` was inherited. + +There are no separately documented unsafe-API postconditions to classify. The safe methods' type-level promise to return valid references is part of the failed soundness claim. + +## Snapshot, boundary, and coverage + +Reviewed every supplied target file: `REQUEST.md`, `DESIGN-NOTE.md`, and `lib.rs`. Scope is the public `View<'a, T>` type, its private `ptr` and `borrow` representation, safe `new`, `get`, and `get_mut`, and implicit move/drop/auto-trait behavior. There are no supplied dependencies, generated artifacts, macros, conditional compilation branches, unsafe traits/impls, FFI, assembly, allocators, or concurrency code. + +Intended invariant **VIEW**: `ptr` remains derived from the live, aligned, initialized `T` exclusively borrowed by `new` for `'a`, and every reference subsequently formed from it obeys reference aliasing requirements. `new` produces the pointer/lifetime relationship, and private fields plus `PhantomData<&'a mut T>` prevent safe callers from replacing the pointer or outliving the original borrow. They do not serialize references returned by the methods. + +## Finding F-1: receiver borrow does not constrain returned reference + +`get_mut` is effectively: + +```rust +fn get_mut<'s>(&'s mut self) -> &'a mut T +``` + +The explicit output lifetime is the struct's `'a`, not the receiver loan `'s`. Consequently, once each call returns, its loan of the `View` can end even though the returned reference remains live. Safe code can therefore do: + +```rust +fn duplicate<'a>(v: &mut View<'a, u8>) + -> (&'a mut u8, &'a mut u8) +{ + let first = v.get_mut(); + let second = v.get_mut(); + (first, second) +} + +fn witness() { + let mut value = 0u8; + let mut view = View::new(&mut value); + let (first, second) = duplicate(&mut view); + *first = 1; + *second = 2; +} +``` + +Both raw dereferences target the pointer copied from the same `&mut value`. At the return/use of the pair, both exclusive references are live and alias the same non-`UnsafeCell` byte. The [Rust 1.70 Reference's undefined-behavior list](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html) classifies breaking pointer aliasing rules as undefined behavior and states that `&mut T` follows the scoped `noalias` model. The [Rust 1.70 lifetime-elision rules](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html) only assign the receiver lifetime to an *elided* method output lifetime; they do not rewrite the explicit `'a` here. Thus safe surface `get_mut` fails invariant VIEW's exclusivity obligation. `get` has the analogous escape and can leave an `'a` shared reference live across a later `get_mut`, giving a second safe aliasing route. + +The two unsafe blocks also have no adjacent `SAFETY` proof. That documentation defect is material: the missing proposition—"the current receiver borrow excludes every conflicting reference for the returned reference's entire lifetime"—is false for both signatures. + +## Obligation disposition + +- `new`: pointer origin, alignment, initialization, and referent lifetime are supplied by its `&'a mut T` input; **PROVED only as producer facts used here**. +- `get`: dereference lifetime/alignment follow from VIEW's producer facts, but its returned shared reference is not kept exclusive of later mutable access; **UNSOUND in composition**. +- `get_mut`: dereference lifetime/alignment follow from the producer facts, but uniqueness for the returned `'a mut T` is not enforced; **UNSOUND**, with the safe witness above. +- Drop/move and all configurations: no destructor or configuration-specific transition repairs or prevents the witness. The defect is parametric over ordinary profiles and targets on which the source compiles. + +TCB is limited to the cited Rust 1.70 Reference propositions. No dependency, tool, platform, test, or prior-review premise is consumed. No build, execution, expansion, or test evidence was used. Because Rust 1.70 is expressly in the support set, this audit need not assume forward compatibility to refute the requested `1.70+` universal theorem. + +## Proposed remedy (not the snapshot verdict) + +Tie each result to its receiver borrow: + +```rust +pub fn get<'s>(&'s self) -> &'s T { /* proved dereference */ } +pub fn get_mut<'s>(&'s mut self) -> &'s mut T { /* proved dereference */ } +``` + +(Ordinary elided `-> &T` / `-> &mut T` spellings have those relationships.) Then a live result keeps the corresponding borrow of `View`, preventing a conflicting safe call. Retain the private pointer and `PhantomData`, and add adjacent `SAFETY` proofs covering origin, non-nullness, alignment, initialization, referent lifetime, and aliasing enforced by the receiver loan. + +This is an **UNPROVED proposed change**, not a verdict on an implemented snapshot. It requires implementation and fresh review; changing only comments cannot repair the current API. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r008.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r008.md new file mode 100644 index 0000000000..1c7e4b009b --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r008.md @@ -0,0 +1,52 @@ +# `decode` configuration-preserving review and redesign + +## Scope and claim + +Artifact: the supplied `lib.rs`, reviewed as source only. The required domain is Rust 1.70+, every target and pointer width, every ordinary profile, and the exhaustive Boolean partition `feature="compact"` / `not(feature="compact")`. Each selected `decode` is a public safe API, so its soundness claim quantifies over every `u16` or `u32` input with no caller safety precondition. The documented return-or-panic behavior is also in scope. There are no other source, generated artifacts, dependencies, or unsafe sites in the supplied target. + +Authoritative Rust 1.70 premises: + +- A `char` is a scalar in `0..=0x10FFFF` excluding `0xD800..=0xDFFF`; the [standard-library validity section](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity-and-layout) says no non-scalar `char` may be constructed. More decisively, the [Reference invalid-value rule](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#invalid-values) classifies producing an invalid value as UB and lists a surrogate-valued `char` as invalid. +- Rust 1.70 [`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html) documents that an optimized build does not execute it unless debug assertions are enabled. +- An unsigned widening cast zero-extends ([Reference numeric casts](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast)), so `raw as u32` preserves every `u16` value. +- [`char::from_u32`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32) safely converts a valid input and returns `None` for an invalid `char` value. [`Option::expect`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect) returns the contained `Some` value and panics for `None`. + +## Current artifact + +**Soundness verdict for the complete published set: `UNSOUND`.** + +Witness: Rust 1.70, `compact` enabled, an ordinary optimized profile with debug assertions disabled, and the well-typed safe call `decode(0xD800)`. The assertion at `lib.rs:6` is not executed. The cast preserves `0xD800`, and `char::from_u32_unchecked` at line 7 produces a surrogate `char`. By the Reference rule above, that production is undefined behavior. One supported configuration and valid safe call suffice to refute the universal claim; target and pointer width are irrelevant to this witness. + +The reconstructed regional proof succeeds only when debug assertions are enabled: surrogate inputs panic before line 7; every remaining `u16` is at most `0xFFFF` and outside the surrogate interval, hence is a scalar satisfying the unchecked conversion's obligation. The unsafe block has no adjacent `SAFETY` proof, but documentation alone could not repair the disabled-assertion branch. + +With `compact` disabled, the complementary branch contains no unsafe operation and directly exposes `char::from_u32`'s `Option` behavior. No defect was found in that branch under the cited Rust 1.70 contract. + +**Compact documented postcondition over the complete set: `UNPROVED`, not `CONTRACT-BROKEN`.** The same supported surrogate call does not establish the promised panic, but its execution contains UB, so it cannot witness a UB-free behavioral violation. The postcondition is proved only in the debug-assertions-enabled region. + +The overall `UNSOUND` result needs no premise about post-1.70 compatibility: Rust 1.70 itself is in the published set. + +## Recommended redesign + +Counterfactual replacement (no source edit was requested): + +```rust +/// With `compact`, returns the represented scalar and panics for a surrogate. +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + char::from_u32(raw as u32).expect("surrogate code point") +} + +/// Without `compact`, returns the represented scalar or `None`. +#[cfg(not(feature = "compact"))] +pub fn decode(raw: u32) -> Option { + char::from_u32(raw) +} +``` + +This is preferred over changing `debug_assert!` to `assert!` because it removes the unnecessary unsafe operation and its proof surface. + +Conditional proof plan: in the compact branch, the widening cast preserves `raw`. Since a `u16` cannot exceed `0xFFFF`, its only invalid `char` values are precisely the documented surrogate interval. `from_u32` therefore yields the represented `char` for every non-surrogate and `None` for every surrogate; `expect` returns the former and panics for the latter. There is no profile-sensitive check. The noncompact branch is unchanged. The two `cfg` predicates are mutually exclusive and exhaustive. Fixed-width inputs and standard scalar conversion make the derivation independent of target, pointer width, optimization, and panic strategy (abort versus unwind changes how a panic terminates, not whether the panic is raised). + +Both signatures and return types remain exact. `char::from_u32` is marked stable since 1.52 on the Rust 1.70 page, and `expect` is present there, so the redesign does not raise the 1.70 MSRV. It changes no documented behavior; the panic text was not promised. + +Because `1.70+` is open-ended, a full future-version behavioral proof must either verify these exact contracts for each supported release or explicitly accept **TCB-COMPAT-1**: every later supported Rust release preserves the cited conversion, validity, cast, and `expect` propositions over this domain. A stability badge alone does not prove that premise. Re-audit on a materially changed contract or each newly admitted release if that premise is not accepted. The design sketch itself receives no `PROVED` verdict until implemented and audited as a new artifact. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r009.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r009.md new file mode 100644 index 0000000000..2d72ed66aa --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r009.md @@ -0,0 +1,36 @@ +# Focused unsafe-Rust source review + +## Claim and results + +Artifact: `targets/m2q8/lib.rs` (SHA-256 `42388bb1a03741a7725c9836cc134c205af3061efef7b8ee00ce38d2dc51aeeb`), under the scope fixed by `REQUEST.md` (SHA-256 `9d559df45c2ef498f44516fed36ef84c82fa4a02a59edca55d2ff88a7b93eb02`). The claim covers Rust and `core` 1.80.0, every target on which this exact source is accepted, and all ordinary profiles. It quantifies over every well-typed safe call. No generated artifacts, dependencies, caller obligations, or deployment restrictions are present. + +- **Soundness: UNSOUND.** The valid safe call `classify(0)` reaches undefined behavior. +- **Mandatory documented behavior: CONTRACT-BROKEN.** The valid call `classify(1)` returns normally with `2`, contradicting the promise to return `input` on normal return. Independently, the promised panic for input `0` is not established: that path reaches undefined behavior instead of a guaranteed panic. +- Consequently the combined soundness-and-behavior claim is not `PROVED`. + +These are source-level Rust results relative only to the versioned axioms below; they make no binary/backend-correctness claim. + +## Boundary and obligation inventory + +The complete externally reachable surface is the safe free function `pub fn classify(input: u8) -> u8` at `lib.rs:6`. It has no caller-side safety precondition. There are no public fields, traits, macros, callbacks, hidden APIs, constructors, or unsafe public APIs. The sole unsafe site is the call to `core::hint::unreachable_unchecked` at `lib.rs:8`. There is no invariant-bearing state. + +The controlling documentation has two mandatory clauses: + +1. If `input == 0`, the function panics. +2. On every normal return, the returned value equals `input`. + +## Evidence and derivation + +**AXIOM-MATCH-1.80.** The Rust 1.80.0 Reference says the scrutinee value is sequentially compared with arm patterns and “The first arm with a matching pattern is chosen” ([match expressions](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html)); literal patterns “match exactly the same value” as their literals ([literal patterns](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns)). + +**AXIOM-UNREACHABLE-1.80.** The Rust 1.80.0 `core` contract states: “Reaching this function is *Undefined Behavior*” ([`core::hint::unreachable_unchecked`, Safety](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety)). + +For `input = 0`, AXIOM-MATCH-1.80 and `lib.rs:7-8` select the `0` arm, so the unsafe call is reached. This directly violates its only relevant precondition and, by AXIOM-UNREACHABLE-1.80, exhibits undefined behavior. Because `classify` is safe and `0u8` is an admitted input, this is a valid safe-use counterexample. No local `SAFETY` proof is present; none could discharge the obligation from the current control flow. + +For `input = 1`, the `0` pattern does not match, the `1` pattern does, and `lib.rs:9` evaluates to `2u8`; this is the normal return value of `classify`. Since `2 != 1`, clause 2 has a defined counterexample, establishing `CONTRACT-BROKEN` independently of the UB finding. For each `input` in `2..=255`, the wildcard arm at `lib.rs:10` returns `input`, so clause 2 holds on those paths. For `input = 0`, clause 1 has no valid proof because execution has already reached UB; UB cannot supply a guaranteed panic outcome. + +## Configuration closure, evidence limits, and residual scope + +The input partition `{0}`, `{1}`, and `{2..=255}` is exhaustive for `u8`. The source contains no `cfg`, target feature, layout dependence, build input, generated code, arithmetic, allocation, FFI, concurrency, panic-mode branch, or profile-dependent assertion. The same syntactic match edges establish both counterexamples for every in-scope target/profile; optimization cannot rescue a source execution that the 1.80.0 library contract already classifies as UB. No build, test, execution, or macro expansion was used or needed. + +The TCB consists only of AXIOM-MATCH-1.80 and AXIOM-UNREACHABLE-1.80 as quoted and linked. There are no additional assumptions or tool-derived facts. Any change to the source, its documentation, the Rust/`core` version, or the supported configuration set requires re-review. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r010.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r010.md new file mode 100644 index 0000000000..ce036179c6 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r010.md @@ -0,0 +1,45 @@ +# Focused source review: `decode` + +## Claim, snapshot, and verdict + +Reviewed `REQUEST.md` (SHA-256 `8d8801a4dddb5d5b9a1d1971fcda4eb017bcf0261fe7d97b1cd983bc51240a0b`) and `lib.rs` (SHA-256 `1ebbedcffa0ad4b2c3183ec04b4789d9c2daaeae6c19dc0e1d7f926ac685e89b`). Scope is the two configuration-specific public safe `decode` APIs, their stated behavior, and the one unsafe operation. The published set is stable Rust 1.70+, all targets and pointer widths, ordinary profiles, and both values of `feature = "compact"`. No code was executed or changed. + +**Current-artifact soundness: UNSOUND.** A valid safe call in a supported configuration reaches undefined behavior: with `compact` enabled, an optimized profile whose debug assertions are disabled, and `raw = 0xD800`, line 6 performs no runtime check and line 7 constructs a surrogate `char`. + +**Current documented behavior:** the non-`compact` branch is **PROVED** for Rust 1.70 under the axioms below. The `compact` branch is **PROVED** only where debug assertions execute; over the published profile set its panic guarantee is **UNPROVED**, and the branch is **UNSOUND**. No separate `CONTRACT-BROKEN` verdict is needed: the failing path has UB rather than a defined non-panicking outcome. + +## Derivation and configuration closure + +Rust 1.70 documents `char` as a Unicode scalar: `0..=0x10FFFF` excluding `0xD800..=0xDFFF`; its unchecked constructor ignores validity and can create an invalid `char` ([`char` validity and conversions](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity), [`from_u32_unchecked`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32_unchecked)). The Reference classifies producing a surrogate `char` as producing an invalid value and therefore UB ([undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)). + +The only precondition of line 7 material here is therefore: `raw as u32` is not a surrogate. `u16` ranges through `0..=2^16-1`, and widening an unsigned integer zero-extends it ([integer types](https://doc.rust-lang.org/1.70.0/reference/types/numeric.html#integer-types), [numeric casts](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast)). Thus no `u16` exceeds `char::MAX`; exactly the surrogate interval is invalid. The source check recognizes that interval, but Rust 1.70 says optimized builds do not execute `debug_assert!` by default ([`debug_assert!` uses](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html#uses)). A debug-only check cannot discharge the all-profile precondition. The absent adjacent `SAFETY` proof also fails to record this obligation, but documentation alone cannot repair the implementation. + +The feature predicates are a total, disjoint partition: `cfg(P)` retains an item exactly when `P` is true, while `not(P)` has the opposite truth value ([conditional compilation](https://doc.rust-lang.org/1.70.0/reference/conditional-compilation.html#the-cfg-attribute)). Coverage is: + +| `compact` | Debug assertions | Disposition | +|---|---|---| +| false | either | `char::from_u32(raw)` safely returns the represented scalar or `None`, exactly as documented. | +| true | enabled | Surrogates panic before line 7; every other `u16` is a valid scalar. | +| true | disabled | `decode(0xD800)` reaches line 7 and produces an invalid `char`: **UNSOUND**. | + +No generated code, dependencies, allocation, concurrency, FFI, target layout, or pointer-width premise occurs. Target and pointer-width coverage is parametric; only fixed-width integers and `char` semantics are used. + +## Recommended redesign + +Replace only the `compact` implementation; retain both existing signatures, attributes, documentation, and the non-`compact` implementation: + +```rust +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + char::from_u32(raw as u32) + .expect("compact decode input is a surrogate") +} +``` + +Conditional proof plan: the widening cast preserves `raw`. Within the `u16` domain, `char::from_u32` returns `None` exactly for surrogates and the represented `char` otherwise. Rust 1.70 `Option::expect` returns the contained `Some` value and panics for `None` ([`Option::expect`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect)). Therefore the candidate returns precisely the represented scalar for every non-surrogate and panics for every surrogate, independently of optimization and debug assertions. It contains no unsafe operation or hidden caller obligation. The used APIs exist in Rust 1.70, so the MSRV is unchanged; fixed-width types and safe validation make the proof independent of target, pointer width, and ordinary profile. The other feature branch is unchanged. + +This is a design, not an implemented artifact, so it has no `PROVED` verdict until implemented and audited as a new snapshot. + +## TCB and review triggers + +The Rust 1.70 Reference and standard-library propositions linked above are the only axioms consumed. Applying them to later stable versions requires explicit **TCB-COMPAT-1**: later supported stable Rust releases preserve these exact `cfg`, integer-cast, `char::from_u32`, and `Option::expect` propositions. The open-ended `1.70+` claim is conditional on that premise; re-audit on a material documentation/semantic change, support-set expansion, or any implementation/signature/documentation change. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r011.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r011.md new file mode 100644 index 0000000000..eed3e39e01 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r011.md @@ -0,0 +1,38 @@ +# Focused review and redesign of `ticket` + +## Claim, scope, and trust + +Snapshot: the complete supplied `REQUEST.md` and `lib.rs`, reviewed 2026-07-31 without execution. The requested domain is stable Rust 1.70+ on every target and every ordinary build profile, with no dependencies or deployment restrictions. In scope are the public opaque `Ticket(NonZeroUsize)` type, whose field is private, and the safe `pub fn ticket(id: usize) -> Ticket`; there are no other explicit constructors, methods, trait impls, macros, features, generated code, or target branches in the supplied source. Compiler-provided auto traits add no separate representation invariant beyond that of the private `NonZeroUsize` field. + +The Rust axioms consumed are the Rust 1.70 `core` contracts: + +- [`debug_assert!`](https://doc.rust-lang.org/1.70.0/core/macro.debug_assert.html): it invokes `panic!` when an executed expression is false, but is “only enabled in non optimized builds by default”; optimized builds do not execute it unless `-C debug-assertions` is passed. +- [`NonZeroUsize::new_unchecked`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new_unchecked): constructing from zero “results in undefined behaviour”; its Safety clause says, “The value must not be zero.” The same page says safe `new` “Creates a non-zero if the given value is not zero.” +- [`Option::expect`](https://doc.rust-lang.org/1.70.0/core/option/enum.Option.html#method.expect): it returns the contained `Some` value and panics for `None`. + +The 1.97.1 versions inspected at the audit cutoff retain these propositions. Any claim covering later stable releases is relative to **TCB-COMPAT**: stable Rust preserves these stable safe-API behaviors and safety contracts; otherwise each new release is a re-review trigger. The current-artifact `UNSOUND` verdict itself needs no compatibility premise because Rust 1.70 is in the supported set. + +## Current artifact + +**Soundness verdict: UNSOUND** for the requested supported set. + +The only unsafe obligation is that the argument to `NonZeroUsize::new_unchecked(id)` be nonzero. The safe API imposes no caller safety precondition, and `ticket(0)` is a well-typed safe call. + +- With debug assertions enabled, `debug_assert!(id != 0)` panics for zero before reaching the unsafe call. For nonzero inputs, the assertion passes and the local fact `id != 0` discharges `new_unchecked`'s precondition. +- With debug assertions disabled—most importantly, in an ordinary optimized build by default—the assertion does not execute. `ticket(0)` reaches `new_unchecked(0)`, violates its explicit Safety clause, and has undefined behavior. This is a valid safe-use witness on every target; no target-specific fact is involved. + +For `id != 0`, the implementation is sound and returns a `Ticket` containing that `id`. For `id == 0` with debug assertions enabled, it has the documented panic. For the documented zero-input postcondition over the full supported set, the verdict is **UNPROVED, not CONTRACT-BROKEN**: the release-path witness contains UB, so it cannot also establish a UB-free execution that falsifies the panic guarantee. The unsafe block also lacks an adjacent safety proof, but the release configuration makes the required proof false rather than merely undocumented. + +## Recommended redesign + +Keep the public type, documentation, and exact safe function signature unchanged; replace only the body: + +```rust +pub fn ticket(id: usize) -> Ticket { + Ticket(NonZeroUsize::new(id).expect("ticket id must be nonzero")) +} +``` + +This is the smallest proof surface: it deletes the unsafe operation and makes validation unconditional rather than profile-dependent. For nonzero `id`, `new(id)` supplies `Some` containing that value, `expect` returns it, and `Ticket` stores it. For zero, `new` supplies `None` and `expect` panics. Neither optimization, `debug_assertions`, target properties, nor panic strategy changes which branch is selected; a panic-abort profile changes unwinding into termination but does not bypass the documented panic. No invariant is temporarily suspended and no caller obligation is introduced. + +This is a design proof, not a verdict for unimplemented source. After applying it, re-audit the new snapshot; relative to the cited per-version contracts and TCB-COMPAT, the intended post-change result is **PROVED** for soundness and the documented behavior across the requested domain. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r012.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r012.md new file mode 100644 index 0000000000..3096c1c23f --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r012.md @@ -0,0 +1,85 @@ +# Focused source review: `classify` + +## Claim and verdicts + +Snapshot: `lib.rs` SHA-256 +`42388bb1a03741a7725c9836cc134c205af3061efef7b8ee00ce38d2dc51aeeb`. +The reviewed surface is exactly the safe public function +`classify(input: u8) -> u8` at `lib.rs:6-12`, including its documentation and +the unsafe operation at line 8. There are no other API items, dependencies, +generated artifacts, macros, fields, traits, or representation invariants in +the supplied source. + +Supported set: Rust and `core` 1.80.0, every target on which that toolchain +accepts this source, every accepted edition, and every ordinary profile/panic +strategy. The proof is parametric over those axes: the file contains no `cfg`, +target operation, arithmetic, assertion, build input, or profile-dependent +branch. + +The soundness claim quantifies over every well-typed safe call. The mandatory +behavioral claims are (B1) `input == 0` causes a panic and (B2) every normal +return equals `input`. + +- **Soundness: UNSOUND** throughout the supported set. The valid safe call + `classify(0u8)` reaches undefined behavior. +- **B1 (panic for zero): UNPROVED.** Its only source path reaches UB. That + UB-containing execution cannot establish either a defined panic or a + UB-free behavioral counterexample. +- **B2 (normal-return value): CONTRACT-BROKEN** throughout the supported set. + The separate, UB-free call `classify(1u8)` returns `2`, not `1`. + +Thus the combined API claim is not `PROVED`; it has both an `UNSOUND` +soundness result and an independently witnessed `CONTRACT-BROKEN` behavior +result. + +## TCB and authoritative premises + +TCB `R012-v1` contains only these Rust 1.80.0 axioms; there are no admitted +dependency, implementation, environment, or tool premises: + +1. The [numeric-types table](https://doc.rust-lang.org/1.80.0/reference/types/numeric.html#integer-types) + gives `u8` minimum 0 and maximum 2^8-1, so both witness arguments are valid + `u8` values. +2. The [patterns chapter](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns) + says literal patterns “match exactly the same value” created by the literal; + its [wildcard section](https://doc.rust-lang.org/1.80.0/reference/patterns.html#wildcard-pattern) + says `_` matches any value. The + [match-expression rules](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html) + select the first matching arm and enter its block. +3. The Rust 1.80.0 [`unreachable_unchecked` safety contract](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety) + states: “Reaching this function is Undefined Behavior.” + +These are source-level Rust semantics. No compiler-backend or produced-binary +correctness is assumed or claimed. + +## Obligation ledger and derivation + +**S1 — safe-surface soundness (`lib.rs:6-12`): UNSOUND.** Safe callers have no +safety precondition. For `input = 0`, the `0` literal pattern matches and its +arm is selected. Line 8 therefore reaches `unreachable_unchecked`; premise 3 +makes the whole execution UB. The unsafe block has no adjacent safety proof, +and the necessary proposition—“this call site is unreachable”—is not merely +undocumented but false. This witness is valid on every supported configuration +because all premises and source control flow are target/profile independent. + +**D1 — B1 (`lib.rs:3`): UNPROVED.** The zero arm contains no panic operation +before reaching UB. Under the required whole-execution rule, S1 cannot be +reused to claim a defined failure to panic. No independent UB-free zero-input +witness or proof establishes or refutes B1. + +**D2 — B2 (`lib.rs:5,9`): CONTRACT-BROKEN.** For `input = 1`, the `0` pattern +does not match and the `1` pattern does, so line 9 evaluates to `2` and the +function normally returns `2`. The unsafe arm is not executed; this simple +safe-Rust execution contains no UB. Because `2 != 1`, it is the required +UB-free existential counterexample. For inputs 2 through 255, `_ => input` +satisfies B2; the exhaustive partition is `{0}`, `{1}`, and `{2..=255}`. + +## Findings and disposition + +The implementation defect is reachable by an unrestricted safe caller in every +supported configuration. Minimal remediation is to use a real panic on the +zero arm and return `input` on the one arm (or remove that special arm), then +re-audit the new snapshot. No source change was made. Re-review is required if +the source, documentation, supported toolchain/configurations, or any cited +Rust contract changes. Undocumented robustness and binary/backend correctness +remain outside scope. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r013.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r013.md new file mode 100644 index 0000000000..841e722682 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r013.md @@ -0,0 +1,45 @@ +# Focused review: `Ticket::ticket` + +## Claim, scope, and verdict + +**Current safe API verdict: `UNSOUND`.** For the exact `lib.rs` snapshot (SHA-256 `23f55cd3e96c8372d71a09336d84f72de191fd5f48de693d0be11762537dfa9f`), there is a well-typed safe call, `ticket(0)`, in a supported ordinary optimized build that reaches Rust undefined behavior. This is a focused verdict for the two public items in `lib.rs`, not a whole-crate verdict. + +The controlling support policy is `REQUEST.md` (SHA-256 `f4cb0fe5d667cdde8536d103ea2bf8c46a4219916acb811b0e33c4d2cb5bc376`): Rust 1.70+, every target, every ordinary build profile, no dependencies or deployment restrictions. The relevant configuration partition is debug assertions enabled versus disabled; the source has no `cfg`, generated code, target-specific branch, dependency, or other feature axis. The proof below uses Rust 1.70.0, an optimized profile without `-C debug-assertions`, which is one member of that supported set and therefore suffices to refute its universal soundness claim. + +Documented behavior is separate: + +- For `id != 0`, normal return with a `Ticket` containing `id` is **`PROVED` for Rust 1.70.0**: the value supplied to `new_unchecked` satisfies its precondition and is stored in the private field. +- For `id == 0`, “panics” is **`UNPROVED` over the supported set**, not `CONTRACT-BROKEN`. Debug-assertion-enabled executions panic, but the disabled execution below contains UB. A UB-containing execution cannot witness a UB-free violation of the panic guarantee. + +## Boundary and obligation coverage + +`Ticket` is a public opaque tuple struct whose only field is private. The only in-scope safe producer is the public safe function `ticket(usize) -> Ticket`; there are no public fields, other constructors, methods, explicit trait impls, macros producing APIs, or hidden APIs. Moving and dropping a valid `Ticket` do not create another ingress. The representation invariant is: on every normal return, `Ticket.0` is a valid `NonZeroUsize` representing the supplied `id`. The sole unsafe consumer is `NonZeroUsize::new_unchecked` at `lib.rs:10`. + +## Finding and derivation + +Rust 1.70 documents that an optimized build does not execute `debug_assert!` unless debug assertions are explicitly enabled: “An optimized build will not execute `debug_assert!` statements unless `-C debug-assertions` is passed” ([`debug_assert!`, Rust 1.70](https://doc.rust-lang.org/1.70.0/core/macro.debug_assert.html#uses)). Thus, in the supported optimized/no-debug-assertions class, `ticket(0)` reaches `new_unchecked(0)`. + +The exact callee contract says of zero: “This results in undefined behaviour if the value is zero” ([`NonZeroUsize::new_unchecked`, Rust 1.70](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new_unchecked)). `ticket` is safe and documents no caller safety precondition, so `0` is a valid safe input. The false implication is therefore “the debug assertion establishes `id != 0` at the unsafe call in every supported profile.” It does not. No local `SAFETY` comment exists, but adding one cannot repair the failed premise. + +## Recommended redesign (not applied) + +Preserve the exact public items and documentation, replacing only the body: + +```rust +pub struct Ticket(NonZeroUsize); + +/// Returns a ticket containing `id`; panics when `id == 0`. +pub fn ticket(id: usize) -> Ticket { + Ticket(NonZeroUsize::new(id).expect("id must be non-zero")) +} +``` + +This is preferable to changing `debug_assert!` to `assert!` while retaining `new_unchecked`: it removes the unsafe operation and its proof obligation entirely. Rust 1.70’s checked constructor “Creates a non-zero if the given value is not zero” ([`NonZeroUsize::new`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new)); `Option::expect` returns the contained `Some` value and “Panics if the value is a `None`” ([`Option::expect`](https://doc.rust-lang.org/1.70.0/core/option/enum.Option.html#method.expect)). Hence nonzero input produces a `Ticket` containing that input, while zero input panics. Neither operation depends on optimization or debug assertions, and the panic message was never promised. The signature, field privacy, representation, and documented outcomes remain unchanged on every ordinary profile and target. + +This is a design and conditional proof plan, not a post-change verdict; the proposed source was not implemented or executed. After implementation it should receive a fresh source review. + +## TCB, residual scope, and triggers + +The current `UNSOUND` verdict consumes only the two quoted Rust 1.70 standard-library axioms and local control/data flow; it needs no dependency, environment, test, or compiler-backend premise. The source-level result is relative to documented Rust abstract semantics. + +For the redesign’s open-ended “1.70+” behavioral proof, record **COMPAT-1**: every supported later Rust standard library preserves the exact checked-constructor and `Option::expect` propositions quoted above. The allowed authoritative set contains no version-spanning compatibility theorem, so COMPAT-1 is an explicit compatibility TCB premise, not inferred from stability badges. Re-review if the source, public documentation/signature, support range, ordinary-profile definition, or either consumed standard-library contract changes. Future releases require COMPAT-1 to remain accepted or their exact documentation to be checked. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r014.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r014.md new file mode 100644 index 0000000000..2efca631f4 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r014.md @@ -0,0 +1,108 @@ +# Supported-domain source review + +## Claim and verdict + +**Source-level soundness: PROVED**, relative to `TCB-R014-v1`, for the exact +`lib.rs` snapshot below and every well-typed safe call to its public +`first(&[u8]) -> Option`, over + +`D = {stable Rust 1.79.0, 1.80.0, 1.81.0, 1.82.0} × +{x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu} × {fast off, fast on}`. + +This is a Rust-abstract-semantics theorem: every such call is free of Rust +undefined behavior, with no caller-side safety precondition beyond a valid +safe-Rust call. It is not a compiler/backend, produced-binary, or platform +implementation theorem. The crate documents no postcondition requiring a +separate verdict. + +`D` is an audit domain, not a newly selected support policy. Both published +policy sets are subsets of `D`, so this proof entails source soundness under +Policy A, Policy B, their intersection, or their union without deciding which +set is authoritative. + +## Snapshot, surface, and configuration closure + +Reviewed all seven supplied target files. Key identities are `lib.rs` SHA-256 +`6f87e944cec7ee2727f7c7d32aa382de88987ea791082175d611fff5bf44012b`, +Policy A `387664b8092a74c7f80ae6cccfbec50160e8e7e215f0355677b33d36bc97f479`, +and Policy B `c1885e8ef901624ab9b2813b2f37a4e2c167d3fb53ea0798b6586f87293ed246`. +The manifest is edition 2021, defines only `fast`, and has no dependencies; +the toolchain file selects 1.82.0 by default. There is one public safe surface, +`first`; no fields, traits, macros, generated code, FFI, assembly, concurrency, +or persistent invariant exists. + +The version-matched References say `not()` is true when its predicate is false +and a false `cfg` removes its item. Thus `cfg(not(feature = "fast"))` and +`cfg(feature = "fast")` are mutually exclusive and exhaustive for the feature +boolean. Target, profile, optimization, and panic choices do not affect either +source path or the proof. The result covers all 16 cells of `D` by two feature +cases, not by the sampled CI matrix. + +The policies agree on all eight non-`fast` cells, fast x86_64 on 1.80–1.82, +and fast aarch64 on 1.82. They conflict on fast x86_64/1.79 and fast +aarch64/1.80–1.81. With no authorized precedence rule, the exact predicate +`Supported(c)` is unresolved; neither the 1.82 toolchain default nor CI's +explicitly non-definitional samples resolve it. This is a policy-documentation +defect, not a source-soundness gap, because `D` also covers every disputed +cell (and fast aarch64/1.79, which neither policy claims). + +## Obligation ledger and derivation + +- **O-NORMAL — `lib.rs:3-6` — PROVED.** The non-`fast` implementation contains + no unsafe operation and composes safe slice/`Option` APIs. It imposes no + hidden obligation on the safe caller. + +- **O-FAST — `lib.rs:8-15` — PROVED.** The sole unsafe operation is + `bytes.get_unchecked(0)` at line 13. Its controlling contract says an + “out-of-bounds index is undefined behavior”; the obligation is therefore + that index 0 is in bounds. On the `else` edge, `bytes.is_empty()` returned + false. The version-matched API contract says it “Returns `true` if the slice + has a length of 0”; by contraposition, false implies nonzero length. Slice + length is a `usize`, hence it is positive, and the References state that + slice indices are zero-based, so 0 is in bounds. No callback or mutation + intervenes, and this is the same shared slice. `get_unchecked` therefore + returns its first `&u8`; the safe dereference copies that valid `u8` before + the borrow ends. The empty edge performs no unsafe operation. + +The cited propositions were checked separately, without a forward/backward +compatibility assumption, in the exact releases: +[1.79 cfg](https://doc.rust-lang.org/1.79.0/reference/conditional-compilation.html#the-cfg-attribute), +[indexing](https://doc.rust-lang.org/1.79.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions), +[`is_empty`](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.is_empty), +[`get_unchecked`](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.get_unchecked); +[1.80 cfg](https://doc.rust-lang.org/1.80.0/reference/conditional-compilation.html#the-cfg-attribute), +[indexing](https://doc.rust-lang.org/1.80.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions), +[`is_empty`](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.is_empty), +[`get_unchecked`](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.get_unchecked); +[1.81 cfg](https://doc.rust-lang.org/1.81.0/reference/conditional-compilation.html#the-cfg-attribute), +[indexing](https://doc.rust-lang.org/1.81.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions), +[`is_empty`](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.is_empty), +[`get_unchecked`](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.get_unchecked); +[1.82 cfg](https://doc.rust-lang.org/1.82.0/reference/conditional-compilation.html#the-cfg-attribute), +[indexing](https://doc.rust-lang.org/1.82.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions), +[`is_empty`](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.is_empty), and +[`get_unchecked`](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked). + +## TCB, findings, and residual scope + +`TCB-R014-v1` contains only the preceding exact, version-matched Rust +Reference/std propositions and the ordinary source-level premise that a valid +safe call supplies a valid `&[u8]`. No dependency, test, CI, tool, or +cross-version compatibility premise is consumed. + +**Proof-documentation finding:** line 13 has no adjacent `SAFETY` proof. The +implementation obligation is proved only by the material reconstruction above. +No edit was authorized; suitable wording is: + +```rust +// SAFETY: On this branch `is_empty()` returned false for this unchanged slice. +// Therefore its length is nonzero, so zero-based index 0 is in bounds, as +// required by `get_unchecked`. +``` + +**Policy finding:** publish one reconciled policy or an explicit +precedence/supersession rule before making an exact support-set claim. + +No build, test, or expansion was performed. Re-review is required if the +source, either policy, supported axes/cutoff, or any consumed versioned Rust +contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r015.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r015.md new file mode 100644 index 0000000000..ea5e9de2a8 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r015.md @@ -0,0 +1,55 @@ +# Version-partitioned source review: `advance_marker` + +## Claim and verdict + +Scope is the exact `lib.rs` supplied in `targets/h5p9`, specifically its only public API: + +```rust +pub fn advance_marker() -> *const [u8; 0] +``` + +The claim is source-level Rust soundness for every well-typed safe call, separately under Rust/core 1.79.0 and 1.80.0, on every target supported by the respective toolchain and in every ordinary profile. There are no caller inputs or caller-side safety obligations, and the API documents no behavioral postcondition beyond its type. + +| Region | Soundness verdict | Reason | +|---|---|---| +| Rust 1.79.0 | **UNSOUND** | Every safe call violates `pointer::add`'s allocation precondition and therefore has undefined behavior. | +| Rust 1.80.0 | **PROVED**, relative to TCB below | The byte offset is zero, for which this version explicitly makes `add` well-defined. | +| Combined supported set | **UNSOUND** | The union includes the unsound Rust 1.79.0 region. | + +No separate documented-postcondition failure exists because this source supplies no API documentation promising a particular returned value. + +## Boundary and obligation inventory + +The complete safe surface is the one safe, argument-free public function. Its only unsafe site is: + +```rust +core::ptr::null::<[u8; 0]>().add(1) +``` + +There are no fields, constructors, traits/impls, callbacks, dependencies, macros, generated artifacts, mutable state, destructors, concurrency, FFI, or other invariant transitions. Receiving or handling a raw pointer is safe; any later unsafe operation performed by a caller has its own out-of-scope obligations. + +`size_of` states that `[T; n]` has size `n * size_of::()`; hence `[u8; 0]` has size zero on every target ([1.79.0](https://doc.rust-lang.org/1.79.0/core/mem/fn.size_of.html), [1.80.0](https://doc.rust-lang.org/1.80.0/core/mem/fn.size_of.html)). Both versions describe `null` as creating a null raw pointer whose address is zero ([1.79.0](https://doc.rust-lang.org/1.79.0/core/ptr/fn.null.html), [1.80.0](https://doc.rust-lang.org/1.80.0/core/ptr/fn.null.html)). Consequently `add(1)` computes the mathematical byte offset `1 * 0 = 0`. + +### Rust 1.79.0 derivation + +The applicable [`add` contract](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add) requires both starting and resulting pointers to be in bounds or one byte past the same allocated object; it contains no zero-offset exception. The same version's [`core::ptr` safety text](https://doc.rust-lang.org/1.79.0/core/ptr/index.html#safety) states: “A null pointer is never valid, not even for accesses of size zero.” `null()` supplies the null starting pointer, not a pointer into or derived from an allocated object. The mandatory allocation clause therefore fails. The contract says violating any listed condition makes the result undefined behavior. Since every ordinary safe call unconditionally reaches this site, it is a valid safe-use counterexample: **UNSOUND**. + +The other arithmetic clauses are satisfied (`0` fits `isize`, and address `0 + 0` fits `usize`), but conjunction requires every clause. + +### Rust 1.80.0 derivation + +The [`add` contract](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add) changed the allocation requirement to apply only when the computed byte offset is nonzero and says: “If it is zero, then the function is always well-defined.” Here the byte offset is zero. It also fits `isize`, and adding zero to address zero cannot wrap and fits `usize`. Thus every literal precondition is discharged. The returned raw pointer creates no additional obligation inside this function: **PROVED**. + +## Configuration closure + +The source has no `cfg`, target/layout branch, feature, dependency, profile-sensitive check, overflow operation, allocation, panic path, or generated code. The proof is parametric: the array-size identity yields zero on every target, and the version-specific contracts cover all ordinary profiles. The two singleton version regions are exhaustive for the requested set. No execution, compilation, testing, or expansion evidence was used. + +## Findings and remedy + +**F1 — UNSOUND on Rust 1.79.0 (critical).** The safe API unconditionally performs an `add` whose allocation precondition is false. To support both requested versions, remove the semantically inert `add(1)` or use a safe wrapping operation if pointer arithmetic is intentionally being expressed; merely adding documentation cannot repair a safe API's UB. + +**F2 — proof documentation missing.** The unsafe block has no adjacent `SAFETY` proof. Even in the proved 1.80.0 region, review required reconstructing the material zero-size derivation above. After fixing F1 (or enforcing Rust >=1.80), add an adjacent comment naming the applicable versioned `add` contract, deriving `size_of::<[u8; 0]>() == 0`, and discharging all three clauses. + +## TCB, exclusions, and review triggers + +TCB revision `h5p9-r015-v1` contains only the exact versioned standard-library propositions linked above (`size_of`, `ptr::null`, `pointer::add`, and the 1.79.0 `core::ptr` safety text). There are no admitted dependency, implementation, platform, compatibility, or tool assumptions. Compiler/backend correctness and binary behavior are excluded; this is a source-semantics result. Re-review is required if the function, supported Rust versions, or any consumed standard-library contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r016.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r016.md new file mode 100644 index 0000000000..e09ddc4111 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r016.md @@ -0,0 +1,43 @@ +# Focused review: `Block`, `Page`, and `first` + +## Claim and verdict + +Snapshot: `REQUEST.md` SHA-256 `2bcac27f7acf0ad938ab33108bed08dce1a0eecfec08293c36fb2b15b4d8afac`; `lib.rs` SHA-256 `8e347fd2a5ca16fa1bd9a7b6019fc57227346bd895c5af794d2efa76265f01a3`. Scope is only `Block`, `Page`'s `unsafe impl`, and `first`, for stable Rust 1.70+, all targets and profiles. No build, expansion, execution, or source change was performed. + +- **`Page` implementation: PROVED for Rust 1.70, all targets/profiles.** It establishes every stated `Block` conjunct. +- **`first`: UNPROVED under the literal published contract.** Its implementation is proved if “readable” includes “byte 0 is initialized as a valid `u8` and may be non-atomically loaded without a conflicting access during the borrow.” The text does not define that proposition. This is a contract-documentation gap, not a demonstrated UB counterexample. +- **Requested open-ended Rust 1.70+ claim: UNPROVED.** The Rust 1.70 derivation does not automatically cover every later stable release. `TCB-COMPAT-PENDING` would have to assert that every cited layout, validity, `as_ptr`, and dereference proposition remains applicable on every supported later release. No acceptance of that non-authoritative compatibility premise was supplied. + +There are no documented postconditions on `first` beyond its return type. `Page`'s trait postconditions are proved below. + +## Contract and obligation ledger + +`Block` requires: (B1) `ALIGN != 0`; (B2) `ALIGN` is a power of two; and, for the `&self` borrow, `base()` returns a pointer that is (B3) non-null, (B4) `ALIGN`-aligned, and (B5) readable over bytes `[p, p + 16)`. Rust 1.70 says implementing an unsafe trait may be unsafe while using a correctly implemented one is safe ([Reference, unsafe traits](https://doc.rust-lang.org/1.70.0/reference/items/traits.html#unsafe-traits)); downstream unsafe implementations therefore supply these promises, and safe generic consumers may use exactly them. + +**`Page` producer (`lib.rs:12-21`).** B1-B2 hold because `ALIGN` is 16. Under `repr(C)`, field placement starts at offset zero, so the sole field is at offset zero; `align(16)` raises the struct alignment to 16 ([C field algorithm](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs); [alignment modifiers](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers)). Thus `self.0.as_ptr()` has the `Page` address and is 16-aligned. Rust 1.70 specifies `u8` size 1; size is a multiple of alignment, so `u8` alignment is 1; `[u8; 16]` has size 16 with elements at successive byte offsets ([size/alignment](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#size-and-alignment); [primitive layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#primitive-data-layout); [array layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). A valid `&Page` entails a live, initialized field ([Rust 1.70 validity rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)); `as_ptr` “returns a raw pointer to the slice’s buffer,” and its documented lifetime requirement is met for the `&self` borrow ([std 1.70 `slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)). Therefore B3-B5 hold, including the stronger missing initialization fact. There are no callbacks, mutation, arithmetic, or alternate exits. + +The implementation has no adjacent proof. Suitable non-contract-changing proof text is: “`ALIGN` is 16. `repr(C)` places the sole `[u8; 16]` field at offset zero; `align(16)` aligns `Page` to 16. A valid shared `Page` borrow keeps all 16 initialized contiguous bytes live, and `as_ptr` returns their buffer address until that borrow ends.” + +**`first` consumer (`lib.rs:23-25`).** The load needs byte 0 to be live/non-dangling, aligned for `u8`, initialized, and available for a non-conflicting read until the load. Rust 1.70 calls dereferencing a dangling or unaligned raw pointer UB and calls an integer read from uninitialized memory invalid ([Reference, UB and dangling pointers](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)). The call and load occur within `block`'s borrow. B3 and B5 supply liveness if “readable” has that meaning; `u8` alignment is 1, so neither B1, B2, B4, nor an alignment greater than one is needed. Bytes 1-15 are unused. The smallest missing implication is: **B5 implies that byte 0 is initialized and that this non-atomic load is permitted throughout the stated interval.** Once that is established by the existing contract channel, an adjacent proof can cite only that first-byte subset. + +## What 1.x may simplify + +Within the supplied ordinary-SemVer commitment, keep `Block` and every existing guarantee. Unknown downstream consumers may rely on `ALIGN`, its alignment, or all 16 bytes; repository search cannot narrow a public contract. Unknown downstream implementations also constrain any strengthening. + +Compatible proof-oriented work is limited to representation-neutral documentation and internals: add the complete `Page` proof above; define the already-intended meaning of “readable” only if existing published authority proves it was always that meaning; and factor a private first-byte lemma/helper so `first` visibly consumes only liveness, initialization, and read permission for byte 0. Additive replacement APIs may be introduced and the old API deprecated, but the old guarantees and valid implementations must continue to work throughout 1.x. + +Changing `Page::ALIGN` from 16, dropping `align(16)`/its public layout, reducing 16 readable bytes to one, removing non-null/alignment guarantees, or strengthening implementer obligations with a genuinely new initialization/concurrency condition is not a proof-comment cleanup. Each can invalidate published consumer reasoning or implementations. If the missing initialization proposition is not already authoritative, `first` is a defect requiring disclosed remediation; editing prose cannot retroactively make old implementations invalid. + +## Authorized 2.0 design and migration + +The preferred major-version split is a safe minimal capability: + +```rust +pub trait FirstByte { fn first_byte(&self) -> u8; } +pub fn first(b: &B) -> u8 { b.first_byte() } +impl FirstByte for Page { fn first_byte(&self) -> u8 { self.0[0] } } +``` + +This removes raw-pointer reasoning from `first`; Rust types enforce that a returned `u8` is valid. If downstream users genuinely need the old raw 16-byte/alignment capability, preserve it as a separate, precisely documented unsafe trait rather than burdening `FirstByte`. A 1.x release can add the new capability and migration adapters while retaining `Block`; an explicitly authorized 2.0 may switch bounds, migrate implementations/consumers, remove or rename the old trait, and reconsider `Page`'s alignment/layout. The implemented 2.0 snapshot would require a fresh audit. + +Boundary coverage is complete for this file: the `Page` field is private, and there are no constructors, other methods/impls, macros, or generated surfaces. Configuration closure is parametric: this source has no `cfg`, features, dependencies, FFI, allocation, atomics, assertions, or profile-sensitive arithmetic. The Rust 1.70 layout argument is target-independent; profiles do not select different code. Re-review is triggered by any contract/API/layout change, a new configuration axis, or acceptance/change of `TCB-COMPAT-PENDING`. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r017.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r017.md new file mode 100644 index 0000000000..40adb7adff --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r017.md @@ -0,0 +1,48 @@ +# Focused safe-API redesign review + +## Claim and scope + +Reviewed the complete supplied snapshot: `REQUEST.md` (SHA-256 `f4cb0fe5d667cdde8536d103ea2bf8c46a4219916acb811b0e33c4d2cb5bc376`) and `lib.rs` (SHA-256 `23f55cd3e96c8372d71a09336d84f72de191fd5f48de693d0be11762537dfa9f`). The in-scope surface is the public `Ticket` type, whose tuple field and constructor are private, and the safe free function `pub fn ticket(id: usize) -> Ticket`. The sole unsafe obligation site is `NonZeroUsize::new_unchecked` at `lib.rs:10`. There are no dependencies, features, conditional source, generated code, callbacks, or deployment assumptions in the snapshot. + +The required theorem is soundness and the documented return/panic behavior for every well-typed safe call, on stable Rust 1.70+ in every ordinary profile and on every target providing the documented `core` APIs. Compiler/backend correctness and nonordinary unstable compiler behavior are outside this source-level claim. + +## Current implementation: **UNSOUND** + +`ticket(0)` is a valid safe call; the API cannot assign it an undocumented caller obligation. Rust 1.70 documents that [`debug_assert!`](https://doc.rust-lang.org/1.70.0/core/macro.debug_assert.html#uses) is disabled in optimized builds by default and that such a build does not execute it unless `-C debug-assertions` is passed. It also documents that [`NonZeroUsize::new_unchecked`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new_unchecked) “results in undefined behaviour if the value is zero” and requires that the value not be zero. + +Thus the configuration partition is: + +- If the debug assertion executes, zero panics before the unsafe call; a nonzero `id` satisfies the unsafe precondition. +- If it does not execute, safe input zero reaches `new_unchecked(0)` and has undefined behavior. + +Ordinary optimized builds include the second case, so the required all-profile claim is refuted. The documented zero-input panic is likewise not established there; the counterexample is already undefined behavior, so no separate `CONTRACT-BROKEN` verdict is needed. The unsafe block also has no adjacent safety proof, but documentation alone could not repair this defect. + +## Proposed redesign + +Keep `Ticket` and the existing documentation exactly as written, and replace only the function body: + +```rust +pub fn ticket(id: usize) -> Ticket { + Ticket(NonZeroUsize::new(id).expect("id must be nonzero")) +} +``` + +This preserves the exact safe signature and removes the entire unsafe proof surface. The panic text is not part of the stated contract. + +## Proof of the replacement + +Rust 1.70 documents that safe [`NonZeroUsize::new`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new) “creates a non-zero if the given value is not zero” and returns `Option`. [`Option::expect`](https://doc.rust-lang.org/1.70.0/core/option/enum.Option.html#method.expect) returns the contained `Some` value and panics for `None`. + +- `id == 0`: `new` yields no nonzero value (`None`), so `expect` panics and `Ticket` is not constructed. +- `id != 0`: `new` yields a `NonZeroUsize` representing that same `id`; `expect` returns it and `Ticket(...)` therefore contains `id`. + +Both operations are safe and impose no caller-side safety precondition. There is no alternative exit that can expose an invalid `Ticket`. + +This reasoning is parametric over the target’s `usize` domain: it uses only the exhaustive equality partition `id == 0` / `id != 0`, with no arithmetic, layout, pointer-width, ABI, allocation, or concurrency premise. It is also profile-parametric: the replacement contains no debug assertion, `cfg`, overflow-sensitive operation, or unsafe optimization premise. Under either unwind or abort panic strategy, the zero case invokes the documented panic and does not return a `Ticket`. + +## TCB, coverage, and verdict + +- **AXIOM-RUST-1.70:** the three exact Rust 1.70 `core` contracts linked above. +- **COMPAT-1 (explicit compatibility premise):** every later supported stable Rust release preserves those exact `new` and `expect` propositions, signatures, and target-wide applicability. Re-audit if any cited contract or the supported toolchain/configuration policy changes. + +There are no dependency, tool-result, platform, or deployment TCB entries. **Replacement soundness and the documented behavior are PROVED for Rust 1.70 on all targets and ordinary profiles relative to AXIOM-RUST-1.70; they are PROVED for the full open-ended Rust 1.70+ range relative additionally to COMPAT-1.** If COMPAT-1 is not accepted, only the open-ended later-version portion is `UNPROVED`; the redesign and Rust 1.70 proof are unaffected. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r018.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r018.md new file mode 100644 index 0000000000..d247b5bc31 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r018.md @@ -0,0 +1,102 @@ +# `y9t5` focused unsafe-Rust review + +## Decision and exact verdicts + +**REJECT.** The exact `Piece for Tail` implementation is +**CONTRACT-BROKEN**. This result covers every target and ordinary profile and +does not depend on a Rust-version-specific layout choice: + +- `Pair` declares exactly one direct tuple field, field `0`, of type + `[u32; 2]` (`lib.rs:18`). `Tail::Item` is `u32`, while `Tail::FIELD` is + `"tail"` (`lib.rs:23-24`). There is therefore no direct declared field of + `Pair` having both the advertised name and advertised type, contrary to the + literal associated-constant contract at lines 7-8. +- `project` returns a pointer to `.0[1]` (`lib.rs:26-27`), an element nested in + the array field. It does not return a pointer to the advertised direct field, + contrary to the literal provider postcondition at line 10. This failure + occurs for every call satisfying the caller precondition. + +The Rust 1.70 Reference calls a tuple struct a nominal tuple type and says tuple +fields have increasing numeric names and the corresponding listed types +([structs](https://doc.rust-lang.org/1.70.0/reference/items/structs.html), +[tuples](https://doc.rust-lang.org/1.70.0/reference/types/tuple.html)). It also +says an unsafe trait is safe to use only when correctly implemented +([unsafe traits](https://doc.rust-lang.org/1.70.0/reference/items/traits.html#unsafe-traits)). +The false postconditions invalidate the `unsafe impl` assertion. They establish +`CONTRACT-BROKEN`, not by themselves `UNSOUND`: no valid execution reaching UB +was established for the concrete wrapper. + +**`increment_tail` source soundness: PROVED for Rust 1.70.0, all targets and +ordinary profiles, with no additional TCB.** On normal return it changes only +`pair.0[1]` to its old value plus one modulo `2^32`. No such behavior is +documented on this safe function, so this is an implementation fact rather than +a public postcondition in scope. + +**The literal open-ended `Rust 1.70+` soundness claim is UNPROVED.** The proof +below bottoms out in exact Rust 1.70.0 documentation. Applying those semantic +statements to every later and future release requires version-by-version +authoritative coverage or a Rust compatibility premise; the request permits no +additional TCB. The same proof is parametric for any later release whose exact +applicable Reference/std contracts entail the listed axioms, but that +conditional domain is not the requested unbounded set. This version-coverage +limit does not qualify the source-structural contract counterexample above. + +## Snapshot, boundary, and configuration coverage + +Reviewed `lib.rs` SHA-256 +`d76a5c0d7336aac4e551264a105c621dbd6cf27db097fb648aa3ca1e891e3429`. +In-scope surfaces are the public unsafe trait implementation (associated types, +constant, and unsafe method), public `Pair` representation, and safe +`increment_tail`. `Pair`'s public field admits every `[u32; 2]`; no hidden +representation invariant is needed. There are no dependencies, `cfg`s, +generated artifacts, FFI, assembly, allocation, concurrency, target features, +or callbacks. The proof is target-parametric; debug/release optimization, +overflow checks, and panic strategy do not affect constant in-bounds indexing +or `wrapping_add`. + +## Compact obligation ledger and reconstructed proof + +1. **Unsafe call precondition (line 32): PROVED.** A well-typed safe call gives + `increment_tail` a live exclusive `&mut Pair`; the call argument is coercible + from `&mut T` to `*mut T` + ([coercions](https://doc.rust-lang.org/1.70.0/reference/type-coercions.html#coercion-types)). + Thus this concrete call supplies the live uniquely borrowed owner required + at line 14. +2. **Projection operation (line 27): PROVED for that call.** Rust's default + representation guarantees properly aligned, nonoverlapping fields, and + `[T; N]` has `T` alignment with element `n` at offset + `n * size_of::()` + ([layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout), + [default representation](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-default-representation)). + Index `1` is in bounds for length `2`; built-in array indexing is zero-based + and bounds checked + ([indexing](https://doc.rust-lang.org/1.70.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions)). + `addr_of_mut!` creates the raw pointer without an intermediate reference but + leaves its place expression subject to the usual rules + ([macro](https://doc.rust-lang.org/1.70.0/std/ptr/macro.addr_of_mut.html)). + Hence the result identifies the initialized, aligned second `u32` in the + same live allocation. +3. **Reference creation and mutation (lines 32-33): PROVED.** The derived + pointer is neither dangling nor unaligned and points to a valid `u32`, the + conditions relevant to the raw dereference and produced reference + ([undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). + There is no intervening call or access, `pair` is not reused while the + element reborrow is live, and exclusivity is preserved. `wrapping_add` + performs modular addition for every `u32` + ([`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add)); + storing its `u32` result preserves validity. +4. **Unsafe-API contract clauses: CONTRACT-BROKEN.** The two structural + counterexamples in the verdict discharge the constant and return-value + clauses adversely. The source contains no adjacent safety proof for either + unsafe block; the material derivation required to validate the concrete + operations is reconstructed in items 1-3 above. + +## TCB, residual scope, and trigger + +TCB: only the exact Rust 1.70.0 Reference and standard-library propositions +linked above; no dependency, implementation, tool, external, deployment, or +compatibility assumptions were admitted. No build, test, expansion, or dynamic +tool evidence was used. Any source/contract change or any claim for a Rust +version beyond the exactly documented domain requires re-review. Regardless, +the current snapshot must be rejected because both mandatory `Piece` +postconditions are false. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r019.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r019.md new file mode 100644 index 0000000000..64343c3634 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r019.md @@ -0,0 +1,105 @@ +# Acceptance review: `Piece for Tail` + +## Decision and verdict + +**REJECT.** For the exact supplied `lib.rs`, source-level Rust soundness is +**PROVED** for the concrete `Tail::project` implementation and the safe +`increment_tail` wrapper, but the documented `Piece` provider contract is +**CONTRACT-BROKEN**. Because policy does not permit correcting source or public +contracts, this snapshot cannot be accepted as contract-compliant. + +| Claim | Verdict | +|---|---| +| `Tail::project`: freedom from Rust UB for every call satisfying its stated safety precondition | **PROVED** | +| `increment_tail`: freedom from Rust UB for every well-typed safe call | **PROVED** | +| `Tail`'s `FIELD` guarantee and `project` postcondition | **CONTRACT-BROKEN** | +| Combined requested soundness-and-contract claim | **CONTRACT-BROKEN** | + +`UNSOUND` is not established: the contract defect does not make this concrete +pointer invalid. The verdicts are deliberately separate. + +## Frozen claim and coverage + +Artifact: the supplied [`lib.rs`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/y9t5/lib.rs), +reviewed as source only on 2026-07-31. Scope is the public unsafe trait items as +implemented by `Tail`, and the public safe `increment_tail` function. The +supported set is stable Rust 1.70.0 through 1.97.1 (the stable releases available +at the audit cutoff), every target on which this source compiles, every accepted +edition, and ordinary profiles. Later Rust releases require re-review; this is +an audit cutoff, not an extra runtime restriction. + +There are no dependencies, `cfg` branches, generated artifacts, FFI, assembly, +allocation, concurrency, target features, or profile-sensitive unchecked +arithmetic. The proof is parametric over target layout: it uses typed field and +array projections, never a numeric byte offset or assumed struct layout. + +The complete in-scope surface is: unsafe trait `Piece`; its associated types; +documented associated constant `FIELD`; unsafe method `project`; the `unsafe +impl Piece for Tail`; public tuple field `Pair.0`; and safe free function +`increment_tail`. + +## Obligation proof + +**O1 — `Tail::project` call precondition.** Its sole stated safety obligation is +that `owner` identify a live, uniquely borrowed `Pair` for the call. At the only +safe consumer, [`increment_tail`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/y9t5/lib.rs:34) +starts with `pair: &mut Pair`; coercing that reference for the call preserves +the identity of its live referent, and no competing access intervenes. Thus the +unsafe call satisfies the literal precondition without a hidden caller +condition. + +**O2 — pointer construction.** In +[`project`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/y9t5/lib.rs:29), `(*owner).0` +projects the sole `[u32; 2]` field and `[1]` selects an in-bounds, initialized +`u32`. The live, uniquely borrowed `Pair` premise supplies allocation, +alignment, lifetime, mutability, and exclusivity for that nested place. The +Rust 1.70 documentation says +[`addr_of_mut!` creates a mutable raw pointer to a place without an intermediate +reference](https://doc.rust-lang.org/1.70.0/std/ptr/macro.addr_of_mut.html); +the Rust 1.97.1 contract additionally states that the expression is evaluated +as a place and field/index projections must be in bounds +([exact version](https://doc.rust-lang.org/1.97.1/std/ptr/macro.addr_of_mut.html#safety)). +Those requirements hold here. Consequently the returned pointer designates +that nested `u32` for the relevant borrow. + +**O3 — reference and mutation.** The immediately returned pointer is non-null, +aligned, non-dangling, points to a valid initialized `u32`, and is exclusively +derived from `pair`; hence creating `&mut *ptr` meets the Reference's reference +validity and alias requirements +([Rust 1.97.1](https://doc.rust-lang.org/1.97.1/reference/behavior-considered-undefined.html#undefined-validity-reference-box)). +Only that derived reference accesses the element until its last use. Finally, +`wrapping_add(1)` is defined modular addition +([Rust 1.97.1](https://doc.rust-lang.org/1.97.1/core/primitive.u32.html#method.wrapping_add)), +so overflow and profile settings introduce neither UB nor a panic. Normal +return leaves `pair.0[1]` equal to its prior value plus one modulo `2^32`. + +## Finding F-01 — false provider guarantees + +The controlling text requires `FIELD` to name a **direct declared field** of +`Owner` whose type is `Item`, and requires `project` to return a pointer to +that field. For this impl, `Owner = Pair`, `Item = u32`, and `FIELD = "tail"`. +But [`Pair`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/y9t5/lib.rs:21) is a +one-field tuple struct: its only direct field is field `0`, whose type is +`[u32; 2]`. It has no direct field named `tail` and no direct field of type +`u32`. The implementation returns a pointer to nested array element +`Pair.0[1]`, not a direct field. + +This is not merely missing documentation. The UB-free execution +`increment_tail(&mut Pair([0, 0]))`, established by O1–O3, is a valid witness: +`project` returns normally while its documented postcondition is false. +`FIELD` is also false for every `Pair`. Therefore **CONTRACT-BROKEN** applies to +the unsafe impl's provider obligations independently of its proved soundness. + +`increment_tail` has no documented behavioral postcondition; its name is not a +normative contract. Its safe-surface soundness is proved above, and the actual +modular update is recorded only to complete the dataflow proof. + +## TCB and residual scope + +TCB: only the cited, versioned Rust Reference and standard-library abstract +semantics; no dependency, implementation, tool, deployment, compatibility, or +out-of-band assumption was admitted. No target was built or executed, so there +is no tool-derived evidence. Compiler/backend correctness and behavior of +future Rust releases are outside this source-level claim. Any change to the +source, contracts, supported-version cutoff, or cited pointer/reference rules +triggers re-review. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r020.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r020.md new file mode 100644 index 0000000000..22a60e2f08 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r020.md @@ -0,0 +1,93 @@ +# Version-partitioned review of `advance_marker` + +## Claim and verdict + +**Snapshot.** The complete reviewed source is `lib.rs` SHA-256 +`e561568dfc6262bc6312f1a346e970dfab1f0d8f5102984e80e3b06cf2885e9b`. +Scope is the safe public function `advance_marker` at `lib.rs:3-5`; there are no +dependencies, generated artifacts, macros, inputs, or caller obligations. The +claim is source-level freedom from Rust undefined behavior for every +well-typed safe call, separately under the documented abstract semantics of +Rust 1.79.0 and 1.80.0, on every target and in every ordinary profile. The +function documents no behavioral postcondition beyond its return type. + +| Region | Soundness verdict | Reason | +|---|---|---| +| Rust 1.79.0, every target/profile | **UNSOUND** | Every safe call violates an unconditional allocation-bound precondition of `pointer::add`. | +| Rust 1.80.0, every target/profile | **PROVED**, relative to TCB R020-v1 below | The byte offset is zero, which this version expressly makes well-defined. | +| Combined requested set | **UNSOUND** | The supported set contains the entire unsound 1.79.0 region. | + +## Surface and obligation ledger + +The sole safe surface is `pub fn advance_marker() -> *const [u8; 0]`. It has no +arguments, so every invocation is a valid safe use. Its only unsafe operation +is `core::ptr::null::<[u8; 0]>().add(1)` at `lib.rs:4`. The controlling `add` +contract creates three obligations: the applicable allocation-bound condition, +the byte offset fitting `isize`, and the infinite-precision address sum fitting +`usize` without wrapping. There is no dereference, memory access, state, +unwind path, or downstream unsafe consumer in scope. + +## Derivation + +For both releases, the versioned [`size_of` contract for +1.79.0](https://doc.rust-lang.org/1.79.0/core/mem/fn.size_of.html) and +[1.80.0](https://doc.rust-lang.org/1.80.0/core/mem/fn.size_of.html) says an +array `[T; n]` has size `n * size_of::()`. Therefore +`size_of::<[u8; 0]>() = 0`, and `add(1)` computes byte offset `1 * 0 = 0`. +The versioned [`null` contract for +1.79.0](https://doc.rust-lang.org/1.79.0/core/ptr/fn.null.html) and +[1.80.0](https://doc.rust-lang.org/1.80.0/core/ptr/fn.null.html) establishes +that the starting value is a null raw pointer with address zero. + +**Rust 1.79.0.** The [`*const T::add` +contract](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add) +states that violating any listed condition is UB and unconditionally requires +both start and result to be in-bounds or one byte past the same allocated +object. The starting value here is the null pointer, not a pointer in or +one-past an allocated object; the contemporaneous [`core::ptr` safety +text](https://doc.rust-lang.org/1.79.0/core/ptr/index.html#safety) also states +that null is never valid, including for size-zero access. Thus the allocation +conjunct is false. The zero byte offset does satisfy the `isize` and no-wrap +conjuncts, but conjunction failure makes the executed `add` UB. Since every +safe call reaches it, this is a concrete safe-use counterexample (indeed, all +calls), establishing **UNSOUND**, not merely `UNPROVED`. + +**Rust 1.80.0.** The versioned [`*const T::add` +contract](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add) +conditions the allocation requirement on a nonzero byte offset and explicitly +says, “If it is zero, then the function is always well-defined.” Here the +offset is zero; it fits every `isize`, and address `0 + 0 = 0` fits every +`usize` without wrapping. All contract conjuncts are therefore discharged. +Returning the resulting raw pointer creates no reference and performs no +access, so no further safety obligation exists. This proves the regional safe +API claim. + +## Configuration closure + +The two toolchain regions are an exhaustive partition requested by the task. +Within each, the proof is parametric over targets: array length zero forces the +same zero byte offset for every layout, and zero is representable by every Rust +`isize` and `usize`. The source contains no `cfg`, target operation, allocator, +concurrency, FFI, or profile-sensitive check. Optimization level, debug +assertions, overflow-check settings, and panic strategy therefore introduce no +additional branch. No build, test, expansion, or execution evidence was used. + +## TCB R020-v1 and findings + +TCB R020-v1 contains only the exact Rust 1.79.0/1.80.0 standard-library +propositions linked above: the array-size formula, `null` result, and each +release's `add` safety contract (plus the 1.79.0 null-safety statement). There +are no admitted dependency, compiler-implementation, platform, tool, or +deployment assumptions. Changing the source, either toolchain version, or any +consumed contract triggers re-review. + +**F-1 — implementation defect (Rust 1.79.0):** the safe function invariably +executes UB. The smallest cross-version remedy is to return +`core::ptr::null::<[u8; 0]>()` directly (or use the safe `wrapping_add` if the +operation must remain explicit). No edit was requested or made. + +**F-2 — proof-documentation defect:** the unsafe block has no adjacent +`SAFETY` proof. On 1.80.0 the material reconstruction is: `[u8; 0]` has size +zero; hence `1 * size_of::<[u8; 0]>()` is zero; the 1.80.0 `add` contract makes +zero-offset calls well-defined. That proof cannot justify the supported +1.79.0 region, so documentation alone cannot repair F-1. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r021.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r021.md new file mode 100644 index 0000000000..441901f2a3 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r021.md @@ -0,0 +1,46 @@ +# Focused review: `total` + +## Claim and result + +Scope is the exact `lib.rs`, solely `pub fn total(&[u32]) -> u32` and its three unsafe operations, for every well-typed safe call, Rust 1.70+ through the 2026-07-31 cutoff, every target on which this crate compiles, and every ordinary profile. Required behavior is + +`total(values) = (values[0] + ... + values[n-1]) mod 2^32`, with the empty result `0`. + +- **Current source soundness: UNPROVED** for the full support claim. No UB witness is established. +- **Current behavioral postcondition: UNPROVED** for the full support claim; it is proved for non-empty slices, but the unresolved empty execution cannot be assumed UB-free. +- **Proof documentation: deficient:** all three unsafe operations (`lib.rs:6,9,10`) lack `SAFETY` comments. +- **Safe redesign:** behavior and source-level soundness have a closed conditional proof plan, but it is not implemented and its performance requirement is **UNPROVED** because no benchmark evidence was supplied. + +## Current-source proof ledger + +Let `base = values.as_ptr()`, `n = values.len()`, and, for a non-empty slice, loop index `i` satisfy `ptr = base.add(i)` and `0 <= i <= n`. + +1. **`base.add(n)` (`lib.rs:6`).** Rust 1.70 says `add` requires start and result to be in/all-but-one-past the same allocation, the byte offset to fit `isize`, and no address-space wrap ([`pointer::add`](https://doc.rust-lang.org/1.70.0/core/primitive.pointer.html#method.add)). The Reference fixes `u32` at four bytes, arrays at `N * size_of::()`, and slices to the layout of the array section sliced ([type layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). A live reference cannot dangle, a slice covers its entire range, and its dynamic size cannot exceed `isize::MAX` ([undefined behavior / dangling pointers](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#dangling-pointers)). Thus, for `n > 0`, `4n <= isize::MAX`, the range is one live allocation, and `base.add(n)` is its one-past pointer. + + This does **not** close `n = 0` under the literal MSRV contract. A valid zero-length slice may have a non-null aligned pointer not attached to an allocation; Rust 1.70 permits such zero-size pointers, while its `add` text has no zero-offset exemption. Rust 1.97.1 now requires allocation/range containment only when the offset is nonzero ([1.97.1 `pointer::add`](https://doc.rust-lang.org/1.97.1/core/primitive.pointer.html#method.add)), but that later text is not authoritative evidence for 1.70. The smallest missing proposition is: **on Rust 1.70, `p.add(0)` is defined for every pointer returned by an empty slice's `as_ptr`.** + +2. **`*ptr` (`lib.rs:9`).** For `i < n`, the invariant places `ptr` at the initialized `u32` element `i`; slices' elements are always initialized ([slice types](https://doc.rust-lang.org/1.70.0/reference/types/slice.html)), `as_ptr` exposes their buffer while the slice remains live ([`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/core/primitive.slice.html#method.as_ptr)), and the shared borrow remains live throughout the call. No code mutates the slice. Hence the read is aligned, live, initialized, and within bounds. + +3. **`ptr.add(1)` (`lib.rs:10`).** From `i < n`, `i + 1 <= n`; the four-byte step stays in the same allocation or reaches exactly one-past, fits `isize`, and preserves the invariant. Thin-pointer equality compares addresses ([`ptr::eq`](https://doc.rust-lang.org/1.70.0/core/ptr/fn.eq.html)); non-wrapping four-byte offsets therefore visit exactly indices `0..n` and stop at `n`. + +Each visited value is combined using modular addition ([`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/core/primitive.u32.html#method.wrapping_add)), so overflow checks, optimization, and debug assertions do not alter the result. There is no `cfg`, dependency, generated code, callback, allocation, or target-specific branch. This proves every non-empty target/profile case parametrically. + +TCB consists only of the exact Rust 1.70 documents cited above; the 1.97.1 citation identifies a later regional contract, not an MSRV premise. No tool, benchmark, dependency, backend, or unstated compatibility premise is admitted. Future stable releases require re-review or an explicitly accepted compatibility premise. + +## Safe candidate and recommendation + +Use this candidate for the comparison: + +```rust +pub fn total(values: &[u32]) -> u32 { + let mut acc = 0u32; + for &value in values.iter() { + acc = acc.wrapping_add(value); + } + acc +} +``` + +Rust 1.70 documents that `iter` yields **all items from start to end** ([`slice::iter`](https://doc.rust-lang.org/1.70.0/core/primitive.slice.html#method.iter)); applying the same `wrapping_add` once to each item proves the exact modular result, including `0` for empty input, in every ordinary profile. The body is entirely safe, so it removes all three unsafe obligations and the MSRV documentation gap. It changes no public signature or specified behavior. + +**Next action:** benchmark this exact candidate against the current snapshot using the designated benchmark, identical toolchain/target/profile/hardware/input, and a predeclared acceptance method. Adopt it only if the evidence establishes regression at no more than 2% (if uncertainty is modeled, require the relevant upper bound to be at most 2%), then audit the implemented snapshot. If it fails or is inconclusive, optimize another safe iterator form and rerun; failing that, benchmark a hybrid repair that obtains the endpoint via safe `as_ptr_range` (or returns early for empty input) and adds the reconstructed `SAFETY` proofs. Do not merge the safe redesign without performance evidence, and do not retain the current source as fully proved at the MSRV. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r022.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r022.md new file mode 100644 index 0000000000..4420f186a4 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r022.md @@ -0,0 +1,47 @@ +# Focused review: `total` + +## Claim, snapshot, and verdicts + +Snapshot: `lib.rs` SHA-256 `12ee0d64af16f4b4bea78bb146d333f86c579b49ca0dd38e2552f3896147be5c`; request SHA-256 `8e32395f979236c2f9fc5b811aa5d7d6a83b054dad42febb341b776f1c5dfa2a`. Scope is only the public safe function `total` and its three unsafe operations. The required domain is Rust 1.70+, every target, and every ordinary profile; the behavioral requirement is the left fold of the input under addition modulo `2^32`. This is a source-level review relative to documented Rust abstract semantics. No code, expansion, build, test, or benchmark was run. + +- **Current soundness: UNSOUND** over the declared supported set. A valid call on Rust 1.70 reaches undefined behavior at `lib.rs:6`. +- **Current Rust 1.70 wrapping arithmetic, restricted to executions without that UB: PROVED.** Every consumed element is combined with [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/core/primitive.u32.html#method.wrapping_add), whose Rust 1.70 contract is modular addition, independent of overflow-check and optimization settings. For later releases this conclusion is conditional on retention of that contract. +- **Safe iterator candidate: conditional design proof closes; no artifact verdict.** It removes the defect and all local unsafe obligations while preserving modular addition. +- **Candidate performance requirement: UNPROVED.** No benchmark artifact or result establishes the required regression as at most 2%. + +## Current implementation: obligation ledger + +**O1 — `ptr.add(values.len())`, line 6: failed on a supported case.** Rust 1.70's [`ptr.add`](https://doc.rust-lang.org/1.70.0/core/primitive.pointer.html#method.add) safety contract requires the starting and resulting pointers to be in-bounds or one-past the same allocated object, even for a zero offset. Yet Rust 1.70 [`slice::from_raw_parts`](https://doc.rust-lang.org/1.70.0/core/slice/fn.from_raw_parts.html) expressly permits `NonNull::dangling()` as the data pointer of a zero-length slice, and [`NonNull::dangling`](https://doc.rust-lang.org/1.70.0/core/ptr/struct.NonNull.html#method.dangling) supplies a well-aligned dangling pointer. Thus this input is a valid slice: + +```rust +let p = std::ptr::NonNull::::dangling().as_ptr(); +let s = unsafe { std::slice::from_raw_parts(p, 0) }; // constructor contract met +total(s); // safe API call; line 6 evaluates p.add(0) +``` + +Because `p` is not associated with an allocated object, line 6 violates the controlling Rust 1.70 contract and is UB. This is not merely a missing proof. It applies in every ordinary profile and needs only one supported Rust version to refute the universal `1.70+` claim. The [Rust 1.80 contract](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add) newly says a zero byte offset is always well-defined; that later text removes this particular case on 1.80, but has no stated historical scope and cannot repair the Rust 1.70 claim. + +**O2 — dereference and increment, lines 9–10: reconstructed proof for nonempty slices.** Let `base = values.as_ptr()` and, at each loop head, maintain `ptr = base.add(i)` with `0 <= i <= len`. A valid shared slice supplies one contiguous, aligned, initialized sequence that remains live and unmodified throughout this call; Rust 1.70's [slice-construction contract](https://doc.rust-lang.org/1.70.0/core/slice/fn.from_raw_parts.html) records these representation requirements, while the Reference identifies [dangling/unaligned raw dereference and invalid-value production as UB](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html). Initially `i = 0`. For nonempty input, the full-slice offset is within that single allocation and within `isize::MAX`. If `ptr != end`, the non-wrapping offsets and nonzero-sized `u32` imply `i < len`, so `*ptr` reads the initialized element `values[i]`; `ptr.add(1)` then establishes the invariant with `i + 1 <= len`. At `i = len`, `ptr == end` and the loop exits. This proof is target-parametric and contains no profile-dependent assertion or arithmetic. + +All three unsafe blocks lack adjacent `SAFETY` proofs. The reconstruction above is material; even after fixing O1, the current proof documentation would remain inadequate. + +## Safe iterator redesign + +Use the following candidate, not `Iterator::sum` (ordinary `u32` addition would not preserve the required overflow behavior in every profile): + +```rust +pub fn total(values: &[u32]) -> u32 { + values + .iter() + .copied() + .fold(0u32, |acc, value| acc.wrapping_add(value)) +} +``` + +The Rust 1.70 slice contract says [`iter` yields every item from start to end](https://doc.rust-lang.org/1.70.0/core/primitive.slice.html#method.iter); [`copied`](https://doc.rust-lang.org/1.70.0/core/iter/trait.Iterator.html#method.copied) yields the `u32` values; and [`fold`](https://doc.rust-lang.org/1.70.0/core/iter/trait.Iterator.html#method.fold) starts at `0`, applies the closure to every element left-associatively, and returns the final accumulator. Induction therefore gives exactly the modular sum, including `0` for an empty slice. The source has no unsafe operation, configuration branch, allocation, dependency, generated code, target assumption, or panic path. This conditional proof covers Rust 1.70 and later releases retaining those cited contracts; an implemented snapshot still requires its own review. + +## TCB, evidence gap, and next action + +TCB consists only of the linked exact Rust 1.70 (and, for the version contrast, 1.80) Reference/standard-library propositions. There are no dependency, compiler-codegen, or benchmark assumptions. The open-ended future toolchain range requires re-review if a consumed contract changes. + +**Recommendation:** block acceptance of the current implementation for the stated MSRV, but do not merge the safe candidate on proof quality alone. First obtain the designated benchmark's exact harness, workload, baseline, toolchain, target, profile, environment, metric, and decision rule, then measure this exact candidate against the exact current baseline. The smallest missing performance proposition is: under that agreed protocol, candidate regression is `<= 2%`; if measurement uncertainty crosses 2%, it remains unproved. If it passes, implement the safe candidate and review the new snapshot. If it fails, iterate on safe loop forms; only as a fallback consider a Rust-1.70-compatible empty-input guard plus complete local proofs for the pointer loop, and benchmark that repair too. Neither the soundness defect nor the 2% gate should be waived silently. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r023.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r023.md new file mode 100644 index 0000000000..ee0b95e884 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r023.md @@ -0,0 +1,98 @@ +# Focused source review and redesign + +## Claim and verdict + +**Snapshot and scope.** This review covers the supplied bytes of `REQUEST.md` and +`lib.rs`, specifically the public `Ticket` representation and the safe +`pub fn ticket(id: usize) -> Ticket`. There are no dependencies, generated +artifacts, features, or deployment restrictions. The supported set is Rust +1.70+, every target, and every ordinary build profile. No code was executed. + +**Current soundness: `UNSOUND`.** A well-typed safe call `ticket(0)` reaches +undefined behavior in an ordinary optimized build. This Rust 1.70 witness is +within the supported set and therefore refutes the universal Rust 1.70+ claim; +no premise about later-version compatibility is needed for that verdict. + +**Documented behavior:** the universal promise “panics when `id == 0`” is not +met in profiles with debug assertions disabled: the call reaches undefined +behavior instead of a defined panic. Because the same witness already has UB, +this is included in the `UNSOUND` finding rather than assigned a separate +non-UB `CONTRACT-BROKEN` verdict. + +## Boundary and obligation ledger + +The only explicit public surfaces are the `Ticket` type (whose sole field is +private) and the safe `ticket` constructor. The private field makes construction +of a `Ticket` containing a zero the constructor's responsibility. There are no +other explicit constructors, methods, trait implementations, macros, or unsafe +APIs in the snapshot. + +1. **`new_unchecked` precondition, `lib.rs:10`.** Rust 1.70 documents that + `NonZeroUsize::new_unchecked` requires: “The value must not be zero,” and says + zero causes UB ([official Rust 1.70 documentation](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new_unchecked)). +2. **Current derivation.** `debug_assert!(id != 0)` dominates the unsafe call + textually. When debug assertions execute, it panics for zero and its normal + continuation establishes `id != 0`; the unsafe precondition is then met. + However, Rust 1.70 states that an optimized build does not execute + `debug_assert!` unless debug assertions are enabled + ([official Rust 1.70 documentation](https://doc.rust-lang.org/1.70.0/core/macro.debug_assert.html)). + Thus in that supported configuration `ticket(0)` proceeds directly to + `new_unchecked(0)`, violating obligation 1. A debug-only assertion cannot + establish a safety precondition across all supported profiles. +3. **Return behavior for nonzero inputs.** On `id != 0`, the same input is passed + to `new_unchecked` and stored in the private field, so the returned ticket + contains `id`. This case is proved for all profiles and targets under the + cited method contract. +4. **Proof documentation.** The unsafe block has no adjacent `SAFETY` proof. A + correct proof would need a configuration-independent dominating fact + `id != 0`; no such fact exists. Adding a comment cannot repair the defect. + +Configuration closure is an exhaustive two-case partition: debug assertions +execute or they do not. The first branch is sound and has the documented zero +panic; the second contains the counterexample above. Target, pointer width, +optimization level, and panic strategy do not repair the missing check. + +## Recommended redesign + +Replace only the function body, preserving the exact safe signature and its +documentation: + +```rust +pub fn ticket(id: usize) -> Ticket { + match NonZeroUsize::new(id) { + Some(id) => Ticket(id), + None => panic!("ticket id must be nonzero"), + } +} +``` + +This is the smallest proof surface: it removes all crate-owned unsafe code and +uses the checked constructor. Rust 1.70 documents `NonZeroUsize::new` as +creating a nonzero value when its input is nonzero +([official Rust 1.70 documentation](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new)). +The proof plan is exhaustive: + +- For `id == 0`, `new` yields `None` and the match invokes `panic!`; the panic + message was not part of the old contract. +- For `id != 0`, `new` yields `Some` containing that same value, which is moved + into `Ticket`; normal return therefore contains `id`. +- Neither branch depends on `debug_assertions`, optimization, target properties, + or profile. A panic-abort build may abort after initiating the panic, as is + normal panic-strategy behavior; the API does not promise unwinding. + +The public signature, `Ticket` representation, privacy boundary, and documented +behavior remain unchanged. Making release behavior defined is a repair to the +published contract, not a new caller obligation. + +## TCB, residual scope, and re-audit + +TCB `R023-inline-v1` contains only the cited Rust 1.70 standard-library +propositions and the documented Rust abstract semantics; there are no +dependency or environment assumptions and no tool-derived evidence. The +proposed source is not an implemented snapshot and therefore receives no +`PROVED` verdict. After implementation, audit the exact new bytes. For the +open-ended portion of “1.70+”, either recheck each supported release or record +an accepted Rust compatibility premise preserving the consumed contracts. +Re-audit on changes to the function, `Ticket` representation/visibility, +documented panic behavior, supported configuration set, or consumed standard +library contracts. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r024.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r024.md new file mode 100644 index 0000000000..669edcd1cb --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r024.md @@ -0,0 +1,46 @@ +# Focused review: `Block`, `Page`, and `first` + +## Claim and verdict + +Snapshot: `lib.rs` SHA-256 `8e347fd2a5ca16fa1bd9a7b6019fc57227346bd895c5af794d2efa76265f01a3`; no generated code or dependencies are present in the supplied target. Scope is the published APIs in lines 3–25, Rust 1.70+, every target/profile, and valid implementations of the unsafe trait. + +**Overall soundness: UNPROVED.** `Page` satisfies even a strong, useful reading of the `Block` contract, but the safe generic `first` cannot derive every condition for its raw `u8` read from the contract's undefined word “readable.” No definite `UNSOUND` verdict follows because an alternative reading of “readable” could include all conditions for a defined typed read; that interpretation is precisely the missing premise. + +**Version qualification:** the implementation derivation below is proved against Rust 1.70's documented source semantics and is target/profile-parametric. Applying it to the open-ended later-version range needs `TCB-COMPAT`: later stable Rust preserves the cited propositions. That compatibility premise was not supplied or established, so the literal open-ended `1.70+` claim is independently conditional; future toolchain/documentation changes trigger review. No compiler-backend or binary claim is made. + +## Boundary and obligation ledger + +The complete surface is unsafe trait `Block` (associated constant plus safe method under an unsafe-implementation obligation), public opaque `Page` with private field, its `unsafe impl`, and safe generic consumer `first`. There are no `cfg`s, macros, allocator/FFI/concurrency code, or profile-sensitive checks. + +| Site | Obligation | Result | +|---|---|---| +| `Page::ALIGN` | nonzero power of two | **PROVED:** `16` has both properties. | +| `Page::base` | on normal return, nonnull, 16-aligned, readable for 16 bytes throughout the relevant borrow | **PROVED**, including initialized bytes and no conflicting mutation, under the derivation below. | +| `first` raw dereference | at the dereference, pointer is non-dangling, properly aligned for `u8`, points to initialized `u8`, and the read does not race | **UNPROVED** for arbitrary published `Block` impls: initialization and race freedom are not stated or defined by “readable”; the exact validity interval is also not tied formally to the returned raw pointer. | + +Rust 1.70 says a correctly implemented unsafe trait is safe to use, placing these promises on implementers ([unsafe traits](https://doc.rust-lang.org/1.70.0/reference/items/traits.html#unsafe-traits)). A raw read requires validity for reads, proper alignment, and a properly initialized `T` ([`ptr::read` safety](https://doc.rust-lang.org/1.70.0/std/ptr/fn.read.html#safety)); the Reference separately identifies data races and reading an uninitialized integer as UB ([undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). Rust 1.70's pointer documentation also says exact pointer-validity rules were not yet fully determined ([pointer safety](https://doc.rust-lang.org/1.70.0/std/ptr/index.html#safety)), reinforcing the need to spell out the consumed proposition rather than infer it from “readable.” + +A `MaybeUninit<[u8; 16]>`-backed, over-aligned downstream implementation illustrates the gap: its pointer can be nonnull, 16-aligned, live, and in-bounds/valid for byte access while the first `u8` remains uninitialized. It is a countermodel to the needed implication under the standard-library distinction between “valid for reads” and “initialized,” not an unconditional UB counterexample under every possible meaning of the project's undefined term. + +## Reconstructed `Page` proof + +Rust 1.70 documents `[T; N]` as `N` contiguous elements and gives `[u8; 16]` size 16 ([array layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). `repr(C)` places the first field at offset zero; `repr(align(16))` raises the containing struct's alignment to 16 ([C structs](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs), [alignment modifiers](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers)). A valid `&Page` is nonnull, aligned, and points to a valid `Page` ([references](https://doc.rust-lang.org/1.70.0/std/primitive.reference.html)). Therefore its initialized array begins at the same 16-aligned address and occupies all 16 bytes. Array-to-slice coercion permits `as_ptr`, which returns the buffer pointer ([arrays](https://doc.rust-lang.org/1.70.0/std/primitive.array.html), [`as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)). The live shared borrow keeps this non-`UnsafeCell` storage alive and unmodified. This proves the full intended provider postcondition for `Page` on all targets/profiles covered by those rules. + +This material derivation is absent beside the `unsafe impl`; it should become its `SAFETY` proof when edits are authorized. + +## What can change in 1.x + +Proofs may be simplified without changing the published theorem. Keep every existing `Block` clause, prove all of them for `Page`, but let `first` cite only a derived consumer lemma: “until this read completes, `base()` yields one initialized `u8` that may be read without a conflicting access.” `first` does not consume `ALIGN`, power-of-two-ness, 16-byte extent beyond byte zero, or a separate nonnull promise (validity for a nonzero read already excludes null). Repository-only usage cannot narrow a public trait whose downstream consumers are unknown. + +Nonbreaking preparation can add a separately named safe API/trait and implement it for `Page`, while retaining the old API and contract. For example, `fn bytes(&self) -> &[u8; 16]` makes lifetime, initialization, validity, and read-only access type-enforced; `first` over that interface needs no unsafe block. No edit is authorized here. + +Merely defining “readable” now to include initialization, lifetime, and race freedom is not unquestionably editorial: it strengthens downstream implementer obligations. It therefore cannot close the current proof under the stated ordinary 1.x SemVer promise without an explicit compatibility exception/remediation process. + +## Changes requiring an authorized 2.0 migration + +- Strengthening `Block` to state the missing initialized-`u8`, validity-interval, provenance/access, and no-conflicting-access requirements invalidates implementations. +- Weakening/removing `ALIGN`, alignment, or bytes 1–15 invalidates generic consumers that may rely on the full provider guarantee, even though `first` does not. +- Replacing the raw-pointer trait with `fn bytes(&self) -> &[u8; 16]` (or, if only this behavior is intended, `fn first(&self) -> u8`), making the trait safe, changing/removing `first`, or splitting byte access from a separately justified aligned-storage capability changes implementers/callers. +- Removing `Page`'s public `repr(C, align(16))` layout guarantee is also not justified by the local search and should be treated as breaking. + +Recommended migration: add an opt-in safe interface during 1.x, audit adopters, then in 2.0 make the typed interface primary and retain a narrowly documented unsafe capability only if real consumers require it. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r025.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r025.md new file mode 100644 index 0000000000..3e931704c3 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r025.md @@ -0,0 +1,87 @@ +# Focused review and redesign: `Ticket` + +## Claim, snapshot, and verdict + +Scope is the complete supplied target: `lib.rs` SHA-256 +`23f55cd3e96c8372d71a09336d84f72de191fd5f48de693d0be11762537dfa9f`. +The reviewed theorem is that every well-typed safe call to `ticket`, on every +target and ordinary build profile supported by Rust 1.70+, is free of Rust UB +and implements: return a `Ticket` containing `id` when `id != 0`, and panic +when `id == 0`. + +**Current aggregate verdict: UNSOUND.** This is established already for Rust +1.70.0, so it refutes the advertised `1.70+` supported-set claim without any +premise about later releases. With debug assertions enabled, the scoped +implementation is proved sound and meets its documented behavior; with them +disabled, it is unsound. No separate `CONTRACT-BROKEN` verdict is asserted for +the latter branch because its counterexample reaches UB rather than a defined +non-panicking outcome. + +The supported configuration partition is `debug_assertions = true | false`. +Target width, optimization otherwise, overflow checking, and unwind-versus- +abort panic handling do not affect the argument. There are no dependencies, +features, generated artifacts, FFI, callbacks, or deployment restrictions. + +## Boundary and obligation coverage + +The safe surface is the opaque public `Ticket` type (private +`NonZeroUsize` field, with ordinary move/drop behavior) and the safe free +function `ticket(usize) -> Ticket`. There are no public methods, trait impls, +fields, or macros in the supplied source. The sole unsafe obligation is at +`lib.rs:10`: `NonZeroUsize::new_unchecked(id)` requires `id != 0`. + +Rust 1.70 documents that an optimized build does not execute `debug_assert!` +unless debug assertions are explicitly enabled +([`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html)). +It also states that `new_unchecked(0)` has undefined behavior and requires a +nonzero argument +([`NonZeroUsize::new_unchecked`](https://doc.rust-lang.org/1.70.0/std/num/struct.NonZeroUsize.html#method.new_unchecked)). + +Therefore `ticket(0)` is a well-typed safe call and, when debug assertions are +disabled, reaches `new_unchecked(0)`: a concrete valid-use UB counterexample. +When assertions are enabled, `id == 0` invokes panic; if the assertion returns, +its checked expression establishes `id != 0`, discharging the unchecked +constructor's precondition. The unsafe block has no adjacent `SAFETY` proof, +but adding one cannot repair the false premise in the disabled branch. + +TCB `F8W1-2026-07-31` contains only the quoted authoritative Rust 1.70.0 +standard-library axioms; no dependency, tool, compiler-backend, target, or +environment assumptions are consumed. Rust 1.97.1, the audit cutoff, retains +the same contracts +([`NonZero`](https://doc.rust-lang.org/1.97.1/std/num/struct.NonZero.html#method.new_unchecked), +[`debug_assert!`](https://doc.rust-lang.org/1.97.1/std/macro.debug_assert.html)). + +## Recommended redesign + +No source edit was requested. Replace only the function body with: + +```rust +pub fn ticket(id: usize) -> Ticket { + Ticket(NonZeroUsize::new(id).expect("ticket id must be non-zero")) +} +``` + +This preserves the exact public signature, representation, and documented +behavior. The panic text changes, but no panic text is documented. Both APIs +were available by Rust 1.70. + +Conditional proof plan for the candidate: + +1. `NonZeroUsize::new` safely creates the nonzero value exactly when `id` is + nonzero + ([Rust 1.70 contract](https://doc.rust-lang.org/1.70.0/std/num/struct.NonZeroUsize.html#method.new)). +2. For `id != 0`, it yields the value containing the same `id`; `expect` + returns that contained `Some` value, and `Ticket(...)` preserves it. +3. For `id == 0`, it yields `None`; `expect` panics + ([Rust 1.70 `Option::expect`](https://doc.rust-lang.org/1.70.0/core/option/enum.Option.html#method.expect)). +4. These operations are safe and do not depend on debug assertions, + optimization, target properties, or panic strategy. Thus the unsafe surface + and its invariant proof disappear entirely. + +This is the smallest proof surface; an `assert!` followed by +`new_unchecked` would preserve behavior but retain an unnecessary unsafe +obligation and safety comment. The proposal itself receives no verdict until +implemented and audited as a new snapshot. For releases after the verified +1.70.0--1.97.1 interval, apply the same parametric proof against that release's +applicable standard-library contracts; a material contract change is the +re-audit trigger rather than an implicit timeless compatibility assumption. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r026.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r026.md new file mode 100644 index 0000000000..4ff57cd8a2 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r026.md @@ -0,0 +1,86 @@ +# Supported-domain source review + +## Claim and verdict + +**Source-level soundness: PROVED** for the exact `v7c4` snapshot, relative to +the versioned standard-library axioms listed below, over the conservative +candidate domain `C = POLICY-A ∪ POLICY-B` and the audit cutoff Rust 1.82.0. +The theorem is: for every well-typed safe call to the sole public API +`first(&[u8]) -> Option`, every execution is free of Rust undefined +behavior under the documented Rust abstract semantics, with no caller safety +precondition. + +`C` is: + +- `fast` disabled: Rust 1.79.0, 1.80.0, 1.81.0, or 1.82.0 on either + `x86_64-unknown-linux-gnu` or `aarch64-unknown-linux-gnu`; +- `fast` enabled on x86_64: all four releases; and +- `fast` enabled on aarch64: Rust 1.80.0 through 1.82.0. + +Policy B is a strict subset of Policy A, so this union is exactly Policy A's +15 combinations. Proving `C` covers either policy without selecting one. +**The exact project support predicate remains UNPROVED as an organizational +fact**: both policies are current, conflict for three `fast` combinations, and +provide no authorized precedence rule. `C` is an audit domain, not a new claim +about what the project promises. + +Snapshot: `lib.rs` SHA-256 +`6f87e944cec7ee2727f7c7d32aa382de88987ea791082175d611fff5bf44012b`; +package edition 2021, no dependencies, one Boolean Cargo feature, and no build +script or generated source. `rust-toolchain.toml` selects 1.82.0 locally and +CI samples configurations; neither defines support, as CI itself states. + +## Boundary, configuration, and obligation coverage + +The complete public surface is one safe free function. The two definitions at +`lib.rs:3-15` are selected by complementary `cfg(feature = "fast")` +predicates, so exactly one exists in every member of `C`. There are no fields, +traits, impls, macros, FFI, statics, callbacks, or invariant-bearing state. +Target and profile do not affect the source or proof. The release axis is +covered individually by the four exact documentation versions, not by a +compatibility assumption. + +| Obligation | Domain | Derivation | Status | +|---|---|---|---| +| SAFE-1, non-`fast` `first` (`lib.rs:4-6`) | 8 non-`fast` members of `C` | `slice::first` is a safe operation returning the first reference or `None`; `Option::copied` safely copies it, and `u8: Copy`. No unsafe operation or hidden caller obligation occurs. | PROVED | +| SAFE-2, empty `fast` branch (`lib.rs:9-11`) | 7 `fast` members of `C`, empty inputs | `is_empty()` being true means the slice length is zero; the branch returns `None` and executes no unsafe operation. | PROVED | +| UNSAFE-1, `get_unchecked(0)` (`lib.rs:12-13`) | 7 `fast` members of `C`, nonempty inputs | The `else` branch establishes `!bytes.is_empty()`. Thus `bytes.len() != 0`; because length is a `usize`, `bytes.len() > 0`, hence index `0` is in bounds. This discharges `get_unchecked`'s out-of-bounds safety condition. It returns `&u8`; the expression immediately copies the `u8` into `Some`, so no reference escapes and no further unsafe precondition arises. | PROVED | +| CFG-1, domain closure | all 15 members of `C` | Complementary feature predicates are exhaustive; SAFE-1 covers feature-off and SAFE-2/UNSAFE-1 partition feature-on by empty/nonempty input. The proof is otherwise target-parametric. | PROVED | + +There are no crate-documented postconditions and no separately requested +robustness property. The `get_unchecked` result guarantee consumed above is +discharged locally. + +## TCB and authoritative premises + +`TCB-v7c4-review-1` contains only these authoritative `AXIOM-STD` entries. For +each exact release, the slice page specifies that `is_empty` reports zero +length, `first` returns the first element or `None` for an empty slice, and an +out-of-bounds `get_unchecked` call is UB; the Option page specifies that +`copied` maps `Option<&T>` to `Option` by copying; the primitive page declares +`impl Copy for u8`: + +- Rust 1.79.0: slice [is_empty](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.is_empty), [first](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.first), and [get_unchecked](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.get_unchecked); [Option::copied](https://doc.rust-lang.org/1.79.0/std/option/enum.Option.html#method.copied); [u8: Copy](https://doc.rust-lang.org/1.79.0/std/primitive.u8.html#impl-Copy-for-u8) +- Rust 1.80.0: slice [is_empty](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.is_empty), [first](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.first), and [get_unchecked](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.get_unchecked); [Option::copied](https://doc.rust-lang.org/1.80.0/std/option/enum.Option.html#method.copied); [u8: Copy](https://doc.rust-lang.org/1.80.0/std/primitive.u8.html#impl-Copy-for-u8) +- Rust 1.81.0: slice [is_empty](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.is_empty), [first](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.first), and [get_unchecked](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.get_unchecked); [Option::copied](https://doc.rust-lang.org/1.81.0/std/option/enum.Option.html#method.copied); [u8: Copy](https://doc.rust-lang.org/1.81.0/std/primitive.u8.html#impl-Copy-for-u8) +- Rust 1.82.0: slice [is_empty](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.is_empty), [first](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.first), and [get_unchecked](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked); [Option::copied](https://doc.rust-lang.org/1.82.0/std/option/enum.Option.html#method.copied); [u8: Copy](https://doc.rust-lang.org/1.82.0/std/primitive.u8.html#impl-Copy-for-u8) + +No dependency, external, implementation, compatibility, deployment, or tool +assumption is consumed. No execution-based evidence was used. + +## Finding: missing local proof documentation + +**DOC-1 (proof artifact deficient; implementation still PROVED):** the unsafe +block at `lib.rs:13` has no adjacent `SAFETY` comment. The material derivation +had to be reconstructed above. A sufficient replacement is: + +```rust +// SAFETY: This branch is reached only when `bytes.is_empty()` is false, +// so `bytes.len() > 0` and index 0 is in bounds for `bytes`. +Some(unsafe { *bytes.get_unchecked(0) }) +``` + +No edit was requested or made. Releases after 1.82.0, other targets/features, +and compiler/backend or binary correctness are outside the claim. Re-review is +required if source, either policy, the supported Rust/target/feature set, or a +consumed standard-library contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r027.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r027.md new file mode 100644 index 0000000000..a6cd157964 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r027.md @@ -0,0 +1,42 @@ +# Focused review: `Block` contract evolution + +## Claim and verdict + +Snapshot: the supplied `lib.rs`; scope is `Block`, its `Page` implementation, and `first`, under every target/profile on Rust 1.70+. There are no dependencies, `cfg`s, generated artifacts, FFI, or concurrency in the supplied source. + +- **`Page` implementation: PROVED** at Rust 1.70, relative to the Rust axioms below and to the contract reading stated under “documentation gap.” It establishes every published implementer obligation. +- **`first`: PROVED** on the same basis for every valid `B: Block`; it needs only a strict projection of the published guarantee. +- **Proof documentation: UNPROVED/inadequate.** The unsafe trait has no explicit `# Safety` implementer specification, the `unsafe impl` has no proof, and the dereference has no adjacent `SAFETY` proof. This is a documentation finding, not a demonstrated implementation defect. No `UNSOUND` or `CONTRACT-BROKEN` counterexample was found. +- The literal open-ended Rust 1.70+ verdict is **conditional** on `TCB-COMPAT`: the quoted Rust 1.70 propositions remain applicable throughout the supported later-stable range. Without acceptance of that compatibility premise (or version-by-version verification), the aggregate range is **UNPROVED**. Re-audit is required if any consumed proposition changes. + +## Obligation derivations + +**`unsafe impl Block for Page`.** `ALIGN = 16` is nonzero and a power of two. The Rust 1.70 Reference says `align` raises alignment, and the specified alignment must be a power of two; therefore `Page` has alignment 16. The `repr(C)` field-layout algorithm starts at offset zero, so the sole `[u8; 16]` field begins at the `Page` address. A valid `&Page` is non-dangling and aligned; the array is 16 contiguous `u8` elements, and `as_ptr` returns a pointer to that buffer which remains usable while the borrowed slice outlives it. Consequently `self.0.as_ptr()` is non-null, 16-aligned, and points to the 16 initialized bytes of the field for the `&self` borrow. This argument is representation-based and independent of target and profile. + +Authoritative premises: Rust 1.70 Reference [array layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout), [`repr(C)` struct layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs), and [alignment modifiers](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers); Rust 1.70 standard library [`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr); and the Reference rules that loading through dangling/unaligned pointers and producing an uninitialized integer are UB ([behavior considered undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). + +**`first`.** Let `p = block.base()`. A valid unsafe `Block` implementation supplies the published guarantee throughout the `block` borrow. Reading `*p` occurs immediately within that interval. It needs one initialized, live, provenance-carrying byte and alignment for `u8` (1); the guaranteed readable 16-byte region supplies the first byte. `Block::ALIGN`, its power-of-two property, the stronger `ALIGN` alignment, and bytes 1 through 15 are not consumed. No caller-side condition is hidden in this safe function. + +Suitable adjacent proof text is: + +> `SAFETY`: `Block::base` guarantees that, for this borrow, `p` points to a readable 16-byte region. Hence its first byte is initialized and live for this load; `p: *const u8` has the required alignment of 1. The load occurs before the borrow ends. + +The `Page` impl separately needs an adjacent proof containing the full derivation above; the narrower `first` proof cannot replace it. + +## Documentation gap + +“Readable for 16 bytes” is not defined. The proofs above use the necessary meaning: one live allocation/provenance permits non-mutating loads of all 16 initialized bytes for the entire borrow. The published words should be clarified only if that is confirmed to be their existing meaning. If the clarification would add an obligation not already borne by 1.x implementers, it is a contract strengthening and must wait for 2.0. The missing implication is: **the present word “readable” entails live allocation/provenance and initialization for each `u8` load.** + +## Compatibility boundary + +Within 1.x, keep every normative `Block` clause unchanged. Proof-oriented simplification may: + +1. Document the internal derived lemma “`base()` permits one `u8` load during the borrow.” +2. Make `first` cite only that lemma and explicitly mark `ALIGN` and the remaining 15 bytes unused. +3. Add non-normative `SAFETY` proofs for `Page` and `first`, and make logically equivalent contract wording clearer. + +Repository search cannot justify weakening a public contract: unknown downstream implementations owe the full current obligation, and unknown consumers may rely on every supplied guarantee. + +An explicitly authorized 2.0 migration is required to reduce readability from 16 bytes to one, weaken/remove the alignment guarantee, remove `ALIGN`, strengthen implementer duties, or change the unsafe trait boundary. Reducing obligations helps implementers but simultaneously weakens provider postconditions and invalidates permitted downstream reasoning. + +For v2, choose from actual downstream requirements: if only the value is needed, a safe trait method `fn first(&self) -> u8` removes this unsafe boundary; if 16 bytes remain required, return `&[u8; 16]`; if fixed over-alignment remains required, return a reference to a private-field, aligned wrapper. Stage any separately named additive API in 1.x only after its own compatibility/coherence review; it must not replace or silently narrow `Block`. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r028.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r028.md new file mode 100644 index 0000000000..52fbbde4eb --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r028.md @@ -0,0 +1,37 @@ +# Version-partitioned source review + +## Claim and verdict + +Snapshot: `lib.rs` SHA-256 `e561568dfc6262bc6312f1a346e970dfab1f0d8f5102984e80e3b06cf2885e9b`; request SHA-256 `786c6e6d7d270b20c53e82170066a52a3dee7485526f80bec0e025a8602e411b`. Scope is the sole public safe surface, `advance_marker` (`lib.rs:3-5`), and its sole unsafe operation, `*const [u8; 0]::add(1)` (`lib.rs:4`). The theorem is freedom from Rust undefined behavior for every well-typed safe call, under each requested toolchain region, every target admitted by that toolchain, and every ordinary profile, relative to TCB `r028-inline` below. + +| Region | Soundness verdict | +|---|---| +| Rust 1.79.0 | **UNSOUND** on every requested target/profile | +| Rust 1.80.0 | **PROVED** on every requested target/profile, relative to `r028-inline` | +| Combined support set `{1.79.0, 1.80.0}` | **UNSOUND**, because it contains the 1.79.0 witness | + +There is no documented behavioral postcondition to assess beyond returning the declared raw-pointer type. No pointer is dereferenced. Proof-documentation quality is deficient independently of these implementation verdicts: the unsafe block has no adjacent `SAFETY` proof. + +## Obligation ledger and derivation + +**O-1 — byte offset.** The version-matching `size_of` documentation says `[T; n]` has size `n * size_of::()` and lists `u8` as one byte ([1.79.0](https://doc.rust-lang.org/1.79.0/core/mem/fn.size_of.html), [1.80.0](https://doc.rust-lang.org/1.80.0/core/mem/fn.size_of.html)). Therefore `size_of::<[u8; 0]>() = 0` and `add(1)` computes the mathematical byte offset `1 * 0 = 0`, independently of target and profile. + +**O-2/1.79 — allocation condition: violated.** Rust 1.79.0 documents `ptr::null` as creating a null pointer with address zero ([null](https://doc.rust-lang.org/1.79.0/core/ptr/fn.null.html)); its pointer safety section says a null pointer is “never valid, not even for accesses of size zero” ([pointer safety](https://doc.rust-lang.org/1.79.0/core/ptr/index.html#safety)). The 1.79.0 `add` contract unconditionally requires both starting and resulting pointers to be in bounds or one byte past the end of the same allocated object ([`add` safety](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add)). The null starting pointer does not satisfy that condition; treating address zero as one-past via address-space wrap is also expressly disallowed by the same contract. The zero byte offset satisfies the `isize` and address-arithmetic clauses, but does not waive the allocation clause in this version. + +Thus the valid safe call `let _ = advance_marker();` reaches the contract-violating unsafe operation. That execution contains UB. There are no inputs or branches, so the witness applies uniformly to every requested 1.79.0 target/profile. + +**O-2/1.80 — all `add` clauses: discharged.** Rust 1.80.0 changes the controlling clause: allocation membership is required only if the computed byte offset is nonzero, and it states, “If it is zero, then the function is always well-defined” ([`add` safety](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add)). O-1 establishes exactly the zero-offset case. Independently, zero fits in `isize`, and adding zero to address zero neither overflows nor wraps. The operation performs no access, and returning the resulting raw pointer introduces no further unsafe operation. O-2 is therefore proved for every call and every requested 1.80.0 target/profile. + +## Configuration closure, boundary, and TCB + +The source has no `cfg`, features, dependencies, generated code, macros, FFI, allocator, concurrency, panic, or profile-dependent checks. The proof is parametric over target data layout: only multiplication by the universally fixed array length zero and the representability of zero are used. Thus target/profile axes introduce no additional cases. + +TCB `r028-inline` consists only of these accepted version-matching standard-library axioms: `AX-179-SIZE`, `AX-179-NULL/POINTER`, `AX-179-ADD`, `AX-180-SIZE`, and `AX-180-ADD`, at the linked pages. There are no dependency, tool, implementation, deployment, or compatibility assumptions; no later documentation was carried backward. This was source review only: nothing was built, executed, expanded, or tested. + +## Findings and resolution + +**F-1 — soundness defect, Rust 1.79.0.** A safe, argument-free API unconditionally invokes `add` with a null pointer that fails that version’s contract. To retain 1.79.0 support, remove the unsafe arithmetic (the body can return `core::ptr::null()` if that is the intended result) or use an operation whose 1.79.0 contract permits this case and prove its exact behavior. Merely adding a comment cannot fix F-1. Alternatively, excluding 1.79.0 would be a support-policy change. + +**F-2 — missing local proof.** If the supported set is narrowed to Rust 1.80.0, suitable adjacent wording is: `SAFETY: [u8; 0] has size 0, so add(1) computes a zero-byte offset; Rust 1.80.0 specifies that a zero computed offset is always well-defined.` That wording is false for the currently combined support set and must not be used there. + +Re-review is required if the source, supported Rust versions, relevant standard-library contracts, target set, or configuration axes change. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r029.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r029.md new file mode 100644 index 0000000000..3c74890aba --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r029.md @@ -0,0 +1,86 @@ +# Focused source review: `classify` + +## Claim and verdicts + +**Soundness — UNSOUND.** For the exact `lib.rs` identified below, under Rust +1.80.0 on every target on which this source is accepted and in every ordinary +profile, the safe function `classify` is not sound for all well-typed safe +calls. `classify(0)` reaches undefined behavior without any caller-side unsafe +obligation. + +**Mandatory documented behavior — CONTRACT-BROKEN.** Both documented clauses +fail: the zero case reaches undefined behavior rather than providing the +promised panic, and `classify(1)` returns `2` on normal return rather than its +input, `1`. + +These are source-level Rust-semantic verdicts, not claims about the behavior of +any particular optimized binary after undefined behavior. + +## Snapshot, boundary, and coverage + +- Request: `REQUEST.md`, SHA-256 + `9d559df45c2ef498f44516fed36ef84c82fa4a02a59edca55d2ff88a7b93eb02`. +- Audited source: `lib.rs`, SHA-256 + `42388bb1a03741a7725c9836cc134c205af3061efef7b8ee00ce38d2dc51aeeb`. +- Scope: Rust and `core` 1.80.0; all targets and ordinary profiles; exact safe + API and all of its documented behavior. +- Safe surface: only `pub fn classify(input: u8) -> u8` (`lib.rs:6`). Its safe + signature admits every `u8`; prose cannot impose a hidden safety precondition. +- Unsafe surface/consumer: the call to + `core::hint::unreachable_unchecked` (`lib.rs:8`). There are no fields, + traits, macros, generated artifacts, dependencies, callbacks, or named + invariants in the supplied source. + +There is no conditional compilation or target/profile-dependent source. The +proof below partitions all 256 `u8` inputs, and its premises are abstract +Rust/core contracts with no target or profile qualification. Thus the findings +apply parametrically to every requested configuration; optimization cannot +repair source-level undefined behavior. + +## Authoritative premises and derivation + +TCB `r029-inline-1` contains only these verified Rust 1.80.0 axioms; there are +no additional assumptions or tool-derived facts: + +- **AXIOM-UU:** The 1.80.0 standard-library safety contract says, “Reaching this + function is Undefined Behavior.” + ([`core::hint::unreachable_unchecked` § Safety](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety)). +- **AXIOM-MATCH:** A `match` branches on patterns, with the matching arm selected + by the scrutinee + ([match expressions](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html)); + a literal pattern “match[es] exactly the same value” as its literal and `_` + “matches any value” + ([literal patterns](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns), + [wildcard pattern](https://doc.rust-lang.org/1.80.0/reference/patterns.html#wildcard-pattern)). + +The exhaustive local derivation is: + +| Input | Selected arm and outcome | Disposition | +|---|---|---| +| `0` | Literal arm at line 8; the unsafe call is reached. | AXIOM-UU makes this execution UB. The callee obligation that its site not be reached is false. It also supplies no defined panic satisfying line 3. | +| `1` | Literal arm at line 9; normal return is `2`. | Defined, but `2 != 1`, directly refuting line 5. | +| `2..=255` | Neither literal matches; `_` at line 10 returns `input`. | Defined and satisfies the normal-return clause. | + +Because `classify(0)` is a well-typed call through a safe API, that single +reachable execution refutes universal soundness. Independently, +`classify(1)` is a defined normal-return counterexample to the behavioral +contract. The panic clause for zero is also not upheld: executing an operation +whose contract classifies reachability as UB is not a defined Rust panic. + +## Findings and required resolution + +1. **F-01 — UNSOUND and CONTRACT-BROKEN (`lib.rs:8`).** The required + proposition is that this call site is unreachable, but the dominating + branch establishes exactly `input == 0`, and safe callers may supply zero. + There is no adjacent safety proof, and none can be reconstructed for the + current safe signature and implementation. Resolution must make the + accepted zero path produce the documented defined panic (without invoking + UB); a caller safety obligation cannot be added to this safe API. +2. **F-02 — CONTRACT-BROKEN (`lib.rs:9`).** The normal-return postcondition + requires result `== input`; the `input == 1` path returns `2`. Resolution + must make this path satisfy the published result relation (or deliberately + change the public behavioral contract and assess compatibility). + +No source change, build, test, execution, or macro expansion was performed. +Re-review is required if the source, documentation, Rust/core version, or +supported configuration set changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r030.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r030.md new file mode 100644 index 0000000000..313c87abbf --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r030.md @@ -0,0 +1,58 @@ +# Focused unsafe-Rust source review: `View` + +## Claim and verdict + +**Soundness: UNSOUND** for the exact `lib.rs` snapshot (`sha256 ac762e6e2bd87884bbbcfbe7bbf706e5b9dae9b078e82d01252111a9a6ad84e8`) under the requested theorem: every well-typed safe use, Rust 1.70+, every target, and ordinary profiles. A well-typed safe client on Rust 1.70 reaches undefined behavior. Because 1.70 is in the quantified version set, that witness refutes the whole theorem; no forward-compatibility assumption is needed. + +The review is source-level and covers the three supplied files only. The prior design note was not used as a premise. There are no dependencies, generated artifacts, `cfg`s, unsafe public contracts, or documented postconditions beyond the guarantees inherent in the public types/signatures. + +## Boundary, invariant, and obligation coverage + +The complete explicit safe surface is public `View<'a, T>` plus `new`, `get`, and `get_mut`. `ptr` and `borrow` are private. The unsafe sites are `&*self.ptr` at `lib.rs:16` and `&mut *self.ptr` at `lib.rs:20`; neither has a `SAFETY` proof. + +The intended representation invariant is: `ptr` denotes the live, aligned, initialized `T` originally uniquely borrowed by `new`, and safe operations never create accesses incompatible with references already issued. `new` uses Rust 1.70's documented [`&mut T` to `*mut T` coercion](https://doc.rust-lang.org/1.70.0/reference/type-coercions.html#coercion-types), and privacy prevents a downstream safe caller from forging the fields. Rust 1.70's [`PhantomData` documentation](https://doc.rust-lang.org/1.70.0/std/marker/struct.PhantomData.html) says the containing type can “act as though it stores a value of type `T`” for compiler safety-property calculations, but that does not serialize method calls or tie a returned reference to a receiver borrow. + +| ID | Site | Required proposition | Status | +|---|---|---|---| +| O1 | `new`, lines 11–13 | Establish the initial pointer/borrow relationship without exposing field mutation. | Discharged for the inspected source. | +| O2 | `get`, lines 15–17 | The pointee remains compatible with a shared reference for every time the returned `&'a T` is live. | False in composition with `get_mut`. | +| O3 | `get_mut`, lines 19–21 | The pointee is exclusive for every time the returned `&'a mut T` is live. | False after `get`, and repeated calls can likewise issue aliases. | + +## Safe counterexample and derivation + +```rust +fn clobber(_shared: &i32, unique: &mut i32) { + *unique = 1; +} + +let mut value = 0; +let mut view = View::new(&mut value); +let shared = view.get(); +let unique = view.get_mut(); +clobber(shared, unique); +``` + +Rust 1.70's [lifetime-elision rules](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html) say the receiver lifetime is assigned to “all elided output lifetime parameters.” These outputs are not elided: they explicitly use the impl parameter `'a`. The effective relationships are therefore `get<'s>(&'s self) -> &'a T` and `get_mut<'s>(&'s mut self) -> &'a mut T`. Neither result carries `'s`, so the first receiver borrow does not prevent the second call. The archived [operator rules](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#the-dereference-operator) say dereferencing a pointer “denotes the pointed-to location”; therefore both unsafe blocks borrow the same `value` through the unchanged `ptr`. + +The Rust 1.70 [undefined-behavior rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined) state that a reference passed to a function is “live at least as long as that function call” and that “data reached through a shared reference ... is immutable” unless inside `UnsafeCell`. During `clobber`, `shared` is therefore live while the write through `unique` mutates the same ordinary `i32`. That is undefined behavior caused entirely by safe client code interacting with the two unsafe blocks. + +The witness is independent of target layout, optimization, overflow checking, panic strategy, and threading. Thus one parametric argument covers all targets and ordinary profiles for the Rust 1.70 configuration. No execution or tool result was used. + +## Findings and remediation + +**F1 — implementation defect (UNSOUND):** the safe methods export references for the backing borrow's full `'a` rather than the receiver borrow. Both signatures must be changed (changing only `get_mut` leaves a long-lived shared reference able to overlap later mutation): + +```rust +pub fn get(&self) -> &T +pub fn get_mut(&mut self) -> &mut T +``` + +Lifetime elision then ties each result to its receiver borrow, allowing shared results to coexist only with shared receiver borrows and requiring exclusivity while a mutable result is usable. + +**Proposal status: UNIMPLEMENTED and UNPROVED.** It is not part of this snapshot and needs a fresh audit. That audit should state the representation invariant beside `View` and add adjacent `SAFETY` proofs showing: the private pointer came from `new`'s live `&mut T`; no safe transition changes it; and the receiver-tied result lifetime makes the current receiver borrow enforce the required aliasing interval. + +**F2 — proof-documentation defect:** both unsafe blocks lack any safety comment. This would remain a finding even if an implementation proof could be reconstructed; for the current signatures, no valid local proof can discharge O2/O3. + +## TCB, residual scope, and triggers + +TCB consists only of the five linked archived Rust 1.70 Reference/standard-library propositions. No LLVM alias model, compiler implementation, dependency, test, or prior review is trusted. Cargo metadata and non-supplied crate code were unavailable, but they cannot rescue a safe API counterexample in this source. Re-audit after changing either method signature, field visibility/type, constructors, trait impls, generated code, or the supported Rust/configuration set. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r031.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r031.md new file mode 100644 index 0000000000..1fdc492bbb --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r031.md @@ -0,0 +1,42 @@ +# Focused unsafe-Rust review and redesign + +## Scope and claim + +Artifact: the supplied 12-line `lib.rs`. In scope are its complete safe public surface—`Ticket` (private representation) and `ticket(usize) -> Ticket`—and the sole unsafe operation. The required domain is Rust 1.70+, every target, and every ordinary build profile, with no dependencies or deployment restrictions. No `cfg`, generated code, callbacks, concurrency, FFI, allocation, or target-specific operation occurs. The review is source-only; no build or execution evidence is used. + +## Current artifact + +**Soundness verdict: UNSOUND.** This is established for Rust 1.70 optimized builds with debug assertions disabled, which is one supported case and therefore refutes the universal Rust-1.70+-and-all-profiles claim. + +`ticket(0)` is a well-typed safe call, so it may carry no hidden safety obligation. At `lib.rs:9`, `debug_assert!(id != 0)` does not dominate the unsafe operation in all profiles: Rust 1.70 documents that an optimized build does not execute `debug_assert!` unless `-C debug-assertions` is passed ([`debug_assert!`, Rust 1.70](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html)). The resulting path passes zero at `lib.rs:10` to `NonZeroUsize::new_unchecked`. Its exact contract says, “This results in undefined behaviour if the value is zero” and requires that the value not be zero ([`NonZeroUsize::new_unchecked`, Rust 1.70](https://doc.rust-lang.org/1.70.0/std/num/struct.NonZeroUsize.html#method.new_unchecked)). Thus the unsafe precondition is false on a reachable valid safe call. + +**Documented-behavior verdict: CONTRACT-BROKEN via the same path.** The API promises to panic for `id == 0`; in the supported optimized case it instead reaches undefined behavior, so the promised panic is not established. This is not a separate defined-behavior defect. + +The unsafe block also has no adjacent `SAFETY` proof. Even a proof reconstructed from the current source fails because its only candidate fact, the debug assertion, is configuration-conditional. + +## Recommended redesign + +Keep the public type and exact safe function signature, but remove the unsafe operation: + +```rust +pub fn ticket(id: usize) -> Ticket { + match NonZeroUsize::new(id) { + Some(id) => Ticket(id), + None => panic!("ticket id must be non-zero"), + } +} +``` + +This preserves `pub struct Ticket(NonZeroUsize)` and `pub fn ticket(id: usize) -> Ticket`. Rust 1.70 documents checked `new` as creating a non-zero exactly when the input is nonzero ([`NonZeroUsize::new`, Rust 1.70](https://doc.rust-lang.org/1.70.0/std/num/struct.NonZeroUsize.html#method.new)). Therefore: + +- If `id != 0`, `new` supplies `Some` containing that value, and `Ticket` contains `id`. +- If `id == 0`, the `None` arm panics. +- Neither branch uses unsafe code or an invariant that safe callers can violate. + +The argument is parametric over pointer width and target, and contains no profile-sensitive assertion, overflow, feature, or panic-strategy-dependent safety step. Under panic=abort the `panic!` still initiates a panic and aborts rather than unwinding; the documented contract promises a panic, not unwinding or recovery. + +## TCB, compatibility, and follow-up + +The source-level conclusions use only the quoted Rust 1.70 standard-library contracts and the inspected source. No dependency, environment, compiler-backend, or platform premise is admitted. The current `UNSOUND` verdict needs no forward-compatibility premise because one supported 1.70 configuration is already a counterexample. + +The proposal is a design, not an audited new artifact. After implementation, re-audit the exact snapshot. For later Rust releases, consume their matching versioned standard-library contract (or record an expressly accepted compatibility premise); re-audit if that contract, the supported toolchain/target/profile set, representation, or public behavior changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r032.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r032.md new file mode 100644 index 0000000000..f3aa7a23b8 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r032.md @@ -0,0 +1,50 @@ +# Focused review: `Block`, `Page`, and `first` + +## Claim and verdict + +Snapshot: `lib.rs` SHA-256 `8e347fd2a5ca16fa1bd9a7b6019fc57227346bd895c5af794d2efa76265f01a3`. Scope is the published `Block` contract, the `Page` impl, and safe function `first`; this is not a whole-crate audit. + +**PROVED**: for stable Rust 1.70+ through the audit cutoff (2026-07-31), on every target and in every profile, `Page` satisfies every stated `Block` clause, and `first` is sound for every `B` whose `unsafe impl Block` satisfies that published contract. This is relative to TCB-COMPAT below and to the ordinary reading of “readable for 16 bytes”: the returned address denotes 16 consecutive initialized bytes in a live allocation from which reads are permitted throughout the receiver borrow. If the project intended a weaker meaning, `first` is instead `UNPROVED`; the prose should be made explicit without silently changing the contract. + +There is no source defect, UB witness, or broken documented postcondition. There is a proof-documentation defect: neither `unsafe impl` nor the dereference has an adjacent `SAFETY` proof, and the unsafe trait lacks a conventional `# Safety` section. + +## Premises, surface, and configuration closure + +The public surfaces are unsafe trait `Block`, implementer obligations `ALIGN` and `base`, public `Page` with a private field, its `unsafe impl`, and safe generic `first`. There are no other constructors, methods, fields, macros, generated artifacts, dependencies, `cfg`s, FFI, allocation, target features, or profile-dependent branches in the supplied source. + +Rust 1.70 says that a correctly implemented unsafe trait is safe to use ([unsafe traits](https://doc.rust-lang.org/1.70.0/reference/items/traits.html#unsafe-traits)). Its layout rules give `u8` size 1, array contiguity, the `repr(C)` first-field offset algorithm (which starts at offset zero), and `align` raising the enclosing struct’s alignment ([layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#type-layout), [`repr(C)` structs](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs), [alignment modifiers](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers)). Slice `as_ptr` “returns a raw pointer to the slice’s buffer” and remains usable while the slice lives ([`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)). Rust 1.70 classifies dereferencing a dangling or unaligned raw pointer and producing an uninitialized integer as UB ([undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). + +TCB-COMPAT admits that those exact semantic propositions remain applicable on later supported stable releases. This is necessary because `1.70+` is open-ended; future releases require re-review if any consumed Reference/std proposition changes. TCB-SEMVER is the request’s project premise that the published trait, its contract, and downstream implementation compatibility are 1.x commitments. No compiler-backend or platform-implementation premise is consumed; this is a source-level result. + +## Obligation derivations + +**`Page` (`lib.rs:12-21`) — full contract proved.** `ALIGN = 16`, hence it is nonzero and a power of two. `repr(align(16))` makes every valid `Page` address at least 16-aligned. `repr(C)` places the first and only field at offset zero, so the array buffer returned by `self.0.as_ptr()` has that same alignment. A valid shared `&Page` is non-null and keeps its initialized `[u8; 16]` alive during the borrow. Arrays contain 16 contiguous one-byte `u8` elements, and `as_ptr` identifies the buffer start. Thus the result is non-null, 16-aligned, and permits reads of all 16 bytes for the required interval. The proof is parametric over target and profile. + +`repr(C)` is proof-relevant. `repr(align(16))` alone raises the outer type’s alignment but does not promise field ordering/offset; removing `repr(C)` merely because `first` uses one byte would leave `Page`’s published `ALIGN`-alignment obligation unproved. + +**`first` (`lib.rs:23-25`) — soundness proved.** A valid unsafe impl supplies a non-null pointer readable for 16 bytes during this borrow. The dereference occurs immediately in the same expression, with no intervening transition, and loads only the first initialized byte. `u8` has size 1; because every type’s size is a multiple of its positive alignment, its alignment is 1. Consequently `first` does not consume `ALIGN`, the pointer’s stronger `ALIGN`-alignment guarantee, or readability of bytes 2 through 16. It consumes only the one-byte live/readable portion (with non-nullness already explicit). The returned value is that first byte. + +Suggested adjacent proof text, without changing behavior: + +```rust +// SAFETY (`Page` impl): ALIGN is 16. repr(align(16)) aligns Page, repr(C) +// puts its only field at offset 0, and [u8; 16] is initialized and contiguous; +// as_ptr therefore supplies the full contract for the &self borrow. + +// SAFETY (`first`): Block::base guarantees its first byte remains readable +// during this borrow; u8 has alignment 1, so loading that byte is permitted. +``` + +## What may change in 1.x + +The local proof for `first` may be simplified to the one-byte argument above. Proof comments and `# Safety` documentation may be added or clarified if—and only if—they preserve the existing normative meaning. Internal code may introduce a private safe wrapper around this exact unsafe operation. A new, opt-in safe API may be added alongside `Block`, subject to ordinary Rust name/coherence review, but the old surface and guarantees must remain usable. + +The public contract may **not** be narrowed to one byte, delete or weaken `ALIGN`, relax alignment, remove `base`, make the trait safe, or strengthen implementer duties in 1.x. Narrowing provider guarantees breaks unknown downstream consumers; strengthening duties breaks unknown downstream impls. Repository search cannot close either open-world set. Adding a required trait item is likewise breaking; a default item does not remove the old unsafe contract and can introduce method-resolution collisions. + +## 2.0-only redesign and migration + +If the only required capability is the current `first`, the preferred 2.0 design is a safe trait such as `trait Block { fn first(&self) -> u8; }`; `Page` implements it as `self.0[0]`, and the free function delegates safely. That removes the raw pointer, unsafe trait/impl, layout promise, `ALIGN`, `repr(C)`, and `repr(align(16))` from this proof surface. + +If consumers still need all 16 bytes, a safe `fn bytes(&self) -> &[u8; 16]` carries lifetime, initialization, and extent in the type; retain a separate unsafe/aligned capability only if a demonstrated consumer needs 16-byte address alignment. + +Both designs change published guarantees and downstream impl/call-site contracts, so they require explicit 2.0 authorization and a fresh audit. A compatible migration can introduce the replacement as opt-in during 1.x, retain and optionally deprecate (but not weaken) `Block`, migrate known users, and remove/change the old surface only in 2.0. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r033.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r033.md new file mode 100644 index 0000000000..516ca9807b --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r033.md @@ -0,0 +1,35 @@ +# Version-partitioned source review + +## Claim and verdict + +Snapshot: `lib.rs` SHA-256 `e561568dfc6262bc6312f1a346e970dfab1f0d8f5102984e80e3b06cf2885e9b`. Scope is the sole public safe API, `advance_marker` at lines 3–5, and its unsafe operation at line 4. The theorem is source-level freedom from Rust undefined behavior for every well-typed safe call, separately under exactly Rust 1.79.0 and 1.80.0, on every target and in every ordinary profile, relative to TCB `R033-1` below. + +| Region | Soundness verdict | +|---|---| +| Rust 1.79.0; every target/profile | **UNSOUND** | +| Rust 1.80.0; every target/profile | **PROVED**, relative to `R033-1` | +| Required union of both regions | **UNSOUND** | + +There is no documented behavioral postcondition to assess independently, so no `CONTRACT-BROKEN` verdict applies. + +## Boundary and configuration coverage + +The complete surface is one safe, argument-free free function. Its only path constructs a null `*const [u8; 0]`, invokes unsafe `add(1)`, and returns the raw pointer. There are no exposed unsafe APIs, fields, traits, callbacks, macros, generated artifacts, dependencies, state, or invariants. A null raw pointer is itself a permitted raw-pointer value; callers cannot dereference it in safe Rust. + +The proof is parametric over target and ordinary profile: there is no conditional source or profile-sensitive check. The versioned Reference says an array `[T; N]` has size `size_of::() * N` ([1.79.0](https://doc.rust-lang.org/1.79.0/reference/type-layout.html#array-layout), [1.80.0](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#array-layout)). Thus `[u8; 0]` has size zero on every target, and `add(1)` computes the byte offset `1 * 0 = 0` in both regions. + +## Obligation derivation + +**O-79 — failed.** Rust 1.79.0 documents `ptr::null` as creating a null raw pointer with address zero ([contract](https://doc.rust-lang.org/1.79.0/core/ptr/fn.null.html)); its pointer safety documentation states, “A null pointer is never valid, not even for accesses of size zero” ([pointer safety](https://doc.rust-lang.org/1.79.0/core/ptr/index.html#safety)). The 1.79.0 `add` contract unconditionally requires: “Both the starting and resulting pointer must be either in bounds or one byte past the end of the same allocated object” ([`add`](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add)). The null pointer is not associated with such an allocated object, so this conjunct fails even though the computed offset is zero. Every ordinary call reaches the operation; therefore any safe call is an in-scope UB witness. + +**O-80 — discharged.** Rust 1.80.0 changed the allocation-bound conjunct to apply only when the computed byte offset is nonzero and expressly says, “If it is zero, then the function is always well-defined” ([`add`](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add)). Here the offset is zero; it fits `isize`, adding it to address zero fits `usize`, and no address-space wrapping occurs. The operation is therefore well-defined. Nothing subsequently dereferences the returned pointer, and the sole path returns normally. This covers every target/profile in the 1.80.0 region. + +## TCB, evidence, and findings + +`R033-1` consists only of the exact versioned Rust Reference/std propositions linked above: array layout, `ptr::null`, pointer validity, and each version’s `add` contract. All were directly checked for their named version. There are no dependency, external, deployment, compatibility, probabilistic, or tool-derived premises. No target was built or executed; this is an abstract source proof. Compiler/backend correctness and binary-level behavior are outside the source-level claim. + +**Finding F-1 (soundness):** the safe API is unsound on supported Rust 1.79.0. The smallest repair is to avoid the unsafe in-bounds operation—for example, return `core::ptr::null()` directly if preserving the observed zero-sized result is intended, or use the safe `wrapping_add(1)` if pointer-arithmetic intent matters. Re-audit the chosen replacement; no edit was authorized here. + +**Finding F-2 (proof documentation):** the unsafe block has no adjacent `SAFETY` proof. The reconstructed 1.80.0 derivation above cannot justify the supported 1.79.0 region, so adding a comment alone cannot repair F-1. + +Re-audit on source changes, a change to either supported Rust version/configuration set, or a material change to any cited contract. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r034.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r034.md new file mode 100644 index 0000000000..398b7662e9 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r034.md @@ -0,0 +1,36 @@ +# `Block` published-contract review + +## Claim, scope, and verdicts + +Reviewed the exact `lib.rs` supplied, without execution, for `Block`, `Page`'s `unsafe impl`, and safe `first`; scope is source-level Rust soundness and the published `Block` postconditions on all targets/profiles. Invalid downstream `unsafe impl Block`s are outside valid use: Rust 1.70 says an unsafe trait's extra conditions “must be upheld by implementations,” and `unsafe impl` asserts they were discharged ([Reference](https://doc.rust-lang.org/1.70.0/reference/unsafe-keyword.html#unsafe-traits-unsafe-trait)). No dependencies, `cfg`, generated code, allocation, panic-dependent cleanup, or profile-sensitive operation exists here. + +- **`Page` implementation: PROVED for Rust 1.70**, relative only to the cited Rust 1.70 Reference/std contracts and the ordinary reading below of the published words “readable” and “during the borrow.” It establishes every `Block` clause. +- **`first`: PROVED for Rust 1.70** for every valid `Block` implementation. It consumes strictly less than `Block` promises. +- **Proof documentation: UNPROVED/inadequate as written.** The unsafe trait has no explicit implementer `# Safety` section, and neither the unsafe impl nor raw dereference has an adjacent derivation. “Readable” should be defined as valid to read initialized bytes, and “during the borrow” should name the receiver borrow and normal-return interval. +- **The unbounded Rust `1.70+` claim: UNPROVED.** The checked axioms are exact Rust 1.70 text. No accepted premise was supplied that preserves those exact propositions through every later and future stable release. This is a version-coverage gap, not a source counterexample. Close it with a finite audit cutoff plus re-review on each new stable, or an explicitly accepted, precisely scoped Rust-compatibility TCB premise. + +## Compact derivation + +Obligations are: `ALIGN` is nonzero and a power of two; on normal return from `base`, while its `&self` borrow remains live, the pointer is non-null, its address is a multiple of `ALIGN`, and 16 consecutive initialized bytes can be read; `first`'s raw `u8` dereference must not be dangling/unaligned and must produce an initialized `u8`. + +For `Page`, `ALIGN = 16` directly proves the constant clause. Rust 1.70 specifies `u8` size 1; alignment is at least 1 and size is a multiple of alignment, so `u8` alignment is 1. It also specifies `[T; N]` size `size_of::() * N`, identical array/element alignment, and element `n` at offset `n * size_of::()` ([size/alignment](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#size-and-alignment), [primitive layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#primitive-data-layout), [arrays](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). Thus `[u8; 16]` is 16 contiguous initialized bytes. The `repr(C)` algorithm puts the first field at offset zero, and `align(16)` raises the enclosing alignment ([C structs](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs), [alignment modifier](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers)); consequently the field pointer is 16-aligned. `as_ptr` returns a pointer to the slice buffer and remains usable while that buffer's borrow/storage lives ([Rust 1.70 `slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)). The live `&Page` keeps the containing field live for the promised interval, so the pointer is non-null and all 16 bytes remain readable. This argument is target-parametric and profiles do not alter it. + +For `first`, the valid implementation's postcondition gives a non-dangling, initialized first byte until the receiver borrow ends; the load occurs immediately in that interval. A `u8` has alignment 1, so no `ALIGN` fact is needed. Rust 1.70 classifies dereferencing a dangling/unaligned raw pointer and producing an uninitialized integer as UB ([Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)); the two facts above exclude those cases. Neither bytes 1–15 nor the nonzero/power-of-two/value of `ALIGN` participates in this consumer proof. + +Suggested proof-only wording (no edit is authorized): + +> **Trait `# Safety`:** Each implementation must choose a nonzero power-of-two `ALIGN`. After every normally returning `base` call, for the remainder of the receiver `&Self` borrow, the result must remain non-null, have an address divisible by `ALIGN`, and be valid to read 16 consecutive initialized `u8`s. +> +> **`Page` unsafe impl:** `16` is a nonzero power of two; `repr(C)` places the sole field at offset zero; `align(16)` aligns `Page`; `[u8; 16]` is 16 contiguous initialized bytes; `as_ptr` targets that field, whose storage remains live for the `&Page` borrow. +> +> **`first` unsafe block:** `Block::base` guarantees the returned pointer's first initialized byte remains readable during this `&B` borrow. This dereference occurs before that borrow ends, and `u8` alignment is 1. + +These are valid 1.x documentation improvements only if accepted as clarifications of the existing words, not new obligations. + +## What 1.x may and may not simplify + +In 1.x, simplify **the local proof**, not the published theorem: document that `first` uses only one readable byte and no `ALIGN` fact. Keep `Block`'s complete obligations, keep `Page` proving all of them, and preserve `repr(C, align(16))`. A separately named additive narrow API/trait may be introduced after normal coherence and method-resolution review, but do not silently retarget the published `first` signature; the old trait must continue to mean and supply exactly what 1.x consumers and implementations were promised. + +Repository search cannot authorize weakening: downstream consumers may read all 16 bytes or rely on `ALIGN`, while downstream impls were written against the existing implementer contract. Reducing 16 bytes to one, removing/weakening `ALIGN`, changing the validity interval, removing `repr(C)`, changing required impl items, or making this unsafe trait safe would invalidate published reasoning or impl source and is a breaking change. + +An explicitly authorized 2.0 may replace the raw-pointer contract with a safe, lifetime-bearing surface. If 16 bytes remain meaningful, prefer `fn bytes(&self) -> &[u8; 16]` and implement `first` as `block.bytes()[0]`; if only the first byte is part of the product contract, prefer `fn first_byte(&self) -> u8`. Either can make the trait safe and eliminate this unsafe consumer. If aligned raw access must remain available, split it into a separately documented unsafe trait. Migration must inventory downstream consumers and impls, provide adapters where their old guarantees suffice, and only then remove the 1.x contract in the major release. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r035.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r035.md new file mode 100644 index 0000000000..7b45a874a1 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r035.md @@ -0,0 +1,50 @@ +# Supported-domain source review + +## Claim and verdict + +**Source soundness: PROVED** for the exact `lib.rs` snapshot, for every well-typed safe call to `first`, over the **commitment envelope** below, relative to the stated TCB: + +- stable Rust 1.79.0, 1.80.0, 1.81.0, and 1.82.0 (audit cutoff); +- `x86_64-unknown-linux-gnu` and `aarch64-unknown-linux-gnu`; +- without `fast`: every version/target pair; +- with `fast`: x86_64 on 1.79.0–1.82.0 and aarch64 on 1.80.0–1.82.0. + +This envelope is the union of configurations affirmatively supported by either current publication. Thus the proof covers the supported domain under either policy, and does not select, merge, supersede, or amend a policy. + +**Exact support-set determination: UNPROVED.** The publications conflict. Policy A supports `fast` on x86_64 at 1.79.0 and on aarch64 at 1.80.0–1.81.0; Policy B omits the former and says aarch64 supports *only* 1.82.0. With both documents current and no precedence rule, no unique exact official predicate follows. This is a policy/compatibility defect, not a source-soundness defect, because the source proof covers the larger envelope. + +## Snapshot, boundary, and configuration closure + +Reviewed all supplied files: `lib.rs`, `Cargo.toml`, `rust-toolchain.toml`, both policies, `CI.md`, and `REQUEST.md`. The crate is edition 2021, has only the boolean `fast` feature, and declares no dependencies, generators, build script, FFI, assembly, allocator, concurrency, target-feature, or target-conditional code. The only public surface is the safe function `first(&[u8]) -> Option`; callers therefore have no safety precondition beyond a well-typed safe call. + +The two mutually exclusive `cfg` predicates exhaust the feature axis. Neither implementation depends on target, profile, or optimization facts, so its proof is parametric over both named targets and ordinary profiles. The CI matrix is sampled evidence only and is not used in the proof. `rust-toolchain.toml` selects 1.82.0 by default but does not override the published multi-version commitments. + +## Obligation ledger and derivation + +1. **`fast` disabled (`lib.rs:3–6`) — PROVED.** The body contains only safe calls (`slice::first` and `Option::copied`). Relative to the exact standard-library safe-API trust stated below, no unsafe obligation is exposed or hidden from the caller. + +2. **`fast` enabled, empty branch (`lib.rs:8–11`) — PROVED.** No unsafe operation executes and the function returns `None`. + +3. **`fast` enabled, nonempty branch (`lib.rs:12–13`) — PROVED.** On each audited release, `is_empty` “returns `true` if the slice has a length of 0,” while `get_unchecked` returns a reference without bounds checking and its Safety section makes an out-of-bounds index UB: [1.79.0](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.get_unchecked), [1.80.0](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.get_unchecked), [1.81.0](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.get_unchecked), [1.82.0](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked). The corresponding `is_empty` contracts are on those same exact-version pages: [1.79.0](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.is_empty), [1.80.0](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.is_empty), [1.81.0](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.is_empty), [1.82.0](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.is_empty). The `else` branch establishes `len != 0`; because a slice length is a `usize`, this implies `0 < len`, so index `0` is in bounds. `get_unchecked(0)` therefore satisfies its sole applicable safety obligation and yields `&u8`; dereferencing copies that valid `u8`. No later transition or temporal obligation exists. + +The version proof uses each release's own documentation; it needs no backwards-compatibility assumption. + +## TCB and postcondition scope + +- **AXIOM-SLICE-1.79/1.80/1.81/1.82:** the exact `is_empty` and `get_unchecked` propositions linked above, for the matching stable standard library. +- **STD-SAFE-1.79/1.80/1.81/1.82:** the matching standard library's selected safe `slice::first` and `Option::copied` APIs are sound and behave according to their documented contracts. +- The usual source-level Rust abstract semantics for well-typed `&[u8]`, `usize`, references, and copying `u8`; no compiler-backend or binary-correctness claim is made. + +There is no unsafe public API and no documented unsafe-API postcondition. Broader undocumented safe-API behavior was not requested. + +## Finding: missing local proof documentation + +`lib.rs:13` has no adjacent `SAFETY` comment. The implementation obligation is proved above, but the proof artifact is deficient. No edit was authorized. Proposed wording: + +```rust +// SAFETY: This branch is reached only when `bytes.is_empty()` is false, +// hence `bytes.len() != 0` and index 0 is in bounds for `bytes`. +Some(unsafe { *bytes.get_unchecked(0) }) +``` + +Re-review is required if the function body, feature selection, either policy or its precedence, the supported Rust/target set, or a consumed standard-library contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r036.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r036.md new file mode 100644 index 0000000000..bf55603e93 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r036.md @@ -0,0 +1,44 @@ +# Focused unsafe-Rust review and redesign + +## Claim, snapshot, and verdict + +Scope is exactly `REQUEST.md` (SHA-256 `f4cb0fe5d667cdde8536d103ea2bf8c46a4219916acb811b0e33c4d2cb5bc376`) and `lib.rs` (SHA-256 `23f55cd3e96c8372d71a09336d84f72de191fd5f48de693d0be11762537dfa9f`). The support predicate is stable Rust 1.70+, every target, and every ordinary build profile; there are no dependencies, features, generated artifacts, or deployment restrictions. The reviewed theorem is that every well-typed safe use of `ticket` is free of Rust UB and satisfies its two documented outcomes. Audit cutoff: 2026-07-31. + +**Current soundness verdict: `UNSOUND`.** This is a focused verdict for `Ticket`/`ticket`, not a whole-crate verdict. **Current documented zero-input behavior: `UNPROVED`; there is no `CONTRACT-BROKEN` finding.** + +## Boundary and obligation coverage + +The complete source-visible API is the public, opaque `Ticket` type and safe `pub fn ticket(usize) -> Ticket`. Its tuple field is private, so the function is the only in-scope safe producer. There are no unsafe public APIs, custom traits/impls, mutation paths, callbacks, macros, or destructors. The representation invariant is simply `Ticket.0` is a valid `NonZeroUsize`; line 10 is its sole producer and sole unsafe obligation site. + +Rust 1.70 documents that an optimized build does not execute `debug_assert!` unless debug assertions are explicitly enabled ([`debug_assert!`, “Uses”](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html#uses)). It separately states that `NonZeroUsize::new_unchecked(0)` has undefined behavior and requires a nonzero argument ([`new_unchecked`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new_unchecked)). Therefore, in an ordinary optimized build with default debug assertions disabled: + +1. Safe code calls `ticket(0)`; this has no caller safety precondition. +2. Line 9 supplies no runtime check. +3. Line 10 calls `new_unchecked(0)`, violating its exact precondition and reaching UB. + +This Rust-1.70 supported configuration alone refutes the universal `1.70+` soundness claim, on every target because the proof uses only the target-independent zero value. With debug assertions enabled, zero panics before line 10; with nonzero input, the check succeeds and the unchecked-call precondition holds. Thus the defect is exactly the disabled-debug-assertion branch. + +The documentation’s nonzero-return clause is met: the unchecked constructor receives and stores that same nonzero `id`. The zero-input panic clause cannot be proved across profiles. The optimized witness contains UB, so it cannot also witness a defined failure to panic; accordingly the behavioral verdict is `UNPROVED`, not `CONTRACT-BROKEN`. The unsafe block also has no adjacent `SAFETY` proof, but adding one cannot repair the false local premise. + +## Recommended redesign + +Keep `Ticket`, the function signature, and its documentation unchanged; replace only the body: + +```rust +pub fn ticket(id: usize) -> Ticket { + Ticket(NonZeroUsize::new(id).expect("ticket id must be nonzero")) +} +``` + +This is the smallest proof surface: it deletes the unsafe operation and does not alter the private representation. Rust 1.70 says `NonZeroUsize::new` creates a nonzero value when its input is nonzero ([`new`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new)), while `Option::expect` returns the contained `Some` value and panics for `None` ([`expect`](https://doc.rust-lang.org/1.70.0/core/option/enum.Option.html#method.expect)). Hence: + +- `id != 0`: `new(id)` produces the corresponding `Some(NonZeroUsize)`; `expect` returns it; `Ticket` contains `id`. +- `id == 0`: no nonzero value can be created, so the result is `None`; `expect` panics. + +There is no debug-assertion, optimization, target-width, or target branch. Panic unwind versus abort does not change selection of the documented panic outcome. No caller obligation, unsafe surface, extra invariant, dependency, or migration is introduced; the panic payload was not part of the old contract. + +## TCB, status of the proposal, and review triggers + +The current finding consumes only the exact Rust 1.70 standard-library contracts linked above; there is no additional TCB assumption or tool-derived evidence. The target was not built, run, expanded, or modified. + +The redesign is counterfactual, so it receives no post-change `PROVED` verdict. Its proof is parametric for each supported Rust release whose version-specific standard-library documentation supplies the same `new` and `expect` contracts. A stability badge alone does not prove semantic continuity over the open-ended `1.70+` range; either accept an explicit Rust-compatibility TCB premise or recheck those contracts for the finite release range at implementation audit time. Re-audit after implementing the change, changing the representation/signature/documentation, expanding supported configurations, or materially changing either consumed standard-library contract. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r037.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r037.md new file mode 100644 index 0000000000..077ec1fb58 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r037.md @@ -0,0 +1,90 @@ +# Focused source review: `Buffer` + +## Claim and verdict + +**Soundness: UNSOUND.** For `lib.rs` SHA-256 +`368fa01b2f3fca39e89131414f8ec2641a4ac162b846ccf77b8f8bd293930d9a`, +under Rust and standard library 1.80.0, every target and ordinary profile has a +well-typed, entirely safe use that reaches Rust undefined behavior: + +```rust +let mut buffer = Buffer::from_static(); +buffer.overwrite(0); +``` + +This is a source-level result relative only to the Rust 1.80.0 Reference and +standard-library contracts cited below; it assumes no particular backend, +optimizer, or physical read-only-memory placement. There are no dependencies, +generated artifacts, FFI, features, or separately documented postconditions in +scope. + +## Boundary and obligation coverage + +The fields are private. The only current producers are unsafe +`from_writable` (`lib.rs:16-18`) and safe `from_static` (`lib.rs:20-26`); there +are no literals available to downstream safe code, conversions, trait impls, +macros, or field mutators. The only dereferencing consumer is `overwrite` +(`lib.rs:28-40`). `with_live` (`lib.rs:43-46`) invokes the closure while holding +its reference argument. Ordinary moves, borrows, and implicit drop do not +dereference `ptr` or alter either field. + +Two representation states exhaust the source: + +- **W:** `shared == None`; produced only by `from_writable`. Its unsafe caller + must keep `ptr` non-null, aligned, valid for a one-`u8` write, and free of + conflicting access throughout every possible use of the returned value. +- **S:** `shared == Some(r)`; produced only by `from_static`, with `r == &BYTE` + and `ptr` a mutability-cast raw pointer to the same location. No method changes + this state. + +For W, the reconstructed implementation proof succeeds relative to the unsafe +caller's ongoing contract. The `None` branch performs exactly the write for +which that contract supplies validity and alignment. Rust 1.80's raw-pointer +method delegates to [`ptr::write`](https://doc.rust-lang.org/1.80.0/std/primitive.pointer.html#method.write), +whose safety conditions say “`dst` must be valid for writes” and require proper +alignment ([Rust 1.80.0 documentation](https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html#safety)). + +For S, the obligation is false. The static is one precise location with static +lifetime ([Reference: static items](https://doc.rust-lang.org/1.80.0/reference/items/static-items.html)). +`ptr::from_ref` documents that converting `&T` to `*const T` is equivalent to +the first cast used here, and the Reference says a sized pointer-to-pointer cast +returns the pointer unchanged +([`from_ref`](https://doc.rust-lang.org/1.80.0/std/ptr/fn.from_ref.html), +[casts](https://doc.rust-lang.org/1.80.0/reference/expressions/operator-expr.html#type-cast-expressions)). +Thus `r` and `ptr` designate `BYTE`'s byte. + +At `lib.rs:30`, `r` is passed to `with_live`; the write at line 33 occurs during +that call. Rust 1.80 specifies that a reference “passed to a function, it is +live at least as long as that function call,” and that “bytes pointed to by a +shared reference ... are immutable.” It defines any overlapping write of more +than zero bytes as a mutation +([Reference: behavior considered undefined](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html)). +`size_of::()` is 1 on every target +([`size_of`](https://doc.rust-lang.org/1.80.0/std/mem/fn.size_of.html)), so +`ptr.write(value)` performs a nonempty write overlapping the byte protected by +the live shared reference. This is UB even if `value == 7` and no concurrent +access occurs. + +## Local proof finding + +The line 31 comment is materially false in the `Some` branch: that branch's +`ptr` was produced by `from_static`, not `from_writable`, and is not valid for +writes. No replacement safety comment can prove this implementation; the safe +producer/consumer path must cease writing such storage or use a representation +whose interior-mutability and aliasing rules permit the write. + +The line 36 comment cites the right producer but is incomplete proof +documentation: it omits alignment and the private-field/dataflow fact that +`None` can arise only from `from_writable`. Adequate wording for that branch +would be: + +> SAFETY: `shared == None` is constructed only by `from_writable`; private +> fields and the absence of mutators preserve that fact. Its caller must keep +> `ptr` non-null, aligned, valid for writing one `u8`, and free of conflicting +> access throughout this use. + +Consequently, the `from_writable`/W subset is **PROVED relative to its unsafe +caller contract**, while the requested aggregate theorem for all well-typed +safe uses is **UNSOUND on every requested configuration**. Any change to either +constructor, the fields, `overwrite`, `with_live`, or the Rust version requires +re-review. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r038.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r038.md new file mode 100644 index 0000000000..c889e22e0a --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r038.md @@ -0,0 +1,106 @@ +# Focused unsafe-Rust review: `View` + +## Claim and verdict + +**Soundness: UNSOUND.** The claim reviewed is: for the exact `lib.rs` snapshot +SHA-256 `ac762e6e2bd87884bbbcfbe7bbf706e5b9dae9b078e82d01252111a9a6ad84e8`, +every well-typed safe use is free of Rust undefined behavior on Rust 1.70+, +every target, and every ordinary profile. A safe counterexample reaches an +aliasing violation on Rust 1.70.0, which is included in that universally +quantified set. No verdict from `DESIGN-NOTE.md` was inherited. + +There are no documented unsafe-API postconditions to assess separately. The +return types promise references with lifetime `'a`; the implementation can +produce them, but cannot uphold the safety requirements that references carry. + +## Boundary, invariant, and obligations + +The complete handwritten surface is public `View<'a, T>` with private fields, +and safe methods `new`, `get`, and `get_mut`. There are no explicit trait impls, +macros, generated code, dependencies beyond `core`, FFI, allocation, arithmetic, +or conditional compilation. Compiler-derived auto traits do not enforce the +missing temporal borrow state. + +The necessary representation invariant is **I-VIEW**: `ptr` is the pointer +coerced from the `&'a mut T` accepted by `new`, continues to point to that same +live, aligned, initialized `T`, and every reference reborrowed from it obeys +reference aliasing for the whole time that reference is live. `new` +(`lib.rs:11-13`) establishes the pointer/lifetime relationship: Rust 1.70 +documents coercing `&mut T` to `*mut T`, and `PhantomData<&'a mut T>` makes the +type act as though it contains that reference. Private fields prevent a +downstream safe caller from replacing either field. Dropping `View` performs no +pointer access. + +The consumers do not preserve I-VIEW: + +- `get` (`lib.rs:15-17`) reborrows `ptr` as `&'a T`. Its result is not tied to + the temporary `&self` borrow, so safe code may retain it and call `get_mut`. +- `get_mut` (`lib.rs:19-21`) reborrows `ptr` as `&'a mut T`. Its result is not + tied to the temporary `&mut self` borrow, so safe code may call `get_mut` + again while the first result remains live. + +The second defect alone has this entirely safe witness (not executed): + +```rust +fn trigger() { + fn collide(a: &mut u8, b: &mut u8) { + *a = 1; + *b = 2; + } + + let mut value = 0u8; + let mut view = View::new(&mut value); + let first = view.get_mut(); + let second = view.get_mut(); + collide(first, second); +} +``` + +Both results point to `value`. The receiver borrow of each method call can end +at that call because neither output type mentions it. Both returned references +are live when passed to and used by `collide`. Rust 1.70 says breaking pointer +aliasing rules is UB, treats a reference as live when passed to or returned +from a function, and says an unborrowed mutable reference is “the only way to +access the value it points to.” Two independently created mutable references +accessing the same `u8` contradict that requirement. Thus well-typed safe code +can trigger UB. An analogous witness retains `view.get()` and then calls +`view.get_mut()`, so both signatures require repair. + +The unsafe blocks also have no adjacent `SAFETY` proof or named invariant. That +is a proof-documentation defect in addition to the implementation defect; no +comment can prove the current false temporal premise. + +## Configuration closure and TCB + +The counterexample uses only `u8`, references, and the one unconditional source +path. It is parametric over target and ordinary profile for Rust 1.70.0; there +is no configuration branch, panic path, or generated artifact to partition. +One included toolchain version suffices to refute the requested Rust-1.70+ +universal claim, so no backwards-compatibility premise about later releases is +consumed. This review did not build, test, expand, or execute the target. + +The complete TCB is these Rust 1.70.0 authoritative axioms: + +- [Reference: UB and aliasing](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html): safe misuse reaching UB is unsound; breaking reference aliasing is UB; passed/returned references are live. +- [Reference: pointer types](https://doc.rust-lang.org/1.70.0/reference/types/pointer.html#raw-pointers-const-and-mut): raw pointers have no liveness guarantee, dereference is unsafe, and `&mut *` reborrows one as a reference; the same page states mutable-reference exclusivity. +- [`PhantomData` 1.70.0](https://doc.rust-lang.org/1.70.0/core/marker/struct.PhantomData.html#unused-lifetime-parameters): the marker makes a raw-pointer wrapper act as if it contained the indicated reference; it does not couple method outputs to receiver borrows. + +No dependency, tool-result, compiler-backend, platform, or prior-audit premise +is admitted. + +## Proposed repair (not implemented; no verdict) + +Change **both** signatures to receiver-bounded outputs: + +```rust +pub fn get(&self) -> &T +pub fn get_mut(&mut self) -> &mut T +``` + +Rust 1.70's [method lifetime-elision rule](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html#lifetime-elision-in-functions) +assigns the receiver-reference lifetime to each elided output lifetime. Then a +live shared result keeps `View` shared-borrowed, and a live mutable result keeps +it exclusively borrowed, preventing every conflicting accessor call in safe +code. Add I-VIEW beside the private fields and adjacent proofs for both raw +reborrows. This is only a conditional design argument: the implemented patch, +its auto traits, and its full Rust/configuration range require a fresh audit. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r039.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r039.md new file mode 100644 index 0000000000..a27ec45a24 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r039.md @@ -0,0 +1,50 @@ +# Focused review: `total` + +## Claim, scope, and verdicts + +Snapshot: the supplied `lib.rs` (one public function) and `REQUEST.md`; no manifest, dependencies, generated code, features, or other source were supplied. Scope is `pub fn total(values: &[u32]) -> u32`, for every well-typed safe call, Rust 1.70+, all targets, and ordinary profiles. Required behavior is the left-to-right sum modulo 2^32. This was source review only; no build, expansion, test, or benchmark was run. + +| Claim | Verdict | +|---|---| +| Current implementation, soundness over the requested support set | **UNPROVED** | +| Current implementation, wrapping result where its unsafe preconditions hold | **PROVED** | +| Proposed safe redesign | Conditional design proof only; it is not an implemented artifact and therefore gets no artifact verdict | +| Redesign regresses the designated benchmark by at most 2% | **UNPROVED** (no result or benchmark definition supplied) | + +`UNPROVED` is not `UNSOUND`: this review established no valid UB counterexample. + +## Boundary and obligation ledger + +The only public/safe surface is `total`; it may impose no hidden caller safety condition. There are three unsafe sites: `ptr.add(values.len())`, `*ptr`, and `ptr.add(1)`. No `SAFETY` comment documents any of them. + +For a nonempty slice, the material reconstructed proof uses loop invariant `I(i)`: after `i` iterations, `0 <= i <= len`, `ptr` is the base pointer advanced by `i` elements, `acc` is the modular sum of elements `0..i`, and the shared borrow keeps the slice alive and prevents mutation for the call. + +- End construction: slice contiguity and validity supply one allocation containing the initialized, aligned `u32` elements; advancing by `len` reaches one-past. The Rust 1.70 [`pointer::add` contract](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) requires start and result to be in-bounds or one-past the same allocated object, a byte offset fitting `isize`, and no address-space wrap. +- Dereference: the loop test and same-allocation progression imply `i < len`; thus `ptr` addresses an initialized, aligned element still readable through the live shared slice. +- Increment: from `i < len`, advancing one element stays in-bounds or reaches one-past and establishes `I(i + 1)`. +- Termination and behavior: thin pointers produced by these same-base advances meet at `i == len`. Each iteration uses [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add), whose contract is modular addition, so overflow checks and optimization profile cannot change the result. The empty case should return the initial `0` without dereferencing. + +The proof does not close literally for every valid empty slice on Rust 1.70. `as_ptr()` may represent an empty slice without identifying storage for a `u32`, while the 1.70 `add` text does not exempt a zero offset from its allocated-object requirement. The smallest missing authoritative implication is: **for every valid empty `&[u32]`, `values.as_ptr().add(0)` satisfies Rust 1.70's stated same-allocated-object precondition**. Later wording cannot establish that proposition backward. This is an authoritative-documentation/proof gap, not evidence of UB. + +The open-ended `1.70+` claim has a second coverage gap: exact 1.70 contracts cannot silently be projected onto every later and future stable version. No inspected Reference/std text supplies that compatibility theorem. A future-proof verdict therefore needs either per-version verification with an audit cutoff or explicit acceptance of a narrowly stated compatibility TCB premise. + +Independently, the current artifact's complete absence of adjacent safety proofs is proof-documentation debt. If it is retained, comments must state the `add`/dereference obligations, `I(i)`, empty case, byte-bound/no-wrap derivation, and resulting invariant at each site; merely saying “within the slice” would be insufficient. + +## Safe iterator candidate + +```rust +pub fn total(values: &[u32]) -> u32 { + values + .iter() + .copied() + .fold(0u32, |acc, value| acc.wrapping_add(value)) +} +``` + +The Rust 1.70 slice [`iter`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.iter) contract supplies every element from start to end; [`copied`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.copied) copies each `u32`; and [`fold`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.fold) starts at zero and applies the explicit modular addition once per element. Thus empty input yields zero and every other input yields the same modulo-2^32 fold in every ordinary profile. It removes all user-authored unsafe operations and the raw-pointer invariant. Exact later-version applicability still needs the version-coverage policy above. The exact implemented replacement must be audited anew. + +## Evidence and recommendation + +TCB consumed: only the cited Rust 1.70 standard-library contracts and ordinary validity of a well-typed shared slice. No dependency, tool-derived, platform, probabilistic, or benchmark premise was admitted. The missing compatibility proposition and empty-slice implication are not silently added to the TCB. + +**Next action:** benchmark the exact candidate against this exact baseline before adopting it. First freeze the designated benchmark workload, toolchain, target/CPU, profile, flags, environment, metric, sampling method, and the rule for deciding a 2% regression; then run interleaved repeated measurements. If the agreed analysis establishes slowdown `<= 2%`, adopt the safe version and audit that snapshot. If it does not, retain or optimize further—but do not accept the current unsafe code until its versioned proof gap and adjacent documentation are resolved. Source, support-range, benchmark, compiler, target, profile, or candidate changes trigger re-review. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r040.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r040.md new file mode 100644 index 0000000000..a30e637933 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r040.md @@ -0,0 +1,36 @@ +# Focused source review: `classify` + +## Claim and verdicts + +**Snapshot and claim.** This review covers exactly `/tmp/unsafe-rust-v2-eval.9epWDK/targets/m2q8/lib.rs` as supplied, under Rust and `core` 1.80.0, for every well-typed safe call to `classify`, on all Rust 1.80.0 targets and in all ordinary profiles. The source has no dependencies other than `core`, conditional compilation, generated code, features, mutable state, callbacks, or caller safety obligation. The source-level claim is freedom from Rust undefined behavior plus both documented behaviors. + +* **Soundness: UNSOUND.** The valid safe call `classify(0)` reaches `core::hint::unreachable_unchecked`, whose Rust 1.80.0 contract says that reaching it is undefined behavior. +* **Behavioral contract: CONTRACT-BROKEN.** The defined call `classify(1)` returns `2` normally, contradicting “On normal return, returns `input`.” This counterexample involves no unsafe operation or UB. +* Consequently, the combined requested claim also fails. The documented panic for `input == 0` is not established: that case reaches UB, so the Rust abstract semantics supplies no defined panic guarantee. + +## Boundary and complete obligation ledger + +The only language-reachable API is the safe free function `pub fn classify(input: u8) -> u8` (`lib.rs:6-12`). It exposes no unsafe caller contract. The sole unsafe operation is at line 8. There are no representation invariants or other producers, transitions, or consumers. + +| ID | Required proposition | Complete derivation and status | +|---|---|---| +| S1 | Every safe call is UB-free. | For the well-typed input `0`, the literal `0` arm is selected and executes the unsafe call. AX-1 makes that execution UB. **UNSOUND.** | +| U1 | The call site of `unreachable_unchecked` is unreachable. | The local control-flow fact above directly contradicts the required proposition; the public `u8` parameter is unrestricted. **UNSOUND.** | +| B1 | If `input == 0`, the function panics. | The selected arm reaches UB rather than establishing any defined outcome. The panic guarantee is therefore not proved; this case is already **UNSOUND**. | +| B2 | On every normal return, the result equals `input`. | For `input == 1`, the `1` arm evaluates to `2`; as the function body's tail expression, that value is returned. Thus `classify(1) == 2 != 1` on a defined normal-return path. **CONTRACT-BROKEN.** For inputs unequal to `0` and `1`, `_` selects the expression `input`, so this clause holds only on that subdomain. | + +The unsafe block has no adjacent `SAFETY` proof. The smallest needed proof would have to establish “the `0` arm cannot be reached,” but safe caller input `0` disproves it. Thus no truthful replacement safety comment can validate the current implementation. + +## Authoritative premises / TCB `R040-1` + +No additional assumptions, dependencies, implementations, external specifications, or tool results are admitted. + +* **AX-1 (Rust 1.80.0 `core`).** [`core::hint::unreachable_unchecked`](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety): “Reaching this function is *Undefined Behavior*.” This applies directly to line 8 on every target/profile in scope. +* **AX-2 (Rust 1.80.0 Reference).** [Literal patterns](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns) “match exactly the same value as what is created by the literal”; the [wildcard pattern](https://doc.rust-lang.org/1.80.0/reference/patterns.html#wildcard-pattern) “matches any value.” Together with the inspected arm order and [the definition of a `match` as branching on patterns](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html#match-expressions), these establish the `0`, `1`, and remaining-value partition used above. +* **AX-3 (Rust 1.80.0 Reference).** [Function bodies](https://doc.rust-lang.org/1.80.0/reference/items/functions.html#function-body) states that the tail expression, if evaluated, is returned to the caller. This establishes the normal returns of `2` and `input` in B2. + +## Configuration closure, findings, and residual scope + +The three source cases—`input == 0`, `input == 1`, and all other `u8` values—are exhaustive. There are no configuration-selecting attributes or generated artifacts. The cited contracts and this value/control-flow argument are target- and ordinary-profile-independent. Optimization cannot repair source-level UB, and the `input == 1` counterexample executes no profile-sensitive operation. The verdicts therefore hold in every requested configuration, not merely a tested sample. + +Minimum correction requires removing the reachable `unreachable_unchecked` behavior for `0` while providing the promised panic, and returning `input` rather than `2` for `1`; any corrected source would be a new artifact requiring review. No build, test, macro expansion, or target execution was used. Binary/backend correctness and undocumented robustness properties are outside this focused source claim. Re-review is required if the source, public documentation, Rust/`core` version, or supported configuration set changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r041.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r041.md new file mode 100644 index 0000000000..12f1a8139a --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r041.md @@ -0,0 +1,38 @@ +# Focused source review: `m2q8` + +## Claim and results + +Snapshot: `REQUEST.md` SHA-256 `9d559df45c2ef498f44516fed36ef84c82fa4a02a59edca55d2ff88a7b93eb02`; `lib.rs` SHA-256 `42388bb1a03741a7725c9836cc134c205af3061efef7b8ee00ce38d2dc51aeeb`. Scope is exactly the public API in `lib.rs`, under Rust/core 1.80.0, for every target and ordinary profile in the request. The claim quantifies over every well-typed safe call and has no caller safety precondition. It is a source-level claim relative to documented Rust abstract semantics, not a compiler-backend or binary claim. + +- **Soundness: UNSOUND.** The valid safe call `classify(0)` reaches `core::hint::unreachable_unchecked()`, which is undefined behavior. +- **Documented behavior: CONTRACT-BROKEN.** The valid call `classify(1)` returns normally with `2`, contradicting the promise that a normal return equals `input`. + +The other documented clause, “Panics when `input == 0`,” is not established: that input reaches undefined behavior, so the implementation provides no defined panic behavior. The independent `input == 1` counterexample is why the behavioral verdict is specifically `CONTRACT-BROKEN`, rather than merely being subsumed by the soundness failure. + +## Boundary and obligation inventory + +The sole externally reachable surface is safe `pub fn classify(input: u8) -> u8`. Its sole unsafe operation is the call to `unreachable_unchecked` in the `0` arm. There are no public fields, constructors, traits or impls, callbacks, macros, generated items, dependencies, state invariants, concurrency, FFI, or conditional compilation. The two mandatory documented behaviors are (B1) panic for zero and (B2) on normal return, return the input. + +## Findings and derivations + +### F1 — UNSOUND: reachable `unreachable_unchecked` (`lib.rs:8`) + +The exact Rust 1.80 contract states: “Reaching this function is *Undefined Behavior*.” ([`core::hint::unreachable_unchecked`, Safety](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety)). Thus the local obligation is that control can never reach this call. + +Rust 1.80 specifies that literal patterns “match exactly the same value as what is created by the literal” ([Reference: literal patterns](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns)), and that the first matching arm becomes the branch target ([Reference: match expressions](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html#match-expressions)). For `input == 0`, the first arm therefore matches and immediately calls `unreachable_unchecked`. The required unreachability proposition is false. Because `0` is a valid `u8` supplied through a safe function, this is a valid safe-use UB counterexample, with no caller obligation available to exclude it. + +There is also no adjacent `SAFETY` proof. This is not merely missing documentation: no truthful proof can discharge the false obligation without changing the implementation or safe boundary. + +### F2 — CONTRACT-BROKEN: wrong defined return (`lib.rs:9`) + +For `input == 1`, the literal-pattern and first-matching-arm rules select `1 => 2`; the unsafe `0` arm is not executed. Rust 1.80 says a function block's evaluated tail expression is returned to its caller ([Reference: function body](https://doc.rust-lang.org/1.80.0/reference/items/functions.html#function-body)). Consequently `classify(1)` returns normally with `2`. Since `2 != 1`, this is a defined counterexample to B2. + +Behavioral clause dispositions are exhaustive: zero reaches UB and does not supply B1; one refutes B2; every `2..=255` value selects `_ => input` and satisfies B2. B2 has no normal-return case to assess for zero under defined semantics. + +## Configuration closure, TCB, and resolution + +The source has no configuration selection. Both counterexamples depend only on `u8` literal matching, match control flow, function-tail return, and the Rust 1.80 core contract; these are target-, optimization-, debug-assertion-, overflow-check-, and panic-strategy-independent. The same proof covers all requested targets and ordinary profiles abstractly. No target was built or executed and no tool-derived evidence is used. + +Inline TCB `r041`: only the four exact Rust/core 1.80 axioms linked above. There are no admitted dependency, implementation, environment, compatibility, or external-specification assumptions. + +Minimal correction requires both replacing the zero arm with a defined panic (for example `panic!`) and making the `1` arm return `input` (or removing that special arm). That changed artifact requires review again. Any source/documentation change, Rust-version change, or introduction of configuration-dependent code is a re-review trigger. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r042.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r042.md new file mode 100644 index 0000000000..3d1ec8e57d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r042.md @@ -0,0 +1,47 @@ +# Focused `Buffer` soundness review + +## Claim and verdict + +**UNSOUND.** For the exact supplied `lib.rs`, Rust and standard library 1.80.0, every target accepted by that toolchain, and ordinary profiles, it is false that every well-typed safe use of `Buffer` is free of Rust undefined behavior. The following uses no `unsafe` at the call site and reaches UB: + +```rust +let mut buffer = Buffer::from_static(); +buffer.overwrite(9); +``` + +This is a source-level verdict relative only to TCB-RUST-1.80 below. There are no documented postconditions whose failure warrants a separate `CONTRACT-BROKEN` verdict. + +## Boundary and obligation inventory + +`Buffer`'s fields are private. The complete current producer set is `from_writable` (lines 16–18, unsafe, creates `shared: None`) and `from_static` (lines 20–26, safe, creates `shared: Some(&BYTE)`). The complete operation-consuming set is the two `ptr.write` calls in `overwrite` (lines 33 and 38). `with_live` (lines 43–46) is the only intervening helper. There are no other source files, trait impls, derives, macros, generated artifacts, callbacks, or custom destruction. Moving or dropping `Buffer` does not dereference `ptr`. In Rust 1.80.0, `*mut T` has negative [`Send`](https://doc.rust-lang.org/1.80.0/std/marker/trait.Send.html#impl-Send-for-*mut+T) and [`Sync`](https://doc.rust-lang.org/1.80.0/std/marker/trait.Sync.html#impl-Sync-for-*mut+T) impls, so the field also prevents those auto-trait surfaces. + +The required invariant at either write is: `ptr` is properly aligned and valid for writing one `u8`, and the write violates no aliasing/immutability rule. Rust 1.80.0 [`ptr::write`](https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html#safety) says “`dst` must be valid for writes” and “must be properly aligned.” + +## Finding: safe immutable-byte mutation + +1. `from_static` takes `shared = &BYTE`, casts that reference to `*const u8`, then casts to `*mut u8`. The latter same-sized pointer cast returns the pointer [unchanged](https://doc.rust-lang.org/1.80.0/reference/expressions/operator-expr.html#pointer-to-pointer-cast), so `ptr` addresses the byte to which `shared` refers. Changing raw-pointer mutability grants no write permission. +2. In `overwrite`, the `Some` branch passes that `&u8` to `with_live`. Rust 1.80.0 states that a reference passed to a function is live “at least as long as that function call” ([Reference](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html)). Thus it is live while `operation()` invokes `ptr.write`; the later `let _ = shared` is not needed for this conclusion. +3. The same Reference classifies bytes pointed to by a shared reference as immutable, and defines mutation as any overlapping write of more than zero bytes. [`u8` has size 1](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#primitive-data-layout), so `write::` is exactly such a mutation. This remains UB even when `value == 7`. + +The local safety comment at lines 31–32 is false on this branch: a `Some` state is produced by `from_static`, never by `from_writable`, and no `from_writable` obligation applies. This is a proved valid safe-code counterexample, not merely a missing proof. + +## Remaining producer/consumer partition + +For a `Buffer` from a **valid** `from_writable` call, the `None` branch is **PROVED** relative to that unsafe API's ongoing caller obligations. Privacy and the two constructor bodies make `None` imply this producer. Its contract supplies non-nullness, alignment, write-validity for one `u8`, and absence of conflicting access for every period in which a write may occur; these discharge `ptr::write`'s requirements. Construction itself merely stores the pointer, and repeated writes preserve the same external obligations. + +The lines 36–37 comment is nevertheless incomplete proof documentation: it mentions write-validity but omits the separately required alignment and conflict obligations. A sufficient replacement for that branch would be: + +```rust +// SAFETY: `shared == None` can only come from `from_writable`. Its caller's +// ongoing contract keeps `ptr` aligned and valid for writing one `u8`, with +// no access conflicting with this write, whenever this Buffer may be used. +unsafe { self.ptr.write(value) } +``` + +No comment can repair the `Some` branch. Its write must be removed or its producer/storage semantics changed so the write is actually permitted, followed by re-audit. + +## Configuration closure, TCB, and residual scope + +The argument is parametric over all requested targets and ordinary profiles: there is no `cfg`, feature, optimization-sensitive check, arithmetic, FFI, allocation, panic-dependent invariant, or generated code, and the one-byte `u8` layout is target-independent. No build, test, execution, or expansion evidence was used. + +**TCB-RUST-1.80:** the exact linked Rust 1.80.0 Reference and standard-library propositions are accepted as authoritative. No dependency, compiler-backend, platform, or extra semantic assumption is consumed. Re-review is required if this source, the safety contract, supported Rust version/configurations, or any cited proposition changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r043.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r043.md new file mode 100644 index 0000000000..60bb4ce8ea --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r043.md @@ -0,0 +1,40 @@ +# `Block` published-contract review + +## Claim and verdict + +Scope is exactly `lib.rs`: public unsafe trait `Block`, its `Page` implementation, and safe function `first`, for Rust 1.70+ on every target/profile where the source compiles. There are no dependencies, `cfg`s, generators, FFI, allocation, concurrency, or panic-dependent transitions. + +- **`Page` implementation: PROVED.** It establishes every published `Block` clause. +- **`first`: PROVED** for every `B` whose `unsafe impl Block` satisfies the published contract, taking “readable for 16 bytes” in its Rust safety-contract sense: the region is initialized and valid for shared reads for the stated borrow interval. +- **Proof documentation: UNPROVED as an artifact.** The bare `unsafe` block has no adjacent derivation, and the trait should identify implementers and define the borrow interval explicitly. This is documentation debt, not a demonstrated implementation defect. +- **1.x contract reduction: rejected as SemVer-breaking.** Local proof dependencies can be narrowed, but the published guarantees cannot. + +The source-level result is relative to the Rust 1.70 axioms cited below and `COMPAT-1`: later supported Rust releases preserve those exact layout and pointer propositions. Without accepting that explicit compatibility premise, the open-ended future portion of “1.70+” remains **UNPROVED** and must be rechecked release by release. + +## Contract and implementation proof + +The controlling `Block` contract has five distinct implementer obligations: (1) `ALIGN != 0`; (2) `ALIGN` is a power of two; and, throughout the receiver borrow, `base()` returns a pointer that is (3) non-null, (4) aligned to `ALIGN`, and (5) readable for 16 bytes. Downstream implementations and consumers are part of the quantified public boundary; an in-repository search cannot narrow it. + +For `Page`, `ALIGN == 16` discharges (1)-(2). Rust 1.70 specifies `u8` size as one byte and `[T; N]` size as `size_of::() * N`, with element `n` at offset `n * size_of::()`; therefore `[u8; 16]` is a contiguous initialized 16-byte field ([Reference: size and array layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). The `repr(C)` field-offset algorithm starts at zero, so the sole field starts at the `Page` address, while `repr(align(16))` raises the struct alignment to at least 16 ([Reference: C structs](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs), [alignment modifiers](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers)). `self.0.as_ptr()` returns the slice/array buffer pointer and remains usable while the borrowed object lives ([`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)). Thus it points at offset zero, is non-null and 16-aligned, and the entire 16-byte field remains readable during `&self`. No target or profile changes this derivation. + +For `first`, let `p = block.base()`. The unsafe-impl contract supplies a live, non-null, initialized readable range `[p, p + 16)` during the `&B` borrow; the immediate dereference reads only `[p, p + 1)`. `u8` has size 1; because alignment is at least 1 and size is a multiple of alignment, its alignment is 1. Hence `p` is properly aligned for `u8` independently of `B::ALIGN`. There is no intervening call, mutation, or end of the outer borrow. Rust's pointer rules state that null is never valid, a valid access range must lie within one allocated object, reference-derived pointers remain valid while the object is live subject to access rules, and ordinary typed accesses require pointee alignment ([Rust 1.70 `std::ptr` safety and alignment](https://doc.rust-lang.org/1.70.0/std/ptr/index.html#safety)). These facts discharge the raw dereference. + +The proof should be recorded adjacent to the operation, without changing the contract, for example: + +```rust +// SAFETY: `Block::base` guarantees for this borrow that `p` is non-null +// and its first 16 bytes are initialized and valid for shared reads. This +// dereference occurs before the borrow ends and reads only the first byte; +// `u8` has size and alignment 1. `ALIGN` and bytes 1..16 are not consumed. +unsafe { *block.base() } +``` + +## What 1.x may and may not simplify + +In 1.x, the implementation may narrow the *local proof* to the exact projection above, factor that projection into a private helper, or add a separately named minimal API while retaining `Block` unchanged. A migration path may add a safe trait such as `FirstByte { fn first_byte(&self) -> &u8; }`, plus an adapter for existing valid `Block` implementers and a new separately named consumer. Any additive blanket impl still needs an ordinary coherence/method-resolution compatibility review. No edit is currently authorized. + +The following require an explicitly authorized breaking release: removing or changing `ALIGN`; weakening 16 readable bytes to one; dropping the non-null or `ALIGN`-alignment promise; changing `base`'s signature or validity interval; sealing the trait or making it safe; adding a required trait item; changing `first`'s public generic contract; or changing `Page`'s published representation. Each either invalidates a downstream unsafe proof/consumer or rejects an existing downstream implementation. “Only `first` was found” is not evidence against such uses. + +For 2.0, split capabilities instead of overloading one unsafe trait: make first-byte access a safe, lifetime-carrying reference API, and retain a separately named precisely documented unsafe/aligned-16 capability only for consumers that need it. Introduce the replacement alongside `Block` in 1.x if desired, deprecate rather than weaken `Block`, then switch/remove only in 2.0. + +Review triggers are any change to the trait prose, `Page` representation/body, `first` signature/body, supported Rust/targets, or the cited Rust contracts. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r044.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r044.md new file mode 100644 index 0000000000..d48d0356f5 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r044.md @@ -0,0 +1,83 @@ +# Focused unsafe-Rust source review + +## Claim and verdicts + +**Snapshot.** Exact `lib.rs` supplied in target `m2q8`, SHA-256 +`42388bb1a03741a7725c9836cc134c205af3061efef7b8ee00ce38d2dc51aeeb`. +Scope is the safe public function `classify(u8) -> u8`, Rust and `core` +1.80.0, every target, and every ordinary profile. This is a source-level +result under documented Rust abstract semantics, not a binary/backend claim. + +**Soundness: UNSOUND.** The well-typed safe call `classify(0u8)` reaches +undefined behavior. A safe caller has no safety obligation that could exclude +this input. + +**Documented behavior: CONTRACT-BROKEN.** On the defined, normally returning +call `classify(1u8)`, the implementation returns `2`, contradicting the +documented requirement to return `input`. For `input == 0`, the implementation +does not establish the promised Rust-defined panic: it reaches undefined +behavior instead, after which no behavior is guaranteed. The latter defect is +also the soundness finding above. + +The combined claim (soundness plus all mandatory documented behavior) is +therefore not proved. + +## Boundary and obligation coverage + +The complete external safe surface is the one public safe free function at +`lib.rs:6`. There are no public fields, traits or impls, macros, generated +items, callbacks, dependencies, or other constructors/methods. The sole unsafe +site is the call to `core::hint::unreachable_unchecked` at `lib.rs:8`. There is +no adjacent `SAFETY` proof. + +Rust 1.80.0 gives `u8` the range 0 through 2^8−1 (the [`u8` row of the integer +types table](https://doc.rust-lang.org/1.80.0/reference/types/numeric.html#integer-types) +is “`u8` | 0 | 2^8−1”), so both counterexample inputs are valid values of the +safe parameter. The Reference says, “Literal patterns match exactly the same +value as what is created by the literal” ([literal +patterns](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns)), +and “The first arm with a matching pattern is chosen as the branch target” +([match expressions](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html)). + +The obligations partition exhaustively over `u8`: + +| Input | Selected implementation | Soundness | Documented behavior | +|---|---|---|---| +| `0` | `unreachable_unchecked()` | **UNSOUND** | No guaranteed panic | +| `1` | returns `2` | No unsafe operation on this path | **CONTRACT-BROKEN**: `2 != 1` | +| `2..=255` | returns `input` | No unsafe operation on this path | Required normal result established | + +For the first row, the exact Rust 1.80.0 standard-library safety contract says, +“Reaching this function is Undefined Behavior” +([`core::hint::unreachable_unchecked`](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety)). +The `0` literal arm proves that the site *is* reached when `input == 0`; it +therefore directly falsifies the callee precondition. The smallest missing +implication in the implementation's absent safety proof would be +`input == 0 => this site is unreachable`; the inspected control flow proves +its negation. + +## Configuration closure and TCB + +The source contains no conditional compilation, target-dependent operations, +features, generated code, allocation, concurrency, FFI, assembly, or +profile-dependent checks. The exhaustive value partition above is parametric +over target and ordinary profile. Optimization may change how UB manifests, +but cannot make reaching the unsafe function satisfy its source-level safety +contract. Panic strategy is irrelevant because the `0` arm invokes no defined +panic operation. Thus both counterexamples apply throughout the requested +configuration set. + +TCB `R044-inline-v1` contains only the three exact Rust 1.80.0 axioms quoted +above: the `u8` domain, literal/match selection, and the +`unreachable_unchecked` safety contract. There are no admitted dependency, +implementation, platform, deployment, or tool-derived premises. No target was +built, tested, expanded, or executed. + +## Required resolution and residual scope + +To satisfy the current API, the `0` arm must use a defined panicking path (for +example `panic!`/`unreachable!`) and the `1` arm must return `1`; merely adding a +safety comment cannot repair either defect. No edit was made, as requested. +Re-review is required after any source/API-documentation change or expansion of +the Rust/configuration scope. Nothing outside the supplied `lib.rs` and its +stated safe API is covered. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r045.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r045.md new file mode 100644 index 0000000000..182188a06e --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r045.md @@ -0,0 +1,52 @@ +# Focused `Buffer` source review + +## Claim and verdict + +**UNSOUND** for the exact `lib.rs` snapshot (SHA-256 `368fa01b2f3fca39e89131414f8ec2641a4ac162b846ccf77b8f8bd293930d9a`), Rust/compiler/standard library 1.80.0, every target on which this source compiles, and all ordinary profiles. There is a well-typed, entirely safe use that reaches Rust undefined behavior: + +```rust +let mut buffer = Buffer::from_static(); +buffer.overwrite(7); // UB; the unchanged value does not help +``` + +This is a source-level result under the documented Rust 1.80 abstract semantics, not a claim about a particular backend or binary. No separately documented postcondition is in scope, so there is no independent `CONTRACT-BROKEN` verdict. + +## Boundary and invariant inventory + +The complete current surface is: + +- private representation fields `ptr` and `shared` (`lib.rs:5-8`); +- unsafe producer `from_writable` (`lib.rs:11-18`), which creates `(ptr, None)` under an ongoing caller contract; +- safe producer `from_static` (`lib.rs:20-26`), which creates `(pointer-derived-from-&BYTE, Some(&BYTE))`; +- safe consumer `overwrite` and its two raw writes (`lib.rs:28-40`); +- private callback helper `with_live` (`lib.rs:43-46`); and +- ordinary moves and implicit field drop. There are no other constructors, field mutations, custom trait implementations, macros, generated code, or destructors in the supplied artifact. + +Privacy plus exhaustive producers establishes two stable states: **W**: `shared == None`, created only by `from_writable`; and **S**: `shared == Some(r)`, created only by `from_static`, with `r == &BYTE` and `ptr` derived from `r`. `overwrite` preserves the fields. + +## Obligations and derivation + +The raw-pointer [`write` method](https://doc.rust-lang.org/1.80.0/std/primitive.pointer.html#method.write) directs callers to [`ptr::write`'s safety contract](https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html#safety): the destination must be valid for writes and properly aligned. Rust 1.80 also fixes [`size_of::() == 1`](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#primitive-data-layout) on every target. + +| Site | Result | Compact proof | +|---|---|---| +| `from_writable` producer | **PROVED**, for calls satisfying its documented ongoing unsafe contract | Merely storing a raw pointer performs no access. It establishes state W. The contract expressly supplies non-nullness, alignment, validity for one-`u8` writes throughout use, and absence of conflicting access. | +| `overwrite`, state W (`lib.rs:35-39`) | **PROVED** relative to that caller contract | Private fields, exhaustive producers, and no transitions make `shared == None` imply state W. The still-active `from_writable` obligations entail every `write` precondition; `value: u8` is initialized by its type. | +| `from_static` producer alone | **PROVED** only for construction/move/drop without `overwrite` | Taking `&BYTE`, casting it to a raw pointer, and storing both values performs no memory write. It establishes state S. | +| `overwrite`, state S (`lib.rs:29-34`) | **UNSOUND** | `shared` is passed into `with_live`, whose call invokes the closure before returning. Under the Rust 1.80 [undefined-behavior rules](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html#behavior-considered-undefined), a reference passed to a function is live for at least that call; bytes pointed to by a shared reference (absent `UnsafeCell`) are immutable; and any overlapping write of more than zero bytes is a mutation even if the contents do not change. Thus `shared: &u8` is live while `ptr.write` writes its one byte to the same `BYTE`. That execution mutates immutable bytes and is UB. | + +The counterexample uses only the two safe public methods. One reachable valid safe execution is enough to refute soundness for all well-typed safe uses. Read-only placement of the static is not assumed and is unnecessary. + +## Local safety-proof finding + +The comment at `lib.rs:31-32` is false for its branch: state S comes from `from_static`, not `from_writable`; indeed the retained shared reference proves the opposite of write permission. No replacement comment can validate that operation under the current safe API. A documentation-only change cannot repair the implementation defect. + +The identical comment at `lib.rs:36-37` cites an applicable contract but omits the material bridge that the `None` branch can only be produced by `from_writable`, as well as the alignment/conflict conjuncts. The implementation subcase is nevertheless proved by reconstruction. Adequate adjacent proof text would be: + +> SAFETY: `shared == None` is produced only by `from_writable` (the fields are private and never changed). Its ongoing caller contract therefore gives alignment and validity for this one-`u8` write and excludes conflicting access. + +## Configuration closure, TCB, and residual scope + +There are no configuration selectors or code-generating inputs. The failure proof is parametric over target and ordinary profile: the source path is unchanged, `u8` has target-independent size one, and the cited immutability/liveness rules are not target- or profile-qualified. No build, test, execution, or macro expansion was used. + +The complete consumed TCB is the three exact Rust 1.80 official-documentation axioms linked above: raw `write` safety, Reference liveness/immutable-byte UB, and `u8` layout. There are no dependencies or additional assumptions. Excluded are compiler/backend correctness, binary/platform behavior, unprovided surrounding crate code, and future Rust versions. Re-audit is required if the source, Rust version, supported configuration predicate, or any consumed contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r046.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r046.md new file mode 100644 index 0000000000..4d6475f9c6 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r046.md @@ -0,0 +1,36 @@ +# Focused vendoring review: `Piece for Tail` + +## Decision and exact verdicts + +**REJECT the snapshot.** The strongest result is: + +- **PROVED (execution soundness):** `Tail::project` is UB-free for calls satisfying its documented caller precondition, and `increment_tail` is UB-free for every well-typed safe call. +- **CONTRACT-BROKEN:** `Piece for Tail` violates both provider guarantees concerning the direct declared field. +- **CONTRACT-BROKEN (combined acceptance claim):** sound execution of the reviewed path does not cure a false mandatory unsafe-trait contract. No in-scope execution is proved to reach UB, so the defect is not labeled `UNSOUND`. + +The soundness verdict covers the exact source snapshot (`lib.rs` SHA-256 `d76a5c0d7336aac4e551264a105c621dbd6cf27db097fb648aa3ca1e891e3429`), released stable Rust 1.70.0 through 1.97.1 (the review cutoff), all targets, and ordinary profiles. The contract counterexample is source-level and applies on any later Rust version that accepts these same declarations. A later-version soundness certification requires re-review. + +## Scope, boundary, and trust + +Reviewed: the public unsafe trait contract, the `Tail` unsafe impl, and the safe `increment_tail` path in [lib.rs](/tmp/unsafe-rust-v2-eval.9epWDK/targets/y9t5/lib.rs:3). Relevant safe surfaces are the public `Pair` tuple constructor/field, the `Tail` unit constructor, safe reads of `Piece::FIELD`, and `increment_tail`; `Piece::project` and implementations of `Piece` are compiler-enforced unsafe boundaries. Arbitrary downstream impls are not consumed because `increment_tail` names `Tail` directly. + +TCB: no dependencies, tools, generated artifacts, deployment facts, compatibility promises, or implementation assumptions. The only ground truths are the applicable version's Rust abstract semantics and official Reference/standard-library contracts. No target was built, expanded, or run. + +There is no `cfg`, FFI, allocation, concurrency, target feature, representation offset, or profile-sensitive arithmetic. Field/index projections use the compiler's actual layout, and `wrapping_add` removes overflow-profile variation. Thus the argument is target/profile-parametric. + +## Compact obligation ledger and derivation + +1. **Call precondition — PROVED.** At line 32, the safe parameter is a live `&mut Pair`. A function argument is a coercion site, and `&mut T` may coerce to `*mut T` ([Rust 1.70 Reference](https://doc.rust-lang.org/1.70.0/reference/type-coercions.html#coercion-sites), [Rust 1.97.1 Reference](https://doc.rust-lang.org/1.97.1/reference/type-coercions.html#r-coerce.types.pointer-mut)). No callback or competing access intervenes, so the `Pair` remains live and uniquely borrowed for the call. + +2. **Projection in `project` — PROVED.** The input therefore identifies an initialized, aligned live `Pair`. Its sole direct field `.0` is an initialized `[u32; 2]`; constant index `1` is in bounds because array indices are zero-based and checked ([1.70](https://doc.rust-lang.org/1.70.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions), [1.97.1](https://doc.rust-lang.org/1.97.1/reference/expressions/array-expr.html#r-expr.array.index.zero-index)). `addr_of_mut!` creates a mutable raw pointer without an intermediate reference; its place/projection requirements are met ([1.70](https://doc.rust-lang.org/1.70.0/core/ptr/macro.addr_of_mut.html), [1.97.1](https://doc.rust-lang.org/1.97.1/core/ptr/macro.addr_of_mut.html#safety)). The result addresses the valid initialized second `u32` and inherits the unique access. + +3. **Raw-pointer reborrow and update — PROVED.** Raw pointers may be reborrowed as `&mut *ptr` ([1.70](https://doc.rust-lang.org/1.70.0/reference/types/pointer.html#raw-pointers-const-and-mut)); the facts above establish non-dangling, alignment, initialization, and exclusivity for the resulting reference. `wrapping_add(1)` is modular addition ([1.70](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add), [1.97.1](https://doc.rust-lang.org/1.97.1/std/primitive.u32.html#method.wrapping_add)). Therefore normal return changes exactly `pair.0[1]` to its old value plus one modulo `2^32`; `pair.0[0]` is unchanged. `increment_tail` documents no broader behavioral postcondition; its name alone is not normative. + +## Contract counterexample + +The contract requires `FIELD` to name a **direct declared** `Owner` field of type `Item`, and `project` to return a pointer to **that** field. But `Pair(pub [u32; 2])` declares one direct tuple-struct field, of type `[u32; 2]`; tuple-struct fields are anonymous ([1.70](https://doc.rust-lang.org/1.70.0/reference/types/struct.html#struct-types), [1.97.1](https://doc.rust-lang.org/1.97.1/reference/types/struct.html#r-type.struct.tuple)). Consequently: + +- `FIELD = "tail"` names no direct field, and no direct field has type `u32`. +- `(*owner).0[1]` is a nested array element, not a direct declared `Pair` field. + +Both unconditional provider guarantees are false for every input, target, and profile. The unsafe blocks at lines 27 and 32 also lack adjacent `SAFETY` proofs; the reconstruction above proves their execution obligations but does not repair that proof-documentation defect. Because policy forbids source or contract changes, the false contract alone requires rejection. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r047.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r047.md new file mode 100644 index 0000000000..4ba94368a3 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r047.md @@ -0,0 +1,49 @@ +# Focused source review: `total` + +## Claim and verdicts + +Scope is exactly `lib.rs` and its sole language-reachable API, safe `pub fn total(&[u32]) -> u32`. The requested domain is Rust 1.70+, every target, and every ordinary profile; callers have no safety precondition. Required behavior is the modular `u32` sum, and the proposed replacement must satisfy the designated-benchmark regression limit of 2%. + +- **Current soundness: UNSOUND.** A valid empty slice reaches undefined behavior under the exact Rust 1.70 contract. Because 1.70 is supported, this refutes the combined configuration claim. +- **Current full-domain wrapping behavior: UNPROVED.** Non-empty executions compute the modular sum, but the empty execution below has UB and therefore no guaranteed result. +- **Safe-iterator redesign: PROVED for target-local source soundness and wrapping behavior under Rust 1.70's documented safe standard-library contracts.** It has no target-local unsafe operation. Applying this conclusion to an open-ended future toolchain range requires the ordinary compatibility premise/review trigger stated below. +- **Redesign performance: UNPROVED.** No benchmark result, benchmark definition, environment, or uncertainty analysis was supplied. Source similarity is not evidence of a <=2% result. + +No build, expansion, test, or benchmark result was used. There are no dependencies, generated artifacts, conditional compilation branches, or target/profile-dependent source branches in the reviewed files. + +## Critical finding: empty input violates Rust 1.70 `add` + +At `lib.rs:6`, `ptr.add(values.len())` executes before the loop, including when `len == 0`. Rust 1.70's [`pointer::add` contract](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) requires both pointers to be in-bounds or one-past the same allocated object. Yet Rust 1.70's [`slice::from_raw_parts` contract](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html) expressly permits `NonNull::dangling()` as the data pointer for a zero-length slice, and [`NonNull::dangling`](https://doc.rust-lang.org/1.70.0/std/ptr/struct.NonNull.html#method.dangling) returns a well-aligned dangling pointer. Thus this is a valid construction followed by a safe call: + +```rust +let p = std::ptr::NonNull::::dangling().as_ptr(); +let empty = unsafe { std::slice::from_raw_parts(p, 0) }; +total(empty); // `p.add(0)` violates the Rust 1.70 allocation requirement +``` + +The caller's unsafe block satisfies its complete documented contract; `total`, being safe, cannot impose the hidden condition that an empty slice's data pointer belong to an allocation. This is also practically reachable from allocation-free empty containers. The Reference identifies dereferencing a dangling/unaligned raw pointer as UB, but here UB already occurs at `add`; see the [Rust 1.70 UB list](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined). + +Rust 1.97's [`add` documentation](https://doc.rust-lang.org/1.97.0/std/primitive.pointer.html#method.add) conditions the allocation/range requirement on a *non-zero* computed offset. That later wording makes this empty case admissible there, but it has no authoritative historical-scope statement and therefore cannot retroactively discharge the Rust 1.70 obligation. The smallest alternative conclusion would still be a Rust-documentation/version applicability gap; it cannot support a full-range `PROVED` verdict. + +For non-empty slices, the missing implementation proof is reconstructable. With loop counter `k`, the invariant is `ptr = base.add(k)` and `0 <= k <= len`. The slice is contiguous, initialized, live for the call, and its byte size is at most `isize::MAX`; therefore the end construction and each increment remain within the slice allocation, and dereference occurs only when `k < len`. Each iteration uses `wrapping_add`, so overflow checks and optimization profile do not change arithmetic behavior. The existing source has no adjacent `SAFETY` explanation for any of its three unsafe sites, so even after repairing the defect, retaining this implementation would require that derivation to be documented locally. + +## Safe redesign and configuration closure + +The direct replacement is: + +```rust +pub fn total(values: &[u32]) -> u32 { + values + .iter() + .copied() + .fold(0u32, |acc, value| acc.wrapping_add(value)) +} +``` + +Iteration covers each slice element once; the initial value is zero, including for an empty slice, and the fold retains exactly the current explicit [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add) operation. Hence the behavior proof is parametric over target and ordinary profile, with no overflow-check branch. All used APIs predate Rust 1.70. Re-audit is triggered by a changed MSRV/support ceiling or a material change to these consumed standard-library contracts; compiler/backend binary correctness is outside this source-level claim. + +## Required next action + +Do **not** approve the status quo, and do not claim the redesign meets the performance gate. Stage the safe redesign as the benchmark candidate, then run the designated benchmark against this exact baseline under a predeclared benchmark revision, inputs, toolchain, target/CPU, profile/flags, repetition/warm-up policy, and noise/confidence rule. Accept it only if the evidence's chosen upper bound on regression is at most 2%. If it passes, adopt the safe redesign. If it fails or is inconclusive, continue performance work or benchmark a repaired pointer version (at minimum, return before `add` on empty input and add complete local proofs); do not restore the Rust 1.70 defect. Release on the stated support range should remain blocked until one sound option also satisfies the measured gate. + +TCB is limited to the linked exact Rust 1.70 Reference/standard-library propositions (plus the explicitly identified future-compatibility premise for later releases). No performance proposition is admitted into the TCB. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r048.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r048.md new file mode 100644 index 0000000000..1d2cd4d644 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r048.md @@ -0,0 +1,56 @@ +# Version-partitioned unsafe Rust source review + +## Claim and verdict + +Snapshot: `targets/h5p9/lib.rs` SHA-256 `e561568dfc6262bc6312f1a346e970dfab1f0d8f5102984e80e3b06cf2885e9b` (request SHA-256 `786c6e6d7d270b20c53e82170066a52a3dee7485526f80bec0e025a8602e411b`). Scope is the sole public safe API, `advance_marker`, for every well-typed safe call, under the documented source-level Rust semantics. Supported configurations are exactly Rust/core 1.79.0 or 1.80.0, every target on which this source is accepted, and every ordinary profile. There are no caller safety obligations. + +- **Rust 1.79.0: UNSOUND**, on every target/profile. +- **Rust 1.80.0: PROVED**, on every target/profile, relative to TCB `r048-v1` below. +- **Combined supported set: UNSOUND**, because it contains the 1.79.0 region. +- **Documented postconditions:** none are stated; the empty mandatory postcondition set is **PROVED**. No additional robustness claim was requested. + +This is a source-level result, not a claim that any compiler/backend produces a correct binary. + +## Boundary, inventory, and configuration closure + +The complete reachable surface is the safe, argument-free free function at `lib.rs:3-5`. Its only obligation site is the `unsafe` call to `*const [u8; 0]::add(1)` at line 4. There are no fields, constructors, traits/impls, macros, generated artifacts, dependencies beyond `core`, callbacks, state, invariants, FFI, concurrency, allocation, conditional compilation, or alternate exits. + +Target layouts are covered parametrically: each version’s Reference says an array `[T; N]` has size `size_of::() * N` ([1.79.0](https://doc.rust-lang.org/1.79.0/reference/type-layout.html#array-layout), [1.80.0](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#array-layout)). Therefore `size_of::<[u8; 0]>() = 0` on every target, and `add(1)` computes byte offset `1 * 0 = 0`. No source selection or relevant semantic fact varies by profile, optimization, overflow checks, panic strategy, or target. Thus the two Rust-version regions exhaust the requested configuration set without sampling or execution. + +## Obligation ledger and derivation + +| ID | Required proposition | 1.79.0 | 1.80.0 | +|---|---|---|---| +| O1 | `add(1)` has byte offset zero | PROVED by array layout above | PROVED by array layout above | +| O2 | Every `add` safety clause holds | **FAILED** | **PROVED** | +| O3 | Every safe call is UB-free | **UNSOUND** via F1 | **PROVED** from O1/O2 and absence of other operations | + +For 1.79.0, `core::ptr::null` “Creates a null raw pointer” whose address is 0 ([`null`](https://doc.rust-lang.org/1.79.0/core/ptr/fn.null.html)); the same version states that a null pointer is “never valid, not even for accesses of size zero” ([pointer safety](https://doc.rust-lang.org/1.79.0/core/ptr/index.html#safety)). Its [`add` contract](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add) unconditionally requires both starting and resulting pointers to be in bounds or one byte past the same allocated object. Address 0 is not in an allocated object and cannot be one byte past one without going below the address-space minimum; hence the null starting pointer violates this clause. The zero byte offset satisfies the `isize` and no-wrap clauses but does not waive the first clause. + +For 1.80.0, [`null`](https://doc.rust-lang.org/1.80.0/core/ptr/fn.null.html) again creates the null pointer. Crucially, the 1.80.0 [`add` contract](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add) conditions the allocation-bounds clause on the computed byte offset being nonzero and expressly says: “If it is zero, then the function is always well-defined.” O1 makes that clause inapplicable; zero fits `isize`, and `0 + 0` fits `usize` without wrapping. Returning a null raw pointer is permitted; it is not dereferenced. This discharges every operation on every execution. + +The 1.80 wording is not applied backward: it gives no historical scope for 1.79, and later documentation cannot repair the earlier regional proof. + +## Findings + +### F1 — safe call reaches UB on Rust 1.79.0 + +**UNSOUND / implementation defect.** The valid safe program `let _ = advance_marker();` unconditionally reaches the contract-violating `add`. It affects every 1.79 target/profile and needs no caller input or unusual execution. Minimum resolution is to remove the unsafe arithmetic (for the presently documented API, returning `core::ptr::null()` has no unsafe precondition) or enforce Rust >=1.80. Any behavioral intent beyond the signature must first be documented and audited. Re-audit both regional and combined claims after remediation/support changes. + +### F2 — local proof artifact is missing + +The unsafe block has no adjacent `SAFETY` proof. On 1.79 no truthful proof exists. If 1.79 is excluded or the site is otherwise restricted to >=1.80, sufficient wording is: + +```rust +// SAFETY: `[u8; 0]` has size 0, so `count * size_of::()` is +// `1 * 0 = 0`. Rust 1.80's `add` contract expressly makes a zero-byte +// offset always well-defined, including for this null starting pointer. +``` + +The 1.80 implementation obligation is PROVED despite this documentation deficiency; reconstruction does not change F1. + +## TCB `r048-v1`, evidence, and residual scope + +Accepted AXIOM entries are only the exact versioned array-layout, `null`, pointer-safety, and `add` standard-library/Reference passages linked above, each consumed solely in its named version region. No compatibility, implementation, platform, dependency, tool, test, deployment, or probabilistic premise is admitted. No tools or executions supplied proof evidence; independent review was not performed. + +Residual scope excludes undocumented behavior of the returned pointer and binary/compiler correctness. Re-audit on any source/API documentation change, Rust support change, relevant authoritative-document change, or expansion of configurations. Reviewed 2026-07-31 using unsafe-rust package `b4x9`. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r049.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r049.md new file mode 100644 index 0000000000..4eddc61428 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r049.md @@ -0,0 +1,37 @@ +# Focused unsafe-Rust review: `total` + +## Scope and claim + +Artifact: the supplied `lib.rs` and `REQUEST.md`, reviewed without execution on 2026-07-31. The only safe API surface is `pub fn total(&[u32]) -> u32`; its required behavior is the wrapping sum in every ordinary profile. The stated support set is stable Rust 1.70 and later, all targets, all ordinary profiles. This is a source-level result under documented Rust semantics, not a compiler-binary theorem. No dependencies, generated code, features, or `cfg` branches appear in the supplied artifact. + +## Current implementation + +**Soundness verdict: UNSOUND over the stated support set.** Rust 1.70 alone supplies an in-scope counterexample, so no assumption about later releases is needed to refute the universal claim. + +A valid empty slice may be formed from `NonNull::::dangling().as_ptr()` and length zero: Rust 1.70's [`slice::from_raw_parts` contract](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html#safety) expressly identifies `NonNull::dangling()` as usable for zero-length slices, while [`NonNull::dangling`](https://doc.rust-lang.org/1.70.0/std/ptr/struct.NonNull.html#method.dangling) produces a dangling, aligned pointer. Calling `total` with that valid `&[u32]` is a valid use of this safe API. + +`values.as_ptr()` returns the buffer pointer ([Rust 1.70 `slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)). The first unsafe operation therefore evaluates `ptr.add(0)` on the dangling pointer. Rust 1.70's controlling [`pointer::add` safety contract](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) requires both the starting and resulting pointers to be in-bounds or one-past the same allocated object; unlike later wording, this version states no zero-offset exception. The documented dangling pointer satisfies neither alternative, so this operation has undefined behavior before the loop. + +**Wrapping-result postcondition: UNPROVED for the full domain; PROVED for nonempty valid slices.** The empty execution above contains UB and therefore cannot establish either a wrapping result or `CONTRACT-BROKEN`. For `n > 0`, reconstructing the absent proof gives the loop invariant `ptr = base.add(i)`, `0 <= i <= n`, and `acc` equal to the wrapping sum of elements before `i`. A valid slice has initialized elements ([Rust 1.70 slice types](https://doc.rust-lang.org/1.70.0/reference/types/slice.html#slice-types)) and dynamic size at most `isize::MAX` ([Rust 1.70 Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#dangling-pointers)). Thus the end offset and each increment stay in the slice's allocation or reach one-past without arithmetic/address wrapping; the dereference occurs only for `i < n`. Each update uses [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add), whose modular behavior is profile-independent. At `i = n`, the returned accumulator is the required wrapping sum. + +The three unsafe operations have no adjacent `SAFETY` proofs. Even apart from the defect, the nonempty derivation above is material and should not remain implicit. + +## Safe iterator candidate + +```rust +pub fn total(values: &[u32]) -> u32 { + let mut acc = 0u32; + for &value in values.iter() { + acc = acc.wrapping_add(value); + } + acc +} +``` + +This is available on Rust 1.70, contains no local unsafe boundary, and imposes no hidden caller obligation. Rust 1.70 documents that [`slice::iter`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.iter) yields all items from start to end. Induction over those items, combined with `wrapping_add`, proves the required result for empty and nonempty slices, parametrically over target and ordinary profile. This is a candidate proof plan, not a verdict for an unimplemented snapshot; the implemented change would require a fresh review. Open-ended future toolchain support also needs the project's normal compatibility premise or re-audit trigger. + +## Performance evidence and recommendation + +**No-more-than-2% regression: UNPROVED.** No benchmark artifact, configuration, baseline result, candidate result, measurement uncertainty, or applicable tool theorem was supplied. Source similarity and hoped-for optimizer behavior cannot entail that quantitative proposition, and it should not be admitted as a TCB assumption. + +Treat the current Rust-1.70 soundness failure as a release blocker. Next, benchmark the exact safe candidate against a pinned current baseline using the designated benchmark and its agreed performance configuration/protocol. Accept the redesign only when the evidence establishes a regression of at most 2%, then audit the implemented snapshot. If it fails, do not retain the present code unchanged: benchmark a minimally repaired pointer variant that handles `values.is_empty()` before any `add` and adds complete local `SAFETY` proofs, or continue optimizing safe candidates. This sequence preserves both requirements: proof closure is mandatory, while the performance gate remains evidence-dependent rather than silently discarded. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r050.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r050.md new file mode 100644 index 0000000000..2c97be9c1b --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r050.md @@ -0,0 +1,38 @@ +# Focused review: published `Block` contract + +## Scope and verdicts + +Reviewed the exact `lib.rs` supplied, source-only, for Rust 1.70+, all targets and profiles. The in-scope surfaces are the public unsafe trait `Block` (including `ALIGN` and safe method `base`), `unsafe impl Block for Page`, and safe function `first`. There are no dependencies, `cfg`s, generated artifacts, allocation, panic, FFI, or target-specific operations. + +- **`Page` implementation — PROVED** for Rust 1.70 abstract semantics, all targets/profiles: it establishes every stated `Block` postcondition. +- **`first` soundness — UNPROVED under the exact published wording.** Its implementation is proved if “readable” and “during the borrow” have the precise meaning stated below, but that implication is not presently documented. No valid-use UB counterexample was established, so this is not an `UNSOUND` verdict. +- **Rust 1.70+ coverage — conditional.** The source argument is target/profile-parametric. Applying the Rust 1.70 documentation to an open-ended later-release range additionally needs `TCB-COMPAT`: the exact cited layout, unsafe-trait, slice-pointer, and UB propositions continue to hold on every supported later stable Rust. Without acceptance of that compatibility premise (or a finite release cutoff checked separately), the open-ended claim remains `UNPROVED`. + +## Current-artifact proof + +An unsafe trait defines extra implementer safety conditions, and an `unsafe impl` asserts they are discharged ([Rust 1.70 Reference](https://doc.rust-lang.org/1.70.0/reference/unsafe-keyword.html#unsafe-traits-unsafe-trait)). Thus `Page` must provide the *whole* published contract, independently of known consumers. + +For `Page`, `ALIGN = 16` is nonzero and a power of two. Rust 1.70 specifies `u8` size as one byte; size is a multiple of alignment, so `u8` alignment is one. `[u8; 16]` is therefore 16 contiguous bytes ([size/alignment and primitive layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#size-and-alignment), [array layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). `repr(align(16))` raises `Page`'s alignment to 16, while the `repr(C)` layout algorithm places the first field at offset zero ([alignment modifier](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers), [`repr(C)` structs](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs)). Therefore the array buffer starts at the non-null, 16-aligned address of the live `Page`. Arrays coerce to slices, and `as_ptr` “returns a raw pointer to the slice's buffer” ([array](https://doc.rust-lang.org/1.70.0/std/primitive.array.html), [`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)). While `&self` remains live, all 16 `u8`s are initialized and the shared borrow permits reads. This proves `Page::base`'s non-nullness, 16-alignment, and 16-byte readable region on every target/profile. + +`first` calls `base` and reads one `u8`. It does **not** consume `ALIGN`, alignment stronger than `align_of::() == 1`, or bytes 1–15. Rust makes a raw-pointer dereference UB when dangling or unaligned, and separately forbids data races and producing an integer from uninitialized memory ([Rust 1.70 UB rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)). The smallest missing contract implication is: + +> At the dereference after `base` returns, while the particular `&B` passed by `first` remains live, the returned pointer denotes one initialized `u8` in a live allocation and a non-atomic read through it violates neither aliasing nor data-race rules. + +“Readable” is undefined, and “during the borrow” identifies neither which borrow nor whether the guarantee survives method return. A raw-pointer result carries no lifetime tying that intended interval into the type. Consequently the current local proof cannot derive the exact dereference preconditions. Once the quoted implication is made controlling, the proof is immediate: the live `block` reference covers the call and dereference, and one byte is a subset of the promised 16-byte readable range. The unsafe block also needs an adjacent `SAFETY` comment recording exactly that derivation. + +## What 1.x can do + +Repository search finding only `first` does not bound public consumers or implementations. Therefore 1.x may not remove `ALIGN`, reduce 16 readable bytes to one, reduce the alignment guarantee, or replace/remove `base`: each weakens provider guarantees on which downstream unsafe code may rely. Nor may it add genuinely stronger lifetime/interference duties to implementers merely as a “clarification”; that can invalidate published downstream `unsafe impl`s. A wording clarification is compatible only if the project can establish that it is logically equivalent to the already-published meaning. + +Proof-oriented, nonbreaking work available in 1.x is: + +1. Add complete trait-level `# Safety` documentation and adjacent proofs for `Page` and `first`, without changing any proposition. If the missing implication is not already entailed, treat this as defect remediation with explicit compatibility handling, not simplification. +2. Keep the legacy trait/function operational, but add a separate safe minimum-capability trait such as `FirstByte { fn first_byte(&self) -> u8 }`, implement it for `Page` with `self.0[0]`, add a safe generic consumer, and deprecate the legacy path. Downstream types can opt in. This creates a migration lane but does not discharge or shrink existing `Block` obligations. + +Merely moving the dereference into a helper/default method changes proof location, not the unsafe surface or the full implementer theorem. + +## Authorized 2.0 migration + +The preferred 2.0 design for the demonstrated requirement is the safe `FirstByte` capability above, with `first` bounded by it and `Page` returning `self.0[0]`. That removes the raw pointer, associated alignment, unsafe trait/impl, temporal invariant, and unsafe dereference from this path. + +If real downstream users need aligned 16-byte raw access, split it into a separately named unsafe capability with a precise lifetime, initialization, aliasing/interference, provenance/live-allocation, non-nullness, alignment, and 16-byte read contract. Migrate old implementers to whichever capability they actually provide and old consumers to whichever they consume. Removing or weakening old `Block`, changing `first`'s bound, and any newly strengthened implementer contract require the explicitly authorized major release and a fresh audit of the resulting source. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r051.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r051.md new file mode 100644 index 0000000000..71dff96150 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r051.md @@ -0,0 +1,45 @@ +# Focused `Buffer` source review + +## Claim and verdict + +**UNSOUND** for the exact supplied `lib.rs`, Rust 1.80.0, every target and ordinary profile, for all well-typed safe uses. The universal safe-use soundness claim is refuted by this entirely safe call sequence: + +```rust +let mut buffer = Buffer::from_static(); +buffer.overwrite(0); +``` + +The undefined behavior occurs in `overwrite` at line 33. This is a source-level Rust result; it does not depend on whether a particular linker places `BYTE` in physically read-only memory. No documented behavioral postcondition is in scope that supports a separate `CONTRACT-BROKEN` verdict. + +Snapshot/scope: the supplied `lib.rs`; every current producer and consumer of `Buffer`, both `ptr.write` sites, and their local comments. There are no dependencies, `cfg` branches, generated code, FFI, allocator operations, or explicit trait implementations. No code was executed. + +## Boundary, invariants, and obligation ledger + +`Buffer`'s fields are private. The complete current producer set is therefore `from_writable` and `from_static`; there is no field-mutating transition. `overwrite` is the only pointee consumer. Moving, borrowing, and dropping `Buffer` do not dereference `ptr`. + +| Site | Obligation and result | +|---|---| +| `from_writable` (16–18) | **PROVED for valid unsafe calls.** Construction itself only stores values. Its contract requires the particular pointer to remain non-null, aligned, valid for a one-`u8` write, and free of conflicting access for the stated interval. It establishes `W`: `shared == None` and those caller-maintained facts apply to `ptr`. | +| `from_static` (20–26) | Construction alone performs no access, but establishes `S`: `shared == Some(r)` and `ptr` designates the same `BYTE` byte as `r`. It does **not** establish `W`. Returning this state from a safe constructor makes the later safe consumer unsound. | +| `overwrite`, `None` (35–39) | **PROVED relative to `from_writable`'s contract.** Privacy plus the exhaustive producer review gives `None => W`; `W` supplies both write validity and alignment required by `write`. A valid unsafe caller must continue satisfying the temporal/no-conflict clauses. | +| `overwrite`, `Some` (29–34) | **UNSOUND.** The safe witness above selects this branch and writes one byte while the shared reference to that byte is live. | +| `with_live` (43–46) | Calls the closure during the function call. Passing `shared` into it is sufficient to make the reference live throughout that call; `let _ = shared` is not needed for the counterexample. | + +## Derivation of the safe UB witness + +1. Rust 1.80 says a static is a precise memory location and all references to it refer to that same location. `from_static` takes `&BYTE`; the sized raw-pointer mutability cast returns the pointer unchanged. Thus its stored `ptr` and `shared` designate the same byte. [Static items](https://doc.rust-lang.org/1.80.0/reference/items/static-items.html#static-items), [pointer-to-pointer casts](https://doc.rust-lang.org/1.80.0/reference/expressions/operator-expr.html#pointer-to-pointer-cast). +2. In the `Some` branch, that shared reference is passed to `with_live`, and the closure writes before the call returns. Rust 1.80 states: “When a reference ... is passed to a function, it is live at least as long as that function call.” [Reference UB rules](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html#behavior-considered-undefined). +3. The same rules say bytes pointed to by a shared reference are immutable and define mutation as any overlapping write of more than zero bytes. `size_of::()` is 1 on every target. Therefore `self.ptr.write(value)` mutates the byte protected by the live `&u8`, even when `value == 7`, and is UB. [Reference UB rules](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html#behavior-considered-undefined), [primitive layout](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#primitive-data-layout). +4. Consistently, Rust 1.80's `ptr::write` contract says behavior is undefined unless `dst` is valid for writes and properly aligned; Rust's pointer-validity documentation also says reference and raw-pointer accesses cannot be interleaved. [ptr::write](https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html#safety), [pointer validity](https://doc.rust-lang.org/1.80.0/std/ptr/index.html#safety). + +This proof is parametric over targets and ordinary profiles: the source has no relevant configuration branch, `u8` is always one byte, and the cited abstract rule is not target- or optimization-conditional. + +## Proof-documentation findings + +The line 31 comment is false for its branch: `Some` values come from `from_static`, not `from_writable`. The smallest failed implication is `self.shared.is_some() => self.ptr satisfies from_writable's write contract`; the audited producers prove its negation. No replacement `SAFETY` comment can justify the current operation. The safe path must be prevented from writing through this pointer (or an unsafe caller obligation must be enforced at the API boundary), followed by re-audit. + +The line 36 comment cites write validity but omits `write`'s separate alignment obligation and the material privacy/producer derivation `None => from_writable provenance`. The implementation on this branch is provable, but the local proof is inadequate. Suitable proof content would name the exhaustive private producers, derive `None => W`, and state that `W` supplies validity, alignment, and absence of conflicting access at this write. + +## TCB and residual scope + +TCB: only the exact Rust 1.80.0 Reference and standard-library propositions linked above; no additional assumptions or tool-derived evidence. Broader behavior, future source, other Rust versions, custom compiler semantics, and binary/backend correctness are excluded. Re-review is required if any field visibility, producer, transition, `overwrite`/`with_live` behavior, supported toolchain, or cited semantic contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r052.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r052.md new file mode 100644 index 0000000000..3bc6c17851 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r052.md @@ -0,0 +1,44 @@ +# Evidence-sensitive review of `total` + +## Claim, snapshot, and verdicts + +Scope is the sole public safe API `total(&[u32]) -> u32` in `lib.rs` (SHA-256 `12ee0d64af16f4b4bea78bb146d333f86c579b49ca0dd38e2552f3896147be5c`), plus the proposed replacement. The requested supported set is Rust 1.70+, every target, and every ordinary profile. The required result is the left fold of all input values modulo \(2^{32}\). No dependencies, generated code, features, benchmark definition/result, or exact later-toolchain cutoff were supplied. + +- **Current implementation soundness: UNSOUND** for the combined supported set, relative to the literal Rust 1.70 standard-library contracts. A valid zero-length slice can reach undefined behavior at `lib.rs:6`. +- **Current behavior:** the modular-sum derivation is **PROVED for nonempty inputs on Rust 1.70**; no total behavioral verdict is available because the empty execution has UB. +- **Safe iterator candidate:** source-level soundness and the modular-sum postcondition are **PROVED on Rust 1.70** from the cited safe APIs, parametrically over target and ordinary profile. Applying those 1.70 behavioral contracts to the open-ended later-version range remains **UNPROVED** without checking each release or accepting an explicit compatibility premise and audit cutoff. +- **Replacement performance: UNPROVED.** There is no evidence from which the required at-most-2% regression can be derived. Therefore adoption of the replacement is also **UNPROVED**, even though it removes the identified unsafe-code defect. + +## Obligation ledger and reconstructed proof + +**O1 — `ptr.add(values.len())`, line 6: failed for `len == 0`.** Rust 1.70 says both pointers for [`add`](https://doc.rust-lang.org/1.70.0/core/primitive.pointer.html#method.add) must be “in bounds or one byte past” the same allocation, with violation causing UB. Yet [`slice::from_raw_parts`](https://doc.rust-lang.org/1.70.0/core/slice/fn.from_raw_parts.html) expressly permits `NonNull::dangling()` as the data pointer for a zero-length slice, and [`NonNull::dangling`](https://doc.rust-lang.org/1.70.0/core/ptr/struct.NonNull.html#method.dangling) creates a “dangling, but well-aligned” pointer. Thus this valid input exists: + +```rust +let p = std::ptr::NonNull::::dangling().as_ptr(); +let s = unsafe { std::slice::from_raw_parts(p, 0) }; // contract satisfied +total(s); // safe call; line 6 performs p.add(0) +``` + +The 1.70 `add` precondition is false at line 6. Rust 1.97.1 instead conditions allocation provenance on a [non-zero computed offset](https://doc.rust-lang.org/1.97.1/core/primitive.pointer.html#method.add), but that later wording has no supplied authority giving it Rust 1.70 scope. + +**O2–O4 — nonempty end construction, dereference, and increment: proved, but undocumented locally.** Let `N = values.len() > 0`. [`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/core/primitive.slice.html#method.as_ptr) returns the buffer pointer and requires the slice to outlive it; the parameter remains live throughout this call and the code performs no mutation or callback. Valid slices are contiguous and have total byte size at most `isize::MAX`. Consequently `base.add(N)` is one-past the same allocation with a fitting, non-wrapping byte offset. + +Loop invariant after `k` iterations: `0 <= k <= N`, `ptr = base.add(k)`, and `acc` is the modular sum of elements `0..k`. When `k < N`, `ptr` is aligned, live, initialized, and points to element `k`, so the dereference does not meet the Rust 1.70 Reference’s listed [dangling/unaligned dereference UB](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html). `ptr.add(1)` remains in the allocation or reaches one-past, establishing the invariant for `k+1`. At `k=N`, pointer equality ends the loop. [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/core/primitive.u32.html#method.wrapping_add) is modular addition, so overflow checks, optimization level, and debug assertions cannot change the arithmetic result. The implementation has no `SAFETY` comments; this material proof would need adjacent documentation even after fixing O1 if unsafe code remains. + +## Safe redesign + +The review candidate is: + +```rust +pub fn total(values: &[u32]) -> u32 { + values.iter().fold(0u32, |acc, &value| acc.wrapping_add(value)) +} +``` + +On Rust 1.70, [`slice::iter`](https://doc.rust-lang.org/1.70.0/core/primitive.slice.html#method.iter) yields every item from start to end, and [`Iterator::fold`](https://doc.rust-lang.org/1.70.0/core/iter/trait.Iterator.html#method.fold) applies the closure to every element, left-associatively, starting with `0`. Combining those contracts with `wrapping_add` proves exactly the required modular sum; the empty case returns `0`. There is no unsafe operation or caller safety obligation, and the argument is independent of target and ordinary profile. This is a stronger proof boundary than repairing and documenting three raw-pointer sites. + +## Recommended next action + +Treat O1 as release-blocking, but do not merge the iterator replacement on assertion alone. First define and run the designated benchmark outside this review against the exact baseline and candidate, pinning toolchain, target/CPU features, profile, codegen/LTO settings, inputs, and metric. Use interleaved repeated samples and a predeclared uncertainty method; accept only if the evidence establishes that the slowdown’s upper bound is at most 2%. A result crossing 2% is inconclusive, not a pass. + +If it passes, merge the safe iterator. If it fails or is inconclusive, retain neither the unmodified current code nor an undocumented unsafe variant: benchmark an optimized safe candidate or a minimally repaired pointer loop (for example, an empty fast path) accompanied by the complete O2–O4 proof. Also close the Rust-version claim by choosing an audit cutoff and verifying intervening contracts, or explicitly approving a compatibility TCB premise with a re-review trigger. Re-review on source, support-range, std-contract, benchmark, compiler, target, or profile changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r053.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r053.md new file mode 100644 index 0000000000..ddc1cbfe1d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r053.md @@ -0,0 +1,36 @@ +# Acceptance review: `Piece for Tail` + +## Decision and exact claim + +**REJECT.** For the exact `REQUEST.md` and `lib.rs` snapshot, the `unsafe impl Piece for Tail` is **CONTRACT-BROKEN** over the entire requested target/profile domain. No undefined behavior is needed to establish that verdict. The concrete `increment_tail` implementation has a complete source-level soundness proof for Rust 1.70, all targets, and ordinary profiles; however, the requested open-ended Rust `1.70+` soundness claim is **UNPROVED** beyond 1.70 because the permitted evidence contains no cross-release compatibility premise and this review verified versioned Rust 1.70 documentation only. This evidence limitation does not affect the full-domain contract counterexample or rejection. + +Scope is only `Tail`'s impl and the safe `increment_tail` surface. Relevant reachable construction/mutation of `Pair` and `Tail` was included. Other possible `Piece` implementations and unrelated robustness are excluded. There are no dependencies, generated artifacts, `cfg`s, features, callbacks, FFI, concurrency, allocators, or representation attributes in the snapshot. + +## Contract counterexample + +The controlling contract says: + +1. `FIELD` is “the name of a direct declared field of `Owner` whose type is `Item`”; and +2. `project` returns a pointer to **that direct declared field**. + +Here `Owner = Pair` and `Item = u32`, but `Pair` is declared `Pair(pub [u32; 2])` (`lib.rs:18`). It has one direct tuple field, of type `[u32; 2]`; it has no direct field named `tail` and no direct field of type `u32`. The Rust 1.70 [struct grammar](https://doc.rust-lang.org/1.70.0/reference/items/structs.html) defines each tuple-struct field as one `Type`, confirming that the array—not either element—is the declared field. + +Consequently, `FIELD = "tail"` (`lib.rs:24`) falsifies clause 1. `project` returns `addr_of_mut!((*owner).0[1])` (`lib.rs:26-28`), a pointer to an element nested inside the sole array field, falsifying clause 2 on every call satisfying its safety precondition. Rust 1.70 describes an [`unsafe impl`](https://doc.rust-lang.org/1.70.0/reference/unsafe-keyword.html#unsafe-trait-implementations-unsafe-impl) as the programmer's assertion that the unsafe trait's obligations have been discharged. They have not. Verdict: **CONTRACT-BROKEN**, all targets/profiles and every Rust version in the requested range for which this source has the stated tuple-struct meaning. + +## Concrete soundness derivation (Rust 1.70) + +`Tail::project` itself does not perform UB for a call satisfying its stated precondition. Interpreting “identif[ies] a live, uniquely borrowed `Owner`” literally as a usable pointer designating that live, exclusively borrowed `Pair`, `owner` is aligned and non-dangling. Index `1` is in bounds for `[u32; 2]`; Rust's [array-index rule](https://doc.rust-lang.org/1.70.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions) is zero-based and yields the element memory location. [`addr_of_mut!`](https://doc.rust-lang.org/1.70.0/core/ptr/macro.addr_of_mut.html) creates a mutable raw pointer to that place without an intermediate reference. Thus the returned pointer designates the live, initialized, aligned second `u32`. This proves actual pointer validity, but cannot turn that nested element into the promised direct field. + +For every well-typed safe call to `increment_tail` (`lib.rs:31-34`): + +- `pair: &mut Pair` exclusively accesses a live `Pair`; Rust 1.70 states that an unborrowed [mutable reference is the only way to access its value](https://doc.rust-lang.org/1.70.0/reference/types/pointer.html#mutable-references-mut), and [coercion from `&mut T` to `*mut T`](https://doc.rust-lang.org/1.70.0/reference/type-coercions.html#coercion-types) is permitted at function arguments. This discharges `project`'s caller precondition. +- The concrete-body derivation above makes `&mut *Tail::project(pair)` a live, aligned, initialized, exclusive `u32` reference. No competing access occurs before that reference's last use. This avoids the raw-dereference, invalid-reference, and alias failures enumerated by the Rust 1.70 [UB rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html). +- [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add) performs modular addition, so overflow is profile-independent. The write leaves a valid `u32` and therefore a valid `Pair`. + +On normal return the second array element is its old value plus one modulo `2^32`, and the first is unchanged. `increment_tail` documents no broader behavioral postcondition. Its soundness proof uses the inspected concrete projection, not the false direct-field guarantee; therefore the impl's contract breach does not by itself make this particular safe wrapper unsound. + +## Coverage, TCB, and residual result + +The proof is parametric over target layout and ordinary profile: field/index expressions compute the implementation-selected address, no offset/layout fact is assumed, index bounds are constant, and wrapping arithmetic removes overflow-check differences. There are no panic or unwind branches in the established path. + +TCB consists only of the cited Rust 1.70 Reference and standard-library propositions; no dependency, tool, compiler-backend, deployment, or external-specification assumption is admitted. Applying those 1.70 propositions to every later release would require precisely the compatibility premise the request disallows. Re-audit against each later version's applicable authoritative text (or an expressly permitted compatibility premise) is required to upgrade the open-ended soundness verdict. The unconditional contract failure already requires rejection under the accept/reject policy. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r054.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r054.md new file mode 100644 index 0000000000..738898a2da --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r054.md @@ -0,0 +1,40 @@ +# Focused review: `Block`, `Page`, and `first` + +## Claim, scope, and verdict + +Artifact: the supplied `lib.rs`; Rust 1.70+, every target/profile on which it is accepted. In scope are the public unsafe trait and its associated constant/method, `Page` and its `unsafe impl`, and safe `first`. There are no `cfg`s, generated artifacts, dependencies, callbacks, panics after pointer production, or profile-dependent checks. The source proof is therefore parametric over targets and profiles. + +- **`Page` implementation — PROVED for Rust 1.70 semantics.** It establishes the full strong pointer proposition stated below, not merely what `first` needs. +- **`first::` — PROVED for Rust 1.70 semantics.** It reads `Page.0[0]` while the `Page` borrow is live. +- **Generic safe `first` under the published text — UNPROVED, not shown UNSOUND.** Its derivation closes only if “readable for 16 bytes” and “during the borrow” entail C1 and C2 below. The text does not define those material terms precisely enough to prove that every published-contract-conforming downstream implementation supplies them. +- **Literal open-ended Rust 1.70+ claim — UNPROVED.** The citations establish Rust 1.70.0. Extending them to every later and future stable release needs either an accepted TCB premise that these exact propositions are preserved, or a finite audit cutoff plus rolling re-audit. The project’s 1.x SemVer promise does not establish Rust’s language/library compatibility. + +No dependency, tool, platform, or compiler-implementation premise is consumed. The only pending TCB proposition is `RUST-COMPAT`: every supported post-1.70 toolchain preserves the cited abstract-semantic propositions. Re-audit on any relevant Reference/std change. + +## Contract normalization and proof + +Rust 1.70 says an unsafe trait defines extra implementation conditions, and an `unsafe impl` asserts they are discharged ([Reference](https://doc.rust-lang.org/1.70.0/reference/unsafe-keyword.html#unsafe-traits-unsafe-trait)). Thus every valid `Block` implementation owes both clauses: `ALIGN` is a nonzero power of two; on each normal return `base` supplies a non-null, `ALIGN`-aligned pointer readable for 16 bytes for the stated interval. + +For proof-grade use, the last clause must entail: + +- **C1 (read capability):** bytes `p..p+16` lie in one live allocation, are initialized as `u8`, and may be non-atomically read without a race or alias violation. +- **C2 (interval):** C1 remains true after `base` returns through the consuming read, while the caller retains the receiver borrow. + +These are the smallest missing implications, not new facts inferred from `Page`. Rust 1.70 makes evaluating `*p` UB when a raw pointer is dangling or unaligned and makes reading an uninitialized integer invalid ([Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)); “dangling” includes null or a pointee span outside one live allocation ([same page](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#dangling-pointers)). + +**`Page`.** `ALIGN = 16` satisfies the constant clause. Rust 1.70 specifies `u8` size 1; alignment is at least 1 and a power of two, and size is a multiple of alignment, so `u8` alignment is 1. `[u8; 16]` has size 16, the element alignment, and contiguous element offsets ([layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#primitive-data-layout), [array layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). The `repr(C)` field-placement algorithm starts at offset zero, so the sole field begins at the `Page` address; `repr(align(16))` raises the struct alignment to 16 ([C structs](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs), [alignment modifier](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers)). Arrays coerce to slices, and `as_ptr` “returns a raw pointer to the slice's buffer”; its documentation requires the slice to outlive the pointer ([array](https://doc.rust-lang.org/1.70.0/std/primitive.array.html), [`as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)). A valid `&Page` keeps the initialized array live during its borrow. Consequently `self.0.as_ptr()` is non-null, 16-aligned, and readable for all 16 initialized bytes for that interval. No alternate exit changes this conclusion. + +**`first`.** Let `p = block.base()`. Under C1/C2, `p` remains non-dangling through the next expression and its first byte is initialized/readable. A `u8` requires alignment 1, so the raw dereference is permitted and yields that byte. `first` consumes neither `ALIGN`, the power-of-two condition, nor bytes 1–15. There is no documented `first` postcondition beyond soundness; for `Page`, the operational result is `self.0[0]`. The unsafe block lacks this material adjacent derivation, and the `unsafe impl` likewise lacks a safety proof. The trait should also have explicit `# Safety` text naming implementers and defining C1/C2. + +## What can change in 1.x + +The full published contract remains an obligation even though the only known in-tree consumer needs one byte. Downstream implementations and unsafe consumers may rely on every clause. In 1.x, after authorization, the project may: + +1. Add adjacent proof comments for `Page` and `first`, and clarify public documentation only to the extent C1/C2 are confirmed to be the existing meaning. If that wording actually lengthens the interval or strengthens “readable,” it strengthens implementer obligations and is breaking rather than a minor documentation edit. +2. Add a parallel, new-named safe capability such as `trait FirstByte { fn first_byte(&self) -> u8; }`, implement it for `Page` with `self.0[0]`, and add a new safe generic function. Keep `Block`, its full contract, its `Page` impl, and existing `first` intact (possibly deprecated). Do not add a blanket `impl FirstByte for T`: it retains the unsafe proof and can overlap downstream impls. + +This lets new code use a proof with no raw pointer, but cannot remove the legacy proof burden during 1.x. + +## What requires an authorized 2.0 migration + +Reducing 16 readable bytes to one, dropping `ALIGN`/alignment/non-null guarantees, removing or sealing `Block`, making it safe, changing `base` to a reference-returning API, or changing existing `first` to a new trait bound can invalidate published consumers or implementations. All require the major-version decision requested by the project policy. The preferred 2.0 design is the safe `FirstByte` capability if that is the actual requirement; retain a separately named, precisely documented unsafe raw-block capability only if downstream requirements demonstrate a need for it. Implemented 2.0 source would require a fresh audit. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r055.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r055.md new file mode 100644 index 0000000000..d7794586e6 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r055.md @@ -0,0 +1,43 @@ +# Focused review: `total` + +## Claim and verdicts + +Snapshot: `lib.rs` SHA-256 `12ee0d64af16f4b4bea78bb146d333f86c579b49ca0dd38e2552f3896147be5c`. Scope is only the public safe function `total`, including its three unsafe operations and the requested iterator alternative. The requested domain is Rust 1.70+, every target, and every ordinary profile; there are no dependencies, generated artifacts, `cfg`s, or target-specific branches in the supplied source. + +- **Current implementation soundness: UNPROVED** for that full domain. The unresolved case is an empty slice on Rust 1.70; no concrete UB verdict is asserted. +- **Current implementation, nonempty slices on Rust 1.70: PROVED** relative to the Rust 1.70 standard-library contracts cited below, for all targets and ordinary profiles. +- **Wrapping-sum postcondition: PROVED** on that nonempty/Rust-1.70 subset and **UNPROVED** for the full requested domain because the empty execution's soundness is unresolved. +- **Iterator candidate:** design only, so it has no post-change verdict. Its conditional source proof closes without crate-local unsafe code. +- **Performance requirement: UNPROVED.** No benchmark result, benchmark definition, environment, or uncertainty analysis was supplied; source similarity or expected optimization cannot prove a maximum 2% regression. + +## Obligation ledger and reconstructed proof + +Let `base = values.as_ptr()` and `n = values.len()`. Rust 1.70 says [`as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr) returns the slice buffer pointer and requires the slice to outlive its use. The valid-slice construction contract requires one allocation, `n` consecutive initialized values, no mutation during the shared lifetime, and at most `isize::MAX` total bytes; it also expressly permits `NonNull::dangling()` for zero-length slice data ([`from_raw_parts`](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html#safety)). + +For `n > 0`, maintain the loop invariant `ptr = base.add(i)` for one unique integer `0 <= i <= n`. The Rust 1.70 [`pointer::add`](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) contract requires starting and resulting pointers to be in bounds or one-past the same allocation, an `isize`-representable byte offset, and no address-space wrap. Slice validity discharges these for `base.add(n)`. If `ptr != end`, the invariant gives `i < n`; therefore `ptr` denotes an aligned, initialized `u32` still protected by `values`, proving the read at line 9. Then `i + 1 <= n`, proving line 10's `add(1)` and re-establishing the invariant. At `i = n`, pointer equality ends the loop. This covers the one-element and final one-past transitions as well as all larger nonempty slices. + +For `n = 0`, line 6 evaluates `base.add(0)`. Rust 1.70's literal contract still requires both pointers to relate to an allocated object, while the valid empty-slice contract allows a dangling data pointer. Thus the needed implication—“every valid empty-slice data pointer satisfies the Rust 1.70 `add(0)` allocation condition”—is not supplied by the inspected authorities. Current documentation limits the allocation condition to a *nonzero* offset ([current `pointer::add`](https://doc.rust-lang.org/1.97.1/core/primitive.pointer.html#method.add)), but later wording cannot silently establish the proposition for 1.70. This is the smallest missing proof; it is enough for `UNPROVED` without claiming a UB counterexample. + +On each proved iteration, [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add) is modular addition. Hence after `i` iterations, `acc` is the first `i` elements' sum modulo `2^32`. This operation is explicitly wrapping and therefore independent of overflow-check and optimization profile settings. + +The implementation has no `SAFETY` comments at lines 6, 9, or 10. Even on the proved subset, the material invariant and derivation above are absent. Comments alone cannot close the empty-slice gap; the operation or control flow must first change. + +## Safe iterator redesign + +The focused candidate is: + +```rust +pub fn total(values: &[u32]) -> u32 { + values + .iter() + .fold(0u32, |acc, &value| acc.wrapping_add(value)) +} +``` + +Rust 1.70 documents that [`slice::iter`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.iter) yields every item from start to end, and [`Iterator::fold`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.fold) starts with the supplied accumulator, applies the closure to every element, and returns the final accumulator. Combined with modular `wrapping_add`, induction proves exactly the same modulo-`2^32` sum, including `0` for an empty slice, in every ordinary profile. It removes all crate-local unsafe obligations and the raw-pointer proof-documentation defect. For the open-ended `1.70+` wording, either adopt an explicit compatibility premise for these exact semantics with a re-audit trigger, or set a finite audit cutoff; API stability badges alone do not provide semantic version closure. + +## Recommendation + +Do **not** replace the code yet, and do not retain the current source as fully proved. Next, benchmark the exact iterator candidate against this exact baseline using the designated benchmark, with the compiler/target/profile/environment fixed and a predeclared rule whose uncertainty-adjusted upper bound permits no more than 2% regression. If it passes, adopt it and audit the new snapshot. If it fails or is inconclusive, keep investigating rather than waiving either requirement; a raw-pointer fallback must at minimum eliminate the Rust-1.70 empty `add(0)` obligation (for example, through safe range construction or an empty fast path), add adjacent proofs for every unsafe operation, and itself satisfy the same benchmark gate. + +TCB/evidence: only the linked Rust 1.70 standard-library propositions are consumed for the proved subset. No tests, benchmark, compiler-codegen claim, dependency assumption, or later-to-earlier compatibility premise is admitted. Re-review is required after any source change, support-range change, accepted compatibility premise, or benchmark-definition/environment change. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r056.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r056.md new file mode 100644 index 0000000000..7815617e85 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r056.md @@ -0,0 +1,102 @@ +# `Piece for Tail` / `increment_tail` acceptance review + +## Decision and verdicts + +**REJECT.** The exact snapshot is `lib.rs` SHA-256 +`d76a5c0d7336aac4e551264a105c621dbd6cf27db097fb648aa3ca1e891e3429`. + +- **Contract compliance: CONTRACT-BROKEN** for the requested Rust 1.70+, + all-target, ordinary-profile domain. A valid UB-free Rust 1.70.0 execution + falsifies `Piece::project`'s documented postcondition, and `Tail::FIELD` + independently violates its associated-constant contract. +- **Soundness: PROVED** for Rust 1.70.0, all targets and ordinary profiles, for + every call to `Tail::project` satisfying its stated safety precondition and + every well-typed safe call to `increment_tail`, relative only to the cited + Rust 1.70 Reference/std axioms. +- **Full open-ended soundness claim: UNPROVED**, not `UNSOUND`, for unqualified + Rust 1.70+. No additional compatibility TCB is permitted, and an exact + backwards-applicability premise preserving every cited semantic proposition + for every later and future release is absent. The preceding exact-version + result is the strongest proved soundness region. Audit cutoff: 2026-07-31; + each newly claimed Rust release requires its applicable contracts to be + checked. + +The proved contract breach alone requires rejection under the stated +accept-or-reject policy. + +## Boundary and literal obligations + +The reviewed surfaces are the unsafe `Piece for Tail` assertion and method +([lib.rs:21](/tmp/unsafe-rust-v2-eval.9epWDK/targets/y9t5/lib.rs:21)), the public +`Pair` representation, and safe `increment_tail` +([lib.rs:31](/tmp/unsafe-rust-v2-eval.9epWDK/targets/y9t5/lib.rs:31)). There are +no other files, dependencies, `cfg` branches, generated project code, FFI, +assembly, allocation, or concurrency in the supplied target. Other `Piece` +implementations and consumers are outside this focused review. + +The controlling provider obligations are literal: `FIELD` must name a direct +declared `Owner` field of type `Item`; `project` must return a pointer to *that* +field; a caller must supply a pointer identifying a live, uniquely borrowed +`Owner` for the call. Safe `increment_tail` may impose no hidden caller safety +condition. It has no documented behavioral postcondition; its name does not +create one. + +## Finding: the unsafe implementation cannot satisfy its contract + +`Pair` declares exactly one tuple field, of type `[u32; 2]` (line 18), while +the impl selects `Item = u32` (line 23). The Rust 1.70 struct grammar defines a +tuple field directly by one `Type`, and the array contract says an array is a +“fixed-size sequence of `N` elements of type `T`.” Thus the direct field is the +array; its two `u32`s are elements, not direct fields +([structs](https://doc.rust-lang.org/1.70.0/reference/items/structs.html), +[arrays](https://doc.rust-lang.org/1.70.0/reference/types/array.html)). No direct +`u32` field exists, so `FIELD = "tail"` (line 24) is false under either possible +reading of tuple-field naming. + +Likewise, `(*owner).0[1]` first selects direct field `.0`, then nested array +element `[1]`. It therefore cannot return a pointer to the promised direct +field. An UB-free witness is an exclusively borrowed live `Pair([0, 0])` passed +to `project`: the call returns the address of its second array element. That +element exists because array indices are zero-based and `1 < 2` +([indexing](https://doc.rust-lang.org/1.70.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions)). +The execution is valid by the soundness derivation below, yet its returned +pointer is not to a direct `Pair` field. This is the independent UB-free witness +required for `CONTRACT-BROKEN`; no UB witness is known or needed. + +## Reconstructed Rust 1.70.0 soundness proof + +For a valid `project` call, the premise supplies a live, exclusive `Pair` and +hence a non-dangling, aligned pointer to initialized storage. Default Rust +representation guarantees “The fields are properly aligned”; array layout +places element `n` at `n * size_of::()`, and all array elements are +initialized +([layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-default-representation), +[array layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout), +[array initialization](https://doc.rust-lang.org/1.70.0/reference/types/array.html)). +Consequently `.0[1]` is an in-allocation, aligned, initialized `u32` place on +every target, without assuming field order. `addr_of_mut!` creates its raw +pointer “without creating an intermediate reference”; its ordinary-expression +requirements are met here +([macro](https://doc.rust-lang.org/1.70.0/std/ptr/macro.addr_of_mut.html)). +No aliasing competitor or intervening call exists. + +At the safe wrapper, function arguments are coercion sites and Rust 1.70 +expressly permits `&mut T` to `*mut T`, so `pair` establishes `project`'s only +safety precondition +([coercions](https://doc.rust-lang.org/1.70.0/reference/type-coercions.html#coercion-types)). +The returned pointer has the properties just proved. Forming `&mut *` is +therefore a valid mutable borrow; the original `pair` is not accessed +thereafter, and all reads/writes occur through `value`. The Reference states +that during a mutable borrow the place may not otherwise be accessed +([borrows](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#borrow-operators)); +the code meets that rule. Finally, `u32::wrapping_add` is documented as +“Wrapping (modular) addition,” so overflow checks, optimization, and debug +assertions do not create a profile branch +([`wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add)). +The assignment leaves a valid `u32`, and the borrow ends on return. + +Both unsafe blocks lack adjacent `SAFETY` proofs; the material derivation above +is reconstructed review evidence, not present proof documentation. No testing, +building, execution, or macro expansion was used. The TCB contains only the +exact cited Rust 1.70 abstract-semantics/std propositions; there are no admitted +dependency, tool, implementation, platform, or deployment assumptions. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r057.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r057.md new file mode 100644 index 0000000000..eaa900408d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r057.md @@ -0,0 +1,48 @@ +# Supported-domain source review + +## Claim and verdict + +**Soundness: PROVED** for every well-typed safe call to the public `first` function in the exact `v7c4` snapshot, under the configuration envelope `E = POLICY-A ∪ POLICY-B`, relative to TCB R1 below. This is a source-level Rust claim: every permitted execution is free of Rust undefined behavior under the documented abstract semantics. It is also **PROVED independently under POLICY-A and under POLICY-B**; no choice of controlling policy is needed. + +`E` is the following set through the requested Rust 1.82.0 cutoff (edition 2021; exact stable releases): + +| Feature | Target | POLICY-A | POLICY-B | Envelope E | +|---|---|---|---|---| +| no `fast` | both published targets | 1.79.0–1.82.0 | 1.79.0–1.82.0 | 1.79.0–1.82.0 | +| `fast` | `x86_64-unknown-linux-gnu` | 1.79.0–1.82.0 | 1.80.0–1.82.0 | 1.79.0–1.82.0 | +| `fast` | `aarch64-unknown-linux-gnu` | 1.80.0–1.82.0 | 1.82.0 | 1.80.0–1.82.0 | + +The envelope is a proof-coverage device, not a ruling that POLICY-A controls. The exact authoritative support predicate remains underdetermined because both publications are current and their `fast` exclusions conflict. CI is sampling evidence only and was not used as a premise. + +Snapshot: all seven files in `targets/v7c4`; `lib.rs` SHA-256 `6f87e944cec7ee2727f7c7d32aa382de88987ea791082175d611fff5bf44012b`, POLICY-A `387664b8092a74c7f80ae6cccfbec50160e8e7e215f0355677b33d36bc97f479`, POLICY-B `c1885e8ef901624ab9b2813b2f37a4e2c167d3fb53ea0798b6586f87293ed246`. No dependencies, build script, generated code, FFI, macros, unsafe declarations/traits/impls/fields, or mutable invariant-bearing state exist. + +## Boundary, configuration, and obligation coverage + +The sole public surface is safe `fn first(&[u8]) -> Option`. The complementary `cfg(feature = "fast")` and `cfg(not(feature = "fast"))` predicates form a total, disjoint partition. Target, release, profile, and codegen choices do not alter either selected body, so the proof is parametric over those axes within E. + +**O-NORMAL (`lib.rs:3–6`) — PROVED.** This branch contains only safe standard-library calls and has no caller-side safety obligation or local unsafe consumer. + +**O-FAST-EMPTY (`lib.rs:8–11`) — PROVED.** If `bytes.is_empty()` is true, the function returns `None` before reaching unsafe code. + +**O-FAST-NONEMPTY (`lib.rs:12–14`) — PROVED by reconstructed proof.** In each audited release, `is_empty` states: “Returns `true` if the slice has a length of 0” ([1.79.0](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.is_empty), [1.80.0](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.is_empty), [1.81.0](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.is_empty), [1.82.0](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.is_empty)). The `else` branch therefore implies `bytes.len() != 0`; because length is a `usize`, `bytes.len() > 0`. The Reference says indices are zero-based ([1.79.0](https://doc.rust-lang.org/1.79.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions), [1.80.0](https://doc.rust-lang.org/1.80.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions), [1.81.0](https://doc.rust-lang.org/1.81.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions), [1.82.0](https://doc.rust-lang.org/1.82.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions)); hence index 0 is in bounds. + +In every audited release, `get_unchecked`'s safety clause states: “Calling this method with an out-of-bounds index is undefined behavior” ([1.79.0](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.get_unchecked), [1.80.0](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.get_unchecked), [1.81.0](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.get_unchecked), [1.82.0](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked)). The dominating branch proves the required index condition. The returned shared `&u8` is read immediately while the input borrow remains live; there is no intervening mutation, callback, unwind point, or concurrent access introduced by this function. Constructing `Some(u8)` is safe. Thus all exits preserve soundness for arbitrary valid `&[u8]` inputs. + +No crate-defined unsafe API has a documented postcondition to check, and no broader safe-API behavioral property was requested. No UB or postcondition counterexample is known. + +## Findings + +1. **Local proof documentation: UNPROVED as written; implementation obligation PROVED.** The unsafe expression at `lib.rs:13` has no adjacent `SAFETY` comment, so its necessary derivation exists only in this report. Proposed wording (no edit requested): + + ```rust + // SAFETY: This is the `else` branch of `bytes.is_empty()`. Thus the + // slice length is nonzero, so zero is an in-bounds index. + ``` + +2. **Published support authority is inconsistent (non-soundness finding).** POLICY-A supports `fast` on x86_64/1.79 and aarch64/1.80–1.81, while POLICY-B's narrower ranges exclude them. With no precedence rule, the project cannot state one exact controlling support predicate. The minimal resolution is an authorized reconciliation or precedence rule. This does not weaken the verdict above because E covers both interpretations. + +## TCB R1, residual scope, and triggers + +- **AXIOM-STD-1:** the exact Rust 1.79.0, 1.80.0, 1.81.0, and 1.82.0 Reference/standard-library propositions linked above, including that the standard-library operations fulfill those contracts under the Rust abstract semantics. Consumer: O-FAST-NONEMPTY. Safe standard-library operations in O-NORMAL are trusted at their safe typed boundaries. +- No dependency, external-specification, tool-result, test, compiler-backend, platform-runtime, or probabilistic premise is consumed. Binary/code-generation correctness and configurations outside E are excluded. +- Re-review on source/Cargo/policy changes, a new target or feature, support beyond Rust 1.82.0, or material changes to a cited authoritative contract. Skill package used: `b4x9`. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r058.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r058.md new file mode 100644 index 0000000000..2b49f08530 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r058.md @@ -0,0 +1,45 @@ +# Supported-domain source review + +## Verdict + +**Soundness: PROVED** for the exact supplied `lib.rs`, for every well-typed safe call to `first`, over the conservative domain `U` below, relative to the version-matched Rust abstract semantics and standard-library contracts. `U` contains every configuration supported by either current policy. The policies still do not determine one authoritative support predicate; this verdict neither chooses between them nor creates a new support promise. + +This is a source-level undefined-behavior result. It does not claim package buildability, correctness of a particular compiler/backend or binary, or undocumented behavioral properties. + +## Scope and configuration closure + +Reviewed: `REQUEST.md`, `Cargo.toml`, `CI.md`, `POLICY-A.md`, `POLICY-B.md`, `lib.rs`, and `rust-toolchain.toml`, at the requested Rust 1.82.0 cutoff. The only public surface is safe `first(&[u8]) -> Option`. The only unsafe obligation is `bytes.get_unchecked(0)` in the `fast` implementation (`lib.rs:13`). There are no dependencies, unsafe declarations/impls, generated sources, FFI, concurrency, or target-specific source. + +Both policies cover Rust `{1.79.0, 1.80.0, 1.81.0, 1.82.0}`, the two named GNU/Linux targets, and every non-`fast` pair. Their union is: + +- non-`fast`: every version/target pair; +- `fast`, x86_64: Rust 1.79.0–1.82.0; +- `fast`, aarch64: Rust 1.80.0–1.82.0. + +Call this `U`. Policy B's `fast` set is a subset of Policy A's, so `U` covers both possible commitments (and their cumulative reading). `CI.md` expressly says its samples do not define support, and the 1.82.0 toolchain selector is only a default; neither resolves the conflict. The two `cfg` predicates are exact complements, so precisely one implementation exists for either feature value. + +## Proof + +The applicable Rust 1.79, 1.80, 1.81, and 1.82 slice documentation has the same propositions in every version: + +- `is_empty` reports whether the slice has a “length of 0” ([1.79](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.is_empty), [1.80](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.is_empty), [1.81](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.is_empty), [1.82](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.is_empty)). +- `get_unchecked` returns a reference to the indexed element without checking bounds and states that an “out-of-bounds index is undefined behavior” ([1.79](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.get_unchecked), [1.80](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.get_unchecked), [1.81](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.get_unchecked), [1.82](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked)). +- The References state that slice elements are “always initialized” ([1.79](https://doc.rust-lang.org/1.79.0/reference/types/slice.html), [1.80](https://doc.rust-lang.org/1.80.0/reference/types/slice.html), [1.81](https://doc.rust-lang.org/1.81.0/reference/types/slice.html), [1.82](https://doc.rust-lang.org/1.82.0/reference/types/slice.html)). + +Without `fast`, `first` contains only safe operations; no caller-side safety premise is introduced. + +With `fast`, an empty slice returns before the unsafe expression. Reaching `else` means `bytes.len() != 0`; since slice length is a `usize`, `bytes.len() >= 1`, hence `0 < bytes.len()` and index `0` is in bounds. This discharges the complete relevant `get_unchecked(0)` obligation. Its returned shared `&u8` refers to initialized element zero and is immediately read to copy a `u8`; it neither escapes nor permits mutation. Thus every well-typed safe input is sound. The reasoning uses no target/layout/profile property and applies uniformly throughout `U`. No cross-version compatibility premise is needed because each release's own documentation was checked. + +## Proof-documentation finding + +`lib.rs:13` has no adjacent `SAFETY` comment. The implementation obligation is proved above, but the source omits a material derivation required to review the unsafe operation locally. Proposed wording (no edit requested): + +```rust +// SAFETY: This branch is reached only when `bytes.is_empty()` is false, +// so `bytes.len() >= 1` and index 0 is in bounds for `get_unchecked`. +Some(unsafe { *bytes.get_unchecked(0) }) +``` + +## TCB and residual scope + +TCB: the applicable Rust abstract semantics and the exact versioned Reference/standard-library propositions linked above. No dependency, testing, CI-coverage, tool-result, platform, or backwards-compatibility assumption is consumed. No tests, builds, or execution were used. Re-audit is required if the source, feature selection, supported versions/targets, or a consumed contract changes. Policy authority itself remains unresolved, but it is not a soundness gap because the proof covers the conservative union. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r059.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r059.md new file mode 100644 index 0000000000..d23a5c4e66 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r059.md @@ -0,0 +1,44 @@ +# Focused review: configuration-preserving `decode` redesign + +## Claim and verdict + +Scope is the complete supplied snapshot, `lib.rs`, and its two public, mutually exclusive `decode` definitions. Supported configurations are Rust 1.70+, every target and pointer width, every ordinary profile, and both values of `compact`. Safe callers may pass every value of the signature's integer type. + +**Existing implementation: UNSOUND over the published support set.** More precisely: + +- With `compact` enabled and debug assertions disabled, the valid safe call `decode(0xD800)` reaches `char::from_u32_unchecked(0xD800)`. The Rust 1.70 Reference makes producing an invalid value undefined behavior and specifically lists “a value in a `char` which is a surrogate” as invalid ([invalid values](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#invalid-values)). This is a reachable safe-API UB counterexample on an ordinary release configuration. +- With `compact` enabled and debug assertions enabled, the range check panics for every surrogate before the unsafe call; every other `u16` is a scalar and reaches the call validly. This configuration-specific implementation and its documented behavior are **PROVED** for Rust 1.70 relative to the axioms below. +- With `compact` disabled, `char::from_u32` implements the documented `Some(char)`/`None` result for every `u32`; this branch is **PROVED** for Rust 1.70 relative to the axioms below. + +The existing `SAFETY` proof is absent, and no valid proof can be reconstructed for the failing branch: [`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html) says optimized builds do not execute it by default and explicitly cautions that it should be used “only in safe code.” Thus it cannot establish the unchecked conversion's precondition across supported profiles. + +## Redesign + +```rust +/// With `compact`, returns the represented scalar and panics for a surrogate. +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + match char::from_u32(raw as u32) { + Some(scalar) => scalar, + None => panic!("surrogate code point"), + } +} + +/// Without `compact`, returns the represented scalar or `None`. +#[cfg(not(feature = "compact"))] +pub fn decode(raw: u32) -> Option { + char::from_u32(raw) +} +``` + +No caller-facing contract, signature, return type, or feature selection changes. No unsafe operation or safety comment remains. + +## Compact proof and configuration closure + +Rust 1.70 defines a `char` as a scalar in `0..=0x10FFFF` excluding surrogates `0xD800..=0xDFFF` ([`char` validity](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity)). Its [`char::from_u32`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32) contract says it returns `None` exactly when the input is not a valid `char`; the same page marks the method stable since 1.52.0, so this does not raise the 1.70 MSRV. The Reference says widening an unsigned integer cast zero-extends ([numeric casts](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast)), so `raw as u32` preserves every `u16` value. + +Consequently, for `compact=true`, a non-surrogate `raw` is at most `0xFFFF` and therefore valid; the match returns its represented scalar. A surrogate produces `None`, and [`panic!`](https://doc.rust-lang.org/1.70.0/std/macro.panic.html) “panics the current thread,” so the documented exceptional behavior holds. For `compact=false`, the unchanged checked conversion directly supplies the documented result. These two cfg predicates are complementary and exhaustive. Pointer width and target cannot affect the fixed-width value proof; optimization, debug assertions, and panic strategy cannot bypass the unconditional match. There is no generated code, dependency, state invariant, callback, or other API surface in the supplied snapshot. + +**Redesigned implementation: PROVED for Rust 1.70** for soundness and both documented behaviors, relative only to the cited Rust 1.70 Reference/std axioms. For the literal open-ended range “1.70+”, the same result is **PROVED conditional on `COMPAT-1`**: every later supported stable Rust release preserves the cited `char` validity, widening-cast, `char::from_u32`, and `panic!` propositions. That compatibility proposition is not established by the inspected versioned documentation and must remain an explicit TCB premise; without accepting it (or imposing an audit cutoff and checking each release), the formal open-ended verdict is **UNPROVED**, not a reason to change the design or MSRV. + +Re-audit if either cfg predicate/signature/body/documentation changes, a new API or generator is added, the support set changes, or a consumed Rust contract changes materially. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r060.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r060.md new file mode 100644 index 0000000000..4388346776 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r060.md @@ -0,0 +1,59 @@ +# Focused review: `decode` + +## Scope and claim + +Artifact: the supplied 15-line `lib.rs`. The required set is stable Rust 1.70+, every target and pointer width, every ordinary profile, and both independent values of `compact`. Both exported functions are safe, so soundness must hold for every well-typed input without a hidden caller obligation. In scope behavior is exactly the two rustdoc statements and the configuration-specific signatures. No target was executed. + +## Current artifact + +**Soundness verdict: UNSOUND** for the full published set, under Rust 1.70 abstract semantics. A single supported counterexample suffices: + +1. Select `compact`, an optimized ordinary build in which debug assertions are off, and call the safe function with `raw = 0xD800`. +2. Rust 1.70 documents that an optimized build does not execute `debug_assert!` unless debug assertions are explicitly enabled [AX-DEBUG]. Thus the check does not dominate the unsafe call in this configuration. +3. The unsigned `u16 as u32` widening preserves the value by zero extension [AX-CAST], so the unchecked input remains `0xD800`. +4. `0xD800` is a surrogate. Rust identifies a surrogate-valued `char` as invalid and classifies producing invalid values as undefined behavior [AX-UB]. `char::from_u32_unchecked` can construct such an invalid value [AX-CHAR]. + +This is reachable by ordinary safe code. The public safe boundary cannot impose an undocumented non-surrogate precondition. The unsafe block also has no `SAFETY` proof; the missing proposition would be “`raw` is not a surrogate in every profile,” and the counterexample proves it false. + +**Documented-behavior verdict: UNPROVED** for the full set. The `compact` contract requires a surrogate to panic, but the case above reaches undefined behavior instead, so no defined panic postcondition follows. No separate UB-free `CONTRACT-BROKEN` case was found. + +Configuration disposition: + +- `compact = false`: `decode(u32) -> Option` directly uses checked `char::from_u32`; it is sound and has the documented `Some`/`None` behavior [AX-CHAR]. +- `compact = true`, non-surrogate input: the current unchecked conversion receives a valid scalar. +- `compact = true`, surrogate input, debug assertions on: the assertion panics before conversion. +- `compact = true`, surrogate input, debug assertions off: **UNSOUND**, on every target and pointer width. Other profile and target axes cannot repair the missing check. + +## Recommended redesign + +Replace only the `compact` body; keep its attributes, documentation, and signature unchanged: + +```rust +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + char::from_u32(raw as u32) + .expect("a non-surrogate u16 should be a Unicode scalar value") +} +``` + +Leave the non-`compact` definition unchanged. This is the smallest non-dominated design: it removes the unsafe operation rather than attempting to prove a profile-dependent guard. + +Conditional proof plan for the changed snapshot: + +- Widening preserves `raw` [AX-CAST]. Every `u16` is at most `0xFFFF`; hence the exhaustive cases are the surrogate interval `0xD800..=0xDFFF` and values that are valid Unicode scalar values [AX-UB]. +- In the non-surrogate case, `from_u32` returns `Some` of the represented scalar, and `expect` returns that value [AX-CHAR, AX-EXPECT]. +- In the surrogate case, `from_u32` returns `None`, and `expect` panics [AX-CHAR, AX-EXPECT]. This preserves the documented behavior; the panic message was not part of the contract. +- There is no unsafe block or invariant-bearing representation. The argument is independent of optimization, debug assertions, pointer width, target layout, and panic strategy. The feature partition is total: the redesigned branch covers `compact`; the unchanged safe branch covers `not(compact)`. +- The APIs used are present with these signatures in Rust 1.70, so the MSRV and both public signatures remain unchanged. + +Per the design-review rule, this candidate has no post-change `PROVED` verdict until implemented and the resulting source snapshot is reviewed. + +## Authoritative premises and TCB + +- **AX-UB (Rust Reference 1.70):** the UB list includes “Producing an invalid value”; for `char`, it names a surrogate-valued value as invalid. [Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html) +- **AX-DEBUG (std 1.70):** “An optimized build will not execute `debug_assert!` statements unless `-C debug-assertions` is passed.” [debug_assert!](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html) +- **AX-CHAR (std 1.70):** checked conversion “will return `None` if the input is not a valid value for a `char`”; the unchecked function “may construct invalid `char` values.” [char](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32) +- **AX-EXPECT (std 1.70):** `expect` returns the contained `Some` value and “Panics if the value is a `None`.” [Option::expect](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect) +- **AX-CAST (Rust Reference 1.70):** widening an unsigned integer zero-extends it. [Numeric casts](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast) + +The current `UNSOUND` verdict needs only the exact 1.70 axioms because Rust 1.70 is itself supported. For the candidate’s open-ended Rust 1.70+ behavior proof, either re-check the corresponding versioned contracts for each later release or explicitly accept **TCB-COMPAT**: later supported stable releases preserve the candidate-relevant consumed semantic propositions. Relevant contract changes or support-policy changes trigger re-review. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r061.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r061.md new file mode 100644 index 0000000000..61b192a0f6 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r061.md @@ -0,0 +1,37 @@ +# Focused review: `Piece for Tail` and `increment_tail` + +## Decision and exact verdicts + +**REJECT the snapshot for vendoring.** The requested combined claim is not acceptable. + +- **Contract compliance — `CONTRACT-BROKEN`.** This applies to the requested Rust 1.70+ domain because the domain contains a Rust 1.70.0 configuration with a valid, UB-free witness that falsifies mandatory documented guarantees. +- **Source soundness at Rust 1.70.0 — `PROVED`** for `Tail::project` on every call satisfying its stated precondition, and for every well-typed safe call to `increment_tail`, on all Rust targets and ordinary profiles, relative only to the Rust 1.70 Reference/core axioms cited below. +- **Source soundness over the entire open-ended Rust 1.70+ range — `UNPROVED`, not `UNSOUND`.** The implementation proof is parametric, but the request permits no additional TCB premise that Rust 1.70's documented propositions persist in every later release. Exact later-version premises were not established for every member of that unbounded range. Thus no full-range `PROVED` verdict is available; no UB witness was found. + +Audit cutoff: 2026-07-31. Snapshot: the complete supplied `REQUEST.md` and `lib.rs`; no dependencies, `cfg`, generated code, FFI, concurrency, allocators, target features, or profile-sensitive operations occur. No target was built, expanded, tested, or executed. + +## Contract-breaking finding + +`Pair` declares exactly one direct field, tuple field `.0`, whose type is `[u32; 2]`. It has no field named `tail` and no direct field of type `u32`. Nevertheless, the impl sets `Owner = Pair`, `Item = u32`, and `FIELD = "tail"`. This falsifies the associated constant's literal guarantee: “The name of a direct declared field of `Owner` whose type is `Item`.” + +The method guarantee is independently false. `project` returns `addr_of_mut!((*owner).0[1])`, a pointer to an **array element nested inside** field `.0`, not a pointer to a direct declared field of `Pair`, much less the nonexistent field designated by `FIELD`. + +A UB-free witness is: construct `Pair([0, 0])`, retain its unique mutable borrow, and call `Tail::project` with the resulting `*mut Pair`. This satisfies the sole caller safety precondition. Index `1` exists in `[u32; 2]`; the call returns normally, but its result points to `.0[1]`. Merely observing `Tail::FIELD` also exposes the false constant guarantee. These are whole-execution postcondition refutations, not observations preceding UB. Accordingly both guarantees, and therefore the `unsafe impl` assertion, are `CONTRACT-BROKEN` rather than merely undocumented or `UNPROVED`. + +## Obligation ledger and reconstructed soundness proof + +| Site | Required proposition | Result | +|---|---|---| +| `Tail::project` call contract | `owner` identifies a live, uniquely borrowed `Pair` for the call | Satisfied by the reborrow/coercion of `pair: &mut Pair` | +| `addr_of_mut!((*owner).0[1])` | place computation is permitted and returns a raw pointer without an intermediate reference | PROVED at 1.70.0: the owner is live; `.0` is its aligned field; `[1]` is in bounds | +| `&mut *Tail::project(pair)` | returned pointer is live, aligned, non-null, initialized, and exclusive for the produced reference | PROVED at 1.70.0 from the exact impl: it designates the valid `u32` at `.0[1]`; the parent `&mut Pair` is unique, no intervening call occurs, and `pair` is not used while `value` lives | +| read, `wrapping_add(1)`, write | operation is defined in every ordinary profile | PROVED at 1.70.0; modular addition cannot overflow or panic | +| `FIELD` and `project` provider guarantees | designate and return the promised direct `Pair` field of type `u32` | **CONTRACT-BROKEN** | + +The Rust 1.70 Reference says function arguments are coercion sites and permits `&mut T` to `*mut T` ([coercions](https://doc.rust-lang.org/1.70.0/reference/type-coercions.html#coercion-types)). It states that an unborrowed mutable reference is the only access path to its value and that raw-pointer dereference may convert it to a reference by `&mut *` ([pointer types](https://doc.rust-lang.org/1.70.0/reference/types/pointer.html#raw-pointers-const-and-mut)). Default-representation fields are “properly aligned,” while `[T; N]` has `T`'s alignment and element `n` is at `n * size_of::()` ([layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-default-representation), [array layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). Arrays are fixed-size, initialized sequences and safe indexing is bounds-checked ([arrays](https://doc.rust-lang.org/1.70.0/reference/types/array.html)); index `1` is statically within length `2`. `addr_of_mut!` creates the raw pointer “without creating an intermediate reference,” while its place must obey ordinary rules ([macro](https://doc.rust-lang.org/1.70.0/core/ptr/macro.addr_of_mut.html)). `u32::wrapping_add` is documented as modular addition wrapping at the type boundary ([method](https://doc.rust-lang.org/1.70.0/core/primitive.u32.html#method.wrapping_add)). Together with the local facts, these discharge every unsafe operation at 1.70.0 for all targets; no layout offset or target-specific alignment value is assumed. + +`increment_tail` has no Rustdoc postcondition. Its name is not a normative contract. Its actual normal-return effect at the proved version is `pair.0[1] = old_pair_0_1.wrapping_add(1)`; this does not cure the unsafe trait's contradictory direct-field guarantees. + +## Trust, proof artifacts, and residual scope + +TCB: only the exact Rust 1.70 Reference/core propositions above; no dependency, implementation, deployment, compatibility, or other additional assumption. There is no tool-derived evidence. Both unsafe blocks lack adjacent `SAFETY` proofs; the material missing derivation is supplied above, so this is proof-documentation debt separate from implementation soundness. No changes are proposed because the acceptance request forbids them. Later Rust releases require their own applicable axioms (or an authorized compatibility premise) before extending the regional soundness proof, but that cannot change the present acceptance decision: the literal contract already has an in-scope UB-free refutation. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r062.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r062.md new file mode 100644 index 0000000000..7140a780ca --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r062.md @@ -0,0 +1,47 @@ +# Focused source review: `m2q8` + +## Claim and verdicts + +Audited snapshot: `REQUEST.md` (SHA-256 `9d559df45c2ef498f44516fed36ef84c82fa4a02a59edca55d2ff88a7b93eb02`) and `lib.rs` (SHA-256 `42388bb1a03741a7725c9836cc134c205af3061efef7b8ee00ce38d2dc51aeeb`). The scope is the sole public safe API, `pub fn classify(u8) -> u8`, its sole unsafe operation, and both documented behaviors, under Rust/core 1.80.0 on all targets and ordinary profiles. This is a source-level result; no binary/backend claim is made. + +- **Soundness: UNSOUND**, relative to `TCB-m2q8-r1`. The valid safe call `classify(0)` reaches undefined behavior. +- **Documented behavior: CONTRACT-BROKEN**, relative to the same TCB. The defined execution `classify(1)` returns `2`, contradicting the promise that a normal return equals `input`. The `input == 0` panic guarantee is also not upheld: that path reaches UB, so Rust supplies no guaranteed panic outcome. +- Consequently, the combined safe-API claim is not `PROVED`. + +## Boundary and obligation coverage + +There are no fields, constructors, traits, callbacks, generated APIs, or invariant-bearing state. `classify` is safe, so every `u8` is admitted without a caller-side safety obligation. The complete ledger is: + +| ID | Required proposition | Evidence and status | +|---|---|---| +| S1 | Every well-typed safe call is UB-free. | **UNSOUND**: `classify(0)` is a counterexample. | +| U1 (`lib.rs:8`) | The call site of `unreachable_unchecked` is unreachable. | **False**: matching `input == 0` is exactly what reaches this arm. No check or invariant excludes it. | +| B1 (`lib.rs:3`) | `input == 0` causes a panic. | **Not upheld / subsumed by S1**: the path performs an operation whose contract classifies reaching it as UB, rather than establishing a defined panic. | +| B2 (`lib.rs:5`) | Every normal return equals `input`. | **CONTRACT-BROKEN**: input `1` normally returns literal `2`. Inputs `2..=255` do satisfy the clause via `_ => input`; the cases are exhaustive. | + +## Findings and derivations + +### F1 — reachable `unreachable_unchecked` (`UNSOUND`) + +Rust 1.80.0 declares `core::hint::unreachable_unchecked` as an unsafe function and states: “Reaching this function is *Undefined Behavior*.” ([exact 1.80.0 standard-library contract](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety)). The exact-version Reference further states that an `unsafe` block does not relax the requirement to avoid UB and defines unsafe code as unsound when safe code can trigger UB ([Rust 1.80.0 Reference](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)). + +For the well-typed safe input `0u8`, the literal `0` match arm is selected and the unsafe call is reached. Thus the callee's required proposition is false and UB follows directly from AXIOM-1. The unsafe block has no adjacent `SAFETY` proof, but this is not merely missing documentation: no truthful proof exists for the current control flow. This counterexample applies without caller misuse. + +### F2 — false normal-return guarantee (`CONTRACT-BROKEN`) + +For `1u8`, the `1 => 2` arm is selected and returns normally. The result is `2`, whereas the documented result is the input, `1`. This path does not execute the unsafe arm, so it is an independent, defined-behavior contract counterexample. For input zero, F1 prevents any source-level guarantee that a panic occurs; a particular compiler/profile happening to trap or panic would not repair the abstract source contract. + +## Configuration closure and evidence + +The source contains no `cfg`, features, target-dependent operations, profile-dependent checks, macros generating API/code, dependencies, FFI, concurrency, allocation, or build inputs. The exhaustive input partition `0`, `1`, and `2..=255` has identical source control flow on every Rust 1.80.0 target and ordinary profile. The standard-library safety contract is likewise the Rust 1.80.0 contract, so F1 and F2 cover the entire requested configuration set parametrically. No target was built, tested, executed, or macro-expanded. + +`TCB-m2q8-r1` contains only: + +- **AXIOM-1:** the quoted Rust 1.80.0 `core::hint::unreachable_unchecked` safety contract, for every in-scope target/profile; +- **AXIOM-2:** the linked Rust 1.80.0 Reference rule that unsafe code remains responsible for preventing safe clients from triggering UB. + +There are no dependency, tool-derived, external-specification, deployment, or compatibility assumptions. Compiler/backend correctness and behavior after UB are excluded. + +## Minimum resolution and review triggers + +No source change was requested or made. A sound implementation must use a defined panicking path for input zero; satisfying the current return contract also requires returning `1` for input one. Merely adding a `SAFETY` comment cannot resolve F1. Re-audit after any source or contract change, Rust-version change, or expansion of the configuration surface. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r063.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r063.md new file mode 100644 index 0000000000..6d81b2ea15 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r063.md @@ -0,0 +1,109 @@ +# Implemented-candidate unsafe Rust review + +## Claim and verdict + +**UNSOUND** for the requested aggregate claim: the exact `lib.rs` supplied in +`targets/p1j4`, used only through well-typed safe code, is not sound over the +requested Rust 1.70+ / all-target / ordinary-profile domain. The safe client +below can create conflicting references. Establishing the counterexample for +Rust 1.70, which is inside that universal domain, is sufficient to refute the +claim; no premise that projects Rust 1.70 documentation onto later releases is +used. + +This is a source-level verdict relative to the Rust abstract semantics, not a +claim about a particular compiler binary. There are no unsafe public APIs or +documented unsafe-API postconditions to grade separately. + +## Snapshot, boundary, and coverage + +Reviewed from scratch: `REQUEST.md`, `DESIGN-NOTE.md`, and the complete +23-line `lib.rs`. The prior conditional design approval is not a premise. +`lib.rs` has no dependencies beyond `core`, conditional compilation, generated +code, macros, FFI, assembly, allocator use, concurrency, or target-specific +operations. Thus its source and API surface are identical across the requested +targets and ordinary profiles. No code was built, executed, tested, or +expanded. + +The complete language-reachable surface is: + +- `View<'a, T>` (public type; both fields private): `ptr: *mut T` and + `borrow: PhantomData<&'a mut T>`; +- safe `View::new(&'a mut T) -> View<'a, T>`; +- safe `View::get(&self) -> &'a T` backed by `&*self.ptr`; +- safe `View::get_mut(&mut self) -> &'a mut T` backed by + `&mut *self.ptr`. + +The intended representation invariant is that `ptr` remains derived from the +exclusive input borrow, points to its live, aligned, initialized `T`, and is the +abstraction's sole route to that `T` for `'a`. `new` supplies the pointer and +the phantom borrow carries the relationship, but this invariant alone does not +serialize references already returned to safe callers. + +## Finding F-1: return lifetimes escape receiver borrows + +Both unsafe blocks need the current access to be compatible with every live +reference to the same `T`. That obligation fails because each result has the +struct lifetime `'a`, not the lifetime of the `self` borrow. Rust 1.70's +[lifetime-elision rule](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html#lifetime-elision-in-functions) +assigns the receiver lifetime only to *elided* output lifetimes; these outputs +explicitly name `'a`. Consequently, the borrow of `View` may end after each +call while the returned reference remains usable. + +```rust +fn safe_client() { + let mut value = 0_i32; + let mut view = View::new(&mut value); + let shared = view.get(); + let unique = view.get_mut(); + *unique = 1; + assert_eq!(*shared, 1); +} +``` + +Every operation at the client boundary is safe. `shared` is live across the +write because it is dereferenced afterward. `get_mut` nevertheless reborrows +the same raw pointer as `&mut T`, and the write mutates the bytes observed by +the live `&T`. The Rust 1.70 Reference lists +[breaking pointer-aliasing rules as undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined), +and the Rust 1.70 standard-library contract states that mutating data through +an alias of `&T` [“is considered undefined behavior”](https://doc.rust-lang.org/1.70.0/std/cell/struct.UnsafeCell.html#aliasing-rules). +Therefore a valid safe use reaches UB. Repeated calls to `get_mut` can likewise +return two simultaneously usable mutable references; the same documentation +requires a released `&mut T` to be unique. + +The adjacent proof artifacts are also deficient: neither unsafe block has a +`SAFETY` comment stating the reference-formation obligations or deriving them +from a named invariant. That documentation defect cannot be repaired for the +current signatures because the required alias-exclusion proposition is false. + +## Obligation disposition and TCB + +- **O-NEW — producer:** pointer/lifetime relationship established locally; + insufficient to protect escaped references. +- **O-GET — consumer:** shared-reference validity and alias-compatible access; + **UNSOUND in composition with `get_mut`**. +- **O-GET-MUT — consumer:** unique, alias-compatible mutable access for the + returned lifetime; **UNSOUND**. +- **O-CONFIG:** no configuration branch exists; the Rust 1.70 counterexample is + an in-scope witness, so the universal requested claim is refuted. + +TCB `R063-v1` contains only the two exact Rust 1.70 authoritative contracts +linked above plus the lifetime-elision contract. There are no dependency, +tool-derived, external, deployment, or probabilistic assumptions. + +## Remediation (unimplemented; not audited) + +Tie each output to its receiver borrow: + +```rust +pub fn get<'s>(&'s self) -> &'s T; +pub fn get_mut<'s>(&'s mut self) -> &'s mut T; +``` + +Prefer storing `&'a mut T` directly and implementing these as safe reborrows; +that removes the raw pointer, `PhantomData`, and both unsafe blocks. Shortening +the effective return lifetimes can break callers that relied on the current +signatures, but preserving those signatures alongside both methods preserves +the defect. This proposal has **no verdict** until implemented and re-audited +as a new snapshot. Any raw-pointer version also needs a documented invariant +and adjacent proofs for lifetime, validity, and aliasing. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r064.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r064.md new file mode 100644 index 0000000000..d25fab5235 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r064.md @@ -0,0 +1,71 @@ +# Unsafe Rust source review: `p1j4` + +## Claim and verdict + +**Soundness: UNSOUND.** The reviewed artifact is exactly `targets/p1j4/lib.rs` as supplied. The claim is freedom from Rust undefined behavior for every well-typed safe use on stable Rust 1.70.0 and later, on every target where this source compiles, in ordinary profiles. The audit cutoff is 2026-07-31. This is a source-level result relative to TCB `R064-1`; it makes no compiler-backend or binary-correctness claim. + +Rust 1.70.0, which is a member of that supported set, admits the safe-client witness below. One bad member refutes the requested universal claim, so no premise about carrying Rust 1.70 documentation forward is needed. The failure is target-, edition-, optimization-, panic-, and profile-independent. + +There are no documented unsafe-API postconditions and no broader robustness property was requested. Accordingly, there is no separate `CONTRACT-BROKEN` verdict; the witness execution contains UB. + +## Boundary, invariant, and obligation coverage + +The complete explicit safe surface is public `View<'a, T>` plus safe methods `new`, `get`, and `get_mut`. Its fields are private to downstream code. Auto traits do not add a concurrent route: Rust 1.70 declares `*mut T` both `!Send` and `!Sync` ([standard-library pointer docs](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#trait-implementations)), so `View` cannot derive either; its other structural traits expose no pointee access. There are no unsafe public APIs, explicit traits/impls, macros, generated code, dependencies, FFI, assembly, allocator operations, or conditional branches. The two obligation sites are the raw-pointer-to-reference operations at lines 16 and 20. + +`I-VIEW` should say: `ptr` denotes the `T` supplied to `new`, remains aligned, non-null, live, and valid for `'a`, and every reference issued from it obeys Rust's aliasing requirements. `new` establishes the pointer-origin portion: the struct field is a coercion site and Rust 1.70 permits “`&mut T` to `*mut T`” ([Reference: coercions](https://doc.rust-lang.org/1.70.0/reference/type-coercions.html#coercion-types)). The `PhantomData<&'a mut T>` carries the originating borrow in the type. But neither accessor preserves the aliasing portion: + +- `get` has effective type `for<'s> fn(&'s View<'a, T>) -> &'a T`. +- `get_mut` has effective type `for<'m> fn(&'m mut View<'a, T>) -> &'a mut T`. + +Thus each receiver borrow can end after its call while the returned reference remains usable for `'a`. The retained `View` can issue another, incompatible reference. + +## F-1: safe accessors permit conflicting live references + +Severity/status: **critical — UNSOUND**, affecting both unsafe sites and all safe clients of `get`/`get_mut`. + +This is a well-typed safe use; no line in the client requires `unsafe`: + +```rust +fn overwrite(shared: &i32, unique: &mut i32) -> i32 { + *unique = 1; + *shared +} + +fn trigger() { + let mut value = 0; + let mut view = View::new(&mut value); + let shared = view.get(); + let unique = view.get_mut(); + let _ = overwrite(shared, unique); +} +``` + +Because `get`'s result is tied to `'a`, not to the shared receiver borrow, `shared` does not keep `view` shared-borrowed. The subsequent safe `get_mut` is therefore admitted and returns an alias to the same `i32`. During `overwrite`, both references are passed as arguments. Rust 1.70 says a passed reference is “live at least as long as that function call” and that “all data reached through a shared reference ... is immutable” absent `UnsafeCell` ([Reference: behavior considered undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)). `i32` contains no `UnsafeCell`; `*unique = 1` mutates the bytes reached through the live `shared`. This violates the cited rule and is UB. The same retained-capability defect also permits repeated `get_mut` calls. + +The smallest false implication is: returning a reference valid for `'a` somehow prevents later access through `View` for the returned reference's live interval. Nothing in either signature enforces that implication. Both unsafe blocks also lack adjacent `SAFETY` proofs, but documentation alone cannot repair this safe-API defect. + +## Proposed repair (not a verdict on this snapshot) + +Tie each result to its receiver borrow: + +```rust +pub fn get(&self) -> &T { + // SAFETY: `new` is the only producer and derives `ptr` from a live, + // aligned, valid `&mut T`. This shared receiver borrow is carried by the + // returned reference, preventing mutable access through `View` meanwhile. + unsafe { &*self.ptr } +} + +pub fn get_mut(&mut self) -> &mut T { + // SAFETY: the same pointer invariant holds. The returned reference carries + // this exclusive receiver borrow, preventing every other access through + // `View` until it is no longer live. + unsafe { &mut *self.ptr } +} +``` + +Rust 1.70's receiver-elision rule assigns the receiver-reference lifetime to elided output lifetimes ([Reference: lifetime elision](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html#lifetime-elision-in-functions)). This removes the retained-capability path while preserving the raw-pointer representation. It weakens the public return-lifetime contract and may reject existing callers, so it is a necessary compatibility break (or requires new consuming accessors if `'a` returns are required). **Status: proposed and unimplemented; not `PROVED`.** Re-audit the implemented replacement and its auto-trait/API effects. + +## Configuration closure, TCB, and triggers + +The counterexample and derivation contain no configuration-sensitive operation; `T = i32` avoids layout, unsizing, drop, and `UnsafeCell` cases. No testing or execution was used. `R064-1` consists only of the exact Rust 1.70 Reference and standard-library propositions cited above; there are no dependency, external-specification, tool, or deployment assumptions. Re-audit on any implementation/signature/privacy change, addition of another field producer, supported-set change, or material change to a consumed authoritative proposition. `DESIGN-NOTE.md` supplied no premise or inherited verdict. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r065.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r065.md new file mode 100644 index 0000000000..be09b14901 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r065.md @@ -0,0 +1,120 @@ +# Unsafe Rust review: `p1j4` + +## Claim and verdict + +**Soundness: UNSOUND.** For `lib.rs` SHA-256 +`ac762e6e2bd87884bbbcfbe7bbf706e5b9dae9b078e82d01252111a9a6ad84e8`, +there is a well-typed, entirely safe use that reaches Rust undefined behavior. +This occurs on Rust 1.70.0, on every target on which this source and `u8` +compile, and is independent of ordinary debug/release profile choices. +Because 1.70.0 belongs to the requested `1.70+` set, this counterexample +decisively refutes the universal claim without any premise about future Rust +compatibility. Rust 1.97.1 documentation independently retains the relevant +uniqueness rule. + +There are no prose-documented postconditions or unsafe caller contracts, so +there is no separate `CONTRACT-BROKEN` result. The returned reference types' +language contracts are violated and that violation is already included in the +`UNSOUND` verdict. The prior design note supplied no premise or inherited +verdict. + +## Boundary, invariant, and obligation coverage + +The complete public surface is the `View<'a, T>` type with private fields and +the safe methods `new`, `get`, and `get_mut`. There are no public unsafe APIs, +explicit trait implementations, macros, conditional compilation, generated +artifacts, dependencies, FFI, assembly, allocation, or concurrency in the +supplied snapshot. Default move/drop behavior does not dereference `ptr`. + +The intended representation invariant (`I-View`) is: `ptr` came from the +exclusive `&'a mut T` accepted by `new`, remains aligned, non-null, live, and +points to a valid `T`, while the `View` carries the exclusive-borrow capability. +`new` establishes the pointer facts, and `PhantomData<&'a mut T>` makes the +type act as though it stores that reference for compiler safety-property +analysis ([Rust 1.70 `PhantomData`](https://doc.rust-lang.org/1.70.0/core/marker/struct.PhantomData.html)). +It does **not** serialize references subsequently returned through the raw +pointer. + +The unsafe operations at `lib.rs:16` and `lib.rs:20` must additionally prove +alias compatibility for the full lifetime of the reference they create. Both +proofs fail: their results use `'a`, not the lifetime of the receiver borrow. +Rust 1.70's elision rule says, “Each elided lifetime in the parameters becomes +a distinct lifetime parameter” +([Reference](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html)). +Thus `get_mut` is effectively +`fn get_mut<'s>(&'s mut self) -> &'a mut T`; `'s` does not constrain the +returned reference. + +This safe counterexample follows directly from that signature: + +```rust +fn touch(a: &mut u8, b: &mut u8) { + *a = 1; + *b = 2; +} + +fn safe_ub() { + let mut value = 0u8; + let mut view = View::new(&mut value); + let first = view.get_mut(); + let second = view.get_mut(); + touch(first, second); +} +``` + +Each receiver reborrow may end after its call because neither result borrows +`view` in the type system. Both results nevertheless point to the same `u8`. +The Rust 1.70 Reference classifies broken pointer aliasing as UB and guarantees +for a passed reference that “it is live at least as long as that function +call” +([Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). +The 1.70 standard-library contract states that there is no legal way to obtain +aliasing `&mut` and that “A `&mut T` must always be unique” +([`UnsafeCell`](https://doc.rust-lang.org/1.70.0/std/cell/struct.UnsafeCell.html)). +Consequently both aliases are live during `touch`, contradicting the exact +precondition consumed by `&mut *self.ptr`. This is a concrete safe-to-UB +derivation, not merely a missing proof. + +`get` has the same lifetime defect: a caller can retain its `&'a T` and then +call `get_mut`, so its raw-pointer dereference also lacks a proof that the +shared reference will not overlap mutation. The current unsafe blocks contain +no `SAFETY` comments; that proof-documentation defect is secondary to the +implementation defect. + +## Configuration closure and TCB + +The supported predicate is the requested exact source on Rust 1.70+, all +compiling targets, and ordinary profiles. The implementation has one +unconditional path; using `T = u8` removes layout, DST, allocator, panic, +atomic, and target-feature branches. Profile and target axes therefore cannot +exclude the counterexample. No build or test evidence was used. + +The complete consumed TCB is the exact Rust 1.70 Reference lifetime-elision +and undefined-behavior passages and the Rust 1.70 standard-library +`PhantomData` and `UnsafeCell` contracts linked above. The matching +[Rust 1.97.1 `UnsafeCell` contract](https://doc.rust-lang.org/1.97.1/std/cell/struct.UnsafeCell.html) +is corroborative only. No dependency, compiler-backend, platform, tool, or +deployment assumption is consumed. Changes to the source, method contracts, +supported minimum Rust, or these authoritative contracts require re-review. + +## Unimplemented repair proposal + +Tie both outputs to their receiver borrows: + +```rust +pub fn get<'s>(&'s self) -> &'s T { + unsafe { &*self.ptr } +} + +pub fn get_mut<'s>(&'s mut self) -> &'s mut T { + unsafe { &mut *self.ptr } +} +``` + +This makes a live shared result keep `View` shared-borrowed and a live mutable +result keep it exclusively borrowed, preventing every conflicting method call. +Add adjacent `SAFETY` proofs expanding `I-View`: `new` supplies validity, +alignment, provenance, and lifetime; the receiver borrow plus all prior +method-derived references being tied to it supplies shared compatibility or +uniqueness. This candidate is **unimplemented and unaudited**; it has no +`PROVED` status until the changed snapshot and comments receive a fresh audit. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r066.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r066.md new file mode 100644 index 0000000000..5d65941957 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r066.md @@ -0,0 +1,100 @@ +# Version-partitioned source review + +## Claim and verdict + +**Snapshot.** Exact `lib.rs` SHA-256 +`e561568dfc6262bc6312f1a346e970dfab1f0d8f5102984e80e3b06cf2885e9b`. +Scope is the sole public API, `pub fn advance_marker() -> *const [u8; 0]`, and +its implementation at lines 3–5. No generated code, dependencies, or prior +audit result is in scope. + +**Theorem.** For every well-typed safe call, under each requested Rust/core +version, target, and ordinary profile, execution of this function is free of +Rust undefined behavior under that version's documented abstract semantics, +relative to `R066-TCB-1` below. There is no caller-side safety precondition. + +| Region | Soundness verdict | +|---|---| +| Rust 1.79.0, every target and ordinary profile | **UNSOUND** | +| Rust 1.80.0, every target and ordinary profile | **PROVED**, relative to `R066-TCB-1` | +| Combined supported set | **UNSOUND**: the supported union contains the Rust 1.79.0 region. | + +There are no documented API postconditions, so there is no separate +`CONTRACT-BROKEN` finding. + +## Boundary and obligation ledger + +The complete safe surface is the zero-argument free function. Its only unsafe +obligation site is line 4, `null::<[u8; 0]>().add(1)`. There are no fields, +constructors, traits, callbacks, macros, mutable state, or invariants. Returning +a raw pointer does not dereference it. + +**O-SIZE — computed byte offset (both versions): PROVED.** The versioned +`size_of` contract says `[T; n]` has size `n * size_of::()`; therefore +`size_of::<[u8; 0]>() == 0` on every target. `add(1)` consequently computes the +mathematical byte offset `1 * 0 == 0`. See +[Rust 1.79.0 `size_of`](https://doc.rust-lang.org/1.79.0/std/mem/fn.size_of.html) +and [Rust 1.80.0 `size_of`](https://doc.rust-lang.org/1.80.0/std/mem/fn.size_of.html). + +**O-179 — Rust 1.79.0 allocation relation: VIOLATED.** `ptr::null` creates a +null pointer with address zero +([1.79.0 contract](https://doc.rust-lang.org/1.79.0/std/ptr/fn.null.html)); the +same version states that a null pointer is never valid, even for a zero-sized +access +([pointer safety](https://doc.rust-lang.org/1.79.0/std/ptr/index.html#safety)). +Nevertheless, the 1.79.0 `add` contract requires both the starting and result +pointer to be in bounds or one byte past the end of the same allocated object, +and says violation is undefined behavior +([`pointer::add`](https://doc.rust-lang.org/1.79.0/std/primitive.pointer.html#method.add)). +The null starting pointer supplies no such allocated object; zero byte distance +does not waive this 1.79.0 clause. Thus every safe call executes UB at line 4. +This is a valid counterexample requiring no caller input or later dereference. + +**O-180 — Rust 1.80.0 `add` preconditions and result: PROVED.** The 1.80.0 +contract newly conditions the same-allocation requirement on a nonzero byte +offset and expressly says a zero offset makes the function “always +well-defined” +([`pointer::add`](https://doc.rust-lang.org/1.80.0/std/primitive.pointer.html#method.add)). +O-SIZE establishes that case. Independently, zero fits in `isize`, and adding +zero to address zero neither overflows nor wraps `usize`, discharging the other +listed arithmetic clauses. The result is returned without access or +dereference, so there is no further unsafe consumer or terminal obligation. + +## Configuration closure + +`Supported(c)` is exactly: Rust/core is 1.79.0 or 1.80.0; the target is any +target supported by that toolchain; and the build uses any ordinary profile. +The two Rust-version rows are an exhaustive partition. The source has no +`cfg`, generated code, target-dependent operation, assertion, allocation, +panic path, or profile-dependent branch. O-SIZE is target-parametric because +the zero array length annihilates the element size; both regional pointer +contracts and the fixed arithmetic are profile-independent. Hence each +regional result covers every requested target/profile combination abstractly, +without sampling. + +## TCB and evidence (`R066-TCB-1`) + +Accepted `AXIOM` entries are only the exact Rust 1.79.0 and 1.80.0 +standard-library contracts linked above: `size_of`'s array-size formula, +`ptr::null`'s null/address-zero result, the 1.79.0 pointer validity statement, +and each version's `pointer::add` safety contract. Their consumers are O-SIZE, +O-179, and O-180 for the matching versions and all targets/profiles. There are +no dependency, external, deployment, compatibility, or tool-derived premises. +No build, test, interpreter, or macro expansion was used. Compiler/backend +correctness and binary/platform behavior are outside this source-level claim. + +## Findings and resolution + +1. **Soundness defect:** the safe API unconditionally executes UB on Rust + 1.79.0. To retain both versions, remove the unsafe operation (for example, + return the already-computed `null()` pointer directly) or otherwise use an + implementation whose 1.79.0 preconditions are proved. Alternatively, + enforce and document Rust 1.80.0 as the minimum supported version. +2. **Proof-documentation defect:** line 4 has no adjacent `SAFETY` proof. The + reconstructed O-180 derivation proves the 1.80.0 region, but no comment can + prove the current operation across both supported versions. After resolving + O-179, document every remaining unsafe operation's version-applicable + derivation; removing unsafe code needs no `SAFETY` comment. + +Re-audit on any source change, supported-version/target/profile expansion, or +material change to a consumed versioned standard-library contract. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r067.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r067.md new file mode 100644 index 0000000000..a450935d16 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r067.md @@ -0,0 +1,102 @@ +# Focused unsafe-Rust review: `View` + +## Claim and verdict + +**UNSOUND** for the requested combined claim: the exact `lib.rs` snapshot in +`targets/p1j4`, under all well-typed safe uses on stable Rust 1.70 and later, +all targets on which it is accepted, and ordinary profiles. A well-typed safe +use admitted by Rust 1.70 reaches undefined behavior, so the universal claim is +false; no premise about later-version compatibility is needed. This is a +source-level result under Rust's documented abstract semantics, not a binary or +compiler-correctness claim. + +The review is fresh. `DESIGN-NOTE.md` supplies history only and no proof premise. +There are no dependencies, `cfg` branches, generated code, macros, FFI, custom +traits, or profile-dependent operations in the supplied snapshot. + +## Boundary and obligation inventory + +The complete language-reachable surface is the public opaque `View<'a, T>` and +safe methods `new`, `get`, and `get_mut`; both fields are private. `new` is the +sole representation producer. It records a raw pointer derived from +`&'a mut T`, while `PhantomData<&'a mut T>` is intended to carry the borrow. +`get` and `get_mut` are the only pointer consumers, at `lib.rs:16` and +`lib.rs:20` respectively. + +Both consumers require the pointed-to `T` to remain live, aligned, valid, and +accessible with aliasing compatible with the reference being created. The +constructor/private-field story can carry the first three facts. It does **not** +carry exclusivity across references already returned by safe methods. The +necessary invariant—“a returned reference cannot outlive the borrow of this +`View` that authorized it”—is false under the published signatures. Neither +unsafe block has an adjacent safety proof; more importantly, no correct proof +of its aliasing obligation exists for this implementation. + +## Safe UB witness and derivation + +```rust +fn conflict(shared: &u8, unique: &mut u8) -> u8 { + *unique = 1; + *shared +} + +fn trigger() { + let mut value = 0u8; + let mut view = View::new(&mut value); + let shared = view.get(); + let unique = view.get_mut(); + let _ = conflict(shared, unique); +} +``` + +This uses no `unsafe`. `get` returns the explicit lifetime `'a`, not the +receiver-borrow lifetime; `get_mut` does the same. Rust 1.70's +[lifetime-elision rules](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html#lifetime-elision-in-functions) +assign the receiver lifetime only to *elided* output lifetimes. Thus the borrow +of `view` used by either call need not last as long as its result, and the +second call is admitted while `shared` remains usable. Both results are formed +from the unchanged `ptr`, so they designate the same `u8`. + +During `conflict`, both arguments are live: the Rust 1.70 +[undefined-behavior rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html) +state that a reference passed to a function is live for at least that call. +They also classify data reached through a shared reference as immutable (apart +from `UnsafeCell`, which `u8` is not). The write through `unique` therefore +mutates data reached through the live `shared` reference and is undefined +behavior. The same page defines unsafe code as unsound when a safe client can +trigger UB. The Rust 1.70 +[borrow-operator contract](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#borrow-operators) +independently states the same shared-borrow prohibition. + +This proof is parametric over target and ordinary profile: it uses no layout, +arithmetic, panic, optimization, or target-specific premise. One Rust 1.70 +member already refutes the requested Rust-1.70+ universal theorem. + +## Required repair (proposal only) + +At minimum, tie each returned reference to the receiver borrow: + +```rust +pub fn get(&self) -> &T { unsafe { &*self.ptr } } +pub fn get_mut(&mut self) -> &mut T { unsafe { &mut *self.ptr } } +``` + +Under the cited elision rule, a live result then keeps the corresponding borrow +of `View` active, preventing a conflicting call. If an `'a`-long mutable result +is required, expose a consuming operation such as `into_mut(self) -> &'a mut T` +instead of allowing reuse of the capability. An even smaller unsafe surface is +to store `&'a mut T` directly and implement the accessors by safe reborrowing. + +These are unimplemented candidates and receive **no verdict**. The chosen +repair needs a fresh audit, including adjacent proofs for any retained raw +dereference. Shortening accessor result lifetimes can reject existing callers +and is therefore an API-compatibility change. + +## TCB, evidence, and residual scope + +TCB: only the exact linked Rust 1.70 Reference propositions. No additional +assumptions, dependencies, tests, compilation, interpreter, or analyzer results +were consumed. Broader safe-API behavior was not requested, and no separate +documented unsafe-API postconditions exist. Changes to source, signatures, +visibility, supported Rust range, or the cited semantic contracts require +re-review. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r068.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r068.md new file mode 100644 index 0000000000..7e6232cf9f --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r068.md @@ -0,0 +1,101 @@ +# Focused unsafe-Rust review and redesign + +## Claim, snapshot, and verdict + +Reviewed `targets/f8w1/lib.rs` (SHA-256 +`23f55cd3e96c8372d71a09336d84f72de191fd5f48de693d0be11762537dfa9f`) +and its request only. The in-scope safe surfaces are the public `Ticket` type +and `pub fn ticket(usize) -> Ticket`. The requested theorem covers every +well-typed safe call on Rust 1.70+, every target, and every ordinary build +profile (in particular, configurations with debug assertions enabled and +disabled), with no dependency or deployment assumptions. It requires freedom +from Rust undefined behavior, return of a ticket containing every nonzero +`id`, and a panic for `id == 0`. + +**Current-artifact soundness: UNSOUND.** A valid safe call, `ticket(0)`, reaches +undefined behavior in an ordinary optimized configuration with debug +assertions disabled. This Rust 1.70 counterexample is inside the supported set +and therefore refutes the universal Rust 1.70+ claim. The documented zero-input +behavior is not established in that configuration: execution reaches undefined +behavior before any guaranteed panic. I do not assign a separate +`CONTRACT-BROKEN` verdict because no well-defined non-panicking execution was +established; the behavioral claim is unproved as a consequence of the +soundness failure. + +## Boundary, invariant, and obligation proof + +`Ticket`'s tuple field has no visibility modifier and is therefore private; +Rust 1.70 states, subject to its two listed exceptions, “By default, everything +is private” ([Reference: Visibility and Privacy](https://doc.rust-lang.org/1.70.0/reference/visibility-and-privacy.html)). +The relevant representation invariant is +`TICKET-NZ`: whenever a `Ticket` exists in a defined execution, its field is a +valid `NonZeroUsize` containing the ticket's identifier. The only inspected +producer is `ticket`; moving, borrowing, and dropping the value do not mutate +the private field. There is no caller-side unsafe contract, so the safe +function must discharge the unchecked constructor's requirement for every +`usize`. + +At `lib.rs:9`, `debug_assert!(id != 0)` does not establish that requirement in +all supported profiles. Rust 1.70 documents that an optimized build +“will not execute `debug_assert!` statements unless `-C debug-assertions` is +passed” ([`debug_assert!` documentation](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html)). +Thus, with debug assertions disabled and `id == 0`, control reaches +`lib.rs:10`. The exact Rust 1.70 contract for +[`NonZeroUsize::new_unchecked`](https://doc.rust-lang.org/1.70.0/std/num/struct.NonZeroUsize.html#method.new_unchecked) +says “The value must not be zero” and specifies undefined behavior for zero. +The local fact is the opposite (`id == 0`), so the required implication fails. +This reasoning is independent of target layout. With assertions enabled, zero +panics before the unsafe call; for nonzero inputs, the call's stated +precondition holds. Those partial cases do not repair the uncovered supported +case. The unsafe block also has no adjacent `SAFETY` proof, but documentation +alone could not fix the false premise. + +## Recommended redesign + +Keep `Ticket`, the exact safe function signature, and its documentation, and +replace only the function body with: + +```rust +pub fn ticket(id: usize) -> Ticket { + Ticket(NonZeroUsize::new(id).expect("id should be non-zero")) +} +``` + +This is the smallest proof surface because it removes the unsafe operation and +the configuration-sensitive assertion. Rust 1.70's safe +[`NonZeroUsize::new`](https://doc.rust-lang.org/1.70.0/std/num/struct.NonZeroUsize.html#method.new) +“creates a non-zero if the given value is not zero.” Rust 1.70's +[`Option::expect`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect) +returns the contained `Some` value and “Panics if the value is a `None`.” +Consequently: + +- for nonzero `id`, `new` supplies a `NonZeroUsize` containing `id`, `expect` + returns it, and wrapping it establishes `TICKET-NZ` and the documented return; +- for zero, construction cannot supply a nonzero value and the `None` path + panics, satisfying the documented outcome; and +- no `cfg`, target property, optimization setting, overflow behavior, or debug + assertion selects another path. Panic strategy may determine unwind versus + abort, but `expect`'s documented panic is invoked in either ordinary profile. + +The proposal changes neither public signature nor representation and the +current documentation promises no particular panic message. It is a design, +not an audited new artifact; after implementation, audit that exact snapshot. + +## Configuration coverage, TCB, and residual scope + +The current counterexample consumes only the cited Rust 1.70 `debug_assert!` +and `new_unchecked` standard-library axioms; the boundary inventory also uses +the cited Rust 1.70 privacy rule. No dependency, tool, compiler-backend, +environment, or deployment premise is admitted. The redesign additionally +consumes the cited Rust 1.70 `new` and `expect` contracts. These citations +directly establish the MSRV case. For later releases, the proof is parametric on their applicable +standard-library contracts retaining those exact propositions; Rust 1.70 text +alone is not silently extrapolated to an unbounded future range. A materially +changed contract requires version-specific verification or an explicit +compatibility premise. + +No target was built, tested, or macro-expanded. Generated code, dependencies, +FFI, concurrency, allocators, and other crate code do not exist in the supplied +target and are outside this focused review. Re-audit after implementing the +proposal, changing the Rust support range or either consumed standard-library +contract, exposing the field, or adding another constructor or mutator. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r069.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r069.md new file mode 100644 index 0000000000..be2533a61f --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r069.md @@ -0,0 +1,51 @@ +# Focused `Buffer` source review + +## Claim and verdict + +**UNSOUND** for the exact `lib.rs` snapshot SHA-256 `368fa01b2f3fca39e89131414f8ec2641a4ac162b846ccf77b8f8bd293930d9a`, under Rust 1.80.0 abstract semantics, on every target and ordinary profile for which this source is compiled, relative only to TCB `TCB-r069-v1` below. The refuted theorem is: every well-typed entirely-safe use of the current `Buffer` API is free of Rust undefined behavior. No separate documented-postcondition failure was found. + +A wholly safe counterexample is: + +```rust +let mut buffer = Buffer::from_static(); +buffer.overwrite(9); +``` + +The constructor alone need not exhibit UB; the call to `overwrite` does. This is a source-level result, not a claim about a backend, writable data segment, or observed execution. + +## Boundary and invariant inventory + +The complete supplied artifact has one private immutable static and these `Buffer` surfaces: + +- `from_writable` (lines 11–18) is the sole unsafe producer. Its ongoing caller contract requires `ptr` to remain aligned and valid for a one-byte write, without conflicting access, whenever the returned buffer may be used. It establishes `shared == None` and stores that pointer. +- `from_static` (lines 20–26) is the sole safe producer. It stores a mutable raw pointer obtained from `&BYTE` and also stores that same shared reference in `Some`. +- `overwrite` (lines 28–40) is the sole explicit consumer and the only unsafe-operation site. `with_live` (lines 43–46) invokes its closure while holding the supplied shared reference. +- Both fields are private. There are no other constructors, trait impls, macros, generated items, callbacks, or destruction logic in scope. Moving or dropping these fields performs no raw-pointer access. The raw-pointer field prevents safe cross-thread transfer/sharing in Rust 1.80; concurrency supplies no escape from the finding. + +The necessary representation invariant would be: at each `ptr.write`, `ptr` is aligned and valid for a one-`u8` write, including the absence of a conflicting live shared reference. `from_writable` transfers exactly this ongoing obligation to its unsafe caller. `from_static` does not establish it. + +## Derivation and obligation ledger + +**O1 — `from_writable`/`shared == None`: PROVED, conditional on its documented unsafe-caller obligations.** The `else` branch can arise only from the current `from_writable` producer. Its contract supplies write validity and alignment at the call; hence it satisfies `*mut u8::write`. The existing local comment is nevertheless incomplete proof documentation: it does not state the producer partition (`None` implies `from_writable`) or discharge alignment and conflicting-access aspects. A compact adequate proof would be: “`shared == None` is produced only by `from_writable`; its ongoing contract gives this stored pointer validity for one `u8` write, proper alignment, and no conflicting access at this call, satisfying `ptr::write`.” + +**O2 — `from_static`/`shared == Some`: UNSOUND.** The derivation is: + +1. `shared = &BYTE` points to `BYTE`'s precise static location. The reference-to-pointer cast produces a pointer to that same memory, and the following sized raw-pointer-to-pointer cast returns the pointer unchanged; `ptr` therefore addresses the byte referenced by `shared`. +2. `overwrite` copies that reference from `Some` and passes it to `with_live`. Rust 1.80 says a reference passed to a function is live at least for the duration of that function call. The closure and `self.ptr.write(value)` execute inside that call. This conclusion does not depend on whether `let _ = shared` itself counts as a later use. +3. Rust 1.80 says bytes pointed to by a shared reference are immutable while relevantly live, and defines a mutation as any overlapping write of more than zero bytes, even if it preserves the value. `write::` overwrites one byte at exactly that location. The write therefore mutates an immutable byte and is UB. + +The `Some`-branch safety comment is false: this value was produced by `from_static`, not `from_writable`, and the required write-validity fact does not hold. No hidden unsafe caller or deployment precondition can repair a safe constructor/method pair. + +## Configuration closure, TCB, and residual scope + +There is no `cfg`, target feature, profile-sensitive assertion/arithmetic, dependency, FFI, macro, build input, or generated source in the supplied snapshot. The same source path and Rust-language rules apply parametrically to all requested targets and ordinary profiles; optimization and physical static placement are irrelevant. + +`TCB-r069-v1` consists only of these exact Rust 1.80.0 axioms: + +- [`ptr::write`](https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html#safety): it overwrites the pointed-to location; UB results unless the destination is valid for writes and aligned. +- [`std::ptr` safety](https://doc.rust-lang.org/1.80.0/std/ptr/index.html#safety): a pointer cast from a reference concerns access to the same memory and is valid only subject to the documented access restrictions. +- [Reference: undefined behavior](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html#behavior-considered-undefined): passed-reference liveness spans the call; shared-reference-pointed bytes are immutable; any nonzero overlapping write is a mutation. +- [Reference: static items](https://doc.rust-lang.org/1.80.0/reference/items/static-items.html): a static has a precise location and references to it refer to that location. +- [Reference: pointer-to-pointer casts](https://doc.rust-lang.org/1.80.0/reference/expressions/operator-expr.html#pointer-to-pointer-cast): for sized pointees, the pointer is returned unchanged. + +No code was built, executed, expanded, or tested, and no tool-derived theorem is claimed. Re-review is required if either producer, either field's visibility/type, `overwrite`, `with_live`, the supported Rust version/configuration set, or any consumed authoritative text changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r070.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r070.md new file mode 100644 index 0000000000..94f41ad72d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r070.md @@ -0,0 +1,33 @@ +# Acceptance review: `Piece for Tail` and `increment_tail` + +## Decision and verdicts + +**REJECT.** The exact snapshot cannot satisfy its literal public contracts. + +- **Contract compliance: CONTRACT-BROKEN** for `unsafe impl Piece for Tail`, on every requested target/profile and independently of Rust-version evolution. `FIELD` and `project` each violate a mandatory provider guarantee. +- **Source soundness on Rust 1.70.0: PROVED** for every `Tail::project` call satisfying its documented precondition and for every well-typed safe call to `increment_tail`, on all targets and ordinary profiles, relative only to the Rust 1.70 Reference and standard-library axioms listed below. No UB counterexample is established. +- **Source soundness over the literal open-ended range “Rust 1.70+”: UNPROVED.** The implementation argument is parametric for later versions having the same cited contracts, but only the exact 1.70.0 contracts were verified. With no additional TCB permitted, there is no admitted compatibility proposition that extends those facts to every later and future Rust release. This is a version-coverage limitation, not evidence of UB. + +## Frozen claim and surface + +Reviewed `lib.rs` SHA-256 `d76a5c0d7336aac4e551264a105c621dbd6cf27db097fb648aa3ca1e891e3429`. In scope are the `Tail` implementation's associated types, `FIELD`, and `project` (`lib.rs:21-29`), plus safe `increment_tail` (`lib.rs:31-34`). `Pair`'s public constructor/field and `Tail`'s public construction are included as ways safe callers can supply values. There are no dependencies, `cfg` branches, generated artifacts, callbacks, concurrency, FFI, or allocation. Other possible `Piece` implementations and whole-crate robustness are excluded. + +`project` must be UB-free whenever `owner` identifies a live, uniquely borrowed `Pair` for the call, and must return a pointer to the direct declared `Pair` field named by `FIELD`, of type `u32`. Safe `increment_tail` has no caller safety precondition and no written behavioral postcondition; soundness is mandatory. + +## Contract counterexample + +`Pair` is declared as the one-field tuple struct `Pair(pub [u32; 2])` (`lib.rs:18`). Tuple-struct fields are unnamed in their declaration grammar, and tuple indexing addresses their numeric field names; `.0` denotes the direct field at location `0` ([Rust 1.70 struct declarations](https://doc.rust-lang.org/1.70.0/reference/items/structs.html), [tuple indexing](https://doc.rust-lang.org/1.70.0/reference/expressions/tuple-expr.html#tuple-indexing-expressions)). Thus the only direct declared field has name `0` and type `[u32; 2]`. There is no direct field named `tail`, nor any direct field of type `u32`. + +Accordingly, `FIELD = "tail"` (`lib.rs:24`) falsifies its documented guarantee. `project` evaluates `.0[1]`: `.0` selects the direct array field, then `[1]` selects its second element ([Rust 1.70 array indexing](https://doc.rust-lang.org/1.70.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions)). The returned `*mut u32` points to a nested array element, not a direct declared `Pair` field. Hence the method's return guarantee is also false for every valid call. This concrete postcondition counterexample establishes `CONTRACT-BROKEN` without requiring UB. + +## Reconstructed soundness proof (Rust 1.70.0) + +1. **`Tail::project`.** A precondition-satisfying `owner` points to an extant, aligned `Pair`. Raw-pointer dereference in place context is forbidden for dangling or unaligned pointers; neither condition holds ([Rust 1.70 undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). Field `0` exists and constant index `1` is in bounds for `[u32; 2]`; array indexing therefore selects an assignable memory location. `addr_of_mut!` creates a mutable raw pointer to that place without an intermediate reference, while leaving the place expression subject to its ordinary rules ([Rust 1.70 `addr_of_mut!`](https://doc.rust-lang.org/1.70.0/std/ptr/macro.addr_of_mut.html), [raw address-of](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#raw-address-of-operators)). It performs no value read or write. The actual result is consequently a pointer to `owner.0[1]`; this proves operation safety while separately failing the stronger declared-field postcondition. + +2. **`increment_tail`.** A function argument is a coercion site, and `&mut T` may coerce to `*mut T` ([Rust 1.70 coercions](https://doc.rust-lang.org/1.70.0/reference/type-coercions.html#coercion-types)). The incoming `&mut Pair` supplies a live, valid, exclusive `Pair` throughout the call, so the `project` precondition holds. By the implementation result above, the returned pointer addresses initialized, aligned `pair.0[1]`. Forming `&mut` to that location is valid; no alias, callback, or intervening access exists, and all access remains through `value` until its last use. `wrapping_add(1)` is defined modular addition ([Rust 1.70 `u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add)). Therefore the function returns with element `1` incremented modulo `2^32` and element `0` unchanged, without UB. + +The proof uses language-level field/index selection and makes no representation-offset, endianness, optimization, overflow-check, or panic-strategy assumption, so target and ordinary-profile axes are parametric. + +## TCB, evidence, and review triggers + +TCB: only the exact Rust 1.70.0 Reference/std propositions linked above; no additional assumption, dependency, tool theorem, test, or execution. The target was not built, tested, expanded, or executed. Re-review is required for any source/public-contract change, or to extend the soundness proof to an exact later Rust-version cutoff. The configuration-independent contract failure already determines the acceptance decision. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r071.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r071.md new file mode 100644 index 0000000000..5b95b1aae0 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r071.md @@ -0,0 +1,86 @@ +# Focused review and redesign: `ticket` + +## Claim, snapshot, and boundary + +Reviewed `lib.rs` SHA-256 +`23f55cd3e96c8372d71a09336d84f72de191fd5f48de693d0be11762537dfa9f`, +under `REQUEST.md` SHA-256 +`f4cb0fe5d667cdde8536d103ea2bf8c46a4219916acb811b0e33c4d2cb5bc376`. +The in-scope theorem is: for every target, Rust 1.70+, ordinary build profile, +and `usize` input, the safe `ticket` call has no undefined behavior; for +nonzero `id` it returns a `Ticket` containing that exact value, and for zero it +panics. There are no dependencies, generated artifacts, features, callbacks, +or deployment assumptions. No code was executed. + +The safe surface is the opaque public `Ticket` type and +`pub fn ticket(usize) -> Ticket`; its tuple field is private to the defining +module. The sole invariant is **TICKET-NONZERO**: every constructed `Ticket` +contains a valid, nonzero `NonZeroUsize`. The constructor is its sole producer; +there are no transitions or unsafe consumers in this snapshot. + +## Current artifact verdict: **UNSOUND** + +The verdict covers the full supported set and follows from one supported case: +`ticket(0)` with debug assertions disabled. Rust 1.70 documents that an +optimized build does not execute `debug_assert!` unless +`-C debug-assertions` is supplied ([`debug_assert!`](https://doc.rust-lang.org/1.70.0/core/macro.debug_assert.html)). +Thus `lib.rs:9` does not establish `id != 0` in that case. Execution reaches +`lib.rs:10`, while the exact unsafe contract says, “The value must not be +zero,” and says zero produces undefined behavior +([`NonZeroUsize::new_unchecked`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new_unchecked)). +This is a well-typed call to a safe public function, so no caller obligation +can exclude it. + +The exhaustive relevant partition is: + +| Input / configuration | Result | +|---|---| +| `id != 0`, debug assertions either enabled or disabled | The unsafe precondition holds; the returned field is the same `id`, so TICKET-NONZERO and the documented return behavior hold. | +| `id == 0`, debug assertions enabled | `debug_assert!` invokes `panic!`; the unsafe expression is not reached. | +| `id == 0`, debug assertions disabled | `new_unchecked(0)` violates its safety contract: **UNSOUND**. | + +Consequently the documented zero-input behavior is not established over the +supported set. It is not separately labeled `CONTRACT-BROKEN`: this branch +already has undefined behavior, rather than a defined execution proved to +violate only the postcondition. The unsafe block also lacks an adjacent safety +proof; an adequate proof could not be written for the current source because +the supposed dominating check is configuration-dependent. + +This counterexample and verdict are established already for Rust 1.70 on every +target; no cross-version compatibility premise is needed to refute the stated +Rust 1.70+ universal claim. + +## Recommended redesign + +Keep `Ticket` and the exact safe signature and replace only the body: + +```rust +pub fn ticket(id: usize) -> Ticket { + Ticket(NonZeroUsize::new(id).expect("ticket id must be nonzero")) +} +``` + +This is the smallest proof surface: it removes `unsafe`, the manual invariant +establishment, and the debug-assertion configuration branch. Rust 1.70's safe +constructor “Creates a non-zero if the given value is not zero” +([`NonZeroUsize::new`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new)); +`Option::expect` returns the contained `Some` value and panics for `None` +([`Option::expect`](https://doc.rust-lang.org/1.70.0/core/option/enum.Option.html#method.expect)). +Therefore: + +- for nonzero `id`, `new` produces the `NonZeroUsize` for that same value, + `expect` returns it, and `Ticket` establishes TICKET-NONZERO; +- for zero, `new` produces `None` and `expect` initiates the documented panic + before any `Ticket` exists. + +The argument is parametric over target pointer width, optimization, +debug-assertion setting, and panic strategy; those axes do not select different +source operations. The panic text was not part of the old public contract. + +This is a design proposal, not a post-change `PROVED` verdict. Its Rust 1.70 +proof uses only the exact standard-library axioms cited above. For the +open-ended `1.70+` range, applicability must be parameterized on each supported +release retaining those exact stable contracts, or an explicit Rust +compatibility premise must be accepted; a future material contract change +triggers re-review. After implementation, audit the new snapshot and confirm +the published signature/documentation are unchanged. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r072.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r072.md new file mode 100644 index 0000000000..8055e57bbe --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r072.md @@ -0,0 +1,111 @@ +# Focused source review: `Buffer` and `overwrite` + +## Claim and verdict + +**Soundness: `UNSOUND`.** For `lib.rs` SHA-256 +`368fa01b2f3fca39e89131414f8ec2641a4ac162b846ccf77b8f8bd293930d9a`, +under Rust 1.80.0 on all targets and ordinary profiles, the claim that every +well-typed safe use of the current `Buffer` producers and consumers is free of +Rust undefined behavior is false. A caller using only `from_static` and +`overwrite` can trigger UB. This is a source-semantics verdict; it needs no +compiler, backend, platform, or deployment premise. + +No documented postcondition distinct from the in-scope safety obligations was +found, so there is no separate `CONTRACT-BROKEN` verdict. + +## Boundary and obligation inventory + +| Site | Role and disposition | +|---|---| +| `Buffer` fields, lines 5–8 | Private representation. Safe external literals and field access are excluded. Ordinary move, borrow, and drop do not dereference `ptr`. | +| `from_writable`, lines 11–18 | Unsafe producer; establishes `shared == None`. For a call satisfying its ongoing contract, the pointer remains aligned and valid for a one-`u8` write and conflicting access is excluded whenever the `Buffer` is used. | +| `from_static`, lines 20–26 | Safe producer; establishes `shared == Some(r)` and `ptr` points to the same `BYTE` byte as `r: &'static u8`. Construction itself performs no write. | +| `overwrite`, lines 28–40 | Safe consumer and both unsafe `ptr::write` proof sites. The `Some` path is unsound; the `None` path is proved only relative to a valid `from_writable` call and its ongoing obligations. | +| `with_live`, lines 43–46 | Private liveness helper. It invokes the write closure before the call carrying `shared` returns. | + +These are all current producers, representation transitions, and consumers. +There are no explicit trait impls, macros, generated items, conditional code, +dependencies, or other pointer consumers in the supplied artifact. + +The current closed representation supports two case invariants: **W**: +`shared == None` is produced only by `from_writable`, carrying that unsafe +caller's ongoing contract; and **S**: `shared == Some(r)` is produced only by +`from_static`, and `ptr` targets the byte pointed to by `r`. Private fields and +the exhaustively inspected producers establish the partition. + +## F-1 — safe `from_static` path writes through a live shared reference + +Status: **`UNSOUND`**, all requested targets and profiles. + +A complete safe witness is: + +```rust +let mut buffer = Buffer::from_static(); +buffer.overwrite(0); +``` + +Derivation: + +1. `from_static` creates `r = &BYTE`, derives `ptr` from that same reference, + and stores both as invariant S. +2. `overwrite` selects the `Some` branch and passes `r` to `with_live`. The + Rust 1.80.0 Reference says a reference passed to a function is live “at + least as long as that function call” (absent `UnsafeCell`, which `u8` does + not contain). The closure executes before `with_live` returns, so `r` is + live during `ptr.write(value)`. +3. Rust 1.80.0 documents `size_of::() == 1`; therefore `ptr::write::` + performs a positive-size write overlapping the byte pointed to by `r`. +4. The same Reference says bytes pointed to by a shared reference “are + immutable” and defines any positive-size overlapping write as a mutation. + It lists mutation of such immutable bytes as UB, even if the value written + would leave the contents unchanged. +5. The Rust 1.80.0 raw-pointer `write` method delegates its safety conditions + to `ptr::write`, whose contract requires that “`dst` must be valid for + writes” and “`dst` must be properly aligned.” The shared reference's + immutability means the first requirement cannot hold here. + +The local comment at lines 31–32 is inapplicable: this state came from +`from_static`, not `from_writable`. `with_live` does not justify the write; it +makes the conflicting shared-reference liveness explicit. No replacement +comment can prove the current operation. The minimum resolution is to prevent +the safe producer/method combination from writing this shared byte, then +re-audit both state cases. + +## `from_writable` path and proof-documentation result + +For a valid `from_writable` call, invariant W selects the `None` branch. Its +ongoing caller contract supplies write validity, alignment, and absence of +conflicting access at the call; `value: u8` is valid, and `ptr::write` neither +changes `ptr` nor the tag. Thus this branch's implementation obligation is +**`PROVED` relative to that unsafe API contract**. + +Its lines 36–37 comment is nevertheless incomplete proof documentation: it +does not establish the `None`-to-`from_writable` producer link and mentions +write validity but omits alignment and conflicting-access obligations. Adequate +replacement wording would be: + +```text +SAFETY: In the current closed representation, `shared == None` is established +only by `from_writable`. Its caller must keep `ptr` aligned and valid for a +one-`u8` write and exclude conflicting accesses for every use of this Buffer; +those ongoing obligations therefore hold for this call. +``` + +## Configuration coverage, TCB, and residual scope + +The proof/counterexample is parametric over target and profile: there is no +`cfg`, generated code, target layout choice, panic dependence, or optimization +premise, and the cited `u8` size is target-independent. Coverage is therefore +all configurations requested, not a tested sample. + +TCB `R072-v1` contains only these Rust 1.80.0 axioms: + +- [Raw-pointer `write` delegation](https://doc.rust-lang.org/1.80.0/std/primitive.pointer.html#method.write) + and [`ptr::write` safety contract](https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html#safety). +- [Reference: alias liveness and immutable-byte UB](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html#behavior-considered-undefined). +- [`size_of` primitive-size table](https://doc.rust-lang.org/1.80.0/std/mem/fn.size_of.html). + +No tool-derived evidence, testing, build, macro expansion, or additional +assumption was used. Scope is exactly the supplied `REQUEST.md` and `lib.rs`; +changes to either producer, either field, `overwrite`, `with_live`, supported +Rust version, or cited semantics require re-review. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r073.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r073.md new file mode 100644 index 0000000000..dd076007b4 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r073.md @@ -0,0 +1,47 @@ +# Supported-domain source review + +## Claim and verdict + +**Soundness: PROVED**, relative to TCB `R073-1`, for the exact `lib.rs` snapshot (SHA-256 `6f87e944cec7ee2727f7c7d32aa382de88987ea791082175d611fff5bf44012b`) and every well-typed safe call to its public `first(&[u8]) -> Option`, under Rust's source-level abstract semantics, for the full 16-case envelope + +`Rust ∈ {1.79.0, 1.80.0, 1.81.0, 1.82.0}` × `target ∈ {x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu}` × `fast ∈ {off, on}`. + +This envelope is deliberately a proof scope, not a new support policy. It contains every configuration supported by Policy A and every configuration supported by Policy B (and also `fast`/aarch64/Rust 1.79.0). Thus soundness is independently **PROVED under Policy A** and **PROVED under Policy B**, and under any reconciliation selecting configurations from this envelope. No precedence decision is needed. + +**Support-policy status: unresolved, not a soundness failure.** Policy A supports `fast` on x86_64 from 1.79 and aarch64 from 1.80, whereas Policy B starts x86_64 at 1.80 and says aarch64 supports only 1.82 (`POLICY-A.md:9–10`; `POLICY-B.md:9–10`). Because both are current, the snapshot does not identify one canonical support predicate. This review does not resolve that publication defect. + +There is no public unsafe API and no documented target-API postcondition. The standard-library `get_unchecked` postcondition consumed locally is discharged below. This is not a compiler/backend or binary-level theorem. + +## Snapshot, boundary, and configuration closure + +The reviewed package is `domain-review` 1.0.0, edition 2021, with no dependencies and one Cargo feature, `fast`. `rust-toolchain.toml` selects 1.82.0, while the two policies place 1.79.0–1.82.0 in review scope. All seven supplied target files were inspected. No generated code, build script, FFI, assembly, traits, mutable invariant-bearing state, or target-specific source exists. Nothing was built, tested, or macro-expanded; CI is sampling evidence only and is not consumed. + +The sole language-reachable surface is the safe free function `first`. The versioned Rust References state that a false `cfg` predicate removes its item and a true one includes it ([1.79](https://doc.rust-lang.org/1.79.0/reference/conditional-compilation.html#the-cfg-attribute), [1.80](https://doc.rust-lang.org/1.80.0/reference/conditional-compilation.html#the-cfg-attribute), [1.81](https://doc.rust-lang.org/1.81.0/reference/conditional-compilation.html#the-cfg-attribute), [1.82](https://doc.rust-lang.org/1.82.0/reference/conditional-compilation.html#the-cfg-attribute)). Therefore `cfg(feature = "fast")` and its `not(...)` form select exactly one definition. Target, profile, and optimization do not alter either branch or its proof. Exact documentation was checked separately for all four Rust releases, so no forward- or backward-compatibility assumption is used. + +## Obligation ledger and derivation + +**O-NORMAL (`lib.rs:3–6`, all envelope cases with `fast` off): PROVED.** The body contains only safe standard-library calls. In each reviewed release, `slice::first` returns the first element or `None` for an empty slice ([1.79](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.first), [1.80](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.first), [1.81](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.first), [1.82](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.first)); `Option::copied` copies the referenced `u8`, with `Copy` enforced by its type bound. No caller-side safety premise or local unsafe operation exists. + +**O-FAST-BOUNDS (`lib.rs:10–13`, all envelope cases with `fast` on): PROVED.** The controlling `get_unchecked(0)` contract says an out-of-bounds index is UB and that the operation returns a reference to the indexed element ([1.79](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.get_unchecked), [1.80](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.get_unchecked), [1.81](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.get_unchecked), [1.82](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked)). In each release, `is_empty` “returns true if the slice has a length of 0” ([1.79](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.is_empty), [1.80](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.is_empty), [1.81](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.is_empty), [1.82](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.is_empty)). The unsafe call is reachable only in the `else` branch. By contraposition, that branch has nonzero slice length, hence at least one element, so index 0 is in bounds. The returned `&u8` refers to that element; dereferencing copies a valid `u8` into `Some`. There is no intervening call or mutation. This argument is target-parametric and covers every `fast` case. + +No persistent invariant is produced, transitioned, or consumed. The only proof state is the branch-local nonempty fact, which dominates and is immediately consumed by the unsafe call. + +## TCB `R073-1` + +- **AXIOM-STD:** the exact Rust 1.79.0, 1.80.0, 1.81.0, and 1.82.0 Reference/std propositions linked above, consumed only in the stated release. +- **AXIOM-RUST:** ordinary well-typed reference, control-flow, `cfg`, `Copy`, and value-validity semantics of the corresponding Rust release and edition, for the source-level theorem. + +There are no dependency, external-specification, implementation, tool, deployment, probabilistic, or compatibility premises. + +## Finding: missing local proof documentation + +**Implementation PROVED; proof artifact deficient.** `lib.rs:13` has no adjacent `SAFETY` comment. The bounds derivation above is material and had to be reconstructed. No UB counterexample is known. Proposed wording (not applied, as requested): + +```rust +// SAFETY: This branch is reached only when `is_empty()` is false. If the +// length were zero, `is_empty()` would be true; therefore len > 0 and index 0 +// is in bounds, satisfying `get_unchecked`'s requirement. +Some(unsafe { *bytes.get_unchecked(0) }) +``` + +Re-audit on changes to `lib.rs`, either policy, the feature/target/version envelope, or any consumed versioned Rust contract. Independently reconcile the two published policies so downstream users can determine the actual support commitment. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r074.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r074.md new file mode 100644 index 0000000000..fd68fd27a5 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r074.md @@ -0,0 +1,41 @@ +# Supported-domain source review + +## Claim and verdict + +**Soundness: PROVED** for the exact `v7c4` source, for every well-typed safe call to the cfg-selected public `first` function, on every configuration in the maximal published-commitment envelope + +`U = D_A ∪ D_B = { !fast on either named target at Rust 1.79.0–1.82.0 } ∪ { fast on x86_64 at 1.79.0–1.82.0 } ∪ { fast on aarch64 at 1.80.0–1.82.0 }`. + +This is a source-level Rust result under the documented abstract semantics, relative only to the versioned Rust axioms below. It is not a claim about compiler/backend correctness. `U` is a coverage envelope, **not a newly selected support policy**. It contains every configuration either current policy positively commits to, so the result proves Policy A's domain and Policy B's domain separately. + +The exact project predicate `Supported(c)` cannot be stated from the current publications without an unauthorized precedence decision. Policy A includes, while Policy B excludes, these `fast` configurations: x86_64/Rust 1.79.0 and aarch64/Rust 1.80.0 and 1.81.0. No conclusion about their *official support status* is justified. Their source soundness is nevertheless proved above. This policy defect does not make any source obligation `UNPROVED` or `UNSOUND`. + +## Snapshot and boundary + +Reviewed all files in `/tmp/unsafe-rust-v2-eval.9epWDK/targets/v7c4`; `lib.rs` SHA-256 is `6f87e944cec7ee2727f7c7d32aa382de88987ea791082175d611fff5bf44012b`. The crate is edition 2021, has no dependencies, and declares only the boolean `fast` feature. The audit cutoff is Rust 1.82.0. + +The complete public surface is one safe free function, `first(&[u8]) -> Option`, with complementary cfg definitions. There are no public unsafe APIs, invariant-bearing fields, traits, macros, generated sources, FFI, concurrency, or persistent state. The only unsafe operation is `bytes.get_unchecked(0)` at `lib.rs:13`. No crate-level documented postcondition exists; broader safe-API behavior was not requested. + +## Obligation derivation and configuration closure + +1. For each audited Rust version, `#[cfg(feature = "fast")]` includes the fast definition exactly when that predicate is true, while `#[cfg(not(feature = "fast"))]` includes the other exactly when it is false. Thus the two items are an exhaustive, disjoint partition. Each audited Reference says a false cfg removes its item and `not` is “true if its predicate is false”: [1.79.0](https://doc.rust-lang.org/1.79.0/reference/conditional-compilation.html), [1.80.0](https://doc.rust-lang.org/1.80.0/reference/conditional-compilation.html), [1.81.0](https://doc.rust-lang.org/1.81.0/reference/conditional-compilation.html), [1.82.0](https://doc.rust-lang.org/1.82.0/reference/conditional-compilation.html). + +2. With `fast` off, `first().copied()` uses only safe standard-library operations, so it creates no caller or local unsafe obligation. + +3. With `fast` on, an empty slice returns before the unsafe call. On the other branch, `is_empty()` is false. In every audited standard-library version, `is_empty` “Returns `true` if the slice has a length of 0,” while `get_unchecked` says “Calling this method with an out-of-bounds index is undefined behavior.” See the exact slice documentation for [1.79.0](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.get_unchecked), [1.80.0](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.get_unchecked), [1.81.0](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.get_unchecked), and [1.82.0](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked). Therefore the branch fact entails `bytes.len() != 0`, hence `0 < bytes.len()`: index 0 is in bounds and the unsafe precondition is discharged. The returned shared reference is immediately read as `u8`; no intervening mutation, callback, or temporal invariant exists. + +4. This proof depends on neither target's layout nor target-specific behavior and was checked against each version's own documentation. It therefore covers both named targets and every point of `U` abstractly. `CI.md` samples only some points and is not consumed as universal evidence. No build or test result is part of the proof. + +## TCB and findings + +TCB: the eight exact versioned Reference/standard-library pages linked above, as `AXIOM-CFG-{1.79…1.82}` and `AXIOM-SLICE-{1.79…1.82}`. There are no dependency, tool, platform, deployment, or compatibility assumptions. + +**Proof-documentation finding:** `lib.rs:13` has no adjacent `SAFETY` comment. The implementation obligation is proved by the reconstruction above, but the source proof artifact is inadequate. Proposed wording (no edit requested): + +```rust +// SAFETY: This branch is reached only when `bytes.is_empty()` is false, +// so `bytes.len() != 0`; therefore index 0 is in bounds for `bytes`. +Some(unsafe { *bytes.get_unchecked(0) }) +``` + +No UB counterexample or contract failure was found. Re-review is required if the source, feature selection, either policy, the Rust range/targets, or a consumed Rust contract changes. Resolving official membership of the three disputed points requires an authorized policy reconciliation; it does not require a source-code change. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r075.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r075.md new file mode 100644 index 0000000000..98aec894ac --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r075.md @@ -0,0 +1,95 @@ +# Focused review: `total` + +## Scope and claims + +Reviewed exactly `lib.rs` as supplied. The public safe surface is +`total(&[u32]) -> u32`; the requested behavioral postcondition is the sum of all +elements modulo \(2^{32}\), independent of ordinary profile, on Rust 1.70+ and +all targets. The three unsafe sites are the initial `add(len)`, the load, and +the per-iteration `add(1)`. No build, test, expansion, or benchmark was run. + +**Current soundness verdict: UNSOUND** for the stated supported set. Rust 1.70, +which is in that set, supplies a valid empty-input witness. **Current wrapping +postcondition: UNPROVED overall**: the witness contains UB, so it cannot also +establish or refute a defined return value. This is a source-level result +relative to the exact Rust 1.70 contracts cited below, with no compiler/backend +claim. + +## Current implementation proof and failure + +For a nonempty valid `&[u32]`, the reconstructed loop invariant after `k` +iterations is: `0 <= k <= len`, `ptr` addresses element `k` (or the one-past +pointer when `k == len`), `acc` is the modular sum of elements `0..k`, and the +borrowed slice remains live. Rust 1.70 says a live reference is not dangling or +unaligned, dynamically sized values do not exceed `isize::MAX`, and slices have +the layout of their array section. Thus the initial `add(len)` has a fitting, +non-wrapping byte offset within the same allocation; while `ptr != end`, the +load is from a live, aligned, initialized `u32`; `add(1)` reaches the next +element or one-past pointer. Raw-pointer comparison is by address. Finally, +`wrapping_add` is explicitly modular. This proves the nonempty regional +soundness and wrapping result. The source contains no adjacent `SAFETY` +comments carrying this material proof. + +The empty case breaks the full claim. Rust 1.70 documents +`NonNull::::dangling()` as dangling but aligned, and explicitly permits it +as the data pointer for a zero-length slice created by `slice::from_raw_parts`. +That yields a valid `&[u32]` and a valid call to this safe API. `total` then +executes `ptr.add(0)`. The Rust 1.70 `add` contract says both starting and +resulting pointers must be in-bounds or one byte past the same allocated +object; a dangling pointer does not satisfy that clause, and violation is UB. +Later documentation that exempts zero offsets cannot silently repair the +historical 1.70 contract. + +Authorities: + +- [Rust 1.70 UB and dangling-pointer rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html) +- [Rust 1.70 slice/array layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#slice-layout) +- [Rust 1.70 raw-pointer comparison](https://doc.rust-lang.org/1.70.0/reference/types/pointer.html) +- [Rust 1.70 `pointer::add`](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) +- [Rust 1.70 `slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr) +- [Rust 1.70 `from_raw_parts`](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html) +- [Rust 1.70 `NonNull::dangling`](https://doc.rust-lang.org/1.70.0/std/ptr/struct.NonNull.html#method.dangling) +- [Rust 1.70 `u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add) + +## Safe iterator redesign + +The preferred candidate is: + +```rust +pub fn total(values: &[u32]) -> u32 { + values + .iter() + .fold(0u32, |acc, value| acc.wrapping_add(*value)) +} +``` + +Its crate-local unsafe surface and pointer invariants are empty. Rust 1.70 +documents that [`slice::iter`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.iter) +yields all items from start to end and that +[`Iterator::fold`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.fold) +starts with the supplied accumulator and applies the closure to every item. +Together with the cited modular-addition guarantee, induction proves exactly +the required result, including `0` for an empty slice. Explicit +`wrapping_add`, rather than `+`, makes overflow-check/profile settings +irrelevant. These APIs exist in the 1.70 documentation and the reasoning is +target-parametric. This is a design proof, not a verdict on an unimplemented +snapshot; each later supported Rust release must retain the consumed contracts +or trigger re-audit. + +## Performance evidence and recommendation + +**The <=2% designated-benchmark obligation is UNPROVED.** Source shape, +optimizer expectations, or proof simplicity is not benchmark evidence, and no +result was supplied. Do not merge the iterator replacement solely on the +soundness argument, but also do not retain or release the current implementation +unchanged. + +Next, block the release and benchmark the exact iterator candidate against the +exact baseline using the designated benchmark's fixed revision, inputs, +toolchain, target, profile, flags, and hardware. Use enough repeated samples +and the project's accepted statistical rule that measurement uncertainty does +not hide a regression above 2%. If it passes, adopt it and audit the resulting +snapshot. If it fails, tune safe iterator forms and repeat; the fallback is a +separately audited raw-pointer version with an empty-slice early return and +adjacent proofs for every unsafe operation, also benchmarked under the same +gate. This sequence preserves both proof quality and the performance contract. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r076.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r076.md new file mode 100644 index 0000000000..62fa938780 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r076.md @@ -0,0 +1,47 @@ +# `Block` published-contract review + +## Claim, scope, and verdict + +Snapshot: the supplied `lib.rs`; review scope is the public unsafe trait `Block`, its `Page` impl, and safe function `first`. There are no dependencies, `cfg`s, generators, macros, FFI, or profile-dependent branches. The all-target/profile argument is parametric over the cited Rust layout and pointer rules; nothing in the source selects a target or profile. + +**Combined current-artifact verdict: `UNPROVED`.** `Page` discharges the full apparent implementer contract, but `first` needs two propositions that the published wording does not define precisely: that “readable for 16 bytes” includes a properly initialized `u8` at byte zero, and that “during the borrow” covers the instant after `base` returns when `first` dereferences the result. No definite UB witness follows without choosing a meaning for those ambiguous terms, so this is not an `UNSOUND` verdict. + +Applicability is also open-ended. The cited propositions were checked in exact Rust 1.70.0 documentation and remain present in 1.97.1. Applying them to every intervening and future `1.70+` toolchain requires a pending compatibility premise—“every supported toolchain retains these exact abstract-semantic propositions”—or a version-by-version check and future re-audit trigger. The request also does not say whether nightly/custom-target toolchains are included. Thus no unconditional verdict is issued for the literal unbounded range. + +TCB: only the versioned Rust axioms linked below; no dependency, tool, or environmental assumptions. `RUST-COMPAT` above is pending, not silently accepted. + +## Contract and obligation coverage + +The published unsafe-impl obligations at `lib.rs:3-10` are: (B1) `ALIGN != 0`; (B2) it is a power of two; and, throughout the stated borrow, `base()` returns a pointer that is (B3) non-null, (B4) `ALIGN`-aligned, and (B5) readable for all 16 bytes. Rust 1.70 says unsafe traits define extra conditions implementations must uphold and `unsafe impl` asserts they are discharged ([Reference](https://doc.rust-lang.org/1.70.0/reference/unsafe-keyword.html#unsafe-traits-unsafe-trait)). Unknown downstream impls are therefore admissible only when they honor the published contract; they are not trusted merely because they compile. + +### `Page`: full impl obligation `PROVED` (within the version domain above) + +`ALIGN = 16` proves B1-B2. The Reference says alignment is at least one and a power of two, size is a multiple of alignment, and `u8` has size one; hence `u8` alignment is one. `[u8; 16]` therefore has size 16 and alignment one ([size/alignment](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#size-and-alignment), [array layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). + +For a `repr(C)` struct, layout begins at offset zero and the first field's offset is zero; `align(16)` raises the containing representation's alignment ([C layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs), [alignment modifier](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers)). Thus every valid `&Page` is non-null and 16-aligned and its sole array begins at that same address. A valid `[u8; 16]` contains 16 initialized bytes; an integer obtained from uninitialized memory is invalid ([invalid-value rule](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)). `as_ptr` returns the slice buffer pointer and requires the slice to outlive its use ([`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)); the field and its owner remain live for the receiver borrow. The field is private and this snapshot has no safe constructor or mutator. Therefore B3-B5 hold. There are no transitions, alternate exits, or panic paths. + +The implementation is correct, but the `unsafe impl` has no adjacent proof. Proposed non-normative proof text: + +> `SAFETY: ALIGN is 16. repr(C) places the sole [u8; 16] field at offset 0, and repr(align(16)) makes Page's address 16-aligned. A valid shared Page owns 16 initialized u8s for the receiver borrow; as_ptr points at that live buffer. The result is therefore non-null, 16-aligned, and readable for all 16 bytes for that interval.` + +### `first`: subset identified; derivation incomplete under the literal prose + +Evaluating `*p` is UB when the raw pointer is dangling or unaligned, and producing an uninitialized integer is UB ([Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)); the standard library decomposes a read as requiring validity for reads, proper alignment, and a properly initialized value ([`ptr::read` safety](https://doc.rust-lang.org/1.70.0/std/ptr/fn.read.html#safety)). `first` needs only byte zero. It does **not** consume `ALIGN`, the power-of-two clause, `ALIGN`-alignment, or bytes 1-15. If B5 is authoritatively normalized to “valid for a non-atomic read of 16 initialized `u8`s through the receiver-borrow interval,” the one-byte read follows immediately (and `u8` alignment is one), no pointer escapes, and the function is `PROVED`. The current words do not explicitly supply that normalization. A `# Safety` section defining the implementer subject, initialization/access meaning, and exact lifetime would close the documentation proof; deciding that this is only clarification, rather than a stronger obligation, requires project authority. + +## What is compatible in 1.x + +Keep `Block`, `ALIGN`, `base`, their full provider guarantees, downstream implementability, and `first`'s signature unchanged. The known local consumer is not an exhaustive compatibility argument. + +Within those constraints, proof-oriented work can: + +- add the reconstructed local proof for `Page` and, once the contract meaning is authorized, an adjacent `SAFETY` proof for `first` using only the one-byte lemma; +- add a separately named safe capability/API for new code (for example, `FirstByte::first_byte(&self) -> u8`, with `Page` using `self.0[0]`) while retaining the legacy API; and +- deprecate, but not remove or weaken, the old raw-pointer surface to create a migration runway. + +Adding a required item to `Block`, sealing it, strengthening implementer obligations, or treating stronger prose as a mere correction is not safely assumed compatible. A new provided method also deserves collision/source-compatibility review; a separate named trait or free API is cleaner. + +## What requires an authorized 2.0 migration + +If the real requirement is only `first`, replace the unsafe raw capability with a safe trait method returning `u8`; `Page` indexes its array and generic consumers contain no unsafe code. If consumers need all 16 bytes, use a safe `&[u8; 16]` capability. If 16-byte alignment is genuinely required, expose a safe reference to a dedicated aligned 16-byte wrapper; otherwise keep alignment out of the minimal contract. Split any genuinely necessary raw-pointer capability into a separately named unsafe trait. + +Removing/renaming `ALIGN` or `base`, weakening their guarantees, changing `base` to a reference/value API, making the unsafe trait safe, sealing it, changing `first`'s bound/signature, or strengthening what existing impls must prove all require explicit breaking-change authorization. A 2.0 plan must inventory/migrate downstream consumers and impls through the published contract channel, then audit the implemented replacement as a new artifact. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r077.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r077.md new file mode 100644 index 0000000000..6faf69a027 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r077.md @@ -0,0 +1,40 @@ +# Focused review: `total` + +## Claim, scope, and verdict + +Snapshot: the supplied `lib.rs`, specifically the public safe function `total`, reviewed at 2026-07-31. The requested support set is Rust 1.70+, every target, and every ordinary profile. There are no dependencies, generated artifacts, features, or other APIs in the supplied target. A valid call supplies any valid `&[u32]`; it has no caller safety precondition. Required behavior is the left fold from zero using addition modulo \(2^{32}\), independent of overflow-check/profile settings. + +**Current implementation soundness: `UNSOUND` over the requested support set.** Rust 1.70.0 is in that set and admits a valid empty-slice execution that reaches undefined behavior. This one covered version is enough to refute the universal support-set claim. The wrapping-result postcondition is `PROVED` for every UB-free execution of this loop, but the UB execution is not also a postcondition witness. + +**Proof documentation: deficient.** None of the three unsafe operations has an adjacent `SAFETY` derivation. + +**Safe redesign performance requirement: `UNPROVED`.** No result—or even benchmark workload, build, machine, noise model, or comparison protocol—was supplied, so source inspection cannot establish a regression of at most 2%. + +## Current-artifact obligation ledger + +1. `values.as_ptr()` returns the slice-buffer pointer, but a valid empty slice need not have an allocation-backed data pointer. Rust 1.70 [`slice::from_raw_parts`](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html) expressly recommends `NonNull::dangling()` as usable data for a zero-length slice. Thus a contract-satisfying caller can form such a slice and safely call `total`. + +2. `ptr.add(values.len())` is then `dangling.add(0)`. Rust 1.70 [`pointer::add`](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) says both pointers must be in bounds or one byte past the same allocated object, with violation producing UB. Choose an execution with no live allocation covering the dangling pointer: the valid empty slice reaches UB before the loop condition. This is an implementation defect, not merely a missing comment. Rust 1.80's [revised contract](https://doc.rust-lang.org/1.80.0/std/primitive.pointer.html#method.add) says a zero computed offset is always well-defined, but that later text cannot retroactively discharge the Rust 1.70 obligation. + +3. For a nonempty slice, the remaining pointer proof can be reconstructed. The Rust 1.70 Reference defines a dangling pointer in terms of all pointee bytes belonging to one live allocation and says a dynamically sized Rust value never exceeds `isize::MAX` ([UB/dangling rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#dangling-pointers)). A slice has the layout of the array section it slices, while array element `n` is at `n * size_of::()` ([layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). Consequently the initial pointer through the one-past-end pointer stays in one allocation and the full byte offset fits `isize`; each executed dereference is aligned, initialized, live, and in-bounds. The loop invariant is: before the test, `ptr` addresses element `i` and `acc` is the wrapping fold of elements `0..i`, for `0 <= i <= len`. For `i < len`, dereference reads element `i`, `add(1)` advances within that allocation, and the invariant is restored for `i + 1`; at `i = len`, the pointers compare equal and the loop exits. + +4. Each accumulator update uses [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add), documented as modular addition. Therefore overflow checks, optimization level, and debug/release profile cannot change arithmetic behavior. + +TCB: only the exact Rust 1.70 and 1.80 Reference/std propositions linked above. No tests, compiler implementation behavior, or benchmark assumptions are consumed. Releases after the 2026-07-31 cutoff require re-review or an explicitly accepted compatibility premise; the open-ended `1.70+` wording cannot itself prove future releases. + +## Safe iterator candidate + +```rust +pub fn total(values: &[u32]) -> u32 { + values + .iter() + .copied() + .fold(0u32, u32::wrapping_add) +} +``` + +This keeps the public contract and MSRV: `copied` is documented since 1.36 and copies every iterator element, while [`fold`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.fold) applies the closure to every element and returns the final accumulator. Combining that with `wrapping_add` gives the same modulo-\(2^{32}\) result for empty and nonempty slices in all profiles and targets. It removes all local unsafe operations and their pointer/provenance proof surface. This is a design proof plan, not a verdict for an unimplemented snapshot. + +## Recommendation + +Block release of the current implementation on the advertised support set. Next, benchmark the safe candidate against the exact current baseline using the designated benchmark and a predeclared, repeatable protocol. If its confidence-bounded regression is at most 2%, adopt it and audit the new snapshot. If it misses the gate, benchmark the minimal soundness repair—return `0` before any pointer arithmetic when `values.is_empty()`—and add complete adjacent `SAFETY` proofs for the retained unsafe operations. Do not claim either candidate meets the 2% requirement until that evidence exists; do not retain the Rust 1.70 UB while waiting for performance evidence. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r078.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r078.md new file mode 100644 index 0000000000..7959d40d25 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r078.md @@ -0,0 +1,38 @@ +# Focused review: `decode` + +## Claim, scope, and verdict + +Snapshot: the supplied `lib.rs` only; audit cutoff 2026-07-31. The support predicate is Rust 1.70+, every target and pointer width, every ordinary profile, and each value of `feature = "compact"`, as fixed by `REQUEST.md`. The reviewed surfaces are the two mutually exclusive public safe `decode` functions and the single unsafe call in the compact branch. There are no dependencies, generated artifacts, stateful invariants, or caller safety obligations in the supplied source. TCB for the current witness is `AXIOM-RUST-1.70`: only the exact official Rust 1.70 propositions cited below; no tool result or additional assumption is consumed. + +**Current-artifact verdict: `UNSOUND`.** One supported configuration and safe call suffice to refute the universal claim: Rust 1.70, `compact` enabled, an optimized profile without `-C debug-assertions`, and `decode(0xD800)`. Rust 1.70 documents that optimized builds do not execute `debug_assert!` by default ([`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html#uses)). Thus line 6 supplies no fact in this configuration and line 7 calls `char::from_u32_unchecked(0xD800)`. Its contract says that it ignores validity and may construct an invalid `char` ([`from_u32_unchecked`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32_unchecked)). The Reference defines producing an invalid value as UB and specifically makes a surrogate-valued `char` invalid ([Rust 1.70 Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). `0xD800` is a valid `u16`, so this is a valid use of the safe API with no hidden caller obligation. + +The compact guarantee “panics for a surrogate” is **`UNPROVED`** over the full support set, not `CONTRACT-BROKEN`: the demonstrated optimized execution contains UB, so it cannot also witness a defined postcondition failure. With debug assertions enabled, the check does panic before the unsafe call. The non-compact branch is **`PROVED` at Rust 1.70** relative to the cited standard-library contract: it directly returns `char::from_u32(raw)`, which returns `None` exactly when the input is not a valid `char` ([`from_u32`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32)). + +The unsafe block also has no adjacent `SAFETY` proof. A reconstructed proof would require “`raw` is not a surrogate” at line 7; the only purported producer is configuration-dependent and therefore does not dominate the call in all supported profiles. + +## Recommended configuration-preserving redesign + +```rust +/// With `compact`, returns the represented scalar and panics for a surrogate. +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + char::from_u32(raw as u32).expect("raw should not be a surrogate") +} + +/// Without `compact`, returns the represented scalar or `None`. +#[cfg(not(feature = "compact"))] +pub fn decode(raw: u32) -> Option { + char::from_u32(raw) +} +``` + +This is review-only proposed source; the target was not edited. + +## Conditional proof plan and compatibility + +- The two `cfg` predicates are exhaustive and disjoint: Rust 1.70 specifies that `not(P)` has the opposite truth value and that `cfg` includes an item exactly when its predicate is true ([conditional compilation](https://doc.rust-lang.org/1.70.0/reference/conditional-compilation.html#the-cfg-attribute)). Both public signatures therefore remain unchanged in their respective feature configurations. +- A `u16` ranges from 0 through `2^16 - 1` ([integer types](https://doc.rust-lang.org/1.70.0/reference/types/numeric.html#integer-types)); widening it to `u32` zero-extends and preserves its value ([numeric casts](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast)). Within that range, the only invalid `char` values are `0xD800..=0xDFFF`. Consequently `from_u32` returns `Some` containing the represented scalar for every non-surrogate and `None` for every surrogate. `Option::expect` returns the contained `Some` value and panics on `None` ([`Option::expect`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect)). This proves exactly the documented compact behavior without unsafe code or a profile-sensitive assertion. +- The false-feature branch is textually unchanged. Fixed-width integer semantics make the argument independent of target and pointer width; no profile-sensitive construct remains. `char::from_u32` was already stable by Rust 1.52, so the proposal does not raise the 1.70 MSRV. +- The panic message was not documented, so changing it is not a documented behavior change. A surrogate changes from UB in affected current builds to the already-promised panic; that is defect remediation, not contract narrowing. + +This candidate is a design, not a post-change `PROVED` verdict. For the open-ended Rust 1.70+ range, its future-version proof requires the explicit compatibility premise that later supported stable releases preserve the cited `from_u32`, `Option::expect`, integer, cast, and `cfg` propositions. Re-audit the implemented snapshot and revisit on any material change to those contracts, the support predicate, signatures, documentation, or feature selection. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r079.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r079.md new file mode 100644 index 0000000000..01635b2c39 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r079.md @@ -0,0 +1,44 @@ +# Focused review: `total` + +## Claim and verdicts + +Scope is exactly `lib.rs::total` and the proposed safe iterator replacement, for Rust 1.70+, all targets, and ordinary profiles. Required behavior is the left fold from `0u32` using wrapping addition. No dependencies, generated code, features, or target-conditional source are present. + +- **Current implementation soundness: UNSOUND on Rust 1.70**, hence UNSOUND for the claimed supported set. A valid empty slice can reach undefined behavior before the loop. +- **Safe iterator design soundness and behavior: PROVED for Rust 1.70** from the cited 1.70 standard-library contracts, with a re-audit/compatibility premise required for the open-ended `1.70+` range. +- **Replacement performance (`<= 2%` regression): UNPROVED.** No benchmark definition, result, artifact identity, environment, or uncertainty analysis was supplied. + +These are source-review verdicts, not binary/backend performance claims. + +## Current implementation obligation ledger + +`end = ptr.add(values.len())` is the failing site. Rust 1.70 documents `add` as UB unless both pointers are in-bounds or one-past the same allocation, the byte offset fits `isize`, and the mathematical address does not wrap ([`pointer::add`, Rust 1.70](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add)). Yet Rust 1.70 explicitly permits a zero-length slice whose data is `NonNull::dangling()` ([`slice::from_raw_parts`](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html)); that constructor's other obligations are vacuous or satisfied at length zero. [`NonNull::dangling`](https://doc.rust-lang.org/1.70.0/std/ptr/struct.NonNull.html#method.dangling) creates a dangling, aligned pointer, not a pointer in an allocation. Thus a correctly constructed empty `&[u32]` is a valid argument, but `total` immediately performs `add(0)` on a pointer that fails Rust 1.70's allocation clause. This is a concrete valid-use UB derivation, not merely a missing comment. + +For a nonempty valid slice, the remaining derivation succeeds: [`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr) supplies the buffer pointer; slice validity supplies one allocation, initialized consecutive `u32`s, and total byte size at most `isize::MAX`; induction over the loop index keeps every dereference on the current initialized element and every `add(1)` within the allocation or exactly one-past it. The live shared slice prevents mutation for the call. The loop performs one [`wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add) per element, so overflow behavior is profile-independent. Raw-pointer dereference of dangling or unaligned pointers is UB under the [Rust 1.70 Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined); the induction discharges that condition only in the nonempty case. + +The source contains no `SAFETY` comments. Even absent the empty-slice defect, the three unsafe sites would therefore have inadequate proof documentation; the material allocation, size, alignment, initialization, lifetime, and loop-invariant derivation is missing. + +## Safe iterator redesign + +Use this candidate for evaluation: + +```rust +pub fn total(values: &[u32]) -> u32 { + values + .iter() + .copied() + .fold(0u32, u32::wrapping_add) +} +``` + +Rust 1.70 documents that slice iteration yields all items from start to end ([`slice::iter`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.iter)), `copied` copies each referenced item ([`Iterator::copied`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.copied)), and `fold` starts from the supplied initial value, applies the closure to every element left-associatively, and returns the accumulator ([`Iterator::fold`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.fold)). Combined with modular `wrapping_add`, this returns zero for empty input and the requested wrapping sum for every slice, independent of overflow checks, debug assertions, and optimization. All APIs exist in Rust 1.70. It eliminates the local unsafe TCB and its documentation burden. + +The proof above is exact for Rust 1.70. Applying it unchanged to every later and future stable release needs either version-by-version documentation verification or an explicitly accepted Rust compatibility premise preserving these propositions; an API stability badge alone is insufficient. + +## Recommendation + +**Do not approve the current implementation, and do not claim the replacement meets the performance gate yet.** The next action is to benchmark the exact candidate against the exact current artifact using the designated benchmark and a pinned target, toolchain(s), profile, codegen settings, CPU/runtime environment, inputs, and measurement method. + +Predeclare the decision rule around the slowdown ratio, including measurement uncertainty: accept the safe replacement only when the evidence supports an upper bound of at most 2%; reject that candidate if the lower bound exceeds 2%; if the interval crosses 2%, improve measurement precision or evaluate another safe formulation. Also run any separately designated target/profile matrix; one benchmark configuration cannot establish a universal performance claim unless the requirement itself names that configuration. + +This sequencing preserves both requirements: the iterator proof is the preferred design, while the missing performance proposition remains explicitly unresolved rather than being replaced by an optimizer assumption. Any source, MSRV, benchmark, target/profile scope, compiler-contract, or codegen change triggers re-review. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r080.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r080.md new file mode 100644 index 0000000000..4cd9dd208a --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r080.md @@ -0,0 +1,51 @@ +# Focused review: configuration-preserving `decode` redesign + +## Scope and claim + +Artifact: `targets/z4n7/lib.rs` as supplied. The complete in-scope surface is the two mutually exclusive public `decode` definitions. Supported configurations are stable Rust 1.70+, all targets and pointer widths, ordinary profiles/panic strategies, and both Boolean values of `compact`. Valid use means every safe call with any value of the configuration-specific argument type. Required behavior is exactly the two doc comments; no panic message is promised. This is a source-level review only; no generated code, dependencies, target-specific code, or invariant-bearing state exists in the supplied target. + +## Verdict on the current source + +**Overall soundness: UNSOUND.** The `compact` implementation is unsound in every supported configuration where debug assertions are disabled. `decode(0xD800)` is a well-typed safe call. Rust 1.70 documents that “An optimized build will not execute `debug_assert!` statements unless `-C debug-assertions` is passed” ([`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html)). Thus the check at `lib.rs:6` does not dominate the unsafe operation over the complete profile set. + +The widening cast preserves the numeric value: Rust 1.70 says a smaller unsigned integer cast to a larger integer is zero-extended ([numeric casts](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast)). The unchecked constructor can therefore receive `0xD800`; its documentation says it ignores validity and may create an invalid `char` ([`char::from_u32_unchecked`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32_unchecked)). The Reference lists “A value in a `char` which is a surrogate or above `char::MAX`” as invalid and classifies producing an invalid value—including returning one from an operation—as undefined behavior ([undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). This is a concrete supported safe-use counterexample, not merely a missing proof. + +Configuration partition: + +- `compact = false`: **PROVED** relative to the Rust 1.70 standard-library contract. The safe function directly returns `char::from_u32(raw)`, exactly the documented `Option` conversion. +- `compact = true`, debug assertions enabled: **PROVED**. Surrogates panic before the unsafe call; `RangeInclusive::contains` returns true exactly for an item in the range ([Rust 1.70 documentation](https://doc.rust-lang.org/1.70.0/std/ops/struct.RangeInclusive.html#method.contains)). Otherwise, `u16::MAX` is 65,535 ([`u16::MAX`](https://doc.rust-lang.org/1.70.0/std/primitive.u16.html#associatedconstant.MAX)); after the value-preserving cast the argument is at most `0xFFFF` and is not in `0xD800..=0xDFFF`, hence it is a Unicode scalar value. Rust 1.70 defines scalar values as `0..=0x10FFFF` excluding that surrogate range ([`char` validity](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity)). +- `compact = true`, debug assertions disabled: **UNSOUND**, by the counterexample above. Consequently the universal documented-behavior claim for this branch is also **UNPROVED**; the surrogate execution has UB rather than the promised panic. + +The unsafe block also has no adjacent `SAFETY` proof. A correct proof could not be written for the existing code over its published profile set. + +## Proposed redesign + +No source edit was requested. Replace only the `compact` body as follows, leaving both signatures and the non-`compact` implementation unchanged: + +```rust +/// With `compact`, returns the represented scalar and panics for a surrogate. +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + match char::from_u32(raw as u32) { + Some(c) => c, + None => panic!("surrogate code point"), + } +} + +/// Without `compact`, returns the represented scalar or `None`. +#[cfg(not(feature = "compact"))] +pub fn decode(raw: u32) -> Option { + char::from_u32(raw) +} +``` + +## Redesign proof and coverage + +Rust 1.70 describes `from_u32` as converting a `u32` to `char` and says it returns `None` when the input is not a valid `char` ([`char::from_u32`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32)). By the `u16::MAX`, widening-cast, and `char`-validity axioms above, every input is in exactly one of two cases: + +1. `raw` is outside the surrogate range. Its widened value is a valid scalar, so `from_u32` returns the represented `char`. +2. `raw` is a surrogate. It is the only possible invalid `char` value in the `u16` range, so `from_u32` returns `None`; `panic!` “Panics the current thread” ([Rust 1.70 `panic!`](https://doc.rust-lang.org/1.70.0/std/macro.panic.html)). + +The redesign contains no unsafe operation and no profile-sensitive assertion. Its argument arithmetic uses fixed-width types, so target pointer width and layout are irrelevant. The complementary `cfg` predicates exhaust the two feature values and preserve the exact published return types. `from_u32` was stable before Rust 1.70 (its cited page marks it stable since 1.52), and `panic!` since 1.0, so the redesign does not raise the MSRV. Panic-unwind versus panic-abort changes termination mechanics, not whether the documented panic is initiated. + +**Redesign verdict: PROVED** on Rust 1.70 for all stated feature, target, pointer-width, and ordinary-profile cases. For the open-ended `1.70+` range, this verdict is relative to **TCB-COMPAT-1**: later supported stable Rust releases preserve the cited availability and semantic propositions. This compatibility premise is explicit rather than inferred from stability badges; any weakening of those contracts or expansion beyond stable Rust triggers re-review. No tests or execution were used as evidence. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r081.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r081.md new file mode 100644 index 0000000000..2fa1c82568 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r081.md @@ -0,0 +1,35 @@ +# `Block` contract review + +## Scope and verdict + +Reviewed the exact supplied `lib.rs`, without execution or expansion, for source-level Rust soundness on all targets/profiles. There are no `cfg`, generated-code, panic, allocation, concurrency, or profile-dependent branches, so the arguments below are parametric over targets and profiles. + +- **`unsafe impl Block for Page`: PROVED** against the published clauses, under Rust 1.70 abstract semantics. +- **`first`: PROVED** for every correctly implemented `Block`, provided “readable for 16 bytes” has the Rust-safety meaning “the first 16 bytes are valid for non-atomic reads as initialized `u8`s throughout the receiver borrow.” It reads only byte zero. +- **Proof documentation: UNPROVED/inadequate as written.** Neither the `unsafe impl` nor the raw dereference has an adjacent proof, and “readable” is not defined. If the quoted meaning is not already the controlling published meaning, the smallest missing proposition is that byte zero is in a live allocation, initialized, provenance-permitted, and readable without an aliasing violation at the dereference. That ambiguity must not be repaired by silently strengthening the 1.x contract. +- **Open-ended Rust 1.70+ claim: UNPROVED without an accepted compatibility premise.** The citations below directly establish the needed propositions for 1.70.0. Extending them to every later and future stable release requires either per-release verification or an explicit TCB premise that these exact propositions remain guaranteed, with a release re-audit trigger. No dependency, tool, compiler-backend, or platform premise is otherwise consumed. + +The project’s stated 1.x SemVer promise and the supplied published contract are review premises, not inferred Rust axioms. + +## Reconstructed proof + +The Rust 1.70 Reference says that size is a multiple of alignment and alignment is at least one; it gives `u8` size 1. Therefore `u8` alignment is exactly 1 on every target. It also says `[T; N]` has size `size_of::() * N`, the same alignment as `T`, and contiguous element offsets ([layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#primitive-data-layout), [arrays](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). + +For `Page`, `ALIGN = 16` is a nonzero power of two. The `repr(C)` field-layout algorithm begins at offset zero, so the sole `[u8; 16]` field begins at the `Page` address; `align(16)` raises the struct alignment to at least 16 ([C layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs), [alignment modifier](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers)). A valid `&Page` therefore locates sixteen initialized `u8`s at a 16-aligned live address. `as_ptr` returns the slice buffer pointer and warns that the slice must outlive it ([`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)); here the array is embedded in the immutably borrowed `Page`, no call or mutation intervenes, and the promise is limited to that borrow. Thus `Page::base` establishes every published clause. The private field also gives downstream safe code no construction or mutation path that could invalidate this reasoning. + +For `first`, Rust permits use of a correctly implemented unsafe trait ([unsafe traits](https://doc.rust-lang.org/1.70.0/reference/items/traits.html#unsafe-traits)). The contract supplies a readable 16-byte region for the active borrow, hence a readable first byte. The returned pointer is non-null; alignment cannot fail for `u8` because its alignment is 1. The dereference occurs immediately, with no intervening callback, mutation, or pointer arithmetic. Rust 1.70 identifies dereferencing a dangling/unaligned raw pointer and producing an uninitialized integer as undefined behavior ([UB rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)); the stated contract reading discharges each condition. For `B = Page`, the value returned is `page.0[0]`. + +Suggested proof text (not applied): the unsafe-trait `# Safety` section should spell out implementer responsibility, the exact 16-byte live/initialized/read-permission proposition, address divisibility by `ALIGN`, and the interval. The `unsafe impl` should cite the representation/array derivation above. The dereference should state that it consumes only byte zero, that `u8` alignment is 1, and that the `Block` borrow remains active through the read. + +## What may change in 1.x + +Proof-oriented simplification is local: document `first` against only the subset it consumes; factor that subset into a named internal lemma; and add/reword proof comments or `# Safety` text only where the new wording is demonstrably equivalent to the existing contract. A new, additive safe API may be introduced alongside `Block`—for example, a safe `FirstByte { fn first_byte(&self) -> u8 }`, or, if sixteen bytes remain useful, a method returning `&[u8; 16]`. Existing `Block`, its implementations, and old consumers must remain supported through 1.x. This can stage an opt-in migration without claiming repository search is exhaustive. + +The following are breaking and require an explicitly authorized 2.0 migration: + +- weakening `base` from 16 readable bytes to one, dropping/changing `ALIGN`, non-nullness, alignment, or the borrow-duration guarantee: this invalidates unknown downstream unsafe consumers; +- strengthening or otherwise redefining implementer obligations: this can invalidate unknown downstream `unsafe impl`s; +- removing/renaming items, changing signatures, sealing the trait, or making it safe while retaining prose-only implementer obligations; +- removing/changing `Page`’s public `repr(C, align(16))` layout merely because the present implementation could be rewritten. + +The clean 2.0 endpoint for the observed need is a safe trait returning the byte by value, making `first` entirely safe and deleting the raw-pointer proof boundary. If downstream buffer access remains a supported use case, return `&[u8; 16]` instead. Add the replacement during 1.x, migrate known users voluntarily, then remove the legacy unsafe trait only in the authorized major release. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r082.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r082.md new file mode 100644 index 0000000000..eda86db4a9 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r082.md @@ -0,0 +1,75 @@ +# Focused unsafe Rust source review + +## Claim and verdicts + +**Scope.** Exact `lib.rs` snapshot SHA-256 +`42388bb1a03741a7725c9836cc134c205af3061efef7b8ee00ce38d2dc51aeeb`, under +Rust/core 1.80.0, on every target and ordinary profile requested by +`REQUEST.md` (SHA-256 +`9d559df45c2ef498f44516fed36ef84c82fa4a02a59edca55d2ff88a7b93eb02`). +The sole API surface is the safe public function `classify(u8) -> u8`. Thus +soundness quantifies over every `u8` without a caller safety precondition. The +mandatory behaviors are: (B1) `input == 0` panics; and (B2) every normal return +equals `input`. + +- **Soundness: UNSOUND.** The valid safe call `classify(0)` reaches undefined + behavior at `lib.rs:8`. +- **Behavioral contract: CONTRACT-BROKEN.** Independently of that UB, + `classify(1)` normally returns `2`, falsifying B2. B1 is also not guaranteed: + its path reaches UB rather than establishing a panic. +- **Combined requested claim: UNSOUND and CONTRACT-BROKEN.** These results hold + on every configuration in scope, relative only to TCB axiom AXIOM-1 below. + +## Boundary and obligation coverage + +There are no public fields, constructors, traits, impls, callbacks, macros, +generated items, dependencies, or invariant-bearing state. There is one unsafe +operation: the call to `core::hint::unreachable_unchecked` at `lib.rs:8` inside +the safe API. + +| ID | Site/contract | Required proposition | Evidence and result | +|---|---|---|---| +| S1 | `lib.rs:8`, `unreachable_unchecked()` | Control must never reach the call. | For the valid value `input = 0`, the literal `0` match arm is selected and immediately evaluates the call. This directly negates the obligation. By AXIOM-1, that execution has UB. **UNSOUND.** | +| B1 | `lib.rs:3`, panic guarantee | Every call with `input = 0` must panic. | The same call reaches `unreachable_unchecked`; AXIOM-1 supplies UB, not a panic postcondition. Backend behavior after UB cannot establish a source-level guarantee. **UNSOUND; no panic guarantee is established.** | +| B2 | `lib.rs:5`, normal-return guarantee | If the function returns normally, result equals `input`. | With `input = 1`, `lib.rs:9` returns the literal `2` normally; `2 != 1`. This counterexample executes no unsafe operation and needs no UB. **CONTRACT-BROKEN.** For `input = 2..=255`, `lib.rs:10` does return `input`; that does not repair the universal claim. | + +The unsafe block has no adjacent `SAFETY` proof. More importantly, no truthful +proof can be supplied for the current implementation: the enclosing match +proves that the supposedly unreachable site is reachable. A comment or a +caller-side prose restriction cannot make this safe public API sound. + +## Authoritative premise and derivation + +**AXIOM-1 (accepted, consumed by S1/B1).** Rust 1.80.0 `core::hint` documents +the safety contract of `unreachable_unchecked` as: “Reaching this function is +*Undefined Behavior*.” See the exact versioned +[`core::hint::unreachable_unchecked` Safety section](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety). + +Local control flow establishes reachability when `input = 0`; AXIOM-1 then +entails UB. Because `classify` is safe and accepts every `u8`, this is an +in-scope valid-use counterexample. Separately, the direct `1 => 2` arm provides +the UB-free B2 counterexample. + +No dependency, implementation, tool, platform, probabilistic, or deployment +assumption is consumed. This is a source-level result under documented Rust +abstract semantics, not a claim about one emitted binary. + +## Configuration closure, residual scope, and resolution + +The source contains no `cfg`, target-dependent operation, feature, generated +code, allocator, FFI, concurrency, or profile-dependent check. Both +counterexamples use universally representable `u8` values and the same +unconditional arms. Therefore the argument is parametric over all requested +targets, optimization levels, overflow-check settings, and panic strategies; +neither optimization nor a coincidental runtime trap can cure source-level UB. +No target was built or executed, and none is needed for these universal source +arguments. + +Minimal resolution would require executable panic behavior in the `0` arm and +returning `1` for input `1` (or otherwise making every normal result equal its +input). Retaining `unreachable_unchecked` in the reachable `0` arm cannot be +resolved by documentation alone. No source change was made, as requested. + +Re-audit is required after any change to `classify`, its documentation or +visibility, the supported Rust/configuration scope, or AXIOM-1's controlling +contract. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r083.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r083.md new file mode 100644 index 0000000000..dc7e7ffd03 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r083.md @@ -0,0 +1,104 @@ +# Focused unsafe-Rust review: `p1j4` + +## Claim and verdict + +**UNSOUND.** For `lib.rs` SHA-256 +`ac762e6e2bd87884bbbcfbe7bbf706e5b9dae9b078e82d01252111a9a6ad84e8`, the +claim that every well-typed safe use is free of Rust undefined behavior on +Rust 1.70+, every target, and ordinary profiles is false. A wholly safe client +reaches UB under the Rust 1.70 abstract semantics. Because 1.70 is in the +requested version set, that counterexample refutes the universal claim; no +forward-compatibility premise is needed. The prior design note supplied no +premise or inherited verdict. + +This is a source-level result, not a claim about any compiler binary. The source +has no `cfg`, generated code, dependencies, FFI, assembly, allocator use, or +profile-sensitive checks. The counterexample uses only `u8` and references, so +its Rust-1.70 argument is parametric over every target/profile on which this +source compiles. + +## Boundary and obligation inventory + +The complete public safe surface is `View<'a, T>` and `new`, `get`, and +`get_mut`; both fields are private. There are no explicit trait impls or macros. +The only unsafe operations are the raw-pointer-to-reference constructions at +`lib.rs:16` and `lib.rs:20`. + +The intended representation invariant is that `ptr` came from the unique +`&'a mut T` accepted by `new`, remains live and suitable for reference creation, +and has no conflicting access when consumed. `new` creates the pointer and the +`PhantomData<&'a mut T>` marker. The marker carries the original borrow in the +type, but it does not relate a method result to that method's receiver borrow. + +Both consumer obligations therefore fail compositionally: + +- `get` must not let its new shared reference coexist with mutation of the same + pointee. Its explicit result lifetime is `'a`, so it can remain after the + temporary `&self` borrow ends. +- `get_mut` must have exclusive access for the full lifetime of its returned + reference. Its result is likewise explicitly `'a`, not the lifetime of + `&mut self`; safe code can call another method while the result remains live. + +## Proven safe-client counterexample + +```rust +fn overwrite_then_read(shared: &u8, unique: &mut u8) -> u8 { + *unique = 1; + *shared +} + +fn safe_client() -> u8 { + let mut value = 0_u8; + let mut view = View::new(&mut value); + let shared = view.get(); + let unique = view.get_mut(); + overwrite_then_read(shared, unique) +} +``` + +There is no `unsafe` in this client. In each current method signature, the +receiver lifetime is elided but the output lifetime is the explicit impl +parameter `'a`. Rust 1.70's method-elision rule assigns the receiver lifetime +only to **elided** output lifetimes, so neither returned reference keeps `view` +borrowed ([Rust 1.70 Reference, lifetime elision](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html#lifetime-elision-in-functions)). +Consequently both calls are permitted and both results address `value`. + +At `overwrite_then_read`, both references are passed and thus are live for the +call (the Reference says a passed reference is “live at least as long as that +function call”). The write through `unique` mutates the `u8` reached through +the live shared reference. Rust 1.70 classifies mutating immutable data as UB +and says “all data reached through a shared reference ... is immutable”; the +`UnsafeCell` exception does not apply to `u8` +([Rust 1.70 Reference, behavior considered undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). +This discharges the `UNSOUND` verdict with a valid safe execution rather than a +mere missing proof. + +No unsafe-API postconditions exist. No broader safe-API robustness property was +requested; soundness already fails. + +## Required change and separate proposal status + +Tie each result to its receiver borrow: + +```rust +pub fn get(&self) -> &T { /* raw-pointer reborrow */ } +pub fn get_mut(&mut self) -> &mut T { /* raw-pointer reborrow */ } +``` + +Under the cited elision rule, these elided outputs acquire the receiver +lifetime, preventing the demonstrated escaped shared/mutable pair. The patch +should also add adjacent `SAFETY` proofs that name the representation invariant, +show `new` establishes pointer liveness/validity, and show privacy plus the +receiver-bounded reborrow prevents conflicting access. + +**Proposal status: UNIMPLEMENTED / UNPROVED.** Blocking this counterexample is +not a proof of a source snapshot that does not exist. Re-audit after changing +the signatures or representation, and whenever public construction/traits, +the Rust support range, conditional code, or the invariant changes. + +## TCB and residual scope + +The only consumed axioms are the two exact Rust 1.70 Reference pages linked +above. No dependency, tool-derived, environmental, or prior-audit premise is +consumed. Review was static and source-only; no target was built, run, tested, +or expanded. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r084.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r084.md new file mode 100644 index 0000000000..389d411a24 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r084.md @@ -0,0 +1,37 @@ +# Acceptance review: `Piece for Tail` + +## Decision and verdicts + +**REJECT this exact snapshot.** The rejection is compelled by a version- and target-independent public-contract defect. + +| Claim | Verdict | Applicability | +|---|---|---| +| `Tail` satisfies `Piece::FIELD` and `Piece::project` | **CONTRACT-BROKEN** | Exact source; every Rust version, target, and ordinary profile on which it compiles | +| A valid call of the concrete `Tail::project` performs no UB | **PROVED** | Rust 1.70.0 abstract semantics; all targets and ordinary profiles; no additional TCB | +| Safe `increment_tail` performs no UB and changes `pair.0[1]` to its old value plus one modulo 2^32 | **PROVED** | Rust 1.70.0 abstract semantics; all targets and ordinary profiles; no additional TCB | +| The preceding soundness claim over the literal open-ended range “Rust 1.70+” | **UNPROVED** | No Rust compatibility premise is permitted, so Rust 1.70 documentation cannot establish all later and future versions | + +No in-scope execution reaching UB was established, so the contract defect is not relabeled `UNSOUND`. Contract compliance nevertheless independently fails and is sufficient to reject an immutable vendoring candidate. + +## Controlling contracts and defect + +`Piece::FIELD` must be “the name of a direct declared field of `Owner` whose type is `Item`” (`lib.rs:7-8`), and `project` must return a pointer to that field (`lib.rs:10-15`). For `Tail`, `Owner = Pair`, `Item = u32`, and `FIELD = "tail"` (`lib.rs:21-24`). But `Pair` is a tuple struct with exactly one direct field, `.0`, whose type is `[u32; 2]` (`lib.rs:18`). There is no direct declared field named `tail`, and no direct field of type `u32`. The Reference likewise describes this declaration form as a [tuple struct](https://doc.rust-lang.org/1.70.0/reference/items/structs.html). + +`project` returns `&raw mut (*owner).0[1]` via `addr_of_mut!` (`lib.rs:26-27`): a pointer to an element nested inside the direct array field. Thus both `FIELD`'s assertion and `project`'s stated postcondition are false. This conclusion depends only on the literal source contracts and declarations, so it covers the entire requested configuration and version range. + +## Reconstructed soundness proof + +The absent local safety proofs can be reconstructed for the concrete operations: + +1. A well-typed call to `increment_tail` supplies `pair: &mut Pair`, so the `Pair` is live, initialized, aligned, and exclusively accessible for the call. Rust 1.70 expressly permits coercion from [`&mut T` to `*mut T`](https://doc.rust-lang.org/1.70.0/reference/type-coercions.html#coercion-types). Therefore the pointer passed at `lib.rs:32` satisfies `project`'s sole caller safety precondition. +2. A valid `Pair` contains a valid `[u32; 2]`; the Reference says array elements are [always initialized](https://doc.rust-lang.org/1.70.0/reference/types/array.html). Index `1` is in bounds. Default-representation fields are [properly aligned](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-default-representation), and the [array-layout rule](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout) places element `1` at its compiler-defined in-bounds element offset. This holds parametrically for every target; no numeric layout assumption is used. +3. Rust 1.70 documents that [`addr_of_mut!`](https://doc.rust-lang.org/1.70.0/std/ptr/macro.addr_of_mut.html) creates the raw pointer without an intermediate reference, while its place expression remains subject to the usual rules. Here `owner` is neither dangling nor unaligned and both projections are in bounds, discharging the raw dereference obligations identified by the [undefined-behavior rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html). The result therefore points to the live, aligned, initialized second `u32` and is derived from the exclusive input reference. +4. `&mut *` at `lib.rs:32` consequently creates a valid sub-borrow. No competing pointer/reference is created or used, and there is no callback or intervening code. Reading the initialized `u32`, applying [`wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add)—documented as modular addition—and writing the resulting valid `u32` are sound. The first array element is untouched. + +This direct implementation proof does not consume the false “direct declared field” postcondition, so the concrete safe wrapper is sound at the stated cutoff despite the impl's contract failure. Both unsafe blocks (`lib.rs:27,32`) lack adjacent `SAFETY` proofs; the reconstruction above closes implementation reasoning but does not cure the shipped proof-documentation deficiency. + +## Coverage, TCB, and residual scope + +The inspected target consists only of `REQUEST.md` and `lib.rs`. There are no `cfg` branches, features, dependencies, FFI, concurrency, allocation, generated artifacts, or profile-sensitive assertions. `wrapping_add` removes overflow-profile variation, and target-varying layout is covered by typed field/array projections. TCB: none beyond the cited Rust 1.70.0 Reference and standard-library contracts. + +The audit cutoff is Rust 1.70.0. Extending the soundness proof to any later release requires checking that release's versioned authoritative text (or admitting an exact compatibility premise, which this request forbids). The source-level **CONTRACT-BROKEN** verdict needs no such extension and already decides acceptance. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r085.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r085.md new file mode 100644 index 0000000000..d3e8215448 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r085.md @@ -0,0 +1,26 @@ +# Focused acceptance review: `Piece for Tail` + +## Decision and exact verdicts + +**REJECT.** Artifact: the exact supplied `lib.rs`; scope: `unsafe impl Piece for Tail`, its associated constant and method, and safe `increment_tail`. Audit cutoff: 2026-07-31. `Supported(c)` is the request's open-ended Rust 1.70+ set, every target, and ordinary profiles; there are no features, dependencies, `cfg`s, build scripts, FFI, concurrency, allocation, or generated artifacts in the supplied snapshot. + +- **Contract compliance: CONTRACT-BROKEN.** This is established on Rust 1.70.0 for every target/profile, and therefore refutes the aggregate requested claim. +- **Source-level soundness: PROVED on Rust 1.70.0**, for both `Tail::project` on every call satisfying its documented precondition and every well-typed safe call of `increment_tail`, relative only to TCB-R085-1 below. No UB witness was found. +- **Source-level soundness over the entire open-ended Rust 1.70+ set: UNPROVED.** The inspected exact axioms are Rust 1.70.0 axioms. Applying them to every later and future release requires either per-release authoritative coverage or a compatibility premise; the request forbids an additional TCB premise. The smallest missing proposition is that every semantic fact cited below holds throughout every later member of `Supported(c)`. This coverage limitation is independent of the contract-breaking witness. + +## Boundary and obligation ledger + +The relevant public surfaces are unsafe trait `Piece` (`Owner`, `Item`, `FIELD`, unsafe `project`), public `Pair` and its public tuple field, public `Tail`, the unsafe impl, and safe `increment_tail`. Other possible `Piece` impls are outside this focused review. The only local invariant is **I-Pair**: while the wrapper's `&mut Pair` is live, it exclusively identifies an initialized `Pair`; its second array element is an initialized, aligned `u32`, and no competing access occurs while the derived `&mut u32` is used. + +| Obligation | Result | Compact proof | +|---|---|---| +| `FIELD` names a direct declared `Pair` field of type `u32` | **CONTRACT-BROKEN** | `Pair(pub [u32; 2])` declares exactly one direct tuple field, position `0`, of type `[u32; 2]`; it has no direct `u32` field and no field named `tail`. The Reference grammar distinguishes named fields (`IDENTIFIER : Type`) from tuple fields (`Type` only): [Rust 1.70 structs](https://doc.rust-lang.org/1.70.0/reference/items/structs.html). Thus `FIELD = "tail"` is false independently of layout. | +| `project` returns a pointer to *that direct declared field* | **CONTRACT-BROKEN** | `(*owner).0[1]` selects element 1 nested inside direct field `.0`; it is not a direct field of `Pair`. A UB-free witness is a live uniquely borrowed `Pair([0, 0])` passed to `project`: index 1 is in the two-element array, yet the returned pointer designates the nested `u32`. Rust indexing is zero-based and returns the indexed memory location, with bounds checking: [Rust 1.70 array indexing](https://doc.rust-lang.org/1.70.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions). This whole execution is defined by the next proof, so it independently satisfies the `CONTRACT-BROKEN` witness rule. | +| `Tail::project` avoids UB for every valid call | **PROVED (1.70.0)** | The precondition supplies a live, unique `Pair`, hence a non-dangling, aligned `owner`; `.0` and in-bounds `[1]` preserve those facts. `addr_of_mut!` creates a raw pointer without an intermediate reference, while its expression remains subject to ordinary validity rules: [Rust 1.70 `addr_of_mut!`](https://doc.rust-lang.org/1.70.0/std/ptr/macro.addr_of_mut.html). The relevant forbidden case is dereferencing a dangling or unaligned raw pointer, and references themselves may not be dangling, unaligned, or point to invalid data: [Rust 1.70 UB rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html). Those cases are excluded by the call precondition and the in-bounds projection. The returned raw pointer validly designates initialized element 1, although it violates the stronger direct-field postcondition. | +| Safe `increment_tail` has no hidden safety precondition | **PROVED (1.70.0)** | Its `&mut Pair` establishes I-Pair and therefore `project`'s call precondition. The returned pointer is non-null, aligned, live, dereferenceable, initialized, and exclusive; no intervening call/access exists before `&mut *` or while `value` is used. The 1.70 pointer rules say a reference-derived raw pointer remains valid while the object is live and reference/pointer accesses are not interleaved: [Rust 1.70 pointer safety](https://doc.rust-lang.org/1.70.0/std/ptr/index.html#safety). Reading then writing the `u32` preserves validity; `wrapping_add` performs modular addition at the type boundary: [Rust 1.70 `wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add). Hence debug/release overflow behavior, panic strategy, optimization, target layout, and endianness are irrelevant. `increment_tail` has no documented behavioral postcondition; its name cannot supply one. | + +Both unsafe blocks lack adjacent `SAFETY` proofs; the material reconstruction above proves their 1.70.0 implementation obligations but does not repair the false public contracts. + +## TCB, evidence, and residual scope + +**TCB-R085-1:** only the exact Rust 1.70.0 Reference and standard-library propositions linked above. No safe/unsafe dependencies, external specifications, tools, tests, compiler implementation, deployment restrictions, or compatibility assumptions were consumed. The target was not built, run, tested, or expanded. This is a focused source-level result, not a whole-crate or binary audit. Re-review is required for any source/contract change, or to close the later-Rust region with exact authoritative coverage. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r086.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r086.md new file mode 100644 index 0000000000..8bb0a02c62 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r086.md @@ -0,0 +1,49 @@ +# `decode` configuration-preserving source review + +## Claim and verdict + +Snapshot: `lib.rs` SHA-256 `1ebbedcffa0ad4b2c3183ec04b4789d9c2daaeae6c19dc0e1d7f926ac685e89b`; request SHA-256 `8d8801a4dddb5d5b9a1d1971fcda4eb017bcf0261fe7d97b1cd983bc51240a0b`. Scope is exactly the two conditional public `decode` definitions; there is no generated code, stateful invariant, dependency, or unsafe caller contract. Valid use is every well-typed safe call. The required set is stable Rust 1.70+, every target/pointer width and ordinary profile, with `compact` either set or unset. + +**Current combined verdict: UNSOUND.** The configuration `compact = set` with debug assertions disabled admits undefined behavior. The `compact = unset` branch is **PROVED** source-sound and implements its documented result contract; the `compact = set` branch is sound only when debug assertions execute. Thus the supported-set universal claim fails. + +**Proposed redesign verdict: PROVED at Rust 1.70.0 for all requested feature, target, width, and profile configurations, relative to TCB-R086 below.** For later stable releases, the same result is **PROVED relative to COMPAT-1**. Without accepting that explicit compatibility premise (or rechecking each later release's applicable documentation), the literal open-ended `1.70+` theorem remains **UNPROVED**; this is not a proposal to raise the MSRV or drop a configuration. + +## Finding: release-profile UB + +At `lib.rs:6`, `debug_assert!` is the sole check that `raw` is not `0xD800..=0xDFFF`. Rust 1.70 documents that an optimized build does not execute `debug_assert!` unless debug assertions are enabled ([`debug_assert!`, Uses](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html#uses)). Therefore, in an ordinary profile with them disabled, the safe call `decode(0xD800)` reaches line 7. + +The widening cast preserves the numeric value because an unsigned smaller-to-larger integer cast zero-extends ([Reference: numeric casts](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast)). Rust defines surrogates as `0xD800..=0xDFFF` and excludes them from `char` ([`char` validity](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity)). The unchecked conversion can construct an invalid `char` ([`from_u32_unchecked`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32_unchecked)); the Reference classifies producing a surrogate `char` as undefined behavior ([behavior considered undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). This is a concrete valid-safe-use counterexample on every target. It also prevents establishing the documented surrogate-panic guarantee in that configuration. + +The unsafe block has no adjacent `SAFETY` derivation. Even a complete comment could not repair the false premise; the redesign should delete the unsafe operation. + +## Configuration-preserving redesign + +```rust +/// With `compact`, returns the represented scalar and panics for a surrogate. +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + char::from_u32(raw as u32).expect("raw is a surrogate") +} + +/// Without `compact`, returns the represented scalar or `None`. +#[cfg(not(feature = "compact"))] +pub fn decode(raw: u32) -> Option { + char::from_u32(raw) +} +``` + +No source edit was made. Both public signatures and both documentation contracts are unchanged. All used APIs exist in Rust 1.70. + +## Compact proof and obligation ledger + +- **Feature coverage:** `cfg(feature = "compact")` and its `not(...)` are mutually exclusive and exhaustive because `not` negates its predicate and `cfg` includes an item exactly when its predicate is true ([Reference: conditional compilation](https://doc.rust-lang.org/1.70.0/reference/conditional-compilation.html#the-cfg-attribute)). These are the only public surfaces. +- **Compact, non-surrogate:** every `u16` is at most `0xFFFF` ([`u16::MAX`](https://doc.rust-lang.org/1.70.0/std/primitive.u16.html#associatedconstant.MAX)); after value-preserving widening, a non-surrogate is therefore a Unicode scalar value. Rust 1.70 `char::from_u32` returns `Some(char)` for valid `char` values ([safe conversion](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32)), and `expect` returns the contained `Some` value ([`Option::expect`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect)). The result is exactly the represented scalar. +- **Compact, surrogate:** the same validity definition makes the widened value invalid, so `from_u32` returns `None`; `expect` is documented to panic on `None`. No unsafe operation is reached or required. +- **Non-compact:** the unchanged safe conversion returns the represented scalar in `Some` exactly for valid `u32` inputs and `None` otherwise. +- **Other axes:** the proof uses fixed-width values and documented safe-library behavior, not pointer layout, target properties, optimization, overflow checks, or debug assertions. Panic strategy may unwind or abort after initiating the documented panic; no unwind behavior was promised. Hence targets, pointer widths, and ordinary profiles are parametric. + +## TCB, residual scope, and triggers + +**TCB-R086:** AX-CHAR-170, AX-U16-170, AX-CAST-170, AX-CFG-170, AX-DEBUG-170, and AX-EXPECT-170 are exactly the linked Rust 1.70 Reference/std propositions. There are no dependency, environmental, generated-code, or tool-result assumptions. **COMPAT-1** (explicit non-authoritative compatibility premise): every later supported stable Rust release preserves the availability and exact consumed semantics of the safe conversion, `Option::expect`, unsigned widening, and the two `cfg` predicates over this domain. + +This is a focused source review, not a whole-crate or binary audit. Re-review on changes to either function/documentation, supported configurations or MSRV, or any consumed Rust contract; for an unbounded release range, validate COMPAT-1 against each new stable release. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r087.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r087.md new file mode 100644 index 0000000000..900e0de2ff --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r087.md @@ -0,0 +1,45 @@ +# Focused unsafe-Rust source review + +## Claim, scope, and verdicts + +Artifact: `targets/m2q8/lib.rs`, SHA-256 `42388bb1a03741a7725c9836cc134c205af3061efef7b8ee00ce38d2dc51aeeb`. The complete language-reachable API surface is the safe free function `pub fn classify(u8) -> u8`; there are no public fields, constructors, traits/impls, macros, generated items, callbacks, dependencies other than `core`, or invariant-bearing state in the supplied source. + +`Supported(c)` means exact Rust and `core` 1.80.0, every target on which this source is accepted, every edition accepted by that release, and all ordinary profile choices (including optimization, overflow-check, debug-assertion, and panic-strategy choices). The claim is source-level freedom from Rust undefined behavior for every well-typed safe call, plus both documented behavior clauses. No binary/backend correctness claim is made. + +| Claim | Verdict | +|---|---| +| Safe-API soundness | **UNSOUND** | +| “Panics when `input == 0`” | **UNPROVED** | +| “On normal return, returns `input`” | **CONTRACT-BROKEN** | + +Thus the combined requested claim is not `PROVED`. + +## Authoritative premises / TCB `m2q8-inline-v1` + +- **AXIOM-MATCH (Rust Reference 1.80.0):** A match value is “sequentially compared to the patterns,” and the first matching arm is selected and entered. [Match expressions](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html#match-expressions) +- **AXIOM-LITERAL (Rust Reference 1.80.0):** “Literal patterns match exactly the same value as what is created by the literal.” [Literal patterns](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns) +- **AXIOM-UNREACHABLE (core 1.80.0):** The safety contract states, “Reaching this function is *Undefined Behavior*.” [core::hint::unreachable_unchecked](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety) + +These exact authoritative propositions are the entire TCB consumed by the source-level proof. There are no dependency, platform, deployment, probabilistic, compatibility, or tool-result assumptions. + +## Obligation ledger and derivations + +### O-SOUND: every safe call is UB-free — `UNSOUND` + +`classify(0)` is a well-typed safe call and has no caller-side safety obligation. By AXIOM-LITERAL, the first `0` pattern matches that input; by AXIOM-MATCH, control enters its arm. That arm unconditionally evaluates `core::hint::unreachable_unchecked()`. AXIOM-UNREACHABLE classifies reaching that function as UB. This is a complete valid-use UB witness, so the safe API is unsound. + +The unsafe block has no adjacent `SAFETY` proof. More importantly, no truthful proof can establish its controlling precondition: the local branch condition proves that the call is reachable for exactly the value routed to it. This is an implementation defect as well as missing proof documentation; adding a comment cannot resolve it. + +### O-PANIC: zero input panics — `UNPROVED` + +The only source path for `input == 0` is the UB path above; it does not establish the documented panic outcome. Under the required whole-execution witness rule, that UB-containing execution cannot also witness `CONTRACT-BROKEN`. No separate UB-free zero-input execution or equivalent existence proof exists in the inspected source. The smallest missing implication is: evaluating the zero arm must initiate a Rust panic rather than reach UB. Therefore this behavioral guarantee is `UNPROVED`, independently of the soundness verdict. + +### O-IDENTITY: a normal return equals the input — `CONTRACT-BROKEN` + +Use the independent safe call `classify(1)` in an otherwise UB-free context. AXIOM-LITERAL and AXIOM-MATCH select `1 => 2`; the unsafe arm is not evaluated, and the function returns normally with `2`. Since `2 != 1`, this UB-free execution falsifies the documented conditional postcondition. It is therefore a valid `CONTRACT-BROKEN` witness. + +## Configuration closure, residual scope, and resolution + +The proofs are parametric over the full supported set: the file contains no `cfg`, target/layout operation, arithmetic, assertion, allocation, concurrency, FFI, assembly, generated code, or profile-sensitive branch. Match selection and the cited `core` safety contract apply identically for every requested target/profile class. No build, test, macro expansion, or target execution was used as evidence. + +The minimum acceptable correction is to make the zero arm actually panic and to make the `1` arm return `1` (or deliberately revise the public behavioral contract, with compatibility review). Any source or documentation change requires re-review of all three obligations; a Rust/core version or supported-configuration change requires rechecking premise applicability. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r088.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r088.md new file mode 100644 index 0000000000..452a4967e6 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r088.md @@ -0,0 +1,46 @@ +# Focused review and redesign of `ticket` + +## Claim, scope, and verdict + +Snapshot: `REQUEST.md` SHA-256 `f4cb0fe5d667cdde8536d103ea2bf8c46a4219916acb811b0e33c4d2cb5bc376`; `lib.rs` SHA-256 `23f55cd3e96c8372d71a09336d84f72de191fd5f48de693d0be11762537dfa9f`. This is a source-only review of `Ticket` and `ticket`, not a whole-crate or binary audit. The requested domain is stable Rust 1.70+ on every target and every ordinary build profile, with no dependencies or deployment assumptions. The relevant profile axis is whether debug assertions execute; target width, endianness, optimization, and panic strategy introduce no other source branch here. + +**Current safe-API soundness: `UNSOUND`.** A well-typed safe call on the explicitly supported Rust 1.70.0 in an optimized build with debug assertions disabled reaches undefined behavior. One supported member refutes the universal claim, so the open-ended `1.70+` range does not need a compatibility assumption to establish this verdict. + +**Documented behavior:** for nonzero `id`, the return-value obligation is discharged wherever the cited `NonZeroUsize` contract applies. For zero with debug assertions enabled, the function panics before the unsafe call. For zero with them disabled, soundness is `UNSOUND` and the “panics” postcondition is `UNPROVED`, not `CONTRACT-BROKEN`: the known release-profile witness contains UB and therefore cannot also be a UB-free behavioral counterexample. + +## Boundary and obligation inventory + +The complete exposed handwritten surface is the opaque safe type `pub struct Ticket` (its tuple field is private) and safe free function `pub fn ticket(usize) -> Ticket`. There are no unsafe APIs, custom impls, public fields, callbacks, dependencies, `cfg`s, macros, generated artifacts, FFI, or concurrency. Safe callers may pass every `usize`, including zero. + +`TICKET-NZ`: whenever a `Ticket` exists, its field must be a valid `NonZeroUsize`; the only source producer is `ticket`, and subsequent safe operations in this snapshot only move or drop the opaque value. The sole unsafe obligation at `lib.rs:10` is therefore: at that point, `id != 0`. The source has no adjacent `SAFETY` proof. + +## Finding and derivation + +Rust 1.70 documents `NonZeroUsize` as having `usize`'s bit validity except that zero is invalid, and documents [`new_unchecked`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new_unchecked) with “The value must not be zero” and undefined behavior for zero. Rust 1.70's [`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html#uses) documentation says optimized builds do not execute it unless `-C debug-assertions` is passed. + +Choose Rust 1.70.0, an optimized ordinary build without that flag, and safe input `id = 0`. `lib.rs:9` supplies no runtime check; control reaches `lib.rs:10` with zero; `new_unchecked(0)` violates its exact safety precondition and has undefined behavior. There is no caller obligation to rescue a safe function. The smallest resolution is to validate with a safe constructor and remove the unsafe call. + +## Recommended redesign + +Replace only the function body; retain the type, exact safe signature, visibility, and documentation: + +```rust +pub fn ticket(id: usize) -> Ticket { + match NonZeroUsize::new(id) { + Some(id) => Ticket(id), + None => panic!("id must not be zero"), + } +} +``` + +This is preferable to changing `debug_assert!` to `assert!` while retaining `new_unchecked`: it has no unsafe operation, unsafe contract, or invariant-restoration proof site. The panic message is not documented and thus is not a compatibility commitment. + +Conditional proof plan for the implemented replacement: + +- Rust 1.70's safe [`NonZeroUsize::new`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new) “creates a non-zero if the given value is not zero.” For every target-specific `usize`, the match exhausts `Option`: a nonzero input produces the same nonzero value and constructs `Ticket`, establishing `TICKET-NZ` and the return-value postcondition; zero selects `None` and invokes [`panic!`](https://doc.rust-lang.org/1.70.0/core/macro.panic.html), which “panics the current thread.” +- The body has no assertion-elision, overflow, layout, allocation, target, or panic-strategy-dependent branch. `panic=abort` changes panic handling, not whether `panic!` is invoked. Thus the proof is parametric over ordinary profiles and targets. +- This is a design sketch, not a new audited artifact, so it receives no `PROVED` verdict. At implementation time, audit the exact snapshot and instantiate the same proof with exact documentation for every Rust release through that audit's cutoff. The open-ended future part of `1.70+` requires that per-release check or an explicitly accepted compatibility premise. + +## TCB and residual scope + +Consumed axioms are only the linked Rust 1.70.0 standard-library contracts for `debug_assert!`, `NonZeroUsize::{new,new_unchecked}`, and `panic!`. No additional assumption, dependency, tool result, or prior audit is consumed. This proves a Rust abstract-semantics source finding, not compiler-backend correctness. Re-audit on changes to the body, signature/documentation, `Ticket` representation or field visibility, supported configurations/toolchains, or any consumed standard-library contract. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r089.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r089.md new file mode 100644 index 0000000000..4be43145b2 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r089.md @@ -0,0 +1,42 @@ +# Focused review and redesign of `decode` + +## Claim, scope, and verdict + +Reviewed `lib.rs` SHA-256 `1ebbedcffa0ad4b2c3183ec04b4789d9c2daaeae6c19dc0e1d7f926ac685e89b`, under the support policy in `REQUEST.md` SHA-256 `8d8801a4dddb5d5b9a1d1971fcda4eb017bcf0261fe7d97b1cd983bc51240a0b`. Scope is the two configuration-selected public safe `decode` APIs and the one unsafe operation. `Supported(c)` means Rust 1.70+, any target and pointer width, any ordinary profile, and either value of `compact`; no generated code or dependencies are present in the supplied artifact. + +**Current soundness: UNSOUND.** A well-typed safe call reaches undefined behavior in a supported configuration. This conclusion needs no assumption about post-1.70 compatibility because Rust 1.70 itself supplies the witness. + +**Documented behavior:** the non-`compact` branch is proved at Rust 1.70 by the checked conversion contract. For the `compact` surrogate case, the current postcondition “panics” is **UNPROVED**, not `CONTRACT-BROKEN`: the known counterexample execution contains UB, so it cannot also witness a defined failure to panic. + +## Obligation and finding + +At `lib.rs:8`, `char::from_u32_unchecked(raw as u32)` must produce a valid `char` for every safe `u16` input. Rust 1.70 says a `char` is a Unicode scalar, with surrogates exactly `0xD800..=0xDFFF`, and that the unchecked conversion can create an invalid `char` ([`char` documentation](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32_unchecked)). The Reference says producing a `char` containing a surrogate is UB ([invalid values](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)). + +Witness: choose Rust 1.70, `compact`, an ordinary optimized release build without `-C debug-assertions`, and call the safe API as `decode(0xD800)`. Rust 1.70 documents that an optimized build does not execute `debug_assert!` unless that flag is supplied ([`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html)). The `u16`-to-`u32` cast zero-extends and therefore remains `0xD800` ([numeric casts](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#semantics)). The unchecked call then produces an invalid surrogate `char`, hence UB. The safe caller has no safety obligation that could exclude this input. + +The smallest missing implication in the existing proof is: “at the unchecked call, `raw` is not a surrogate” in configurations where debug assertions are disabled. `debug_assert!` cannot establish it across the supported profile set; Rust's own documentation recommends it only in safe code. There is also no adjacent `SAFETY` proof. + +Configuration partition: + +- `compact = false`: `char::from_u32(raw)` returns `None` exactly when `raw` is not a valid `char`, otherwise the represented scalar ([Rust 1.70 checked conversion](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32)); no unsafe operation exists. +- `compact = true`, debug assertions enabled: a surrogate panics before the unsafe call; every remaining `u16` is at most `0xFFFF` and not a surrogate, hence is a valid scalar. +- `compact = true`, debug assertions disabled: the witness above applies. Target, pointer width, and optimization do not repair the missing value check. + +## Recommended redesign (not implemented) + +Replace only the `compact` body with the checked primitive: + +```rust +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + char::from_u32(raw as u32).expect("surrogate code point") +} +``` + +Leave the non-`compact` definition unchanged. Proof plan: widening preserves the `u16` value; because every `u16` is below `char::MAX`, `char::from_u32` returns `None` precisely for the surrogate range and `Some` of the represented scalar otherwise. `Option::expect` returns the `Some` value and panics on `None` ([Rust 1.70 `Option::expect`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect)). Thus the candidate panics for every surrogate in every profile and returns the scalar for every other input, using only safe APIs. + +This preserves both configuration-specific signatures, the `compact` selection, return types, all documented outcomes, and the Rust 1.70 MSRV; it is parametric over target, pointer width, and ordinary profile. Panic payload and unwind-versus-abort aftermath are not promised by the supplied documentation. Eliminating the unsafe operation also eliminates the local safety-comment obligation. Because this is an unimplemented design, it receives no artifact verdict; audit the resulting source as a new snapshot. + +## TCB and residual scope + +TCB consists only of the cited Rust 1.70 Reference/std propositions and the supplied support policy. No tests, execution, expansion, compiler implementation behavior, or backwards-compatibility premise was used. The source-level result does not claim correctness of a particular compiler binary. Re-review is required after implementation, any signature/documentation/configuration change, or before extending a behavioral proof to later Rust releases using their versioned contracts. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r090.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r090.md new file mode 100644 index 0000000000..4bbda76104 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r090.md @@ -0,0 +1,46 @@ +# Focused unsafe-Rust source review: `m2q8` + +## Claim and verdict + +For `lib.rs` SHA-256 `42388bb1a03741a7725c9836cc134c205af3061efef7b8ee00ce38d2dc51aeeb`, compiled with exactly Rust 1.80.0 on every target and ordinary profile for which this source is accepted, the claim is: every well-typed safe call to the exported API is free of Rust undefined behavior and satisfies every documented behavior, relative to TCB `m2q8-TCB-1` below. + +- **Soundness: UNSOUND.** The valid safe call `classify(0)` reaches `core::hint::unreachable_unchecked()`. +- **Documented behavior, aggregate: CONTRACT-BROKEN.** The UB-free call `classify(1)` returns `2`, falsifying “On normal return, returns `input`.” +- **“Panics when `input == 0`”: UNPROVED.** The zero-input source execution contains UB. Under the required whole-execution witness rule, it cannot also prove a UB-free behavioral refutation. + +These are source-level Rust abstract-semantics results, not claims about a particular backend binary. + +## Snapshot, boundary, and configuration closure + +The supplied snapshot contains only [`lib.rs`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/m2q8/lib.rs:1) and [`REQUEST.md`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/m2q8/REQUEST.md:1). The complete language-reachable API surface is the safe public function `classify(u8) -> u8` at `lib.rs:6`; its sole unsafe obligation site is the call at `lib.rs:8`. There are no fields, traits, impls, macros, generated artifacts, dependencies, FFI, assembly, concurrency, allocation, or invariant-bearing state in the supplied artifact. + +The only semantic partition is `input = 0`, `input = 1`, and `input = 2..=255`, exhaustive for `u8`. There is no `cfg`, target feature, profile-sensitive assertion/arithmetic, or build input. The derivations below therefore apply unchanged to every requested target and ordinary profile; optimization and panic strategy cannot repair a source-level reachable-UB witness or change the value of the defined `input = 1` arm. + +## TCB `m2q8-TCB-1` + +Only these exact Rust 1.80.0 authoritative axioms are consumed; there are no additional assumptions or tool-derived results. + +- **AX-U8:** the Reference gives `u8` minimum `0` and maximum `2^8-1`; hence `0` and `1` are valid values. [Numeric types](https://doc.rust-lang.org/1.80.0/reference/types/numeric.html#integer-types). +- **AX-PATTERN:** literal patterns “match exactly the same value as what is created by the literal,” while `_` “matches any value.” [Literal patterns](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns); [wildcard pattern](https://doc.rust-lang.org/1.80.0/reference/patterns.html#wildcard-pattern). +- **AX-MATCH:** the “first arm with a matching pattern is chosen as the branch target.” [Match expressions](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html). +- **AX-RETURN:** a function block's tail expression, if evaluated, “ends up being returned to the caller.” [Function body](https://doc.rust-lang.org/1.80.0/reference/items/functions.html#function-body). +- **AX-UNREACHABLE:** Rust 1.80.0 documents: “Reaching this function is *Undefined Behavior*.” [`core::hint::unreachable_unchecked`](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety). + +## Obligation ledger and derivations + +| ID | Obligation and derivation | Result | +|---|---|---| +| S1 | A safe API may impose no hidden caller safety precondition. By AX-U8, `0` is valid. By AX-PATTERN and AX-MATCH, it selects the first arm, so the call is reached. AX-UNREACHABLE then entails UB. | **UNSOUND**, all requested configurations. | +| B1 | For input `1`, AX-PATTERN and AX-MATCH select `1 => 2`; AX-RETURN makes the normal result `2`. This path executes only safe literal/match/return operations and never enters the unsafe arm, so it is UB-free under the audited abstract semantics. Since `2 != 1`, the documented identity guarantee is false. | **CONTRACT-BROKEN**, all requested configurations. | +| B2 | For input `0`, the documentation mandates a panic, but S1 proves the execution reaches UB. No independent UB-free witness or derivation establishes either fulfillment or refutation of the panic guarantee. | **UNPROVED**, all requested configurations. | +| B3 | For every `input` in `2..=255`, the literal arms fail, `_` matches, and the tail arm evaluates to `input`; AX-RETURN proves the identity guarantee for this region. | **PROVED** for this region. | + +The unsafe block has no adjacent `SAFETY` proof. Its required local proposition is that control cannot reach the call. The dominating match establishes the opposite when `input == 0`; thus no valid proof can be reconstructed for the current implementation. The smallest false implication is “the `input == 0` arm is unreachable.” + +## Findings and disposition + +1. **Critical implementation defect:** the safe call `classify(0)` causes UB on every requested configuration. Minimum acceptable correction is to remove the reachable `unreachable_unchecked` operation; a real panic on this arm would align with the published behavior. +2. **Behavioral contract defect:** the UB-free `input == 1` arm returns `2` rather than `input`. Minimum acceptable correction is to return `1`/`input` or deliberately revise the published contract with compatibility analysis. +3. **Proof-documentation defect:** the unsafe operation has no `SAFETY` comment, and its actual obligation is false. Documentation alone cannot resolve findings 1–2. + +No source change, build, execution, expansion, or test was performed. Re-review is required if the source, documented contract, Rust version, target/profile scope, or any cited authoritative semantics changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r091.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r091.md new file mode 100644 index 0000000000..331bc53ce9 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r091.md @@ -0,0 +1,50 @@ +# `Block` contract and evolution review + +## Claim and verdict + +Scope is exactly `lib.rs`: `Block`, the `Page` implementation, and `first`, for safe calls using any valid `Block` implementation, Rust 1.70+, and all targets/profiles. There are no dependencies, `cfg`s, generated artifacts, panics, allocation, or concurrency in scope. + +**Current implementation: PROVED** for Rust 1.70, and for later stable Rust releases relative to **COMPAT-1** below. `Page` establishes every published `Block` guarantee; `first` is sound and returns the first byte at `base()`. The source nevertheless has a proof-documentation defect: neither unsafe site has an adjacent `SAFETY` proof, and the unsafe trait should present its implementer obligations under `# Safety` and define “readable” precisely. This reconstruction proves the code; it does not excuse those omissions or change the published contract. + +## Contract and proof ledger + +The controlling implementer contract has four clauses: `ALIGN` is nonzero; it is a power of two; during the `&self` borrow `base()` is non-null and `ALIGN`-aligned; and the 16-byte range beginning there is live, allocated, and initialized for reads. That last expansion records the technical meaning required for “readable.” Rust 1.70 says an unsafe trait has “extra safety conditions that must be upheld by implementations,” and `unsafe impl` asserts they were satisfied ([Reference](https://doc.rust-lang.org/1.70.0/reference/unsafe-keyword.html#unsafe-traits-unsafe-trait)). + +`Page` discharges all clauses: + +- `ALIGN == 16`, hence it is nonzero and a power of two. +- Under `repr(C)`, field layout starts at offset zero, so the sole field begins at the `Page` address. `align(16)` raises the struct alignment to at least 16 ([Rust 1.70 layout rules](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs), [alignment modifier](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers)). Thus the field pointer is 16-aligned on every target. +- `[u8; 16]` consists of 16 contiguous one-byte elements ([array layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). A valid shared `Page` reference keeps that initialized field live; `as_ptr` “returns a raw pointer to the slice’s buffer,” and the borrow outlives the immediate use ([slice `as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)). Therefore the returned pointer is non-null and its first 16 bytes remain readable for the stated interval. + +`first` consumes only a strict subset: one initialized, live byte and alignment for `u8`. Rust 1.70 makes size a multiple of alignment, gives `u8` size 1, and requires alignment at least 1, so `u8` alignment is 1 ([size/alignment](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#size-and-alignment), [primitive layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#primitive-data-layout)). Sixteen-byte readability entails one-byte readability; non-nullness is explicit; no call intervenes before `*block.base()`. This excludes the documented raw-dereference UB cases of a dangling/unaligned pointer and producing an uninitialized integer ([undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined), [dangling definition](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#dangling-pointers)). + +Suggested proof text, without changing behavior: + +```rust +// SAFETY: Block::base guarantees that, for this borrow, `p` is non-null +// and the first byte is live and initialized for reading. A u8 has alignment 1. +let p = block.base(); +unsafe { *p } +``` + +The `unsafe impl` should analogously record `ALIGN == 16`, `repr(C)` field offset zero, raised struct alignment, the 16-byte array layout, and the shared-borrow lifetime. + +## What 1.x may simplify + +The local proof may deliberately mention only the subset `first` consumes. Documentation may be reorganized, terminology made explicit, and proof comments added if those edits preserve every existing obligation and guarantee exactly. None of that permits deleting `ALIGN`, reducing 16 bytes to one, or dropping the 16-alignment promise: unknown downstream generic consumers may rely on each clause. + +A compatible staged redesign can be **added** in 1.x while retaining `Block` unchanged: expose a safe capability such as `trait FirstByte { fn first_byte(&self) -> u8; }` (or an inherent/free safe API), implement it for `Page` with `self.0[0]`, and deprecate `Block` for new uses. This removes unsafe proof burden for new implementations, but the legacy trait, its implementations, and generic `first` must remain supported throughout 1.x. A generic implementation preserving the current `first` signature cannot eliminate its raw-pointer read; only the contract makes that read safe. + +## What needs an authorized 2.0 migration + +Weakening `base` to promise only one readable byte, removing/changing `ALIGN`, changing the method/signature, or removing `Block` weakens guarantees or source compatibility for downstream consumers. Strengthening implementer obligations can invalidate downstream `unsafe impl`s. Either direction is breaking under the stated 1.x policy and requires explicit major-version authorization. + +For 2.0, prefer the safe `FirstByte` capability if that is the actual requirement. If consumers need all bytes, return `&[u8; 16]`; if some independently need dynamic alignment, split that into a separately justified unsafe capability. Introduce the replacement and deprecation in 1.x, publish exact mapping guidance, then remove or redefine legacy `Block` only in 2.0 and re-audit the implemented snapshot. + +## TCB and review triggers + +- **AXIOM-1:** the linked Rust 1.70 Reference/std propositions. +- **COMPAT-1:** later stable Rust releases preserve those exact propositions over supported targets/profiles. Without accepting this compatibility premise, the open-ended `1.70+` result is proved only at 1.70; re-review on a material documentation change. +- **POLICY-1:** the request’s published-contract and ordinary-SemVer commitments. + +Re-review any contract, representation, pointer construction, supported-toolchain, target, or replacement-API change. No source was edited or executed. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r092.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r092.md new file mode 100644 index 0000000000..f9f51e1f06 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r092.md @@ -0,0 +1,59 @@ +# Implemented-candidate unsafe Rust review + +## Claim and verdict + +**UNSOUND.** This verdict covers the exact `lib.rs` snapshot SHA-256 `ac762e6e2bd87884bbbcfbe7bbf706e5b9dae9b078e82d01252111a9a6ad84e8`: all well-typed safe uses of its public API, under the request's supported set of Rust 1.70+, all targets, and ordinary profiles. One valid safe use on Rust 1.70 reaches undefined behavior, which refutes that universal claim. The witness and defect are independent of target and profile. No verdict is inherited from `DESIGN-NOTE.md`. + +The open-ended future part of “1.70+” needs no compatibility premise to refute the combined claim: Rust 1.70 itself is a member. This is a source-level verdict under documented Rust abstract semantics, not a compiler-binary claim. + +## Boundary, invariant, and obligation coverage + +The complete source has one public type and three public safe methods: `View`, `new`, `get`, and `get_mut`. Its fields are private. There are no traits, macros, dependencies, conditional compilation, generated code, FFI, assembly, allocator use, or target/profile branches. The only unsafe operations are the raw-pointer-to-reference conversions at lines 16 and 20. + +`new` obtains `ptr` from a valid `&'a mut T`; `PhantomData<&'a mut T>` tells the compiler that `View` acts as though it stores that type for certain safety properties ([Rust 1.70 `PhantomData`](https://doc.rust-lang.org/1.70.0/std/marker/struct.PhantomData.html)). The intended representation invariant must additionally ensure that `ptr` remains suitable for every reference created and that all such references obey aliasing for their full live intervals. + +The obligations are: + +- `get`: at line 16, prove that producing `&'a T` is valid, aligned, non-dangling, and alias-compatible throughout the returned reference's live interval. +- `get_mut`: at line 20, prove the same facts plus exclusive mutable access throughout the returned `&'a mut T`'s live interval. +- Both safe methods must preserve those obligations against every later safe call. + +Neither obligation closes. Each receiver has a fresh elided lifetime, while the output explicitly uses the struct lifetime `'a`. Rust 1.70 says each elided parameter lifetime becomes distinct; the receiver rule applies only to *elided output* lifetimes ([lifetime elision](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html)). Consequently, neither returned reference keeps `View` borrowed. `PhantomData` does not change these method signatures. + +## Finding F-1: returned lifetime permits conflicting aliases + +This entirely safe client is admitted by the API: + +```rust +fn clash(a: &mut i32, b: &mut i32) { + *a = 1; + *b = 2; +} + +let mut value = 0; +let mut view = View::new(&mut value); +let a = view.get_mut(); +let b = view.get_mut(); +clash(a, b); +``` + +Both calls return pointers to the same `value`, yet the first result does not borrow `view`, so the second call is permitted. At `clash`, both mutable references are simultaneously live and alias the same object. Rust 1.70 classifies breaking pointer-aliasing rules as UB, states that `&mut T` follows the scoped `noalias` model, and makes a passed reference live for at least the function call ([behavior considered undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)). Its standard-library reference documentation also expressly describes preventing “multiple simultaneous mutable borrows” ([reference type](https://doc.rust-lang.org/1.70.0/std/primitive.reference.html)). Thus this safe execution contains UB. No caller precondition can repair a safe API. + +`get` has the analogous composition failure: retain its `&'a T`, then call `get_mut` and pass/use both references. Therefore changing only one method is insufficient. + +The unsafe blocks also have no adjacent `SAFETY` proof. That documentation defect is secondary here: the missing alias-preservation implication is false, not merely undocumented. + +## Required change (unimplemented proposal) + +At minimum, tie both outputs to their receiver borrows: + +```rust +pub fn get(&self) -> &T { unsafe { &*self.ptr } } +pub fn get_mut(&mut self) -> &mut T { unsafe { &mut *self.ptr } } +``` + +Then name the private pointer/lifetime invariant and add adjacent proofs for validity, lifetime, alignment, and aliasing. Preferably eliminate the unsafe representation entirely by storing `value: &'a mut T` and safely reborrowing it with those same receiver-tied return types. Either change may be source-incompatible for callers relying on the overlong result lifetime. Both are candidate designs only: **UNIMPLEMENTED / NOT AUDITED**. The implemented replacement requires a fresh audit. + +## TCB, residual scope, and triggers + +TCB: the quoted Rust 1.70 Reference and standard-library propositions only; no dependencies or tools were trusted, and no execution/testing evidence was used. No separate documented postconditions were supplied beyond the reference-returning signatures. Re-audit after either signature/representation change, any new constructor or trait implementation, visibility change, configuration axis, or supported-set change. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r093.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r093.md new file mode 100644 index 0000000000..8bdf6977ca --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r093.md @@ -0,0 +1,95 @@ +# Supported-domain source review + +## Claim and verdict + +**PROVED (source-level Rust soundness)** for `lib.rs` SHA-256 +`6f87e944cec7ee2727f7c7d32aa382de88987ea791082175d611fff5bf44012b`: +for every well-typed safe call to the exported `first`, execution is free of +Rust undefined behavior in every configuration in review domain `D`, relative +to TCB `R093` below. + +`D` is deliberately not a choice between the two current policies. It is the +union of every configuration that either policy affirmatively supports: + +- Rust 1.79.0, 1.80.0, 1.81.0, or 1.82.0; edition 2021; +- target `x86_64-unknown-linux-gnu` or `aarch64-unknown-linux-gnu`; +- without `fast`: every version/target pair; +- with `fast`: x86_64 on 1.79.0--1.82.0, and aarch64 on + 1.80.0--1.82.0. + +Thus the theorem covers all 15 combinations claimed by either document and is +at least as strong as the soundness theorem under either policy separately. +It does **not** resolve which document controls the project's support promise. +The audit cutoff is 1.82.0. Compiler/backend correctness and binary-level +claims are outside this source-level theorem. No broader safe-API behavioral +postcondition was documented or requested. + +## Snapshot, boundary, and configuration closure + +The reviewed package is `domain-review` 1.0.0, with no dependencies, build +script, generated code, macros, FFI, concurrency, target-specific source, or +invariant-bearing state. The complete public surface is one safe free function, +`first(&[u8]) -> Option`; `cfg(feature = "fast")` selects exactly one of two +implementations. There is no public unsafe API and no caller safety obligation. + +The feature's two predicates are mutually exclusive and exhaustive. Target and +profile do not alter the relevant slice contracts or this source's control or +data flow, so each branch's proof is parametric over both named targets. The +1.82.0 toolchain file chooses a default; it does not erase the published older +version commitments. CI is sampled evidence only and, as `CI.md` itself says, +does not define support; no test result is used in this proof. + +## Obligation ledger and derivation + +**O1, `fast` disabled (`lib.rs:3-6`) — PROVED.** The implementation contains +only safe operations (`slice::first` and `Option::copied`). A well-typed safe +caller supplies a valid shared slice; this branch creates no unsafe obligation. + +**O2, `fast` enabled (`lib.rs:8-15`) — PROVED.** In each exact version's slice +documentation—[1.79.0 `is_empty`](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.is_empty) / [`get_unchecked`](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.get_unchecked), +[1.80.0 `is_empty`](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.is_empty) / [`get_unchecked`](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.get_unchecked), +[1.81.0 `is_empty`](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.is_empty) / [`get_unchecked`](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.get_unchecked), and +[1.82.0 `is_empty`](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.is_empty) / [`get_unchecked`](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked)—`is_empty` says it "Returns true if the slice has a length of 0," while `get_unchecked` says it returns a reference without bounds checking and that calling it with an out-of-bounds index is undefined behavior. + +The unsafe expression is reached only when `is_empty()` is false. Therefore +`bytes.len() != 0`; because slice lengths are nonnegative, `bytes.len() > 0`, +so `0 < bytes.len()` and index 0 is in bounds. This discharges the complete +documented safety condition of `get_unchecked(0)`. It returns `&u8`, whose value +is copied while the input borrow is live; there is no intervening call, +mutation, alias creation, panic edge, or retained reference. The empty path +does not execute the unsafe operation. + +## TCB R093 + +- **AXIOM-STD-1:** the exact Rust 1.79.0--1.82.0 standard-library contracts + linked above, each applied only to its matching release. +- The ordinary Rust validity and safe-operation rules for well-typed `&[u8]` + calls in those releases. + +There are no dependency, external-specification, deployment, tool-derived, or +compatibility assumptions. In particular, no later documentation was projected +backward: all four versioned pages were checked. + +## Findings + +**POLICY-1 — conflicting support publications (not a soundness failure).** +The policies disagree about `fast` on x86_64/1.79.0 and on +aarch64/1.80.0--1.81.0. With no authorized precedence rule, the official +support predicate is indeterminate. The union-domain proof above avoids making +that governance choice and proves the disputed cases too. Resolution requires +the project to publish one reconciled policy or an explicit supersession rule; +CI and the default toolchain cannot supply it. + +**DOC-1 — missing local unsafe proof (implementation obligation proved).** +`lib.rs:13` has no adjacent `SAFETY` comment. The material proof reconstructed +above should be made local, or the safe non-`fast` expression should be reused. +Proposed proof text: + +```rust +// SAFETY: This branch is reached only when `bytes.is_empty()` is false. +// Hence `bytes.len() > 0`, so index 0 is in bounds for `get_unchecked`. +Some(unsafe { *bytes.get_unchecked(0) }) +``` + +Re-audit is required if the source, either policy, the supported Rust/target/ +feature domain, or any consumed versioned standard-library contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r094.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r094.md new file mode 100644 index 0000000000..2d407204a8 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r094.md @@ -0,0 +1,37 @@ +# Version-partitioned source review + +## Claim and verdict + +Artifact: `targets/h5p9/lib.rs`, SHA-256 `e561568dfc6262bc6312f1a346e970dfab1f0d8f5102984e80e3b06cf2885e9b`. Scope is the sole public safe API, `advance_marker`, and its unsafe raw-pointer operation at line 4. For every well-typed safe call, with no caller safety precondition, the claim is freedom from Rust undefined behavior under the documented abstract semantics. + +Supported configurations are exactly Rust/core 1.79.0 and 1.80.0, separately; every target supported by each release; every ordinary profile; and every supported edition. Target, profile, and edition do not select different source or change the cited array layout or pointer contract. There are no `cfg`s, features, dependencies, generated artifacts, inputs, state, callbacks, allocation, concurrency, panic, or FFI axes. + +- **Rust 1.79.0: UNSOUND.** Every safe invocation reaches an unsafe operation whose allocation precondition is false. +- **Rust 1.80.0: PROVED**, relative only to the versioned Rust axioms below. +- **Combined supported set: UNSOUND**, because it includes the 1.79.0 region and a valid safe call in that region reaches UB. + +There are no documented API postconditions beyond the return type, so no separate `CONTRACT-BROKEN` claim is in scope. + +## Obligation ledger and derivation + +`core::ptr::null::<[u8; 0]>()` produces a null raw pointer: both versioned pages say, “Creates a null raw pointer,” and state that its address is 0 ([1.79.0 `null`](https://doc.rust-lang.org/1.79.0/core/ptr/fn.null.html), [1.80.0 `null`](https://doc.rust-lang.org/1.80.0/core/ptr/fn.null.html)). The versioned Reference states: “An array of `[T; N]` has a size of `size_of::() * N`” ([1.79.0 array layout](https://doc.rust-lang.org/1.79.0/reference/type-layout.html#array-layout), [1.80.0 array layout](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#array-layout)). Therefore `size_of::<[u8; 0]>() = 0`, and `add(1)` computes a mathematical byte offset of `1 * 0 = 0` on every target. + +### Rust 1.79.0 + +The controlling [`*const T::add` safety contract](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add) classifies violation of any listed condition as UB and requires: “Both the starting and resulting pointer must be either in bounds or one byte past the end of the same allocated object.” The starting pointer is null and therefore designates no allocated object; that conjunct is false. The other two literal clauses hold: byte offset 0 fits `isize`, and address 0 plus offset 0 does not wrap and fits `usize`. Failure of the allocation clause nevertheless makes executing `add` UB. Because line 4 is unconditional and the safe function has no inputs, an ordinary safe call is a valid UB witness. + +### Rust 1.80.0 + +The revised [`*const T::add` safety contract](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add) gates the same-allocation requirement on a nonzero byte offset and expressly says: “If it is zero, then the function is always well-defined.” The computed offset is zero, so no allocation relationship is required. The remaining clauses also hold: 0 fits `isize`, and adding 0 to address 0 neither wraps nor exceeds `usize`. No dereference, reference creation, or later unsafe consumer occurs. The returned null raw pointer is a valid value of its return type. Thus every safe call is UB-free. + +The proofs are parametric over target and ordinary profile: they use only integer multiplication by zero and contracts stated without target/profile qualification. This closes the requested configuration set without sampling or execution. + +## TCB and evidence + +Accepted `AXIOM` entries are exactly the six linked, version-matched standard-library/Reference propositions for array layout, `ptr::null`, and `pointer::add`. No cross-version compatibility premise, implementation behavior, tool result, dependency, external specification, or deployment assumption is consumed. No target was built or executed. + +## Finding and remediation + +**Critical implementation defect (Rust 1.79.0) / proof-documentation defect (both regions):** the public safe API unconditionally violates the 1.79.0 callee contract, and the unsafe block has no adjacent `SAFETY` proof. Documentation cannot repair the 1.79.0 implementation. The smallest source remedy preserving the defined 1.80.0 return value is to return `core::ptr::null::<[u8; 0]>()` directly and remove the unnecessary `unsafe`/`add`; alternatively, exclude 1.79.0 from support and add an adjacent version-applicable proof of the zero-offset rule. No edit was made, as requested. + +Re-review is required if the source, either supported Rust version, the target/profile domain, or any consumed versioned contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r095.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r095.md new file mode 100644 index 0000000000..f63ebedd25 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r095.md @@ -0,0 +1,66 @@ +# Implemented-candidate source audit + +## Claim and verdict + +**UNSOUND** for the requested aggregate claim: every well-typed safe use of the exact `lib.rs` snapshot, on Rust 1.70+, all targets, and ordinary profiles, is free of Rust undefined behavior. The counterexample below is entirely safe and reaches UB on Rust 1.70.0, which alone refutes that universal domain. The same controlling rule remains explicit in the current Rust 1.97.1 Reference. This audit's release cutoff is 1.97.1; a later release is a review trigger, although it cannot repair the already-refuted aggregate claim. + +Scope is exactly `View`, its private representation, `new`, `get`, `get_mut`, implicit move/drop behavior, and compiler-provided trait behavior in the supplied `lib.rs`. `DESIGN-NOTE.md` supplied context only; no prior verdict was inherited. There are no dependencies, `cfg`s, generated artifacts, macros, FFI, allocation, or target/profile branches. No code was built or executed. + +## Boundary, invariant, and obligations + +The safe surface consists of the public type and three public safe methods; its fields are private and there are no alternate constructors or explicit trait impls. The intended representation invariant is: + +`V-UNIQUE`: `ptr` is derived from the unique `&'a mut T` accepted by `new`, remains aligned, non-dangling, and points to a valid `T` while the `View` can be used, and every reference created from it obeys aliasing for its entire live interval. + +`new` establishes the pointer-origin/lifetime part: it converts the supplied reference directly, while `PhantomData<&'a mut T>` makes the type act as though it contains that borrow for lifetime checking ([Rust 1.70 `PhantomData`](https://doc.rust-lang.org/1.70.0/core/marker/struct.PhantomData.html#unused-lifetime-parameters)). Moving or dropping `View` does not itself dereference `ptr`. Both accessors consume `V-UNIQUE`, but neither unsafe block has an adjacent `SAFETY` proof. + +Obligation dispositions: + +- `get`: pointer alignment, allocation lifetime, and pointee validity follow from construction, but the implementation cannot ensure that the returned shared reference is not later overlapped by `get_mut`: **FAILED**. +- `get_mut`: the same basic pointer facts hold, but the implementation cannot ensure exclusive access for the returned reference's live interval; another accessor call is permitted: **FAILED**. +- Configuration closure: the failure is source-level and uses `i32`; it has no target, optimization, overflow, panic, or generated-code premise. + +## U-1: accessors detach results from receiver borrows + +Both return types explicitly use the struct lifetime `'a` rather than the lifetime of `&self`/`&mut self`. The receiver borrow therefore need last only for the call, so this well-typed client may retain one result and reborrow `view`: + +```rust +fn collide(read: &i32, write: &mut i32) -> i32 { + *write = 1; + *read +} + +let mut value = 0_i32; +let mut view = View::new(&mut value); +let read = view.get(); +let write = view.get_mut(); +let _ = collide(read, write); +``` + +Every operation in the client is safe. During `collide`, both references are live and designate `value`; the write mutates data reached through the live shared reference. Rust 1.70 lists “mutating immutable data” as UB and states that data reached through a shared reference is immutable unless contained in `UnsafeCell` ([Rust 1.70 Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)); `i32` supplies no exception. The current Reference states the same rule even more directly ([Rust 1.97.1 Reference, aliasing/immutable bytes](https://doc.rust-lang.org/1.97.1/reference/behavior-considered-undefined.html#r-undefined.alias)). + +The smallest false implication is: “encoding `&'a mut T` in `PhantomData` makes a borrow of the `View` last for `'a`.” It does not. The receiver-lifetime rule applies to **elided output** lifetimes; these outputs explicitly name `'a` ([Rust 1.70 lifetime-elision rules](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html#lifetime-elision-in-functions)). `get_mut` can likewise be called twice to manufacture two overlapping mutable references. + +There are no separate prose postconditions to classify as `CONTRACT-BROKEN`; the defect violates safe-API soundness and the reference types' required aliasing conditions. + +## Recommended replacement (not a verdict on this snapshot) + +Prefer eliminating the raw pointer and unsafe code: + +```rust +pub struct View<'a, T> { + value: &'a mut T, +} + +impl<'a, T> View<'a, T> { + pub fn new(value: &'a mut T) -> Self { Self { value } } + pub fn get(&self) -> &T { &*self.value } + pub fn get_mut(&mut self) -> &mut T { &mut *self.value } +} +``` + +The output lifetimes are then tied to receiver reborrows, so safe borrowing prevents `get_mut` while a `get` result is live and prevents a second access while a `get_mut` result is live. Changing the current `'a` return guarantee is necessarily source-breaking for callers that retain a result independently of `View`; representation and auto-trait compatibility also require review. This is an **unimplemented proposal**, not `PROVED`; its exact implementation must receive a fresh audit. + +## TCB and residual scope + +TCB: only the exact Rust 1.70 and 1.97.1 Reference/std propositions linked above. No dependency, tool, backend, platform, or compatibility assumption is consumed. Binary-codegen correctness and unspecified future Rust releases are outside this source-level review. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r096.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r096.md new file mode 100644 index 0000000000..e5689f1d67 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r096.md @@ -0,0 +1,44 @@ +# Focused review: `Block`, `Page`, and `first` + +## Claim, scope, and verdict + +Snapshot: `lib.rs` SHA-256 `8e347fd2a5ca16fa1bd9a7b6019fc57227346bd895c5af794d2efa76265f01a3`; no generated code or dependencies. Scope is the three public items shown, stable Rust 1.70+, every target/profile, with audit cutoff 2026-07-31. A valid downstream `unsafe impl Block` must satisfy the published implementer contract; callers and safe code are otherwise adversarial. + +**Soundness: PROVED relative to TCB-c3g8-r1. Published `Block` postconditions for `Page`: PROVED. `first` result (the byte at `base()+0`): PROVED.** No UB or defined contract-breaking witness was found. “Readable for 16 bytes” is read literally as permission to perform initialized, non-atomic `u8` loads at offsets `0..16` without lifetime, aliasing, or data-race UB for the stated borrow. A weaker meaning would not be a safety contract sufficient for `first`. + +TCB-c3g8-r1 contains only: + +- **AXIOM-170:** the exact Rust 1.70 Reference/std propositions cited below. +- **COMPAT-1 (explicit non-Reference premise):** those exact propositions remain applicable to supported stable releases after 1.70 through the cutoff. The same clauses were checked at the cutoff endpoint, Rust 1.97.1 ([layout](https://doc.rust-lang.org/1.97.1/reference/type-layout.html), [`as_ptr`](https://doc.rust-lang.org/1.97.1/std/primitive.slice.html#method.as_ptr), [UB](https://doc.rust-lang.org/1.97.1/reference/behavior-considered-undefined.html)). Rejecting COMPAT-1 leaves versions other than the cited endpoints `UNPROVED`; releases after the cutoff require re-audit. + +There are no configuration branches: `cfg`, features, generated code, FFI, allocation, panic, and profile-dependent checks are absent. The layout and byte-read derivations are target-parametric. + +## Boundary and obligation ledger + +| Site | Obligation | Disposition | +|---|---|---| +| `Block` (lines 3–10) | Unsafe public implementer boundary: `ALIGN` is a nonzero power of two; `base` supplies all four temporal/pointer clauses. | Contract controlling; downstream impls out of implementation scope but their obligations cannot be weakened silently. Rust 1.70 says a correctly implemented unsafe trait is safe to use ([unsafe traits](https://doc.rust-lang.org/1.70.0/reference/items/traits.html#unsafe-traits)). | +| `unsafe impl Block for Page` (15–21) | Establish every trait clause, not merely what `first` consumes. | **PROVED**, derivation P1 below. | +| safe `first` (23–25) | Every correct `B: Block` makes the raw `u8` load defined; no safe-caller precondition. | **PROVED**, derivation P2 below. | + +**P1 — `Page`.** `ALIGN = 16` is locally a nonzero power of two. Rust 1.70 specifies `[T; N]` as `N` contiguous elements and `[u8; 16]` as 16 bytes because `u8` has size 1 ([size/array layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). The `repr(C)` field-placement algorithm starts at offset zero, so the sole field begins at the `Page` address; `repr(align(16))` raises the struct alignment to 16 ([C structs](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs), [alignment modifier](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers)). Thus the field and its first element are 16-aligned. `as_ptr` returns the slice buffer pointer and remains usable while the slice outlives it ([`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)). A valid shared `&Page` is non-null, aligned, live, and contains 16 initialized `u8`s; it also prevents conflicting safe mutation. Consequently the returned pointer is non-null, 16-aligned, and readable for all 16 bytes throughout the borrow. + +**P2 — `first`.** Let `p = block.base()`. The implementer contract supplies non-nullness, `ALIGN` alignment, and initialized read permission for 16 bytes throughout the borrow. Since `ALIGN` is nonzero, it is at least 1; Rust 1.70 gives `u8` size 1 and requires size to be a multiple of alignment, hence `u8` alignment is 1. Offset zero is therefore properly aligned and lies in the readable region. The load occurs immediately while `block: &B` remains borrowed. Rust identifies dereferencing a dangling/unaligned raw pointer and producing an uninitialized integer as UB ([UB clauses](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)); the derived facts exclude each condition. The loaded value is exactly byte zero. + +## Finding: proof artifacts are deficient + +Implementation correctness does not excuse the missing proof. The unsafe trait lacks an explicit `# Safety` implementer section, the `unsafe impl` has no adjacent derivation, and line 24 has no `SAFETY` comment. Add equivalent clarification only—define “the borrow” and “readable” without changing their reach—and place P1/P2 beside their sites. If proposed wording adds lifetime, initialization, aliasing, or synchronization duties not already entailed by “readable,” it strengthens downstream implementer obligations and is not a 1.x documentation-only change. + +## What can change in 1.x + +The proof for `first` may consume only the minimum capability: initialized read permission for byte zero during the borrow. `first` need not mention or validate the other 15 bytes beyond the implication `16 readable => 1 readable`, and it does not operationally need 16-byte alignment. This is a proof simplification, **not** permission to edit `Block`. + +Equivalent proof comments, internal refactoring, and a new parallel safe migration API are possible while retaining `Block`, every clause, `Page`’s full impl, its public representation, and the existing generic `first` unchanged. Adding public trait items or blanket impls needs ordinary coherence/method-resolution compatibility review; it is unnecessary here because `first` is already the safe one-byte projection. + +Weakening any `base` guarantee breaks unknown downstream consumers; strengthening it breaks unknown downstream implementations. Removing/sealing the trait, deleting `ALIGN`, shortening 16 bytes to one, changing `first` to accept only `Page`, or removing `Page`’s promised alignment/layout therefore requires explicit breaking-change authority. Repository search cannot close a public ecosystem proof. + +## Recommended authorized 2.0 migration + +Extract the actual capability as a safe trait such as `FirstByte { fn first_byte(&self) -> u8; }`; implement it for `Page` with `self.0[0]`, and make the free `first` delegate to it. This removes the raw-pointer load, unsafe implementer boundary, `ALIGN`, 16-byte region theorem, and (if no separately retained layout API needs them) the representation/alignment constraints from this path. If downstream users need raw 16-byte access, split that into a separately named unsafe trait retaining the complete old contract rather than burdening `FirstByte`. + +Ship the safe capability alongside the old API during 1.x, deprecate without weakening, publish implementer/consumer migration guidance, and remove or alter old commitments only in an authorized 2.0. Any implemented redesign is a new artifact requiring a fresh audit. Re-audit on source/contract/layout changes, support expansion, or a new Rust release. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r097.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r097.md new file mode 100644 index 0000000000..b7e910b2ac --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r097.md @@ -0,0 +1,39 @@ +# Version-partitioned source review + +## Claim and verdict + +Scope is the exact supplied `lib.rs`, whose only public surface is safe free function `advance_marker() -> *const [u8; 0]` and whose only unsafe operation is `*const::add(1)` at line 4. For each well-typed safe call, the soundness claim has no caller precondition. The supported set is `{Rust 1.79.0, Rust 1.80.0} × every target × every ordinary profile`. + +| Region | Source-level soundness verdict | +|---|---| +| Rust 1.79.0, every target/profile | **UNSOUND** | +| Rust 1.80.0, every target/profile | **PROVED**, relative to `TCB-R097-v1` below | +| Combined supported set | **UNSOUND**, because it includes the Rust 1.79.0 region | + +There is no documented behavioral postcondition beyond the return type, so there is no separate `CONTRACT-BROKEN` result. The function name is not a normative promise. + +## Obligation and derivation + +`core::ptr::null::<[u8; 0]>()` produces a null raw pointer in both [1.79.0](https://doc.rust-lang.org/1.79.0/core/ptr/fn.null.html) and [1.80.0](https://doc.rust-lang.org/1.80.0/core/ptr/fn.null.html). The versioned `size_of` contract states that `[T; n]` has size `n * size_of::()` ([1.79.0](https://doc.rust-lang.org/1.79.0/core/mem/fn.size_of.html), [1.80.0](https://doc.rust-lang.org/1.80.0/core/mem/fn.size_of.html)); hence `[u8; 0]` has size zero on every target. The byte offset of `add(1)` is therefore `1 * 0 = 0`. It fits `isize`, and adding zero cannot wrap the address space, independently of target or profile. + +**Rust 1.79.0.** Its [`add` safety contract](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add) unconditionally requires: “Both the starting and resulting pointer must be either in bounds or one byte past the end of the same allocated object.” The starting pointer is instead the null pointer; the contemporaneous [`core::ptr` rules](https://doc.rust-lang.org/1.79.0/core/ptr/index.html#safety) say it is “never valid, not even for accesses of size zero.” No allocation is produced or identified anywhere in the function. Thus the allocated-object conjunct is false despite the zero byte offset. Violating an `add` safety condition makes the call itself undefined behavior. Every ordinary safe call reaches it, providing a valid safe-use counterexample on every target/profile; the safe API is unsound. + +**Rust 1.80.0.** The revised [`add` contract](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add) conditions the allocated-object requirement on a nonzero byte offset and expressly says: “If it is zero, then the function is always well-defined.” The derived offset is zero, while the remaining overflow and address-wrap clauses hold as shown above. Therefore the unsafe call is well-defined for this null base. Possessing or returning null in a raw-pointer value is permitted; the [1.80.0 raw-pointer documentation](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html) states that raw pointers can be null. Any later dereference requires a separate unsafe act and is not a hidden obligation of this safe function. Soundness is therefore proved for the complete 1.80.0 region. + +## Findings and remediation + +1. **Critical — UNSOUND on Rust 1.79.0 (`lib.rs:4`).** The safe function invariably violates that version's `add` contract. A safety comment cannot repair it. To retain 1.79.0 support, remove the unsafe arithmetic; if a null result is intended, return `core::ptr::null()` directly. Alternatively, enforce Rust 1.80.0 as the minimum supported version. + +2. **Proof-documentation defect on the proved 1.80.0 region (`lib.rs:4`).** The unsafe block has no `SAFETY` comment, and its correctness depends on a material version-specific derivation. If the supported set is narrowed to 1.80.0, suitable adjacent wording is: + + ```rust + // SAFETY: `[u8; 0]` has size zero, so `add(1)` computes a zero-byte + // offset. Rust 1.80.0's `pointer::add` contract says a zero-byte offset + // is always well-defined, including for this null base pointer. + ``` + +## Configuration closure, TCB, and residual scope + +There are no `cfg`s, features, dependencies, macros, generated artifacts, mutable state, FFI, concurrency, allocation, panic paths, or target-specific operations. The size/zero-offset argument is parametric over all targets, and neither debug assertions nor optimization affect a source-level contract violation or discharge. Thus the partition is exhaustive over the requested configurations. + +`TCB-R097-v1` consists only of the exact Rust 1.79.0 and 1.80.0 standard-library propositions linked above: `null`, array `size_of`, raw-pointer representability, and each version's `add` safety contract. There are no additional assumptions or tool-derived facts. Compiler/backend correctness and behavior outside this single safe API are excluded. No build, test, execution, or source modification was performed. Re-audit if the source, supported Rust versions, or any consumed standard-library contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r098.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r098.md new file mode 100644 index 0000000000..9eb61afbe5 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r098.md @@ -0,0 +1,48 @@ +# Focused source review: `Buffer` + +## Claim and verdict + +**UNSOUND.** For the exact [`lib.rs`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/k3r6/lib.rs:1) snapshot (SHA-256 `368fa01b2f3fca39e89131414f8ec2641a4ac162b846ccf77b8f8bd293930d9a`), under Rust 1.80.0 abstract semantics, there is a well-typed entirely safe use of `Buffer` that reaches undefined behavior. This holds for every Rust 1.80.0 target on which this source is accepted and every ordinary profile. The narrower `from_writable`-originating path is **PROVED** sound for calls satisfying that unsafe constructor's complete ongoing contract. + +The reviewed theorem is source-level freedom from Rust UB for every well-typed safe client, while treating an invocation of `from_writable` as valid only if its documented initial and ongoing obligations are met. No binary/backend claim is made. There are no documented postconditions in scope beyond the safety contract. + +## Boundary and coverage + +The representation fields are private. The complete current ingress/consumer set is: + +| Site | Role | Disposition | +|---|---|---| +| [`from_writable`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/k3r6/lib.rs:16) | Unsafe producer; stores `ptr` unchanged and `shared = None` | **PROVED**, relative to its caller contract | +| [`from_static`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/k3r6/lib.rs:20) | Safe producer; derives `ptr` from `&BYTE` and retains that reference | Produces the unsound path | +| [`overwrite`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/k3r6/lib.rs:28), `None` branch | `ptr.write(value)` consumer | **PROVED** only for `from_writable` results whose obligations remain satisfied | +| `overwrite`, `Some` branch | `ptr.write(value)` while `shared` is passed to `with_live` | **UNSOUND** | +| [`with_live`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/k3r6/lib.rs:43) | Keeps the shared reference live across the callback | Confirms, rather than repairs, the conflict | + +Moving and ordinary dropping do not dereference `ptr`; there is no custom `Drop`, trait impl, macro/generated API, field-level safe ingress, dependency, FFI, assembly, or conditional compilation. Rust 1.80.0 raw pointers are `!Send` and `!Sync`, so no auto-trait concurrency path is added ([1.80.0 pointer docs](https://doc.rust-lang.org/1.80.0/std/primitive.pointer.html#trait-implementations)). + +## Derivation and finding F-01 + +Rust 1.80.0 `ptr::write` says `dst` must be “valid for writes” and properly aligned; violation is UB ([contract](https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html#safety)). The Reference makes bytes pointed to by a live shared reference immutable, with `UnsafeCell` the sole escape; any nonzero-byte overlapping write is a mutation ([UB rules](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html), [interior mutability](https://doc.rust-lang.org/1.80.0/reference/interior-mutability.html)). It also states that a reference passed to a function is considered live and remains live for at least that call. + +`from_static` takes `&BYTE`, derives `ptr` from that exact reference, and stores the reference as `Some(shared)`. `overwrite` copies that reference and passes it to `with_live`; `with_live` invokes the closure before returning. Thus the `&u8` is live during line 33. `u8` contains no `UnsafeCell`, and `ptr.write(value)` writes one byte to the location referenced by `shared`. The destination is therefore not valid for writes, irrespective of whether the value changes. + +A complete safe witness is: + +```rust +let mut b = Buffer::from_static(); +b.overwrite(7); +``` + +Writing the existing value still counts as mutation under the cited rule. `from_static` alone does not immediately cause UB; it creates a capability whose safe consumer does. + +For the `None` branch, privacy and the only producer establish that `ptr` is the unchanged argument to `from_writable`. That contract requires it to remain non-null, aligned, valid for a one-`u8` write, and free of conflicting access for the entire usable interval. Those facts discharge both `ptr::write` preconditions at each valid `overwrite` call. This regional proof cannot be reversed into an invariant of every `Buffer`, because `from_static` is a second producer. + +## Local proof quality + +Both `SAFETY` comments claim `from_writable` as their justification. That is false on the `Some` branch. On the `None` branch the implementation proof is reconstructible, but the comment states only write-validity and omits the separately documented alignment obligation and the dataflow fact `shared == None => from_writable` under the private-producer closure. Accordingly, the local proof artifact is rejected even for the conditionally sound branch. + +No comment-only change can resolve F-01. A sound resolution must remove the safe writable capability to `BYTE`, prevent the `Some` branch from writing, or use representation and aliasing rules that genuinely permit mutation; then both branches require fresh proofs. + +## Configuration, TCB, and residual scope + +There are no source configuration axes. The proof and witness depend only on `u8`, shared-reference, and raw-write semantics, so they are parametric over targets and ordinary profiles. TCB: the three exact Rust 1.80.0 pages cited above; no additional assumptions, dependencies, tools, tests, or executed target evidence. Re-review is required if the source, Rust version, supported configuration predicate, constructor contract, or any cited semantic proposition changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r099.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r099.md new file mode 100644 index 0000000000..74bf5466b2 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r099.md @@ -0,0 +1,38 @@ +# Version-partitioned source review + +## Claim and verdict + +Artifact: `lib.rs` SHA-256 `e561568dfc6262bc6312f1a346e970dfab1f0d8f5102984e80e3b06cf2885e9b`. Scope is the public safe function `advance_marker` at lines 3–5, including its unsafe operation at line 4. The theorem is: every well-typed safe call is free of Rust undefined behavior on the supported set `{Rust/core 1.79.0, Rust/core 1.80.0} × every target × every ordinary profile`, relative to `TCB-H5P9-1` below. There are no caller safety preconditions. + +| Region | Soundness verdict | Reason | +|---|---|---| +| Rust 1.79.0 | **UNSOUND** | Every call unconditionally performs `add(1)` from a null pointer, violating the applicable `add` contract. | +| Rust 1.80.0 | **PROVED**, relative to `TCB-H5P9-1` | The computed byte offset is zero, for which the applicable `add` contract expressly makes the operation well-defined. | +| Combined supported set | **UNSOUND** | The supported set includes the unsound 1.79.0 region. | + +The function has no documented postcondition; its name is not normative. No separate postcondition or robustness claim was requested. + +## Boundary, obligations, and derivation + +The complete API surface is one safe, argument-free public function returning a raw pointer. The complete unsafe surface is its one `*const [u8; 0]::add` call. There are no fields, traits, impls, macros, callbacks, dependencies, generated artifacts, or invariant-bearing state. + +**O1 — byte offset (both versions): PROVED.** The versioned Reference says `[T; N]` has size `size_of::() * N` ([1.79.0](https://doc.rust-lang.org/1.79.0/reference/type-layout.html#array-layout), [1.80.0](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#array-layout)). Therefore `size_of::<[u8; 0]>() = 0`, and `add(1)` computes `1 * 0 = 0` bytes on every target. + +**O2 — Rust 1.79.0 `add` preconditions: VIOLATED.** `null` “Creates a null raw pointer” and gives it address 0 ([1.79.0 `null`](https://doc.rust-lang.org/1.79.0/core/ptr/fn.null.html)). The 1.79.0 `add` contract requires: “Both the starting and resulting pointer must be either in bounds or one byte past the end of the same allocated object” ([1.79.0 `add`](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add)). A null pointer does not designate an in-bounds or one-past position in an allocated object. The contract says violating any listed condition is undefined behavior. The call is unconditional, so invoking this safe API is a valid safe-use counterexample. The fact that the byte displacement is zero does not waive the 1.79.0 allocation clause. + +**O3 — Rust 1.80.0 `add` preconditions: PROVED.** `null` again creates a null raw pointer at address 0 ([1.80.0 `null`](https://doc.rust-lang.org/1.80.0/core/ptr/fn.null.html)). The changed contract conditions the allocation requirement on a nonzero byte offset and states: “If it is zero, then the function is always well-defined” ([1.80.0 `add`](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add)). By O1 the offset is zero; zero fits `isize`, and adding zero at address 0 neither wraps nor exceeds `usize`. The result is only returned as a raw pointer and is not dereferenced. Thus all literal clauses are discharged. + +## Configuration closure + +The source contains no `cfg`, feature, target, profile, environment, build-time, or generated-code branch. O1 is parametric over target layout because multiplication by array length zero is zero. Optimization, overflow-check, debug-assertion, and panic-profile choices do not change the source operation or either versioned contract. Partitioning solely by the two requested Rust versions is therefore exhaustive over all targets and ordinary profiles. + +## Findings and remediation + +1. **Implementation defect, Rust 1.79.0; critical soundness impact.** The safe API exposes an unconditional UB execution. The smallest source remedy is to return `core::ptr::null::<[u8; 0]>()` directly, eliminating `unsafe`; no source change was authorized or made. +2. **Proof-documentation defect, both regions.** The unsafe block has no adjacent `SAFETY` proof. If support were restricted to Rust 1.80.0+, suitable wording would be: `// SAFETY: [u8; 0] has size zero, so add(1) computes a zero-byte offset; Rust 1.80's add contract states that a zero computed offset is always well-defined.` This comment cannot repair 1.79.0. + +## TCB, evidence, and residual scope + +`TCB-H5P9-1` consists only of the six exact versioned Rust axioms linked above: array layout, `ptr::null`, and raw-pointer `add` for each audited version. They were opened and checked for version and wording. No compatibility premise is used to project 1.80.0 text backward. There are no dependency, implementation, platform, deployment, or tool-derived assumptions. This is a source-level result under documented Rust abstract semantics, not a claim that a particular compiler binary is correctly implemented. No build, test, interpreter, or macro-expansion evidence was used. + +Re-audit on any source change, supported Rust-version change, or material change to a consumed versioned contract. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r100.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r100.md new file mode 100644 index 0000000000..e771709388 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r100.md @@ -0,0 +1,39 @@ +# Focused source review: `m2q8` + +## Claim, snapshot, and verdicts + +Audited artifact: `lib.rs` SHA-256 `42388bb1a03741a7725c9836cc134c205af3061efef7b8ee00ce38d2dc51aeeb`; request SHA-256 `9d559df45c2ef498f44516fed36ef84c82fa4a02a59edca55d2ff88a7b93eb02`. Scope is exactly the public safe function `classify(u8) -> u8`, under Rust and `core` 1.80.0, on every target and ordinary profile. Valid use means every well-typed safe call; there is no caller safety precondition. + +- **Soundness: UNSOUND.** The valid safe call `classify(0)` reaches `core::hint::unreachable_unchecked()`, which is undefined behavior by its Rust 1.80.0 contract. +- **Documented behavior: CONTRACT-BROKEN.** The valid call `classify(1)` returns normally with `2`, contradicting the documented normal-return guarantee that it returns `input`. The separate promise to panic for zero is not established as defined Rust behavior: that path instead reaches undefined behavior. + +These are source-level verdicts relative to the authoritative Rust semantics below, not claims that any particular compiler/backend binary behaves predictably after UB. + +## Boundary and obligation inventory + +The only language-reachable API surface is the safe free function `classify`; there are no public fields, constructors, traits or impls, macros, hidden items, callbacks, invariant-bearing state, FFI, generated code, or third-party dependencies. The only unsafe operation is the call at `lib.rs:8`. It has no adjacent `SAFETY` proof. + +| ID | Obligation | Disposition | +|---|---|---| +| S1 | Every safe `u8` call is free of UB. | **Refuted:** `input = 0`. | +| U1 | The call site of `unreachable_unchecked` is unreachable. | **Refuted:** the `0` literal pattern matches zero and selects that arm. | +| B1 | `input == 0` panics. | **Not satisfied as a defined source behavior:** the selected arm reaches UB, not a defined panic path. | +| B2 | Every normal return equals `input`. | **Refuted without UB:** `classify(1)` selects `1 => 2` and returns `2`. | + +## Authoritative premises and derivation + +- **AXIOM-LITERAL (Rust Reference 1.80.0):** “Literal patterns match exactly the same value as what is created by the literal.” [Literal patterns](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns). +- **AXIOM-MATCH (Rust Reference 1.80.0):** “The first arm with a matching pattern is chosen as the branch target.” [Match expressions](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html). +- **AXIOM-UU (`core` 1.80.0):** “Reaching this function is *Undefined Behavior*.” [`core::hint::unreachable_unchecked`](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety). + +For `input = 0`, AXIOM-LITERAL makes the first pattern match; AXIOM-MATCH selects its expression; inspection shows that expression calls `unreachable_unchecked`; AXIOM-UU therefore yields UB. Because `classify` is safe and zero is a valid `u8`, this is an in-scope safe-use counterexample, so S1 and U1 are conclusively refuted. + +For `input = 1`, the second arm is selected and its expression is the literal `2`; the function returns normally with `2`. Since `2 != 1`, B2 is conclusively false without relying on the UB path. For every `input` in `2..=255`, neither literal arm matches and `_ => input` returns the input, so that subdomain satisfies B2. + +There is no possible truthful local safety proof for the existing unsafe block: its required proposition is that the call site cannot be reached, while the safe API deliberately routes `input = 0` to it. The missing `SAFETY` comment is therefore secondary to an implementation defect, not merely proof-documentation debt. + +## Configuration closure, TCB, and residual scope + +The exhaustive input partition is `{0}`, `{1}`, and `{2..=255}`. The source has no conditional compilation or configuration-sensitive operations. The literal/match semantics and `core` contract above apply uniformly to Rust 1.80.0 across the requested targets and ordinary profiles, so both counterexamples are parametric over that entire supported set; optimization level cannot make reaching UB sound. + +TCB consists only of AXIOM-LITERAL, AXIOM-MATCH, and AXIOM-UU for the exact versioned official documents. No tests, execution, compiler expansion, tools, external specifications, or additional assumptions were used. Backend correctness, deployment behavior, and code outside the two supplied target files are excluded. Re-review is required if the source, public documentation, Rust/`core` version, or supported configuration set changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r101.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r101.md new file mode 100644 index 0000000000..51f30809bb --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r101.md @@ -0,0 +1,40 @@ +# Version-partitioned source review: `h5p9` + +## Claim and verdict + +Snapshot: `lib.rs` SHA-256 `e561568dfc6262bc6312f1a346e970dfab1f0d8f5102984e80e3b06cf2885e9b`. Scope is the public safe function `advance_marker` and its one unsafe operation. For each requested Rust version, the soundness theorem quantifies over every well-typed safe call, every target, and every ordinary profile, relative only to the version-matched Rust axioms listed below. + +- **Rust 1.79.0: UNSOUND.** Every call executes `add(1)` with a null starting pointer and violates that version's unconditional allocated-object/in-bounds requirement, causing undefined behavior even though the byte offset is zero and the result is not dereferenced. +- **Rust 1.80.0: PROVED.** The byte offset is zero; this version expressly makes `add` always well-defined in that case, and its two arithmetic requirements also hold. +- **Combined requested set: UNSOUND.** The 1.79.0 region supplies a valid safe execution reaching UB. The proved 1.80.0 region does not repair the union. + +There is no documented `advance_marker` postcondition beyond its signature. The returned-value behavior on the proved region is also established below. + +## Boundary, inventory, and configuration closure + +The sole public surface is safe `advance_marker() -> *const [u8; 0]` (`lib.rs:3-5`), so callers have no safety obligation. `core::ptr::null` is the safe producer and `*const T::add` is the unsafe consumer. There is no mutable state, invariant-bearing representation, dependency, callback, conditional compilation, macro-generated code, allocation, concurrency, panic path, FFI, or other unsafe site. + +For each `v` in `{1.79.0, 1.80.0}`, let `S_v` contain every target and ordinary profile using the exact source with `core` version `v`. The source has no configuration branches. The facts `size_of::<[u8; 0]>() = 0`, `1 * 0 = 0`, and `0 + 0 = 0` are target- and profile-independent. The relevant `add` contract varies only by the exhaustive version partition below. Thus the proof and counterexample are parametric over all of `S_v`; no sampled build or execution is used. + +## Obligation ledger and derivation + +**O-ADD (`lib.rs:4`): satisfy every `pointer::add` safety clause and establish the returned pointer.** + +Common local facts, separately documented in both versions: + +1. The versioned `size_of` pages state that `[T; n]` has size `n * size_of::()` ([1.79.0](https://doc.rust-lang.org/1.79.0/core/mem/fn.size_of.html), [1.80.0](https://doc.rust-lang.org/1.80.0/core/mem/fn.size_of.html)). Hence `[u8; 0]` has size zero on every target. +2. The versioned `null` pages say: “The resulting pointer has the address 0.” ([1.79.0](https://doc.rust-lang.org/1.79.0/core/ptr/fn.null.html), [1.80.0](https://doc.rust-lang.org/1.80.0/core/ptr/fn.null.html)). +3. `add` defines `count` in units of `T`, so the computed byte offset is `1 * size_of::<[u8; 0]>() = 0`. + +**Rust 1.79.0.** Its [`add` contract](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add) unconditionally requires both pointers to be “either in bounds or one byte past the end of the same allocated object.” The [1.79.0 Reference](https://doc.rust-lang.org/1.79.0/reference/behavior-considered-undefined.html#dangling-pointers) defines a pointer as dangling if it is null and, for a zero-size pointee, requires it to point inside/just after a live allocation or be constructed directly from a *non-zero* integer literal. The address-zero pointer returned by `null` meets neither alternative. Therefore the starting pointer fails the first `add` precondition. The contract says violation makes the result UB; this occurs at the call itself, irrespective of later use. The remaining arithmetic clauses do hold: byte offset zero fits `isize`, and adding zero does not wrap the `usize` address space. One failed conjunction member suffices for **UNSOUND**. + +**Rust 1.80.0.** Its [`add` contract](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add) conditions the allocated-object requirement on a nonzero byte offset and explicitly says: “If it is zero, then the function is always well-defined.” Here it is zero. Zero fits `isize`; the infinite-precision address sum `0 + 0` fits `usize`, so the other two clauses hold as well. `add` calculates that zero-byte offset, leaving address 0, and the function safely returns the resulting raw pointer. O-ADD is **PROVED** throughout `S_1.80.0`. + +## Findings and remediation + +1. **UNSOUND — Rust 1.79.0, all requested targets/profiles.** The safe API invariably violates its unsafe callee's contract. To retain 1.79.0 support and the 1.80.0 returned value, remove the unnecessary arithmetic and return `core::ptr::null::<[u8; 0]>()` directly. Alternatively, support only Rust 1.80.0 or later and document that enforced minimum. +2. **Proof-documentation defect — all versions.** The unsafe block has no adjacent `SAFETY` proof. If it remains under an enforced 1.80.0 minimum, suitable proof text is: `SAFETY: [u8; 0] has size zero, so add(1) computes a zero-byte offset. Rust >= 1.80 documents add as always well-defined for zero byte offsets; zero also satisfies its isize and address-space arithmetic requirements.` This reconstruction proves 1.80.0 only; no comment can cure the 1.79.0 implementation defect. + +## TCB, evidence, and residual scope + +Inline TCB `r101-h5p9-v1` consists only of the exact versioned `core::mem::size_of`, `core::ptr::null`, `pointer::add`, and (for 1.79.0) Reference dangling-pointer propositions linked above. No compatibility inference crosses versions, and there are no admitted dependency, implementation, platform, or tool premises. This is a source-level Rust abstract-semantics result, not a claim about correctness of a particular compiler binary. No build, test, execution, or expansion evidence was used. Re-audit on source changes, supported-version changes, or material changes to any consumed versioned contract. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r102.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r102.md new file mode 100644 index 0000000000..b391a12c34 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r102.md @@ -0,0 +1,99 @@ +# Supported-domain source review + +## Claim and verdict + +**Soundness: PROVED**, relative to `TCB-R102` below, for the exact `lib.rs` +(SHA-256 `6f87e944cec7ee2727f7c7d32aa382de88987ea791082175d611fff5bf44012b`): +every well-typed safe call to `first` is free of Rust undefined behavior under +the documented abstract semantics for every configuration claimed supported by +**either** current policy, through the Rust 1.82.0 cutoff. This is a source-level +claim, not a claim about a compiler binary/backend. + +The proof domain is the union of the two published commitments, without +selecting either as authoritative: + +- without `fast`: Rust 1.79.0, 1.80.0, 1.81.0, or 1.82.0 on either published + target; +- with `fast`: all four releases on `x86_64-unknown-linux-gnu`, and + 1.80.0–1.82.0 on `aarch64-unknown-linux-gnu`. + +That union happens to equal Policy A's set; this observation is coverage +arithmetic, not a new support-policy decision. Policy B's `fast` set is a +subset, so the same proof separately establishes `PROVED` under Policy B. + +## Snapshot, boundary, and configurations + +I reviewed all seven supplied files. The crate is edition 2021, has no +dependencies or build script, and exposes one safe free function, `first`, +with mutually exclusive and exhaustive `fast`/non-`fast` definitions. There +are no public unsafe APIs, fields, traits/impls, macros, FFI, assembly, +generated code, persistent invariants, callbacks, concurrency, allocation, or +layout obligations. No unsafe-API postcondition is in scope. The +`rust-toolchain.toml` default of 1.82.0 and CI samples do not narrow the two +published commitments; CI expressly disclaims that role. + +Target, profile, optimization, and panic strategy do not affect the proof: +the sole unsafe precondition depends only on the unchanged slice length. The +four exact release documents establish the same contracts, so no forward or +backward compatibility assumption is used. + +## Obligation ledger and derivation + +- **O1, non-`fast` (`lib.rs:3-6`) — PROVED.** The implementation contains only + safe Rust (`slice::first` followed by `Option::copied`) and imposes no hidden + caller safety condition. +- **O2, `fast`, empty branch (`lib.rs:10-11`) — PROVED.** When `is_empty()` is + true, the function returns `None`; the unsafe expression is not evaluated. +- **O3, `fast`, nonempty branch (`lib.rs:10-13`) — PROVED.** For each audited + release, the slice documentation says, “Returns `true` if the slice has a + length of 0,” and says an out-of-bounds `get_unchecked` index causes undefined + behavior: [1.79.0 `is_empty`](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.is_empty), + [`get_unchecked`](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.get_unchecked); + [1.80.0 `is_empty`](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.is_empty), + [`get_unchecked`](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.get_unchecked); + [1.81.0 `is_empty`](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.is_empty), + [`get_unchecked`](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.get_unchecked); + [1.82.0 `is_empty`](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.is_empty), + [`get_unchecked`](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked). + Reaching `else` means the condition returned false. If the same slice had + length zero, `is_empty` would have returned true; hence its `usize` length is + at least one and index 0 is in bounds. There is no intervening operation and + no way for this shared slice value's length to change. Thus + `get_unchecked(0)` satisfies its complete caller-side bounds obligation, + returns the in-bounds shared `u8` reference, and reading/copying that `u8` + introduces no aliasing or lifetime violation. +- **O4, configuration closure — PROVED.** `cfg(feature = "fast")` partitions + the domain into O1 and O2/O3. The proof uses no target-specific fact, and the + cited contract was checked independently for all four releases; therefore it + covers every member of both policy sets, including cases absent from CI. + +## TCB-R102 and evidence + +`TCB-R102` contains only the exact Rust 1.79.0–1.82.0 standard-library +propositions linked above and the ordinary validity of the well-typed `&[u8]` +input at entry. There are no dependency, external-specification, deployment, +tool, test, or Rust-compatibility assumptions. No build, test, expansion, or +target execution was used as evidence. + +## Findings + +1. **Policy defect (does not weaken this soundness verdict).** Policy A supports + `fast` on x86_64 1.79.0 and aarch64 1.80.0–1.81.0; Policy B omits the former + and says aarch64 supports *only* 1.82.0. With both current and no precedence, + the project cannot truthfully state one uniquely controlling support set or + treat the disputed cases as excluded. Establishing precedence or publishing + an authorized replacement is required to fix that policy defect. The union + proof above avoids relying on a resolution. + +2. **Missing local proof documentation (`lib.rs:13`; implementation remains + PROVED).** The unsafe operation has no `SAFETY` comment, so the material + derivation above had to be reconstructed. Proposed adjacent wording: + + ```rust + // SAFETY: A zero-length slice makes `is_empty()` return true. Reaching this + // else branch therefore means `bytes.len() > 0`; no intervening operation + // changes this slice, so index 0 is in bounds for `get_unchecked`. + ``` + +Re-audit is required if either implementation, either policy, the Rust range, +targets, feature selection, or the consumed standard-library contracts change. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r103.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r103.md new file mode 100644 index 0000000000..f593cbb13f --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r103.md @@ -0,0 +1,49 @@ +# `decode` configuration-preserving review + +## Claim, snapshot, and verdict + +Scope is exactly `lib.rs` (SHA-256 `1ebbedcffa0ad4b2c3183ec04b4789d9c2daaeae6c19dc0e1d7f926ac685e89b`) and its two configuration-selected public safe `decode` functions. The required domain is Rust 1.70+, all targets and pointer widths, all ordinary profiles, and both values of `feature = "compact"`. Both rustdoc behavior statements are mandatory. There are no dependencies, generated artifacts, representation invariants, unsafe APIs, or caller safety obligations in scope. No code was executed. + +**Existing source soundness: UNSOUND.** This verdict covers the published set because it contains the counterexample configuration below. The non-`compact` branch is separately **PROVED** under the version qualification below. The `compact` panic guarantee is not established; there is no separate defined-execution counterexample warranting a distinct `CONTRACT-BROKEN` verdict because the failing path itself reaches UB. + +**Proposed redesign:** **PROVED** for Rust 1.70.0, every target/pointer width/profile, and both feature values. For the open-ended Rust 1.70+ range it is **PROVED relative to `TCB-COMPAT-1`**; absent acceptance of that premise, the open-ended version-coverage claim is **UNPROVED**, though the implementation derivation is complete at the documented endpoints checked (1.70.0 and 1.97.1). + +## Finding: release-only check permits an invalid `char` + +At `lib.rs:6-7`, take safe input `raw = 0xD800`, enable `compact`, and use Rust 1.70.0 in an optimized build without `-C debug-assertions`. Rust 1.70 documents that optimized builds do not execute `debug_assert!` unless that option is enabled and explicitly cautions that replacing `assert!` with it is appropriate “only in safe code” ([`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html#uses)). Thus line 6 supplies no fact. + +The unsigned `u16`-to-`u32` cast zero-extends ([numeric casts](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast)), preserving `0xD800`. `from_u32_unchecked` ignores validity and may create an invalid `char` ([contract](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32_unchecked)). The Reference classifies producing an invalid value as UB and specifically makes a surrogate an invalid `char` ([invalid-value rule](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). Therefore a well-typed safe call reaches UB. The reasoning is independent of target and pointer width. A safety comment could not repair the hidden safe-caller obligation. + +## Redesign (proposal only) + +```rust +/// With `compact`, returns the represented scalar and panics for a surrogate. +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + match char::from_u32(raw as u32) { + Some(scalar) => scalar, + None => panic!("surrogate code point"), + } +} + +/// Without `compact`, returns the represented scalar or `None`. +#[cfg(not(feature = "compact"))] +pub fn decode(raw: u32) -> Option { + char::from_u32(raw) +} +``` + +This preserves both signatures, cfg predicates, MSRV, and documented outcomes while removing the unsafe operation. + +## Compact proof and configuration closure + +Rust 1.70 fixes `u16` to `0..=2^16-1` ([numeric types](https://doc.rust-lang.org/1.70.0/reference/types/numeric.html#integer-types)); the widening cast preserves that value. A `char` is a scalar in `0..=0x10FFFF` excluding `0xD800..=0xDFFF` ([`char` validity](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity)). Hence, within the `u16` domain, invalidity is exactly surrogate membership. The checked constructor returns `None` for an invalid input and otherwise converts it to the represented `char` ([`from_u32`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32)). Consequently the `Some` arm returns the represented scalar, while the `None` arm invokes `panic!`, which “panics the current thread” ([`panic!`](https://doc.rust-lang.org/1.70.0/std/macro.panic.html)); the contract promises neither unwinding nor recovery. + +`cfg(feature = "compact")` and its `not(...)` are mutually exclusive and exhaustive because a configuration option is set or unset and `not` negates its predicate ([cfg semantics](https://doc.rust-lang.org/1.70.0/reference/conditional-compilation.html#conditional-compilation)). The non-`compact` body is exactly the documented checked conversion. No proof step uses optimization, debug assertions, `usize`, layout, allocation, or target behavior, so profiles, panic strategies, targets, and pointer widths introduce no additional branch. + +## TCB, residual scope, and triggers + +- **AXIOM-170:** the linked Rust 1.70.0 Reference/std propositions. The corresponding checked-conversion and invalid-`char` propositions were also verified in [Rust 1.97.1 std](https://doc.rust-lang.org/1.97.1/std/primitive.char.html#method.from_u32) and [Reference](https://doc.rust-lang.org/1.97.1/reference/behavior-considered-undefined.html#invalid-values), the audit cutoff. +- **TCB-COMPAT-1:** for every supported Rust release after 1.70, cfg negation, fixed integer ranges/widening, `char` validity, `char::from_u32`, and `panic!` retain the exact propositions consumed above. This explicit compatibility premise is necessary because stability badges alone do not prove preservation of all behavioral text. + +Re-review on any body/signature/documentation/cfg change, support-set change, or material change to a consumed Rust contract. Compiler/backend correctness and non-Rust application behavior are outside this source-level review. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r104.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r104.md new file mode 100644 index 0000000000..2b892d0754 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r104.md @@ -0,0 +1,95 @@ +# Focused unsafe-Rust review: `Buffer` + +## Claim and verdict + +**Source-level soundness verdict: `UNSOUND`.** For the exact `lib.rs` snapshot +(SHA-256 `368fa01b2f3fca39e89131414f8ec2641a4ac162b846ccf77b8f8bd293930d9a`), +Rust 1.80.0, every target, and every ordinary profile, it is false that every +well-typed safe use is free of Rust undefined behavior. Finding F-1 gives a +fully safe witness. This is an abstract-Rust result, not a claim about a +particular compiler binary or backend. + +The valid-use theorem for values returned by `unsafe from_writable` is +conditional on its documented ongoing caller obligations. Invalid fabrication +or mutation of `Buffer` by other unsafe code is outside the safe-use theorem. +There are no documented behavioral postconditions whose failure can be +classified independently as `CONTRACT-BROKEN`. + +## Snapshot, boundary, and configuration closure + +The complete target consists of `REQUEST.md` and `lib.rs`; no dependencies, +generated code, build scripts, conditional compilation, FFI, target features, +or profile-dependent assertions occur. The proof and counterexample are +parametric over target and profile: `u8` has size 1 on every target under the +Rust 1.80 [primitive layout table](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#primitive-data-layout), +and no selected code changes across the requested set. + +The representation fields (lines 6–7) have no visibility modifier. Rust 1.80 +says, “By default, everything is private” and permits private access only from +the current module and descendants +([Reference](https://doc.rust-lang.org/1.80.0/reference/visibility-and-privacy.html)). +The audited source has no descendant modules. Thus the current safe producer +set is exhaustive: unsafe `from_writable` (lines 11–18) and safe `from_static` +(20–26). The only pointee-accessing consumer is safe `overwrite` (28–40), with +`with_live` (43–46) participating in that access. Ordinary move and drop do not +dereference `ptr`; there are no explicit trait implementations, macros, public +fields, or other constructors/consumers. + +## Accepted Rust 1.80 axioms (TCB `r104-TCB-1`) + +- **AX-WRITE.** `*mut T::write` directs readers to `ptr::write` for its safety + conditions + ([method](https://doc.rust-lang.org/1.80.0/std/primitive.pointer.html#method.write)). + That contract requires that “`dst` must be valid for writes” and “must be + properly aligned” + ([function](https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html#safety)). +- **AX-SHARED.** When a reference is passed to a function, it is “live at least + as long as that function call”; “bytes pointed to by a shared reference … are + immutable”; and a mutation includes “any write of more than 0 bytes” + ([Reference](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)). +- **AX-PRIV/U8.** The exact privacy and primitive-layout propositions cited + above. + +These official, versioned axioms are accepted for this review. There are no +dependency, tool, environmental, probabilistic, or implementation assumptions. + +## Producer/consumer obligation ledger + +| Site | Result | Compact derivation | +|---|---|---| +| `from_writable` | **PROVED for valid calls** | Construction only stores the caller's pointer and establishes `shared == None`; it performs no pointee access. Its contract transfers alignment, write-validity for one `u8`, and absence of conflicting access through every possible later use. | +| `overwrite`, `None` branch | **PROVED relative to that contract; comment deficient** | Privacy plus complete producer/transition review establishes that `None` arises only from `from_writable`, and neither field is later changed. At line 38 its ongoing contract supplies AX-WRITE's write-validity and alignment requirements and the nonconflict condition. Writing a safe `u8` needs no read or drop of the old byte. The existing comment omits the producer/transition argument and the separate alignment obligation. | +| `from_static` before pointee access | **PROVED** | It safely forms a raw pointer and stores both that pointer and `Some(&BYTE)`; construction itself performs no write. It establishes that both fields designate the same byte. | +| `overwrite`, `Some` branch | **UNSOUND (F-1)** | This is precisely the `from_static` state, not a `from_writable` state. The write violates AX-SHARED and hence cannot satisfy AX-WRITE. | +| move/drop | **PROVED** | Neither operation accesses the pointee or alters external memory. | + +The reconstructed replacement proof for line 38 is: “`shared == None` can only +come from `from_writable`, and no transition changes either field; its ongoing +contract therefore supplies alignment, write-validity for one `u8`, and +nonconflict at this call.” This material derivation is absent from the source. + +## F-1 — safe `from_static` value reaches UB + +**Affected claim:** soundness of the safe producer/consumer pair at lines +20–33, on every requested configuration. + +Valid safe witness: + +```rust +let mut buffer = Buffer::from_static(); +buffer.overwrite(0); +``` + +`from_static` makes `ptr` and `shared` designate `BYTE`. `overwrite` copies the +shared reference from `Some` and passes it to `with_live`; AX-SHARED makes it +live for that entire call. `with_live` invokes the closure during the call. +The closure's `ptr.write` writes the one-byte `u8` exactly where that live +shared reference points. This is a mutation of immutable bytes and therefore +UB. The conclusion does not depend on the value written—even writing `7` +counts as a mutation. + +The line 31 comment is false on this branch: `from_writable` always produces +`None`, while this branch requires `Some`. No replacement `SAFETY` comment can +prove the current operation. Resolution requires an implementation/API change; +none was requested or made. Re-audit is required after any producer, field +visibility, transition, unsafe contract, Rust-version, or configuration change. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r105.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r105.md new file mode 100644 index 0000000000..c5d2e95252 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r105.md @@ -0,0 +1,40 @@ +# Focused review: `decode` + +## Claim, scope, and trust boundary + +Artifact: supplied `lib.rs`, SHA-256 `1ebbedcffa0ad4b2c3183ec04b4789d9c2daaeae6c19dc0e1d7f926ac685e89b`, reviewed 2026-07-31. The requested support predicate is stable Rust 1.70+, every target and pointer width, every ordinary profile, and each independently selected Boolean value of feature `compact`. The two `cfg` predicates are mutually exclusive and exhaustive: Rust 1.70 says a true `cfg` predicate retains its item and a false one removes it ([Reference](https://doc.rust-lang.org/1.70.0/reference/conditional-compilation.html#the-cfg-attribute)). + +The complete in-scope safe surface is one public `decode` per configuration: `fn(u16) -> char` with `compact`, otherwise `fn(u32) -> Option`. The sole unsafe obligation site is `from_u32_unchecked` at line 7. There are no fields, traits, callbacks, dependencies, generated artifacts, or target-specific operations in the supplied snapshot. No target was executed. + +TCB: only the cited Rust 1.70 Reference and standard-library propositions. The current-artifact refutation needs no later-version compatibility premise because Rust 1.70 is itself supported. Any all-future-release proof for the proposed design is conditional on `TCB-COMPAT`: later supported stable releases preserve the cited `from_u32` and `Option::expect` behavior; otherwise each new release requires re-audit. That premise is not needed for the recommendation’s Rust-1.70 compatibility claim. + +## Finding F1 — safe surrogate input reaches undefined behavior + +**Soundness verdict: `UNSOUND`** for the requested support set. More precisely, the `compact` branch is unsound in every supported optimized configuration that does not enable debug assertions. + +Witness on Rust 1.70: call the safe API as `decode(0xD800)` with `compact` enabled in an optimized ordinary profile without `-C debug-assertions`. This is valid safe use; the API documents a panic, not a caller safety obligation. + +1. Rust 1.70 documents that an optimized build does not execute `debug_assert!` unless `-C debug-assertions` is passed ([`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html)). Thus line 6 does not guard line 7 in this supported configuration. +2. A smaller unsigned integer cast to a larger integer is zero-extended ([numeric casts](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast)), so `raw as u32 == 0xD800`. +3. `from_u32_unchecked` blindly casts to `char` and may create an invalid value ([Rust 1.70 `char` API](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32_unchecked)). The Reference lists a surrogate-valued `char` as invalid and producing an invalid value as undefined behavior ([undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#undefined-behavior)). `0xD800` is the first surrogate ([`char` validity](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity)). + +The existing unsafe block also has no adjacent `SAFETY` proof. A proof cannot be reconstructed for the full configuration domain: `debug_assert!` does not dominate the call in the witness configuration. + +**Documented `compact` panic postcondition: `UNPROVED`, not `CONTRACT-BROKEN`.** The surrogate witness contains UB, so it cannot establish a UB-free execution that fails to panic. In debug-assertion-enabled configurations, line 6 does panic for every surrogate. For non-surrogate `u16` values, the numeric range plus exclusion of `0xD800..=0xDFFF` supplies a valid scalar to the unchecked call, but that regional proof does not repair the release branch. + +The non-`compact` implementation contains no unsafe operation and exactly delegates to `char::from_u32`, which returns `None` precisely for inputs invalid as `char` ([Rust 1.70 documentation](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32)). No defect was identified in that branch. + +## Recommended redesign + +Replace only the `compact` body; leave both attributes, signatures, and the non-`compact` body unchanged: + +```rust +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + char::from_u32(raw as u32).expect("raw must not be a surrogate") +} +``` + +This removes the unsafe operation and its caller-independent precondition. On Rust 1.70, zero-extension preserves the `u16` value. Every non-surrogate `u16` is within `0..=0xFFFF` and is therefore a Unicode scalar; `from_u32` returns its represented `char`, and `expect` returns the `Some` value. Every surrogate is invalid, so `from_u32` returns `None`, and `expect` panics ([`Option::expect`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect)). These operations are available on Rust 1.70, so the MSRV and signature are unchanged. + +Coverage is parametric over target and pointer width because only fixed-width `u16`, `u32`, and `char` semantics are used. It is profile-independent because no debug-only check remains. The two `cfg` predicates preserve the feature partition, and the disabled-feature implementation and return type remain byte-for-byte unchanged. No migration is required. This is a design proposal, not a post-change `PROVED` verdict; the implemented snapshot should be re-reviewed, with later stable releases covered by accepted `TCB-COMPAT` or version-applicable documentation. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r106.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r106.md new file mode 100644 index 0000000000..fe6cbb3215 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r106.md @@ -0,0 +1,60 @@ +# Focused review of `total` + +## Claim and verdict + +Snapshot: `lib.rs` SHA-256 `12ee0d64af16f4b4bea78bb146d333f86c579b49ca0dd38e2552f3896147be5c`; request SHA-256 `8e32395f979236c2f9fc5b811aa5d7d6a83b054dad42febb341b776f1c5dfa2a`. Scope is the sole public safe API, `total(&[u32]) -> u32`, its three unsafe operations, and the requested iterator redesign. The required result is the elements' sum modulo `2^32`. Supported configurations are Rust 1.70+, every target, and ordinary profiles. + +**Current soundness: UNSOUND for the stated supported set.** A well-typed safe call on Rust 1.70.0 can reach undefined behavior. One supported version/configuration is enough to refute the universal claim. This is a source-level verdict; no compiler/backend claim is made. + +**Required wrapping behavior: not established over the full supported set**, because that execution has UB. For nonempty valid slices, the current loop does compute the wrapping sum. + +**Safe iterator candidate: proof-oriented and behavior-preserving by the Rust 1.70 contracts below, but only a proposal, not an audited new snapshot. Performance: UNPROVED.** No benchmark result or benchmark identity is present. + +## Finding: unconditional `add(0)` on a dangling empty buffer + +At `lib.rs:6`, `ptr.add(values.len())` executes before the empty check implicit in the loop. This constructs a valid empty slice under its unsafe constructor's documented contract and then makes an ordinary safe call: + +```rust +let values = unsafe { + std::slice::from_raw_parts( + std::ptr::NonNull::::dangling().as_ptr(), + 0, + ) +}; +let _ = total(values); +``` + +Rust 1.70's [`slice::from_raw_parts`](https://doc.rust-lang.org/1.70.0/core/slice/fn.from_raw_parts.html#safety) contract expressly says [`NonNull::dangling()`](https://doc.rust-lang.org/1.70.0/core/ptr/struct.NonNull.html#method.dangling) is usable as the data pointer for a zero-length slice; it is non-null and aligned, there are zero bytes/values to initialize or keep immutable, and total size is zero. Thus construction satisfies every listed precondition. Slice [`as_ptr`](https://doc.rust-lang.org/1.70.0/core/primitive.slice.html#method.as_ptr) returns that slice's buffer pointer. + +The Rust 1.70 `add` safety contract requires both starting and resulting pointers to be in-bounds or immediately past **the same allocated object** ([`pointer::add`](https://doc.rust-lang.org/1.70.0/core/primitive.pointer.html#method.add)). Here `len == 0`, but both are the dangling buffer pointer and there is no allocated object. The required conjunct fails when line 6 evaluates. There is no existing `SAFETY` comment, check, or invariant that addresses it. + +For nonempty slices, the reconstructed loop invariant is: before the condition, `ptr = base.add(i)`, `0 <= i <= len`, and `acc` is the modular sum of elements `[0, i)`. A valid slice's bytes are in one live allocation and its dynamic size cannot exceed `isize::MAX` ([Reference validity rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#dangling-pointers)). Thus `base.add(len)` is within the Rust 1.70 contract. If `ptr != end`, then `i < len`; the dereference reads the initialized, aligned `u32` at `i`, and `add(1)` stays in-bounds or reaches one-past. The shared slice remains live for the call and this function exposes no mutation or callback. `wrapping_add` is modular addition ([Rust 1.70 contract](https://doc.rust-lang.org/1.70.0/core/primitive.u32.html#method.wrapping_add)), so the invariant is preserved and termination at `i == len` yields the required sum. This material proof is absent from all three unsafe sites. + +| Obligation | Status | +|---|---| +| line 6 `add(len)`, empty slice | **UNSOUND** on Rust 1.70.0 | +| line 6 `add(len)`, nonempty slice | PROVED relative to cited 1.70 axioms | +| line 9 raw read and line 10 `add(1)`, nonempty iterations | PROVED by the invariant above | +| modular result, nonempty slice | PROVED; profiles/overflow checks are irrelevant | + +## Safe redesign and configuration closure + +```rust +pub fn total(values: &[u32]) -> u32 { + values + .iter() + .fold(0u32, |acc, value| acc.wrapping_add(*value)) +} +``` + +Rust 1.70 says slice iteration yields every item from start to end ([`slice::iter`](https://doc.rust-lang.org/1.70.0/core/primitive.slice.html#method.iter)); `fold` starts with the supplied initial value, applies the closure to every item, and returns the final accumulator ([`Iterator::fold`](https://doc.rust-lang.org/1.70.0/core/iter/trait.Iterator.html#method.fold)). Together with the cited `wrapping_add` contract, induction proves the modular sum, including `0` for an empty slice. The candidate contains no local unsafe operation or caller safety precondition. + +The proof is parametric over target and ordinary profile: there is no `cfg`, generated code, FFI, target feature, allocation-sensitive unsafe code, panic-sensitive transition, or ordinary `+`; explicit wrapping semantics do not depend on overflow checks. Rust/std contracts cited for 1.70 are the entire proof TCB; no dependency, tool, benchmark, or implementation premise was admitted. A literal open-ended `1.70+` post-change verdict must use a finite audit cutoff plus re-audit, or an explicitly accepted compatibility premise; a 1.70 citation alone cannot prove unknown future releases. + +## Performance evidence and recommendation + +Source shape, idiom, or an expectation that LLVM removes iterator abstraction does not prove a ≤2% regression. With no benchmark result, harness identity, workload, toolchain, target, profile, metric, sampling method, or decision rule, that requirement remains **UNPROVED**. + +**Next action:** block release of the current artifact under the Rust-1.70+ support claim; stage the safe iterator candidate and run the designated A/B benchmark under a predeclared, reproducible comparison. Merge it only if the benchmark's decision rule establishes regression ≤2%, then audit the exact patch. If it fails, benchmark a minimal empty-slice guard plus the pointer loop (with adjacent proofs for every unsafe operation), or tune another safe iterator form. Do not retain the unguarded line 6, and do not claim either performance compliance or a post-change `PROVED` verdict before those respective checks. + +No source edit, build, test, expansion, or benchmark was performed. Re-review on source change, support-set change, relevant Rust contract change, or benchmark/harness change. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r107.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r107.md new file mode 100644 index 0000000000..85d5ccb749 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r107.md @@ -0,0 +1,52 @@ +# Focused review and redesign + +## Claim, scope, and verdict + +**Snapshot and scope.** This reviews the supplied `lib.rs` only, at the 2026-07-31 cutoff: public `Ticket`, safe `ticket(usize) -> Ticket`, and the single `new_unchecked` call. There are no dependencies, generated artifacts, callbacks, FFI, or deployment assumptions. The request defines the supported set as Rust 1.70+ on every target and every ordinary build profile. For this source, the only material configuration partition is debug assertions enabled versus disabled; target width, optimization, and panic strategy do not affect the zero/nonzero reasoning. + +**Current soundness verdict: `UNSOUND`.** This refutes the universal supported-set claim using Rust 1.70 itself, so no premise about later-version compatibility is needed. A well-typed safe caller may call `ticket(0)`. In an optimized build without `-C debug-assertions`, Rust 1.70 documents that the `debug_assert!` is not executed. Control therefore reaches `NonZeroUsize::new_unchecked(0)`, whose safety requirement is “The value must not be zero” and whose documentation says zero results in undefined behavior. Thus a valid safe use reaches UB. + +**Documented behavior verdict.** The nonzero return case is `PROVED` for Rust 1.70: the unsafe precondition holds and the resulting `Ticket` contains that input. The `id == 0` panic guarantee is `UNPROVED` over the full supported set, not `CONTRACT-BROKEN`: the known assertions-disabled witness contains UB, so it cannot establish a defined failure to panic, and no independent UB-free refutation was identified. + +## Boundary and obligation coverage + +`Ticket` has a private `NonZeroUsize` field; downstream safe code cannot use its tuple constructor or mutate the field. It has no methods or explicit trait implementations. Compiler-supplied move, drop, and auto-trait behavior introduces no ingress that can create zero. `ticket` is the only safe constructor and therefore must enforce nonzero for every `usize` without a caller safety precondition. + +| Obligation | Disposition | +|---|---| +| Safe `ticket` for every input/profile | `UNSOUND`: `ticket(0)` with debug assertions disabled reaches UB. | +| `new_unchecked(id)` requires `id != 0` | Proved only for nonzero inputs and for a returning assertions-enabled path; false for zero in the supported disabled class. | +| Zero must panic | Proved only where the debug assertion executes; full-set result `UNPROVED`. | +| Nonzero result contains the input | `PROVED` at Rust 1.70 from the constructor contract and direct wrapping. | +| Existing local proof documentation | Deficient: the unsafe block has no `SAFETY` proof, and no valid proof exists across all supported profiles. | + +The configuration counterexample is target-parametric: every `usize` target has zero, and neither cited contract is target-qualified. Debug-assertions-enabled and disabled classes exhaust the material profile difference. Other source-visible configuration axes and generated code are absent. + +## Recommended redesign + +Replace only the function body; retain the exact public signature, documentation, and private representation: + +```rust +pub fn ticket(id: usize) -> Ticket { + Ticket(NonZeroUsize::new(id).expect("ticket id must be nonzero")) +} +``` + +This is the smallest proof surface: it removes both `unsafe` and the configuration-sensitive assertion. + +Conditional proof plan for the implemented snapshot: + +1. `NonZeroUsize::new` “creates a non-zero if the given value is not zero.” For nonzero `id`, it produces the `Some` value containing `id`; `expect` returns that contained value, and `Ticket` wraps it unchanged. +2. For zero, the safe constructor yields `None`; `Option::expect` is documented to panic on `None`. Hence the documented zero behavior occurs without constructing an invalid value. +3. The source contains no `cfg`, assertion setting, target-specific operation, allocation, or dependency. The two input cases are exhaustive and the proof is parametric over targets, optimization levels, and ordinary panic strategies. A panic message was never documented, so choosing this message changes no stated behavior. +4. Privacy continues to make this constructor the only safe invariant producer. There is no remaining unsafe contract or local `SAFETY` comment to maintain. + +Because this is a proposal rather than an implemented artifact, it receives no artifact verdict. Rust 1.70 closes the plan directly under the cited contracts. For the open-ended `1.70+` policy, each later supported release must expose the same versioned contracts, or the project must explicitly accept a compatibility premise preserving them; a toolchain-contract change is a re-review trigger. + +## Authoritative premises / TCB-R107-1 + +- Rust 1.70 [`debug_assert!`](https://doc.rust-lang.org/1.70.0/core/macro.debug_assert.html#uses): optimized builds do not execute it by default unless debug assertions are requested. +- Rust 1.70 [`NonZeroUsize::new_unchecked` and `new`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new_unchecked): the unchecked input must be nonzero; the safe constructor creates a nonzero only from a nonzero input. +- Rust 1.70 [`Option::expect`](https://doc.rust-lang.org/1.70.0/core/option/enum.Option.html#method.expect): it returns `Some`'s value and panics on `None`. + +These are source-level Rust axioms only. No compiler-backend correctness, dependency, environmental restriction, testing result, or undocumented implementation behavior is assumed. No build or execution was used. Re-review is required if the source, safe contract, private representation/visibility, supported configuration set, or any consumed standard-library contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r108.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r108.md new file mode 100644 index 0000000000..f4404dda90 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r108.md @@ -0,0 +1,118 @@ +# Focused unsafe-Rust review: `p1j4` + +## Claim and verdict + +**Soundness: UNSOUND.** For `lib.rs` SHA-256 +`ac762e6e2bd87884bbbcfbe7bbf706e5b9dae9b078e82d01252111a9a6ad84e8`, +the claim that every well-typed safe use is free of Rust undefined behavior for +Rust 1.70+, every target, and ordinary profiles is false. The counterexample +below is a valid safe use on Rust 1.70, a member of that universal version set, +so it refutes the aggregate claim. The defect is source-level and independent +of target layout, optimization, overflow checks, panic strategy, or other +ordinary-profile choices. + +No separate documented behavioral postconditions were supplied. **Proof +documentation: UNPROVED**: neither unsafe block has an adjacent safety proof, +and the necessary aliasing proposition is in fact false. + +This is a fresh review of the implemented source. `DESIGN-NOTE.md` supplied no +premise or inherited verdict. + +## Boundary, invariant, and configuration coverage + +The complete public safe surface is `View<'a, T>`, `new`, `get`, and `get_mut`. +Its fields are private. There are no explicit trait implementations, macros, +generated artifacts, dependencies, `cfg` branches, FFI, assembly, allocation, +or concurrency. Ordinary move, drop, and auto-trait behavior creates no +additional boundary needed for the single-threaded counterexample. + +The intended representation invariant is: `ptr` designates the live, aligned, +initialized `T` originally uniquely borrowed for `'a`, while `borrow: +PhantomData<&'a mut T>` carries that borrow. `new` establishes pointer origin +and lifetime tracking. The accessor transitions fail to preserve the required +alias state: they return references for all of `'a`, but borrow `View` only for +the shorter, implicit receiver-borrow lifetime. + +Configuration closure is parametric: the same source is selected in every +configuration and the counterexample uses only `u8`, references, and the three +safe methods. No execution or tool-derived evidence was used. + +## F-1 — accessor results escape the receiver borrow + +Affected sites: `lib.rs:15-20`. + +`get` must ensure that its returned shared reference is not contradicted while +live. `get_mut` must ensure that its returned mutable reference is unique while +live. Instead, their explicit result lifetime `'a` is unrelated to the +implicit lifetime of `&self`/`&mut self`. Consequently the receiver borrow can +end after each call even though the returned reference remains live, and safe +code can call another accessor: + +```rust +fn safe_client() { + let mut value = 0u8; + let mut view = View::new(&mut value); + let shared = view.get(); + let unique = view.get_mut(); + *unique = 1; + let _use_after_write = *shared; +} +``` + +Every operation in `safe_client` is safe. Both references designate `value`; +`shared` is live across the write because it is read afterward. The Rust 1.70 +Reference classifies mutation of data reached through a shared reference as +undefined behavior and states that unsafe code usable by safe code to trigger +UB is unsound ([Rust 1.70 Reference, behavior considered +undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). +The Rust 1.70 standard-library contract also says that a mutable reference must +always be unique ([`UnsafeCell` aliasing +rules](https://doc.rust-lang.org/1.70.0/core/cell/struct.UnsafeCell.html)); two +successive `get_mut` calls can likewise return coexisting aliases to the same +`T`. Thus the missing implication is not merely undocumented: private pointer +origin plus `PhantomData` does **not** imply exclusivity for references whose +lifetime outlasts the receiver borrow. + +Applicability: Rust 1.70, every target on which this source is compiled, every +ordinary profile, and `T = u8`. This suffices to establish `UNSOUND` for the +requested universal domain. For later versions, the same derivation applies +whenever their authoritative semantics retain these reference rules; no +open-ended future-version compatibility premise is needed to refute the +aggregate claim. + +## Required repair (proposal only) + +Tie each result to the receiver borrow: + +```rust +pub fn get(&self) -> &T { + // SAFETY: `ptr` came from `new`'s live `&'a mut T`; the private + // representation retains that borrow. This result is tied to `&self`, + // which prevents mutable access through this View while it is live. + unsafe { &*self.ptr } +} + +pub fn get_mut(&mut self) -> &mut T { + // SAFETY: the same pointer/liveness invariant holds, and this result is + // tied to the unique borrow of the View, excluding every other accessor. + unsafe { &mut *self.ptr } +} +``` + +Rust 1.70's method lifetime-elision rule assigns the receiver-reference +lifetime to elided output lifetimes ([Rust 1.70 Reference, lifetime +elision](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html#lifetime-elision-in-functions)). +An even smaller trusted surface would store `&'a mut T` directly and implement +both accessors with safe reborrows. + +**Proposal status: UNIMPLEMENTED / UNPROVED AS SOURCE.** It does not alter the +snapshot's `UNSOUND` verdict; the exact implemented repair and its adjacent +proofs require a fresh source review. + +## TCB and review triggers + +TCB `r108-v1` contains only the three exact Rust 1.70 Reference/standard-library +propositions linked above; there are no admitted dependencies, tools, external +specifications, or deployment assumptions. Re-review is required after either +accessor signature/body, representation/privacy, constructor, supported Rust +range, or consumed authoritative reference contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r109.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r109.md new file mode 100644 index 0000000000..a91db939ef --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r109.md @@ -0,0 +1,53 @@ +# `decode` review and configuration-preserving redesign + +## Claim, scope, and verdict + +Snapshot: the complete supplied `lib.rs` and `REQUEST.md`; no generated artifacts or dependencies are present. The only language-reachable surfaces are the two mutually exclusive safe `decode` functions selected by `feature = "compact"` and its negation. The requested support predicate is Rust 1.70+, every target/pointer width, every ordinary profile, and either feature value. No target was executed or modified. + +**Current-artifact soundness verdict: `UNSOUND`.** With `compact`, Rust 1.70, and an optimized ordinary profile in which debug assertions are not executed, the well-typed safe call `decode(0xD800)` produces an invalid `char`. This single supported case refutes the universal claim; no compatibility premise about post-1.70 Rust is needed for that verdict. + +Configuration-local results on the exact Rust 1.70 contracts are: + +| Region | Soundness and documented behavior | +|---|---| +| `compact`, debug assertions disabled | **`UNSOUND`** for every target/pointer width; the documented surrogate panic cannot be proved because execution first reaches UB. | +| `compact`, debug assertions enabled | **`PROVED`**: surrogates panic before the unsafe call; every other `u16` denotes a scalar and is returned. | +| not `compact` | **`PROVED`** for all profiles/targets: the safe checked conversion supplies exactly `Some(scalar)` or `None`. | + +`CONTRACT-BROKEN` is not a separate verdict for the failing case: after producing the invalid value, Rust gives no defined non-panicking execution from which to prove a postcondition counterexample. + +## Finding and derivation + +**Critical — safe input reaches UB (`lib.rs:6-7`).** The controlling unsafe obligation is that `char::from_u32_unchecked` must not construct an invalid `char`. + +The exact Rust 1.70 authorities establish: + +- The [Reference UB rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html) classify “producing an invalid value” as UB and specifically make a surrogate-valued `char` invalid. +- [`char` validity and conversions](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity) define surrogates as `0xD800..=0xDFFF`; [`from_u32_unchecked`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32_unchecked) ignores validity and may create an invalid `char`, whereas [`from_u32`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32) returns `None` for an invalid input. +- An unsigned widening cast zero-extends under the [numeric-cast rule](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast), so `0xD800u16 as u32 == 0xD800u32`. +- Rust 1.70 documents that an optimized build does not execute [`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html) unless debug assertions are explicitly enabled. + +Thus, in the disabled region, line 6 establishes nothing. Safe caller input `0xD800` reaches line 7, the cast preserves `0xD800`, the unchecked conversion produces a surrogate-valued `char`, and the Reference classifies that production as UB. + +In the enabled region, the assertion dominates the unsafe call. A surrogate panics. On the remaining path, `raw` is not in the surrogate interval; because a `u16` is at most `0xFFFF`, it is also at most `char::MAX` (`0x10FFFF`). It is therefore a Unicode scalar value, discharging the unchecked conversion's validity obligation. This material derivation is absent from the source; were the unsafe implementation retained, it would need an adjacent `SAFETY` proof and, crucially, an unconditional check. + +For the non-compact branch, the quoted checked-conversion contract directly proves both safety and the documented result. The feature predicates are complementary, and all reasoning uses fixed-width values and target-independent standard-library contracts, so target, pointer width, optimization, overflow checks, and panic strategy introduce no further branch. + +## Recommended redesign (counterfactual; no source edit) + +Replace only the compact body and leave the non-compact branch unchanged: + +```rust +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + char::from_u32(raw as u32).unwrap() +} +``` + +Conditional proof plan: `from_u32` returns `Some(c)` exactly when the widened input is a valid `char`, and Rust 1.70 [`Option::unwrap`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.unwrap) returns the contained value or panics on `None`. Since every `u16` is below `char::MAX`, `None` occurs exactly for the surrogate interval. The candidate therefore returns the represented scalar otherwise and panics for every surrogate in every profile, without unsafe code or a safety invariant. A changed panic message is outside the documented contract. + +Both APIs' signatures and return types remain identical, and all used APIs exist in Rust 1.70, so the candidate does not raise the MSRV or drop a target/configuration. It has no post-change `PROVED` verdict until implemented and reviewed. For the open-ended `1.70+` range, that audit must verify the same contracts in each applicable later toolchain or explicitly accept a compatibility premise that preserves them; any material contract change is a re-review trigger. + +## TCB and residual scope + +The current `UNSOUND` verdict consumes only the linked, exact Rust 1.70 Reference and standard-library propositions plus inspected local source. There are no dependency, environment, generator, tool-result, FFI, allocator, concurrency, or probabilistic assumptions. Whole-crate concerns beyond the supplied two-file artifact are out of scope. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r110.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r110.md new file mode 100644 index 0000000000..b5bcd5f55c --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r110.md @@ -0,0 +1,49 @@ +# Focused review: `decode` + +## Claim, scope, and verdict + +Snapshot: the supplied `lib.rs` only. The supported predicate is Rust 1.70+, every target and pointer width, every ordinary profile, and each truth value of `feature = "compact"`. The in-scope surface is the safe public `decode`: with `compact`, `fn(u16) -> char` must return the represented scalar and panic for every surrogate; without it, `fn(u32) -> Option` must return the represented scalar or `None`. There are no dependencies or generated artifacts in the supplied target. + +**Current combined verdict: UNSOUND.** This is established for Rust 1.70 with `compact` on any target in an optimized ordinary build whose debug assertions are disabled. At Rust 1.70, the non-`compact` branch is safe and implements its documented result contract; it does not cure the supported failing configuration. The compact documented-postcondition claim is also **UNPROVED** over the full set because the same supported execution reaches UB instead of establishing the required panic. This is a focused verdict for `decode`, not the rest of any crate. + +## Finding and current derivation + +At `lib.rs:6`, `debug_assert!` is the only guard for `lib.rs:7`. Rust 1.70 documents that “An optimized build will not execute `debug_assert!` statements unless `-C debug-assertions` is passed” ([`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html)). Thus, in a supported optimized configuration, the well-typed safe call `decode(0xD800)` reaches `char::from_u32_unchecked(0xD800)`. + +Rust 1.70 defines surrogate code points as `0xD800..=0xDFFF`, says no non-scalar may be constructed as a `char`, and describes the unchecked conversion as ignoring validity ([`char` validity and conversions](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity)). The Reference classifies “Producing an invalid value” as UB and specifically makes a surrogate-valued `char` invalid ([Rust 1.70 UB Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). Therefore the safe API admits a valid use that produces an invalid `char`: there is no hidden caller obligation that could discharge this. + +The unsafe block also has no adjacent `SAFETY` proof. Even in configurations where the assertion executes, the required derivation—guard dominance, exact cast preservation, and `char` validity—is absent. Replacing `debug_assert!` with `assert!` could repair the defect, but retaining unnecessary unsafe code and a proof obligation is dominated by the safe redesign below. + +## Recommended configuration-preserving redesign + +No source was edited. Replace the two definitions with the following (the non-`compact` body is intentionally unchanged): + +```rust +/// With `compact`, returns the represented scalar and panics for a surrogate. +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + char::from_u32(raw as u32) + .expect("a u16 is a scalar unless it is a surrogate") +} + +/// Without `compact`, returns the represented scalar or `None`. +#[cfg(not(feature = "compact"))] +pub fn decode(raw: u32) -> Option { + char::from_u32(raw) +} +``` + +This preserves both public signatures, both `cfg` predicates, both documented behaviors, and the Rust 1.70 MSRV. It changes no error type or panic-message contract (none is documented) and exposes no unsafe boundary. + +## Candidate proof plan and configuration closure + +- **Compact conversion.** A `u16` ranges from `0` through `2^16 - 1` ([Rust 1.70 numeric types](https://doc.rust-lang.org/1.70.0/reference/types/numeric.html#integer-types)). Its cast to `u32` zero-extends because it is an unsigned smaller integer ([numeric casts](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast)), so the numeric value is unchanged. Within that range, the only invalid `char` values are the surrogates. Rust 1.70 `char::from_u32` returns `None` for an invalid `char` value and otherwise performs the conversion ([conversion contract](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32)); `Option::expect` returns the `Some` value and “Panics if the value is a `None`” ([`Option::expect`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect)). Hence every non-surrogate returns its represented scalar and every surrogate panics, without UB. +- **Non-compact conversion.** Its input and output are exactly the safe conversion’s input and output; the cited `from_u32` contract directly establishes the documented `Some`/`None` behavior and soundness. +- **Features.** Rust 1.70 specifies that `not(P)` is true exactly when `P` is false ([conditional compilation](https://doc.rust-lang.org/1.70.0/reference/conditional-compilation.html#configuration-predicate)), so `cfg(feature = "compact")` and its negation are an exhaustive, disjoint partition. Each partition retains its required signature. +- **Targets, pointer widths, and profiles.** The proof uses fixed-width `u16`/`u32` semantics and no layout, pointer-sized value, unchecked operation, overflow, debug assertion, optimization premise, allocator, or panic-unwind restoration invariant. It is therefore parametric over every target/pointer width and ordinary profile in scope. + +## TCB and residual qualification + +`AXIOM-R170` consists only of the linked, exact Rust 1.70 Reference and standard-library clauses. The present unsoundness verdict needs no later-version premise because Rust 1.70 is supported. + +For the open-ended candidate claim “Rust 1.70+”, `COMPAT-OPEN` must explicitly admit that every later supported stable release preserves the exact consumed propositions and availability of this source. That compatibility proposition is not established merely by current API stability badges, so the design closes unconditionally at 1.70 and conditionally for later releases relative to `COMPAT-OPEN`. Re-audit if the MSRV, signatures/docs, `cfg` predicates, or any consumed language/library contract changes. As a design sketch rather than an implemented snapshot, the candidate receives no `PROVED` artifact verdict; audit the resulting source after implementation. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r111.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r111.md new file mode 100644 index 0000000000..9fac584601 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r111.md @@ -0,0 +1,47 @@ +# Focused review: `total` + +## Scope and verdicts + +Reviewed exactly `targets/a6d2/lib.rs` as a safe public API, for every valid `&[u32]`, Rust 1.70+, all targets, and ordinary profiles. Required behavior is the elements' sum modulo \(2^{32}\). No code, build, expansion, test, or benchmark was run. + +- **Current implementation soundness: UNSOUND.** A valid empty slice reaches undefined behavior under Rust 1.70's documented `pointer::add` contract. This refutes the requested `1.70+` claim even if later releases loosen the contract. +- **Safe iterator redesign, Rust 1.70 semantics: PROVED** relative to the cited standard-library contracts: it is safe and computes the same modular sum, including zero for an empty slice. +- **Safe redesign on the open-ended future part of `1.70+`: PROVED only conditionally** on the explicit compatibility premise that the cited stable safe APIs retain these semantics. Set a release cutoff and re-review on a relevant contract change rather than silently extrapolating forever. +- **No-more-than-2% benchmark regression: UNPROVED.** There is no benchmark result, so source reasoning cannot discharge this empirical gate. + +## Current implementation: counterexample and remaining proof + +Rust 1.70 documents [`slice::from_raw_parts`](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html) as permitting a zero-length slice with a non-null aligned data pointer and specifically identifies `NonNull::dangling()` as usable for that purpose. [`NonNull::dangling`](https://doc.rust-lang.org/1.70.0/std/ptr/struct.NonNull.html#method.dangling) returns a well-aligned dangling pointer. Therefore this can create a valid input (the unsafe construction fulfills its own contract): + +```rust +let empty: &[u32] = unsafe { + std::slice::from_raw_parts(std::ptr::NonNull::::dangling().as_ptr(), 0) +}; +total(empty); +``` + +Before testing the loop condition, `total` evaluates `ptr.add(values.len())`, hence `ptr.add(0)`. Rust 1.70's [`*const T::add`](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) says both the starting and result pointers must be in-bounds or one-past “the same allocated object”; violating a listed condition is undefined behavior. The dangling data pointer does not meet that condition. This is a valid-use UB witness for the safe API, not merely a missing test. + +For `len > 0`, the implementation proof can otherwise be reconstructed. The slice contract supplies one live allocation containing `len` initialized, aligned `u32`s and bounds its byte size by `isize::MAX`. With loop invariant `ptr == base.add(i)` and `0 <= i <= len`, the initial `add(len)` produces one-past, `ptr != end` implies `i < len` because `u32` is non-zero-sized, dereference reads initialized element `i`, and `add(1)` preserves the invariant. [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add) performs modular addition, independent of overflow checks. No `cfg`, panic, allocation, callback, concurrency, or profile-sensitive operation changes that argument. + +The source contains no adjacent `SAFETY` comments, so even this nonempty derivation is absent from the proof artifact. If a pointer implementation remains, it needs an enforced empty case plus comments proving each `add` and dereference from the loop invariant. + +## Safe redesign + +The direct replacement is: + +```rust +pub fn total(values: &[u32]) -> u32 { + values.iter().copied().fold(0u32, u32::wrapping_add) +} +``` + +Slice iteration visits exactly the elements; `copied` changes `&u32` to its `u32` value; `fold` starts at zero and applies `wrapping_add` once per element. Thus it returns the same modulo-\(2^{32}\) sum for every length, and the empty case returns the initializer. All operations exposed here are safe and available at the MSRV. Explicit `wrapping_add`, rather than `sum`, preserves wrapping behavior in debug and optimized profiles. + +## Required next action + +Do not approve the current safe API, and do not claim that the iterator replacement passes the performance gate. Benchmark the exact iterator candidate against the designated baseline with the benchmark identity, inputs, Rust version, target/CPU, profile and codegen options, statistic, uncertainty, and 2% decision rule recorded. If regression is at most 2%, adopt the safe version. If it exceeds 2%, retain the requirement and benchmark a **sound** optimized fallback (at minimum, an enforced empty fast path followed by the pointer loop with proof-grade comments); do not retain the current unsound function or waive the evidence threshold. This staged decision preserves both proof quality and performance rather than treating either as assumed. + +## TCB and residual scope + +TCB: the exact linked Rust 1.70 Reference/standard-library propositions and, only for releases after the reviewed documentation, the explicit compatibility premise above. There are no dependency premises. Compiler/backend correctness and performance outside the designated benchmark are separate, unproved claims. Re-review is triggered by changing the implementation, MSRV/support set, cited contracts, or benchmark definition/environment. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r112.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r112.md new file mode 100644 index 0000000000..627a868bf4 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r112.md @@ -0,0 +1,85 @@ +# Focused `Buffer` source review + +## Claim and verdict + +**UNSOUND** for the exact `lib.rs` snapshot (SHA-256 +`368fa01b2f3fca39e89131414f8ec2641a4ac162b846ccf77b8f8bd293930d9a`), +Rust 1.80.0, every Rust 1.80 target on which this source is accepted, and all +ordinary profiles. The claim reviewed is freedom from Rust undefined behavior +for every well-typed safe use of the current `Buffer` API, relative only to the +versioned Rust axioms below. A wholly safe client can select a reachable path +that necessarily performs a one-byte write while that byte is immutable. + +This is a source-level result under Rust's documented abstract semantics; it +does not depend on a backend, linker, physical read-only placement, testing, or +optimization. + +## Boundary and invariant inventory + +`Buffer`'s fields are private. The exact source has two producers and no other +literal, conversion, trait, macro, or generated producer: + +- `from_writable` (`lib.rs:16-18`) produces state **W**: + `shared == None`, while its unsafe caller must keep `ptr` non-null, aligned, + valid for a one-`u8` write, and free of conflicting access throughout the + interval in which the returned buffer may be used. +- Safe `from_static` (`lib.rs:20-26`) produces state **S**: + `shared == Some(s)`, where `s == &BYTE`, and `ptr` is the same address cast to + `*mut u8`. This establishes addressability and alignment, but not write + permission. + +The only pointee consumer is safe `overwrite` (`lib.rs:28-40`), with one +`ptr.write(value)` site for each state. `with_live` consumes the state-S shared +reference by receiving it as an argument and invokes the write closure before +returning. Moves, ordinary borrows, and implicit drop do not access the +pointee; there is no custom `Drop`. `overwrite` does not transition either +field state. + +## Obligation ledger and derivation + +| Site | Required proposition | Derivation | Status | +|---|---|---|---| +| `from_writable` producer | Returned state makes later safe operations sound while the documented obligations hold. | Construction only stores the raw pointer and `None`; it performs no pointee access. Its ongoing contract directly supplies the later write-validity, alignment, and non-conflict facts. | **PROVED**, conditional on the unsafe caller contract. | +| `overwrite`, state W (`lib.rs:38`) | `ptr` is valid for writes and aligned. | State W exists only through `from_writable`; its still-applicable documented obligations are exactly these requirements. Writing a `u8` neither reads nor drops the prior byte. | **PROVED**, conditional on the unsafe caller contract. | +| `from_static` producer | State S must support every later safe operation without a hidden precondition. | It retains `&BYTE` and creates only a raw pointer cast. A cast does not turn memory reachable through a shared reference into writable memory. | **FAILED** for `overwrite`. | +| `overwrite`, state S (`lib.rs:33`) | `ptr` is valid for the one-`u8` write, including applicable immutability/aliasing rules. | `shared` is passed to `with_live`. Rust 1.80 specifies that a reference passed to a function remains live for at least the whole call. The closure executes during that call. The same Reference makes bytes pointed to by a live shared reference immutable (absent `UnsafeCell`) and classifies every overlapping nonzero write as mutation. `u8` has size 1. Thus `ptr.write` mutates exactly the byte to which live `shared: &u8` points. | **UNSOUND**. | +| Local comments (`lib.rs:31-32`, `36-37`) | Each comment must identify a premise that applies to the actual producer. | The line-36 comment is an incomplete but materially recoverable reference to state W's contract. The identical line-31 comment is false on that branch: `from_static`, not `from_writable`, produced the object. No `from_writable` obligation applies. | **Invalid proof** on state S; deficient proof text on state W. | + +The decisive well-typed safe counterexample is: + +```rust +let mut buffer = Buffer::from_static(); +buffer.overwrite(0); +``` + +Indeed, every completed call to `overwrite` on an unmodified state-S `Buffer` +has the same result, including `overwrite(7)`: Rust 1.80 defines an overlapping +nonzero write as mutation even if the stored bits do not change. `let _ = +shared` is not needed for the proof; passing `shared` into `with_live` already +makes it live throughout that call. + +## Rust 1.80 axioms / TCB + +- **AXIOM-WRITE:** [`ptr::write` requires its destination to be valid for + writes and properly aligned](https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html#safety), + and writes one `T` without reading or dropping the old value. +- **AXIOM-LIVE-IMMUTABLE:** The Rust 1.80 + [undefined-behavior rules](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html#behavior-considered-undefined) + give the function-call liveness lower bound, make bytes pointed to by a + shared reference immutable except through `UnsafeCell`, and define any + overlapping write of more than zero bytes as mutation. +- **AXIOM-U8-SIZE:** The Rust 1.80 + [primitive layout table](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#primitive-data-layout) + gives `size_of::() == 1` on all targets. + +No dependencies, external specifications, tools, or additional assumptions are +consumed. + +## Configuration closure and residual scope + +There is no `cfg`, feature selection, generated code, FFI, assembly, allocator, +panic-dependent restoration, or profile-dependent assertion. The proof is +parametric over targets and profiles because the consumed Rust rules and the +one-byte `u8` layout cover the full requested set. No execution, build, test, +expansion, redesign, or source modification was performed. No broader +whole-crate claim or undocumented robustness property is included. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r113.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r113.md new file mode 100644 index 0000000000..0d06fe859b --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r113.md @@ -0,0 +1,58 @@ +# Implemented-candidate unsafe Rust review + +## Claim and verdict + +**UNSOUND** for the requested aggregate theorem: for the exact `lib.rs` snapshot, not the earlier design sketch, every well-typed safe use should be free of Rust undefined behavior on Rust 1.70+, every target, and ordinary profiles. Audit cutoff: 2026-07-31. The verdict is source-level and relative only to the Rust axioms listed below. + +Rust 1.70 is a member of that supported set, and the safe counterexample below reaches UB there. One supported member is sufficient to refute the universal theorem; no assumption that Rust 1.70 documentation applies to later or future compilers is needed. The finding is source-level and has no target/profile dependency. This review makes no separate version-by-version verdict for releases after 1.70. + +## Boundary, invariant, and coverage + +The safe public surface is `View<'a, T>`, `new`, `get`, and `get_mut`. Both fields are private; this exact module contains the only constructor and transitions. Moves and ordinary drop do not dereference `ptr`. On Rust 1.70, `*mut T` has negative `Send` and `Sync` implementations, so `View` does not expose a cross-thread auto-trait path ([Rust 1.70 raw-pointer docs](https://doc.rust-lang.org/1.70.0/core/primitive.pointer.html#trait-implementations)). There are no macros, generated code, dependencies, `cfg`s, FFI, assembly, allocator operations, or profile-dependent checks. + +`INV-VIEW`, established by `new`, is the only plausible representation invariant: `ptr` is obtained from the input `&'a mut T`, continues to designate that live, aligned, initialized `T`, and the input borrow remains represented for `'a`. The last part follows from the private construction plus `PhantomData<&'a mut T>`; the Rust 1.70 documentation says a `PhantomData` field makes its container **“act as though it stores a value of type `T`”** ([`PhantomData`](https://doc.rust-lang.org/1.70.0/core/marker/struct.PhantomData.html)). This closes liveness and safe access through the original binding, but it does not track references returned by the methods. + +Obligation disposition: + +- `new`: establishes `INV-VIEW`; no unsafe operation. +- `get`: its dereference can be valid in isolation, but returning `&'a T` lets that shared reference outlive the borrow of `self`, so a later `get_mut` can conflict with it. +- `get_mut`: **failed**. Returning `&'a mut T` lets the receiver borrow end while the returned reference remains live, permitting another call. +- Both unsafe blocks have no adjacent `SAFETY` proof. Their proof-documentation obligations are independently **UNPROVED**; for the current signatures no correct proof can be supplied. + +## Soundness witness + +This program uses no `unsafe`: + +```rust +fn write_both(a: &mut u8, b: &mut u8) { + *a = 1; + *b = 2; +} + +let mut value = 0u8; +let mut view = View::new(&mut value); +let first = view.get_mut(); +let second = view.get_mut(); +write_both(first, second); +``` + +In the method signature, the elided receiver lifetime and the explicit result lifetime `'a` are distinct. Consequently `first` does not keep `view` mutably borrowed, and the second safe call is well-typed. Both results point to `value`. Rust 1.70 classifies **“Breaking the pointer aliasing rules”** as UB, models `&mut T` using scoped no-aliasing, and says a reference passed to a function **“is live at least as long as that function call”** ([Rust 1.70 Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)). During `write_both`, both mutable references are therefore live and both access the same byte through references not derived from one another. This violates the controlling rule. The whole execution contains UB, so it witnesses `UNSOUND`; it is not also used as a `CONTRACT-BROKEN` witness. No independent documented behavioral postcondition was requested or falsified. + +## Required repair (proposal only) + +At minimum, change **both** signatures: + +```rust +pub fn get(&self) -> &T +pub fn get_mut(&mut self) -> &mut T +``` + +Rust 1.70's method-elision rule assigns the receiver-reference lifetime to elided output lifetimes ([Reference](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html#lifetime-elision-in-functions)). Thus a live result from `get` blocks `get_mut`, and a live result from `get_mut` blocks every further borrow of the `View`. Changing only `get_mut` is insufficient because an old `get` result could still coexist with a later mutable result. + +Add adjacent proofs along these lines: for `get`, cite `INV-VIEW` for validity and state that the receiver-bounded shared borrow prevents mutable access for the result's lifetime; for `get_mut`, cite `INV-VIEW` and state that the receiver-bounded exclusive borrow prevents every other method-created reference for the result's lifetime. Preferably, replace the raw pointer and marker with a private `value: &'a mut T` field and implement both methods by safe reborrowing; review the resulting auto-trait/API change if that alternative is chosen. + +These are unimplemented candidate designs, **not audited or PROVED artifacts**. Re-audit the exact implementation, field visibility, signatures, auto traits, and local proofs after any change. No build, test, execution, or expansion was used in this source-only review. + +## TCB and review triggers + +Consumed axioms are only the exact Rust 1.70 Reference alias/liveness clauses and Rust 1.70 standard-library `PhantomData` and raw-pointer trait documentation linked above. The lifetime-elision axiom is consumed only by the repair plan. There are no dependency, tool, compiler-backend, platform, or compatibility premises. Re-review on any representation, visibility, method-signature, trait-implementation, supported-version, or relevant Rust aliasing-contract change. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r114.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r114.md new file mode 100644 index 0000000000..6fc8a74ff0 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r114.md @@ -0,0 +1,45 @@ +# Version-partitioned source review + +## Claim and result + +Snapshot: `lib.rs` SHA-256 `e561568dfc6262bc6312f1a346e970dfab1f0d8f5102984e80e3b06cf2885e9b`; request SHA-256 `786c6e6d7d270b20c53e82170066a52a3dee7485526f80bec0e025a8602e411b`. Scope is the sole public safe API, `advance_marker`, at `lib.rs:3-5`, including its unsafe operation at line 4. The theorem is source-level freedom from Rust undefined behavior for every well-typed safe call, separately under the Rust 1.79.0 and 1.80.0 abstract semantics, on every target and in every ordinary profile, relative only to TCB-R114 below. + +| Region | Soundness verdict | +|---|---| +| Rust 1.79.0, every target/profile | **UNSOUND** | +| Rust 1.80.0, every target/profile | **PROVED** relative to TCB-R114 | +| Required union of both regions | **UNSOUND** | + +There is no documented behavioral postcondition beyond the safe signature and raw-pointer result type, so no separate broader robustness claim is in scope. + +## Boundary, obligations, and derivation + +The complete reachable surface is one argument-free safe free function. It always executes one unsafe operation, `*const [u8; 0]::add(1)`, on the safe `core::ptr::null` result. There are no fields, constructors, traits, callbacks, dependencies, macros, generated artifacts, mutable state, or invariants. + +**O1 — computed byte offset. PROVED in both regions.** The versioned Reference specifies that `[T; N]` has size `size_of::() * N`; therefore `[u8; 0]` has size zero on every target ([1.79 array layout](https://doc.rust-lang.org/1.79.0/reference/type-layout.html#array-layout), [1.80 array layout](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#array-layout)). `add(1)` consequently computes `1 * 0 = 0` bytes. Zero fits `isize`, and adding zero to address zero fits `usize`, independently of pointer width and profile. + +**O2-79 — allocated-object bounds. VIOLATED.** Rust 1.79.0's `add` contract requires both starting and resulting pointers to be in bounds of, or one byte past, the same allocated object; violating a listed condition is undefined behavior ([1.79 `add`](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add)). The producer creates a null raw pointer with address zero ([1.79 `null`](https://doc.rust-lang.org/1.79.0/core/ptr/fn.null.html)); the same version's pointer rules state, “A null pointer is never valid, not even for accesses of size zero,” and describe an allocated object as a memory region ([1.79 pointer safety and allocated objects](https://doc.rust-lang.org/1.79.0/core/ptr/index.html#safety)). Thus the null starting pointer does not satisfy the required allocated-object bounds condition. The zero-byte offset did not waive that condition in 1.79.0. A well-typed safe call such as `let _ = advance_marker();` necessarily reaches the violating call, which is a valid safe-use UB counterexample. No caller precondition can be added to a safe, argument-free API to repair it. + +**O2-80 — allocated-object bounds. PROVED.** Rust 1.80.0 changed the applicable clause: allocated-object bounds are required only when the computed byte offset is nonzero, and, “If it is zero, then the function is always well-defined” ([1.80 `add`](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add)). O1 establishes exactly that antecedent. The remaining two arithmetic clauses are also established by O1. Nullness is therefore permitted for this call under 1.80.0 ([1.80 `null`](https://doc.rust-lang.org/1.80.0/core/ptr/fn.null.html)). The returned raw pointer imposes no safe dereference: dereferencing it would require a separate unsafe act by the caller. + +## Configuration closure + +The only semantic axis is the requested Rust-version partition. There is no `cfg`, target-dependent source, feature, dependency, build input, generation, allocation, panic, concurrency, or FFI path. O1 is parametric over targets because multiplication by array length zero produces a zero byte offset regardless of `u8` layout or pointer width. O2-79 fails and O2-80 succeeds for that same entire target/profile domain. These two cases exhaust the supported set. No build, test, execution, or tool-derived evidence was used. + +## Finding and remediation + +**F-1 — UNSOUND on Rust 1.79.0; all callers and configurations affected.** The implementation supplies no safety comment, and no comment can discharge the false 1.79.0 precondition. The smallest source repair while retaining both toolchains is to remove the unnecessary pointer arithmetic and unsafe block: + +```rust +pub fn advance_marker() -> *const [u8; 0] { + core::ptr::null::<[u8; 0]>() +} +``` + +Alternatively, requiring Rust 1.80.0 or later would remove the 1.79.0 region, but would change the requested support contract. If the original expression were retained under that narrower support policy, its missing adjacent proof should state O1 and cite the 1.80.0 zero-offset waiver. + +## TCB-R114 and residual scope + +TCB-R114 consists only of the exact 1.79.0/1.80.0 Rust Reference array-layout rules and standard-library `ptr::null`, raw-pointer safety, and `pointer::add` contracts linked above. No cross-version compatibility premise is used; each region uses its own documentation. No dependency, compiler implementation, backend, platform, or deployment assumption is admitted. Concrete compiler-binary correctness and behavior outside this exact source/API are excluded. + +Re-audit if the source, supported Rust versions/configurations, or any consumed authoritative contract changes. Review method: unsafe-rust skill SHA-256 `c2f07d263ce89d758985d6ff388ca344e038c1db111f2298cc5ddef051697595`. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r115.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r115.md new file mode 100644 index 0000000000..d10e2b0fd1 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r115.md @@ -0,0 +1,47 @@ +# Focused source review: `Ticket` construction + +## Claim, scope, and verdicts + +Snapshot: the supplied `REQUEST.md` and `lib.rs` only. The in-scope theorem is: every well-typed safe call to `ticket(usize) -> Ticket` is free of Rust undefined behavior, returns a `Ticket` containing the input when it is nonzero, and panics when it is zero. The requested configuration set is stable Rust 1.70+, every standard-library target, and ordinary profiles, including both enabled and disabled debug assertions and unwind/abort panic strategies. This is a source-level claim under documented Rust abstract semantics; compiler/backend correctness is excluded. There are no dependencies, generated artifacts, features, callbacks, FFI, or deployment assumptions. + +**Current implementation — `UNSOUND`.** With debug assertions disabled, the valid safe call `ticket(0)` reaches `NonZeroUsize::new_unchecked(0)`, whose contract says this is undefined behavior. Thus one supported profile and safe input refute the universal claim. + +**Current documented behavior — `UNPROVED` overall.** For nonzero inputs it returns the requested value. For zero, it panics only where debug assertions execute; in the counterexample configuration it reaches UB instead. I do not assign a separate `CONTRACT-BROKEN` verdict because the failing path reaches UB, so Rust semantics support no post-UB behavioral conclusion. + +**Proposed redesign — `PROVED` for Rust 1.70, all targets and profiles.** It is also proved parametrically for later stable releases relative to `TCB-COMPAT` below. Without acceptance of that open-ended compatibility premise, the literal unbounded “1.70+” claim remains `UNPROVED` solely for future documentation coverage, not because of a remaining source obligation. + +## Boundary and obligation inventory + +The safe surface is the public opaque type `Ticket` (its tuple field is private) and the public free function `ticket`. No other fields, constructors, methods, trait impls, macros, or hidden APIs exist. Safe code can obtain a `Ticket` only from `ticket`, then move or drop it. The representation invariant is: the private field is a valid `NonZeroUsize` whose numeric value equals the nonzero `id` used to construct it. + +The sole unsafe site is `NonZeroUsize::new_unchecked(id)`. Its obligation is exactly `id != 0`. The preceding `debug_assert!(id != 0)` is the claimed proof, but it does not establish that proposition in every supported profile, and there is no adjacent `SAFETY` derivation. + +## Finding and authoritative derivation + +Rust 1.70 documents that an optimized build “will not execute `debug_assert!` statements unless `-C debug-assertions` is passed” ([`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html#uses)). It documents `new_unchecked` as creating a nonzero value without checking and says: “This results in undefined behaviour if the value is zero” ([`NonZeroUsize::new_unchecked`](https://doc.rust-lang.org/1.70.0/std/num/struct.NonZeroUsize.html#method.new_unchecked)). Therefore, in an ordinary optimized build without that flag, `ticket(0)` performs no earlier panic/check and calls the unsafe function with the forbidden value. This is a concrete valid safe-use UB counterexample on every target; pointer width and target layout are irrelevant to the zero/nonzero partition. + +Minimum resolution: replace the unchecked constructor rather than changing the safe contract or relying on a profile-sensitive assertion. + +## Redesign + +```rust +use core::num::NonZeroUsize; + +pub struct Ticket(NonZeroUsize); + +/// Returns a ticket containing `id`; panics when `id == 0`. +pub fn ticket(id: usize) -> Ticket { + Ticket(NonZeroUsize::new(id).expect("id must be nonzero")) +} +``` + +This preserves the exact safe signature, visibility, representation, and documented outcomes; the panic message was not part of the contract. + +Proof: Rust 1.70 says `new` “Creates a non-zero if the given value is not zero” ([`NonZeroUsize::new`](https://doc.rust-lang.org/1.70.0/std/num/struct.NonZeroUsize.html#method.new)). `Option::expect` returns the contained `Some` value and “Panics if the value is a `None`” ([`Option::expect`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect)). Hence `id == 0` produces no `Ticket` and invokes panic; `id != 0` yields the same numeric value, which `expect` returns and the private field stores. Every operation is safe, so there is no caller-side safety obligation or local unsafe proof. This argument is parametric over `usize` width, target, optimization, debug assertions, and panic strategy (abort versus unwind changes panic completion, not whether panic is invoked). + +## TCB, residual scope, and review triggers + +- `AXIOM-R170-DEBUG`, `AXIOM-R170-NONZERO`, and `AXIOM-R170-EXPECT`: the exact Rust 1.70 standard-library passages linked above. +- `TCB-COMPAT` (needed only for the open-ended post-1.70 claim): later supported stable Rust releases preserve those stable APIs’ cited semantic propositions over ordinary profiles and standard-library targets. This is an explicit compatibility assumption, not inferred from stability badges. + +No tools or tests supply proof evidence. Re-review if the signature/contract, representation visibility, constructor, supported Rust/configuration set, or any consumed standard-library contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r116.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r116.md new file mode 100644 index 0000000000..2c72aba5d1 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r116.md @@ -0,0 +1,45 @@ +# Version-partitioned review of `advance_marker` + +## Claim and verdict + +For the exact `lib.rs` supplied in `/tmp/unsafe-rust-v2-eval.9epWDK/targets/h5p9`, this review asks whether every well-typed safe call to `pub fn advance_marker() -> *const [u8; 0]` is free of Rust undefined behavior. The supported set is exactly Rust/core 1.79.0 or 1.80.0, every target accepted by the corresponding toolchain, and every ordinary profile. There are no inputs, dependencies, generated artifacts, features, conditional compilation, or documented behavioral postconditions. + +| Region | Soundness verdict | +|---|---| +| Rust 1.79.0, all targets/profiles | **UNSOUND** | +| Rust 1.80.0, all targets/profiles | **PROVED**, relative to `TCB-R116-v1` below | +| Combined supported set | **UNSOUND** (because it includes the 1.79.0 witness) | + +This is a verdict only for this function and exact snapshot, not for any enclosing crate. + +## Boundary and obligation inventory + +The only safe surface is `advance_marker`; safe callers have no safety obligation. The only unsafe operation is `core::ptr::null::<[u8; 0]>().add(1)` at `lib.rs:4`. It must satisfy every version-applicable `*const T::add` precondition before its returned raw pointer may be exposed. There is no state, invariant transition, dereference, callback, panic path, or later consumer. + +`OB-1` (all regions): determine the byte offset. The versioned `size_of` documentation states that `[T; n]` has size `n * size_of::()`; therefore `[u8; 0]` has size zero on every target, and `add(1)` computes the mathematical byte offset `1 * 0 = 0`. See [1.79.0 `size_of`](https://doc.rust-lang.org/1.79.0/core/mem/fn.size_of.html) and [1.80.0 `size_of`](https://doc.rust-lang.org/1.80.0/core/mem/fn.size_of.html). + +`OB-2` (pointer producer): both versioned `null` contracts say the result is a null raw pointer with address zero. See [1.79.0 `null`](https://doc.rust-lang.org/1.79.0/core/ptr/fn.null.html) and [1.80.0 `null`](https://doc.rust-lang.org/1.80.0/core/ptr/fn.null.html). + +## Regional derivations + +### Rust 1.79.0 — `UNSOUND` + +The [1.79.0 `add` safety contract](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add) applies its allocation condition unconditionally: starting and resulting pointers must be in-bounds or one-past the same allocated object. The [1.79.0 pointer module](https://doc.rust-lang.org/1.79.0/core/ptr/index.html#safety) expressly excludes null even from zero-sized validity and identifies non-null addresses as the domain in which a zero-sized allocation may be forged. Thus the null starting pointer does not satisfy `add`'s allocation condition. The zero byte offset satisfies the separate `isize` and no-address-wrap requirements, but it does not waive that first clause in 1.79.0. + +A well-typed safe caller can simply invoke `advance_marker()`. That execution reaches `add(1)` with the null pointer and violates its safety precondition, so it contains UB. This is a complete valid-use witness; no dereference of the result is needed. The function is therefore unsound on every 1.79.0 target/profile. + +### Rust 1.80.0 — `PROVED` + +The [1.80.0 `add` safety contract](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add) changed the allocation clause: it applies only when the computed byte offset is nonzero and expressly says that a zero offset is always well-defined. By `OB-1`, this offset is zero. The remaining clauses also hold: zero fits in `isize`, and adding zero to address zero in infinite precision fits in `usize`. The operation therefore has no violated precondition. Returning the resulting raw pointer performs no access and creates no reference. With no other operations or paths, every safe call is UB-free. + +The 1.80.0 text is used only for 1.80.0; it is not projected backward as a clarification or compatibility premise. + +## Configuration closure and evidence + +The proof is parametric over target and ordinary profile: array length zero makes the byte offset zero independently of `u8` size, pointer width, optimization, overflow checks, assertions, panic strategy, or address layout. The source has no configuration selection, and the cited contracts are from each exact toolchain's core documentation. No builds, tests, execution, or tool-derived evidence were used. + +`TCB-R116-v1` contains only these accepted `AXIOM` entries: the exact 1.79.0 and 1.80.0 `size_of`, `null`, pointer-module (where consumed), and `*const T::add` documentation linked above. There are no dependency, implementation, external-specification, deployment, or compatibility assumptions. This is a source-level Rust-abstract-semantics claim; compiler/backend correctness and binary behavior are outside scope. + +## Finding and resolution + +**Critical — 1.79.0 safe-API soundness defect.** The unsafe block also has no adjacent safety proof; on 1.79.0 no comment can discharge the false allocation premise. If 1.79.0 support is retained, the minimal repair is to return `core::ptr::null()` directly (eliminating the unsafe operation), then review that new snapshot. If support is narrowed to 1.80.0, an adjacent proof must state the zero-sized-array derivation and cite the 1.80.0 zero-offset exception. Re-audit on any source change or supported-Rust expansion/change. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r117.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r117.md new file mode 100644 index 0000000000..767f482651 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r117.md @@ -0,0 +1,50 @@ +# `Block` focused review and redesign + +## Claim and verdict + +Scope is exactly the supplied `lib.rs`: `Block`, `Page`, its `unsafe impl`, and `first`; Rust 1.70+, all targets and profiles. There are no conditional/generated paths or dependencies in the supplied artifact. A valid use of `first` supplies a valid `&B`, and every out-of-scope `unsafe impl Block` satisfies the published contract. + +**Current implementation soundness: PROVED**, relative to TCB-RUST below. **`Page`'s documented `Block` postconditions: PROVED.** No UB or postcondition counterexample was found. This is not a whole-crate or downstream-implementation verdict. + +Proof-documentation quality is deficient: the unsafe trait has no consolidated `# Safety` implementer contract, and neither the `unsafe impl` nor the raw dereference has an adjacent `SAFETY` derivation. The reconstruction below proves these scoped implementation obligations; it does not erase that documentation finding or alter the published contract. + +## Obligation ledger and derivation + +Rust 1.70 says an unsafe trait makes implementation unsafe, a correctly implemented unsafe trait is safe to use, and the impl must be `unsafe` ([Reference](https://doc.rust-lang.org/1.70.0/reference/items/traits.html#unsafe-traits)). Thus `Page` is the producer of every stated guarantee and `first` may consume them from any conforming implementation. + +- **`Page::ALIGN`: PROVED.** It is exactly 16, hence nonzero and a power of two. +- **Address and alignment: PROVED.** A `[u8; 16]` has 16 one-byte elements and size 16 ([array layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). The `repr(C)` field algorithm starts at offset zero, so the sole field is at offset zero ([C-struct layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs)). `repr(align(16))` raises the containing type's alignment to 16 ([alignment modifiers](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers)). Consequently a valid `&Page` and `self.0` have the same 16-aligned address. `as_ptr` returns a pointer to the slice buffer ([Rust 1.70 std](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)), so the result is non-null and 16-aligned. +- **Readable interval and extent: PROVED.** A valid shared borrow keeps the `Page` live for that borrow. Its only field is 16 initialized `u8`s, contains no `UnsafeCell`, and cannot be moved or mutated incompatibly during that valid shared borrow. The returned pointer designates those same 16 bytes; therefore they remain readable throughout the receiver borrow. This also satisfies `as_ptr`'s requirement that the slice outlive use of the pointer. +- **`first`: PROVED.** It obtains the pointer and loads the first `u8` immediately while `block: &B` remains borrowed. “Readable for 16 bytes” entails readability of byte zero; the stated non-null guarantee and `u8` alignment are sufficient for the raw load. Rust 1.70 classifies dereferencing a dangling or unaligned raw pointer as UB and reading an uninitialized integer as invalid ([Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)); the `Block` postcondition rules out those cases. No pointer escapes and no write occurs. The result is the byte at `base()`. + +This proof is parametric over target and profile: the source has no `cfg`, arithmetic overflow, assertions, allocator, panic-strategy, or optimization-dependent branch, and the cited layout rules cover the representation abstractly. + +Suggested 1.x-only documentation, without changing meaning: state in `Block`'s `# Safety` section that every impl must establish the existing constant clause and that, for the full receiver-borrow interval, `[base(), base()+16)` is initialized and valid for reads and `base()` is non-null and `ALIGN`-aligned. Add the preceding layout derivation at the impl and the one-byte derivation at `first`. If that wording would add a temporal, provenance, initialization, or interference obligation not already meant by “during the borrow” and “readable,” it is a contract change, not a clarification, and must not be slipped into 1.x. + +## What 1.x can and cannot simplify + +`first` already forms a narrow safe facade. Its local proof may deliberately consume only the weaker lemma it needs—one readable byte for the immediate load—and other in-tree code can use `first` rather than repeat raw access. Proof comments may be centralized or made explicit, and a new safe API may be added alongside the old one after ordinary additive-API compatibility review. + +The published `Block` contract itself cannot be narrowed in 1.x. Changing 16 readable bytes to one, dropping non-nullness or `ALIGN`/power-of-two/alignment guarantees, shortening the guarantee's interval, or weakening `Page`'s corresponding behavior would invalidate potentially existing downstream unsafe consumers. Conversely, strengthening implementer obligations or adding a required trait item would invalidate potentially existing downstream impls. Repository search for `first` establishes neither absence of those consumers nor absence of impls. The full old contract and all current impl proofs therefore remain mandatory throughout 1.x. Removing `repr(C, align(16))` is also not justified: the current zero-offset/alignment proof consumes both representation properties, and public layout may itself be observed. + +## Explicit 2.0 migration + +If the actual required capability is only “obtain the first byte,” the preferred 2.0 design is a **safe** trait method returning a value: + +```rust +pub trait Block { fn first_byte(&self) -> u8; } +impl Block for Page { fn first_byte(&self) -> u8 { self.0[0] } } +pub fn first(b: &B) -> u8 { b.first_byte() } +``` + +This removes the raw pointer, unsafe trait/impl, `ALIGN`, temporal region invariant, and dereference proof. It necessarily breaks existing implementers and consumers, so it requires an explicitly authorized major migration. + +If downstreams genuinely need all 16 bytes, a safe `fn bytes(&self) -> &[u8; 16]` can encode extent, lifetime, initialization, and read access. If they also require an implementation-chosen stronger alignment, split that into a separately named unsafe capability (or an opaque aligned type) rather than burdening the byte-value interface. Choosing between those 2.0 capabilities requires downstream requirements, not the known `first` call alone. Deprecation/adapters may ease migration, but the 1.x contract must remain intact until the major boundary. + +## TCB and review triggers + +- **TCB-RUST-1.70:** the exact Rust 1.70 Reference/std propositions linked above. +- **TCB-RUST-COMPAT:** for later supported stable Rust releases, those exact layout, pointer, validity, and unsafe-trait propositions remain applicable. This explicit compatibility premise is required for the open-ended `1.70+` claim; reject it and the unbounded later-release portion is **UNPROVED**. +- **Project-policy premise:** the request's statement that `Block`, its contract, and downstream impls are published 1.x ordinary-SemVer commitments. + +Re-review on any contract, representation, method body, MSRV/support-policy, or cited semantic change, and audit any implemented 2.0 candidate as a new artifact. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r118.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r118.md new file mode 100644 index 0000000000..6872ffc362 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r118.md @@ -0,0 +1,90 @@ +# Acceptance review: `Piece for Tail` + +## Decision and exact verdicts + +**REJECT.** For `lib.rs` SHA-256 +`d76a5c0d7336aac4e551264a105c621dbd6cf27db097fb648aa3ca1e891e3429`: + +- **CONTRACT-BROKEN** — `unsafe impl Piece for Tail` does not satisfy either + documented direct-field guarantee at `lib.rs:7-10`. +- **PROVED (source-level soundness)** — for every well-typed safe call to + `increment_tail`, and for every call to this concrete `Tail::project` that + satisfies its stated safety precondition, execution is free of Rust UB. + This covers all targets and ordinary profiles for extant stable Rust + 1.70.0 through the audit cutoff 1.97.1, relative only to the matching + versioned Rust Reference and standard-library contracts below. +- There is no `UNSOUND` finding for the concrete safe wrapper. Its successful + implementation proof does not repair the false public trait promises. + +The open-ended part of “Rust 1.70+” after 1.97.1 is outside this dated proof; +each later Rust release is a re-review trigger. No backwards-compatibility or +other additional TCB premise is admitted. + +## Boundary and contract finding + +The in-scope public surfaces are the unsafe trait and its associated items, +`Pair` and its public field, `Tail`, the `unsafe impl`, and the safe +`increment_tail`. There are no dependencies, `cfg` branches, generated source, +FFI, allocation, concurrency, or invariant-bearing private state. + +`Pair(pub [u32; 2])` declares exactly one direct tuple-struct field: field `0`, +whose type is `[u32; 2]`. The Reference says a tuple index is the field's name +and `.0` evaluates to that field's location; it separately says array elements +are accessed with array indexing ([Rust 1.70 tuple indexing](https://doc.rust-lang.org/1.70.0/reference/expressions/tuple-expr.html#tuple-indexing-expressions), +[array indexing](https://doc.rust-lang.org/1.70.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions)). + +Consequently: + +1. `FIELD = "tail"` (`lib.rs:24`) is not the name of any declared `Pair` + field, and `Pair` has no direct field of type `u32`. +2. `project` returns the address of `.0[1]` (`lib.rs:27`), an element nested + inside direct field `.0`, not a direct declared field of `Pair`. + +Thus both the associated-constant statement and “Returns a pointer to that +direct declared field” are false. This is an implementation responsibility: +the Reference states that an unsafe trait is safe to use only when correctly +implemented ([Rust 1.70 unsafe traits](https://doc.rust-lang.org/1.70.0/reference/items/traits.html#unsafe-traits)). +The failure is unconditional across values, targets, and profiles and requires +no UB counterexample. + +## Reconstructed soundness proof + +For `Tail::project`, the precondition supplies a live, uniquely borrowed +`Pair`. Therefore `owner` is aligned, non-dangling, and identifies initialized +storage for the whole `Pair`. Field `.0` is within that object and constant +index `1` is within a two-element native array. Native array indexing yields an +element place; it does not invoke caller code through `IndexMut`. + +`addr_of_mut!` creates a raw pointer without an intermediate reference. The +versioned contract changed wording but not the proposition needed here: +Rust 1.70-1.74 subjects the place to the usual rules; Rust 1.75 onward states +that the place is not loaded and that projections must be in-bounds +([1.70](https://doc.rust-lang.org/1.70.0/std/ptr/macro.addr_of_mut.html), +[1.75](https://doc.rust-lang.org/1.75.0/std/ptr/macro.addr_of_mut.html), +[1.78](https://doc.rust-lang.org/1.78.0/std/ptr/macro.addr_of_mut.html), +[1.97.1](https://doc.rust-lang.org/1.97.1/std/ptr/macro.addr_of_mut.html)). +The checked live object and in-bounds projections satisfy both formulations, +so the result is an aligned, live pointer to initialized `owner.0[1]` with +provenance derived from `owner`. + +At `increment_tail` (`lib.rs:31-34`), the incoming `&mut Pair` establishes the +callee precondition. No alias, callback, or intervening access competes with +the derived pointer. Creating `&mut *...`, reading its `u32`, and storing back +therefore meet the Reference's reference validity and alias requirements +([Rust 1.70 UB rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html), +[Rust 1.97.1 UB rules](https://doc.rust-lang.org/1.97.1/reference/behavior-considered-undefined.html)). +`wrapping_add(1)` is defined modular addition for every `u32` +([Rust 1.70](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add), +[Rust 1.97.1](https://doc.rust-lang.org/1.97.1/std/primitive.u32.html#method.wrapping_add)). +On return, `pair.0[1]` is its old value plus one modulo `2^32`, `pair.0[0]` is +unchanged, and `Pair` remains valid. This proof is target/layout-parametric: +it uses typed field and in-bounds array projection, never a numeric offset or +representation assumption; wrapping behavior is profile-independent. + +## Evidence, TCB, and residual scope + +The only admitted axioms are the exact version-matched official Rust documents +linked above; no dependency, compiler-backend, deployment, tool, or testing +claim is consumed. This was a source review only. Other implementations of +`Piece`, downstream unsafe consumers, and behavior after Rust 1.97.1 were not +reviewed. Any source/contract change or new Rust release invalidates the result. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r119.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r119.md new file mode 100644 index 0000000000..26f0f33033 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r119.md @@ -0,0 +1,83 @@ +# Focused source review: `classify` + +## Scope and claim + +Artifact: the exact supplied `lib.rs`, lines 1–12. The only safe API surface is +`pub fn classify(input: u8) -> u8`. The claim is source-level freedom from Rust +undefined behavior for every well-typed safe call, plus every documented +behavior, under Rust and `core` 1.80.0 on all requested targets and ordinary +profiles. No caller-side safety precondition is permitted. + +The mandatory behaviors are: + +1. if `input == 0`, the call panics; and +2. on every normal return, the result equals `input`. + +There are no other public items, conditional-compilation branches, generated +code, dependencies, callbacks, state, or invariants in the supplied artifact. +The relevant configuration axes are target and profile only. The proof below is +parametric over both: the source has no `cfg` or profile-dependent check, and +the cited abstract contracts have no target/profile qualification. This is not +a claim that a particular compiler backend emits a correct binary. + +## Authoritative premises / TCB + +- **AXIOM-UU (Rust/core 1.80.0, all in-scope configurations):** + [`unreachable_unchecked`](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety) + says: “Reaching this function is Undefined Behavior.” +- **AXIOM-MATCH (Rust 1.80.0):** the + [match-expression rules](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html) + state that values are compared with arm patterns in sequence and the first + matching arm becomes the branch target. +- **AXIOM-PATTERN (Rust 1.80.0):** + [literal patterns](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns) + match the value created by their literal, while the + [wildcard pattern](https://doc.rust-lang.org/1.80.0/reference/patterns.html#wildcard-pattern) + matches any value. +- **AXIOM-RETURN (Rust 1.80.0):** the + [function-body rule](https://doc.rust-lang.org/1.80.0/reference/items/functions.html#function-body) + says an evaluated tail expression is returned to the caller. + +No additional assumptions or tool-derived evidence are consumed. + +## Coverage and derivation + +The arms form an exhaustive partition of `u8`: `input == 0`, `input == 1`, and +all other values. For `0`, AXIOM-MATCH and AXIOM-PATTERN select line 8, so the +call reaches `unreachable_unchecked`; AXIOM-UU therefore makes that execution +undefined. For `1`, line 8 is not entered, line 9 is selected, and the tail +expression returns `2`. That execution contains no unsafe operation and no +other possible UB site in the supplied source. For every remaining value, +line 10 returns `input`. This partition and reasoning are unchanged by target, +edition accepted by this source, optimization, debug assertions, overflow +checks, or panic strategy. + +## Results + +### Soundness — **UNSOUND** + +The valid safe call `classify(0)` is a witness. It satisfies every enforced +type requirement and the API documents behavior for that input rather than +excluding it. It necessarily reaches line 8, violating AXIOM-UU and producing +Rust undefined behavior. The witness applies across the entire requested +target/profile set. + +The unsafe block also has no adjacent `SAFETY` proof. More importantly, no +truthful proof can discharge its obligation: the branch condition establishes +that the supposedly unreachable call is reachable. + +### “Panics when `input == 0`” — **UNPROVED** + +The only source path for `input == 0` reaches undefined behavior. An execution +containing UB cannot establish a defined panic observation and cannot serve as +the UB-free existential witness required for `CONTRACT-BROKEN`. No independent +UB-free witness exists in the audited source, so this behavioral clause remains +`UNPROVED`, separately from the `UNSOUND` soundness result. + +### “On normal return, returns `input`” — **CONTRACT-BROKEN** + +`classify(1)` is an independent UB-free witness: the first literal pattern does +not match, the `1` pattern selects line 9, and AXIOM-RETURN returns `2` normally. +Because `2 != 1`, this execution falsifies the documented postcondition without +executing line 8 or otherwise encountering UB. The witness is configuration +independent and therefore breaks the contract across the requested scope. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r120.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r120.md new file mode 100644 index 0000000000..12684fbe3e --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r120.md @@ -0,0 +1,69 @@ +# Focused unsafe-Rust source review + +## Claim and verdict + +**UNSOUND.** For the exact `lib.rs` snapshot in `targets/p1j4`, the claim that every well-typed safe use is free of Rust undefined behavior on Rust 1.70+, all targets, and ordinary profiles is false. A safe client can obtain two simultaneously live mutable references to the same object. The witness below applies on Rust 1.70, a member of the requested set, and therefore refutes the universal claim without any premise about compatibility with later Rust releases. + +This is a fresh result; no verdict was inherited from `DESIGN-NOTE.md`. The cutoff is 2026-07-31. The review is source-level under documented Rust abstract semantics. It does not claim compiler/backend correctness. There are no dependencies, generated artifacts, features, `cfg` branches, FFI, allocation, concurrency, or panic-sensitive transitions in the supplied snapshot. + +## Boundary and obligation inventory + +The entire reachable API is the public `View<'a, T>` type with private fields and three safe methods: `new`, `get`, and `get_mut`. There are no public fields, unsafe APIs/traits/impls, trait methods, macros, derives, or hidden items. The unsafe operations are the raw-pointer dereference/reference creations at `lib.rs:16` and `lib.rs:20`. + +The intended representation invariant is: `ptr` is the pointer obtained from the unique `&'a mut T` accepted by `new`; it remains aligned, non-null, live, and points to a valid `T`; the private field is never changed; and access must preserve the uniqueness represented by `PhantomData<&'a mut T>`. `new` establishes the pointer facts. Privacy and the absence of transitions preserve its address/liveness provenance. Those facts discharge the basic dereferenceability obligations, but not the aliasing obligation. + +Both accessors return the stored lifetime `'a`, not the lifetime of their receiver borrow: + +```rust +pub fn get(&self) -> &'a T +pub fn get_mut(&mut self) -> &'a mut T +``` + +Consequently an accessor result does not keep `View` borrowed. Safe code can call `get_mut` again while its prior result remains usable (and can similarly call `get_mut` after `get`). `PhantomData` models the original borrow but does not relate these returned references to each individual receiver loan. + +## Safe UB witness + +```rust +use core::cell::UnsafeCell; + +fn take_both(_: &mut UnsafeCell, _: &mut UnsafeCell) {} + +let mut value = UnsafeCell::new(0); +let mut view = View::new(&mut value); +let first = view.get_mut(); +let second = view.get_mut(); +take_both(first, second); +``` + +The two calls borrow `view` only for their independent receiver loans, while each result has the unrelated stored `'a`; thus the program is well typed using only safe APIs. Both results are derived from the unchanged `ptr` and therefore alias the same `UnsafeCell`. During `take_both`, both are live: the Rust 1.70 Reference says, “Each time a reference or box is passed to or returned from a function, it is considered live.” The same version states that “it is undefined behavior to have multiple `&mut UnsafeCell` aliases.” See the exact [Rust 1.70 undefined-behavior rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html) and [interior-mutability rule](https://doc.rust-lang.org/1.70.0/reference/interior-mutability.html). This proves a complete safe execution reaches UB; it is not merely a missing proof. + +The source and witness contain no target-dependent operation or profile-dependent check. More importantly, existence on Rust 1.70 alone is sufficient for the aggregate verdict. No independent UB-free violation of a documented behavioral postcondition was established, so there is no separate `CONTRACT-BROKEN` verdict. No broader safe-API behavior was requested. + +## Proof-documentation finding + +Both unsafe blocks also lack adjacent `SAFETY` proofs. The necessary aliasing premise would be false for the current signatures, so documentation cannot repair this snapshot. Even after an implementation fix, each block should identify the pointer-origin/liveness invariant and explain how the receiver-bound output lifetime enforces alias compatibility. + +## Minimal proposed repair (not audited as implemented) + +Tie each result to its receiver loan explicitly: + +```rust +pub fn get<'s>(&'s self) -> &'s T { + // SAFETY: `ptr` came from `new`'s `&'a mut T`, is private and unchanged, + // and remains live and valid while this View exists. The result is limited + // to `'s`, so the shared receiver loan prevents mutable access through View. + unsafe { &*self.ptr } +} + +pub fn get_mut<'s>(&'s mut self) -> &'s mut T { + // SAFETY: the same pointer invariant holds. The result is limited to `'s`, + // so the exclusive receiver loan prevents any overlapping accessor result. + unsafe { &mut *self.ptr } +} +``` + +Both signatures must change: fixing only `get_mut` still permits an old `'a` shared result from `get` to overlap a later mutable result. This narrows lifetimes accepted by existing safe callers and therefore requires compatibility review. The proposal is **UNPROVED as a new artifact** until implemented and re-audited, including its exact compiler-enforced variance, drop-check, and auto-trait behavior. + +## TCB and review triggers + +Consumed TCB: only the two exact Rust 1.70 Reference propositions quoted and linked above. No tools, tests, external specifications, or dependency contracts were used. Re-audit after either method signature/body or representation changes, any new constructor/field/trait implementation is exposed, the supported configuration policy changes, or the consumed authoritative rules change. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r121.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r121.md new file mode 100644 index 0000000000..325ebc5a28 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r121.md @@ -0,0 +1,72 @@ +# Focused source review: `classify` + +## Claim, snapshot, and verdicts + +Audited artifact: `targets/m2q8/lib.rs` (SHA-256 +`42388bb1a03741a7725c9836cc134c205af3061efef7b8ee00ce38d2dc51aeeb`), +under the request with SHA-256 +`9d559df45c2ef498f44516fed36ef84c82fa4a02a59edca55d2ff88a7b93eb02`. +The exact scope is the sole public safe API, `classify(u8) -> u8`, for every +well-typed safe call under Rust and `core` 1.80.0, on all Rust 1.80.0 targets +and ordinary profiles. The mandatory behaviors are: (1) panic when `input == +0`; and (2) on normal return, return `input`. + +- **Soundness: UNSOUND.** The safe call `classify(0)` reaches undefined + behavior. +- **Behavioral contract: CONTRACT-BROKEN.** The defined, normally returning + call `classify(1)` returns `2`, not `1`. Independently, the `input == 0` + branch reaches UB and therefore establishes no defined panic behavior. + +These are source-level verdicts relative to `TCB-m2q8-1` below. This was a +source-only review; no build, test, expansion, or execution was used. + +## Authoritative premises (`TCB-m2q8-1`) + +- **AXIOM-UU (Rust 1.80.0 `core`):** the exact safety contract says, + “Reaching this function is Undefined Behavior.” + [`unreachable_unchecked` safety documentation](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety). +- **AXIOM-MATCH (Rust 1.80.0 Reference):** “The first arm with a matching + pattern is chosen as the branch target of the `match`.” + [Match expressions](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html). +- **AXIOM-PATTERN (Rust 1.80.0 Reference):** “Literal patterns match exactly + the same value as what is created by the literal,” and “The wildcard pattern + ... matches any value.” + [Literal patterns](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns) + and [wildcard patterns](https://doc.rust-lang.org/1.80.0/reference/patterns.html#wildcard-pattern). + +No dependency, external-specification, tool, compiler-backend, or deployment +assumption is consumed. The standard-library and Reference statements above +are the complete source-level trust boundary. + +## Obligation ledger and derivation + +| ID | Site / obligation | Evidence and disposition | +|---|---|---| +| S-1 | `lib.rs:8`: satisfy the unsafe callee's requirement that its site is unreachable | A safe caller may pass the valid `u8` value `0`. AXIOM-PATTERN makes the `0` pattern match; AXIOM-MATCH selects that first arm; its expression invokes `unreachable_unchecked`. AXIOM-UU therefore makes this valid safe use UB. **UNSOUND.** | +| B-1 | `lib.rs:3`: panic when `input == 0` | The same exhaustive control-flow derivation reaches UB at line 8, not a defined panic operation. Once UB is reached, no Rust behavioral guarantee follows. The promised panic is therefore not established; this failure accompanies S-1. | +| B-2 | `lib.rs:5`: every normal return equals `input` | For `input == 1`, the `1` literal pattern matches and the first arm does not; AXIOM-MATCH selects line 9. That arm evaluates to `2`, so the function normally returns `2`, and `2 != 1`. This is a defined counterexample independent of S-1. **CONTRACT-BROKEN.** | + +The case split is exhaustive: `0` selects line 8; `1` selects line 9; every +other input reaches the wildcard at line 10 and returns `input`. Thus the +wildcard cases satisfy the return clause, but they cannot rescue either +universal verdict. + +## Configuration closure and proof-artifact finding + +There is no conditional compilation, generated code, macro API, dependency, +target feature, FFI, concurrency, allocator, or profile-sensitive operation in +the audited source. The proof is parametric over target and ordinary profile: +each configuration has the same three-arm source, while AXIOM-UU and the match +semantics are unqualified across the requested Rust 1.80.0 target/profile set. +Optimization and panic strategy cannot repair a source execution that reaches +UB, and the `input == 1` counterexample contains neither overflow nor panic. + +The unsafe block has no adjacent safety proof. The smallest required +proposition would be “the line-8 call site is unreachable for every safe call,” +but `classify(0)` directly falsifies it, so no truthful comment can discharge +the current implementation. If remediation is later authorized, the minimum +contract-preserving changes are to use a defined panic on the zero arm and make +the `1` arm return `1`; any alternative documentation change requires separate +compatibility review. No source was changed. Re-review is required if the +source, documented behaviors, Rust version, or supported configuration set +changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r122.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r122.md new file mode 100644 index 0000000000..4e04840c99 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r122.md @@ -0,0 +1,42 @@ +# Focused review of `total` + +## Claim, scope, and verdicts + +Snapshot: `lib.rs` SHA-256 `12ee0d64af16f4b4bea78bb146d333f86c579b49ca0dd38e2552f3896147be5c`; request SHA-256 `8e32395f979236c2f9fc5b811aa5d7d6a83b054dad42febb341b776f1c5dfa2a`. Scope is the public safe function `total`, its three unsafe expressions, the proposed iterator replacement, modular-sum behavior, and the stated 2% benchmark gate. No build metadata, generated code, dependencies, or benchmark artifact was supplied. No code or benchmark was executed. + +Supported source configurations are stable Rust 1.70+ on all targets and ordinary profiles. This review uses Rust 1.70.0 as the minimum-version contract and Rust 1.97.1 as the audit cutoff; a later stable release is a re-review trigger. + +- **Current safe-API soundness: `UNSOUND` for the declared support set, relative to the literal Rust 1.70 contracts.** A valid empty slice can cause line 6 to call `add(0)` on a dangling data pointer, contrary to that version's `add` precondition. +- **Current non-empty implementation and result: `PROVED`.** For every non-empty valid `&[u32]`, it reads each element once and returns their sum modulo \(2^{32}\), for all targets and profiles, relative to the cited Rust 1.70 axioms. +- **Safe iterator candidate: source soundness and modular result `PROVED` under the Rust 1.70 documented contracts.** It has no target-owned unsafe obligation and uses APIs present at the MSRV. The proof is target/profile-parametric. Later toolchains must retain the cited safe API contracts; do not project later wording backward when auditing old releases. +- **At-most-2% benchmark regression: `UNPROVED`.** No benchmark identity, protocol, measurements, or result is available. There is neither evidence of compliance nor evidence of failure. + +## Obligation proof and finding + +Rust 1.70 documents a slice as contiguous, and `from_raw_parts` requires its range to lie in one allocated object, contain initialized elements, and have total size at most `isize::MAX`; importantly, it expressly permits `NonNull::dangling()` as the data pointer of a zero-length slice ([slice construction contract](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html#safety), [`NonNull::dangling`](https://doc.rust-lang.org/1.70.0/std/ptr/struct.NonNull.html#method.dangling), [Reference: dangling pointers](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#dangling-pointers)). + +For a non-empty input, let `base = values.as_ptr()` and let `k` be the completed iteration count. The loop invariant is: `0 <= k <= len`; `ptr = base.add(k)`; `acc` is the modular sum of elements `0..k`; the shared slice remains live and unmodified. The slice invariants make `base.add(len)` in-bounds-or-one-past without byte or address-space wrap. When `k < len`, `ptr` is aligned and readable for one initialized `u32`; dereference is permitted, `wrapping_add` establishes the next modular sum, and `add(1)` advances within the same allocation or to one-past. Nonzero `u32` element size and non-wrapping offsets make the pointer comparison terminate exactly at `k == len`. No element access through `values` is interleaved with the raw reads. These facts discharge lines 6, 9, and 10 for non-empty slices under the [Rust 1.70 `add` contract](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add), [`as_ptr` contract](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr), and [pointer-validity rules](https://doc.rust-lang.org/1.70.0/std/ptr/index.html#safety). + +The empty case breaks that proof. Rust 1.70 says both the starting and resulting pointers of `add` must be within-or-one-past the same allocated object, without exempting a zero offset. Yet a valid empty slice may use a well-aligned dangling pointer. Thus line 6 violates an unsafe precondition on a permitted input. Rust 1.97.1 limits the allocation requirement to a **non-zero** computed offset ([later `add` contract](https://doc.rust-lang.org/1.97.1/std/primitive.pointer.html#method.add)), but that later guarantee has no stated historical scope and cannot repair Rust 1.70. + +All three unsafe expressions also lack adjacent `SAFETY` proofs. The reconstructed non-empty derivation is material; comments alone cannot repair the empty case. + +## Safe redesign + +```rust +pub fn total(values: &[u32]) -> u32 { + values + .iter() + .fold(0u32, |acc, &value| acc.wrapping_add(value)) +} +``` + +At Rust 1.70, `iter` yields every slice item from start to end, `fold` applies the closure to every item from initial accumulator zero, and `wrapping_add` is modular addition ([`slice::iter`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.iter), [`Iterator::fold`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.fold), [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add)). Therefore the candidate returns exactly the required modular sum, including zero for an empty slice, independently of overflow checks, optimization, panic strategy, target, and ordinary profile. + +## Recommendation + +Make the next action a **benchmark-gated safety remediation**: prepare the exact iterator candidate, then obtain and run the designated benchmark before merge or release. Freeze the baseline hash above, candidate source, workload and inputs, compiler, target, profile/options, metric direction, sampling method, and decision rule; accept only when the benchmark's prescribed statistic establishes no more than 2% regression. + +If it passes, adopt the safe iterator version. If it fails or is inconclusive, do not waive soundness: try another safe iterator formulation, or evaluate a pointer version that first returns on `values.is_empty()` and includes the reconstructed adjacent proofs; benchmark that changed version under the same gate. Until one candidate satisfies both obligations, the current Rust-1.70-compatible implementation should not ship and the replacement should not be claimed performance-compliant. + +TCB is limited to the exact versioned Rust Reference/std propositions linked above. No dependency, tool, compiler-backend, or benchmark premise was admitted. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r123.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r123.md new file mode 100644 index 0000000000..940c742638 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r123.md @@ -0,0 +1,48 @@ +# Supported-domain source review + +## Claim and verdict + +**PROVED** for source-level Rust soundness of the exact `targets/v7c4` snapshot, relative to TCB `V7C4-1`, over the conservative candidate-domain union `U` below. The claim quantifies over every well-typed safe call to the exported `first(&[u8]) -> Option` and over both control-flow outcomes. It assumes a context that has not already violated Rust's rules. There are no public unsafe APIs and no documented unsafe-API postconditions. + +This is the strongest conclusion available without selecting a support policy. It does **not** decide which configurations the project promises to support. + +## Snapshot and supported-set handling + +The reviewed package is edition 2021, has no dependencies, build script, generated source, macros, FFI, assembly, or invariant-bearing state, and has one Cargo feature, `fast`. The audit cutoff is Rust 1.82.0. + +`POLICY-A.md` and `POLICY-B.md` are both controlling and no precedence is authorized. Policy B's set is a strict subset of Policy A's set, so their union is: + +- stable Rust/standard library `V = {1.79.0, 1.80.0, 1.81.0, 1.82.0}`; +- targets `T = {x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu}`; +- without `fast`: every `V x T` pair; +- with `fast`: every version on x86_64, and versions 1.80.0--1.82.0 on aarch64. + +That union is `U`; auditing it is a conservative coverage device, not a new support commitment. `rust-toolchain.toml` merely selects 1.82.0 and `CI.md` merely samples configurations; neither resolves the published-policy ambiguity. The proof is parametric over the two named targets and over profile, optimization, and panic strategy because none selects or changes relevant source behavior. + +## Boundary, configuration, and obligation coverage + +The complete safe surface is one `pub fn first` implementation in each feature state. `cfg(feature = "fast")` and its negation are mutually exclusive and exhaustive. + +- **Non-`fast`: PROVED.** `bytes.first().copied()` contains no unsafe operation or caller-side safety obligation. +- **`fast`, empty input: PROVED.** `bytes.is_empty()` selects `None`; the unsafe expression is not evaluated. +- **`fast`, nonempty input / `get_unchecked(0)`: PROVED.** On each version in `V`, slice documentation says `is_empty` “Returns `true` if the slice has a length of 0,” and `get_unchecked` returns an element reference without bounds checking while calling it with an out-of-bounds index is UB. The exact relevant text was verified in [1.79.0 (`is_empty`)](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.is_empty), [1.79.0 (`get_unchecked`)](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.get_unchecked), [1.80.0 (`is_empty`)](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.is_empty), [1.80.0 (`get_unchecked`)](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.get_unchecked), [1.81.0 (`is_empty`)](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.is_empty), [1.81.0 (`get_unchecked`)](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.get_unchecked), [1.82.0 (`is_empty`)](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.is_empty), and [1.82.0 (`get_unchecked`)](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked). The else branch establishes `bytes.len() != 0`; because a slice length is a `usize`, `0 < bytes.len()`, so index 0 is in bounds. There is no intervening call or mutation. The unsafe call therefore meets its bounds obligation and returns the first `&u8`; dereferencing it copies a valid `u8` while the input shared borrow remains live. + +The implementation and derivation do not inspect or depend on target properties, so these cases cover every member of `U` rather than only the CI samples. + +## Finding: missing local proof documentation + +**Proof-documentation defect; implementation remains PROVED.** `lib.rs:12` has no adjacent `SAFETY` comment. Validation required reconstructing the material bounds derivation above. Proposed wording (no edit was requested): + +```rust +// SAFETY: This branch is reached only when `bytes.is_empty()` is false, so +// `bytes.len() != 0` and therefore `0 < bytes.len()`. Index 0 is in bounds, +// satisfying `get_unchecked`'s safety requirement. No operation between the +// check and the call can change the immutably borrowed slice. +Some(unsafe { *bytes.get_unchecked(0) }) +``` + +## TCB, exclusions, and review triggers + +`V7C4-1` contains only the cited, version-matched standard-library contracts and the applicable Rust abstract semantics. There are no dependency, tool-derived, external, deployment, or probabilistic assumptions. Compiler/backend correctness and binary-level claims are outside this source-level review. No build or test evidence was used. + +Re-review is required if the source, either policy, the feature/target/version domain, edition, or a consumed versioned standard-library contract changes. An authorized policy decision is still needed to make the project's support predicate unambiguous, but not to sustain the soundness theorem over `U`. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r124.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r124.md new file mode 100644 index 0000000000..e1ef3d32b3 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r124.md @@ -0,0 +1,47 @@ +# Supported-domain source review + +## Claim and verdict + +**PROVED — source-level Rust soundness.** For the exact `v7c4` snapshot (notably `lib.rs` SHA-256 `6f87e944cec7ee2727f7c7d32aa382de88987ea791082175d611fff5bf44012b`), every well-typed safe call to the selected public `first(&[u8]) -> Option` implementation is free of Rust undefined behavior over the conservative review domain `U`, relative to `R124-TCB-1` below. There are no caller-side safety preconditions. + +`U` is the union of the two current policy candidates, which equals Policy A: + +- stable Rust `R ∈ {1.79.0, 1.80.0, 1.80.1, 1.81.0, 1.82.0}` (all stable releases in the policies' closed interval); edition 2021; +- targets `x86_64-unknown-linux-gnu` and `aarch64-unknown-linux-gnu`; +- without `fast`: every version/target pair; +- with `fast`: x86_64 on 1.79.0–1.82.0 and aarch64 on 1.80.0–1.82.0. + +This covers all 19 configurations admitted by either policy. It does **not** choose a policy or assert that `U` is the project's actual support promise. That policy-identity proposition remains **UNPROVED**: Policy B excludes `fast` on x86_64/1.79.0 and aarch64/{1.80.0, 1.80.1, 1.81.0}, while Policy A includes them, and `REQUEST.md` authorizes no precedence. The conflict does not weaken the soundness result because the proof covers the union. + +The cutoff is Rust 1.82.0. `rust-toolchain.toml` merely selects 1.82.0, and `CI.md` expressly describes samples rather than support; neither resolves the conflict or supplies universal evidence. + +## Boundary and configuration coverage + +The entire reachable API surface is one safe free function; `cfg` selects exactly one of its two definitions. There are no public fields, unsafe APIs/traits/impls, callbacks, dependencies, macros, generated artifacts, FFI, mutable state, or representation invariants. No documented caller-facing postcondition exists, so the mandatory postcondition scope is empty. + +For each audited Rust version, the Reference says `cfg` includes its item when its predicate is true and removes it when false ([1.79.0](https://doc.rust-lang.org/1.79.0/reference/conditional-compilation.html#the-cfg-attribute), [1.80.0](https://doc.rust-lang.org/1.80.0/reference/conditional-compilation.html#the-cfg-attribute), [1.80.1](https://doc.rust-lang.org/1.80.1/reference/conditional-compilation.html#the-cfg-attribute), [1.81.0](https://doc.rust-lang.org/1.81.0/reference/conditional-compilation.html#the-cfg-attribute), [1.82.0](https://doc.rust-lang.org/1.82.0/reference/conditional-compilation.html#the-cfg-attribute)); each also defines `not` as the Boolean complement ([1.79.0](https://doc.rust-lang.org/1.79.0/reference/conditional-compilation.html#configuration-predicate), [1.80.0](https://doc.rust-lang.org/1.80.0/reference/conditional-compilation.html#configuration-predicate), [1.80.1](https://doc.rust-lang.org/1.80.1/reference/conditional-compilation.html#configuration-predicate), [1.81.0](https://doc.rust-lang.org/1.81.0/reference/conditional-compilation.html#configuration-predicate), [1.82.0](https://doc.rust-lang.org/1.82.0/reference/conditional-compilation.html#configuration-predicate)). Thus `fast` and `not(fast)` form an exhaustive, disjoint partition. + +## Obligation ledger and proof + +| ID | Domain | Obligation | Derivation | Status | +|---|---|---|---|---| +| O-NORMAL | `U ∩ ¬fast` | Safe API admits no UB for any `&[u8]` | `bytes.first().copied()` uses only safe operations; there is no unsafe obligation or hidden caller condition. | PROVED | +| O-FAST | `U ∩ fast` | Index `0` passed to `get_unchecked` is in bounds | The dominating `if bytes.is_empty()` returns before the unsafe operation. The version-matched slice docs say `is_empty` is true exactly when length is 0, so on the remaining branch `bytes.len() > 0`, hence `0 < bytes.len()`. The same docs require the `get_unchecked` index to be in bounds and say an out-of-bounds call is UB. Therefore index 0 satisfies the contract; the returned `&u8` is immediately read and copied with no mutation, callback, or intervening transition. | PROVED | + +The relevant standard-library wording is identical in each audited version: `is_empty` “Returns `true` if the slice has a length of 0” ([1.79.0](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.is_empty), [1.80.0](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.is_empty), [1.80.1](https://doc.rust-lang.org/1.80.1/std/primitive.slice.html#method.is_empty), [1.81.0](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.is_empty), [1.82.0](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.is_empty)); `get_unchecked` says calling it with an out-of-bounds index is undefined behavior even if the reference is unused ([1.79.0](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.get_unchecked), [1.80.0](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.get_unchecked), [1.80.1](https://doc.rust-lang.org/1.80.1/std/primitive.slice.html#method.get_unchecked), [1.81.0](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.get_unchecked), [1.82.0](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked)). This is a per-version proof, not a backwards-compatibility assumption. Neither obligation depends on target facts, profiles, optimization, panic strategy, or CI execution. + +## Finding: missing local proof documentation + +`lib.rs:12` has no adjacent `SAFETY` comment. This does not alter O-FAST's implementation verdict because the derivation above succeeds, but it is a proof-documentation defect. Proposed wording (no edit requested): + +```rust +// SAFETY: This branch is reached only when `bytes.is_empty()` is false, +// so `bytes.len() > 0`; therefore index 0 is in bounds for `bytes`. +Some(unsafe { *bytes.get_unchecked(0) }) +``` + +## TCB, residual scope, and triggers + +`R124-TCB-1` contains only the exact, version-matched Rust Reference `cfg` semantics and standard-library slice contracts linked above. No additional compatibility, dependency, tool, compiler-backend, platform, or deployment premise is consumed. This is a source-level abstract-semantics result; no binary/backend correctness claim, test result, or broader robustness property is asserted. + +Re-audit on any source or policy change, support beyond the cutoff, a new target/feature/build-time code path, or a material change to a consumed versioned Rust contract. Resolve the two published policies to make the project's support predicate itself provable. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r125.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r125.md new file mode 100644 index 0000000000..d64ec82f41 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r125.md @@ -0,0 +1,22 @@ +# `Piece for Tail` acceptance review + +## Decision and exact verdicts + +**REJECT** this snapshot for vendoring. + +- **Contract compliance — CONTRACT-BROKEN.** In [`lib.rs:18`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/y9t5/lib.rs:18), `Pair` is a tuple struct with one direct declared field, `.0`, of type `[u32; 2]`. The Rust Reference confirms that tuple-struct fields are anonymous ([Rust 1.70](https://doc.rust-lang.org/1.70.0/reference/types/struct.html); [Rust 1.97.1](https://doc.rust-lang.org/1.97.1/reference/types/struct.html)). Consequently, [`FIELD = "tail"`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/y9t5/lib.rs:24) is not “the name of a direct declared field of `Owner` whose type is `Item`.” Moreover, [`project`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/y9t5/lib.rs:26) returns the address of `.0[1]`, an element nested inside the direct `[u32; 2]` field, rather than a direct declared `u32` field. Thus both the associated-constant guarantee (line 7) and the method postcondition “that direct declared field” (line 10) are false for every call. Any live `Pair` satisfying the safety precondition is a concrete postcondition counterexample. This is independent of target and profile. +- **Implementation soundness — PROVED for Rust 1.70.0 and 1.97.1, all compiler-supported targets and ordinary profiles, relative only to the cited governing Rust axioms.** Every execution satisfying `project`’s stated precondition is UB-free by the derivation below, and every well-typed safe call to `increment_tail` discharges that precondition. The broken descriptive postcondition is not consumed by this implementation, so it does not create a UB path here. +- **Literal open-ended `Rust 1.70+` aggregate soundness — UNPROVED.** Exact governing text was verified at the lower bound and current audit cutoff (1.97.1). Applying either version’s behavioral text to every intervening or future release would require release-by-release verification or a backwards-compatibility premise; the request permits no additional TCB. This qualification cannot rescue acceptance because the contract counterexample already requires rejection. + +## Boundary and obligation coverage + +The reviewed surfaces are the public unsafe trait contract and `Tail`’s `unsafe impl`, the public and freely constructible `Pair(pub [u32; 2])`, public unit `Tail`, and safe `increment_tail`. There is no representation invariant: every `[u32; 2]` is admissible. Other downstream `unsafe impl Piece` implementations are outside this focused review and are not used by `increment_tail`. + +1. **Unsafe `project` body.** Its precondition supplies a live, uniquely borrowed, aligned, initialized `Pair`. Field `.0` is properly aligned under Rust representation, and arrays have the alignment of their elements with element `n` at `n * size_of::()` ([Rust 1.70 layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-default-representation)). Index `1` is in bounds for length `2`; arrays are initialized and indexing is bounds-checked ([array types](https://doc.rust-lang.org/1.70.0/reference/types/array.html), [index expressions](https://doc.rust-lang.org/1.70.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions)). `addr_of_mut!` forms a mutable raw address without an intermediate reference; its place remains subject to the usual rules, which the live in-bounds owner satisfies ([Rust 1.70 `addr_of_mut!`](https://doc.rust-lang.org/1.70.0/std/ptr/macro.addr_of_mut.html)). The result therefore identifies the initialized, aligned `u32` at `.0[1]`. This proves UB freedom, but proves the wrong postcondition. +2. **Safe wrapper.** A function argument is a coercion site and `&mut T` coerces to `*mut T` ([coercions](https://doc.rust-lang.org/1.70.0/reference/type-coercions.html#coercion-types)). The caller-provided `&mut Pair` makes the owner live and exclusively borrowed throughout the wrapper call. The returned pointer still addresses `.0[1]`; forming `&mut *ptr` is therefore aligned, initialized, live, and non-aliasing, avoiding the listed raw-dereference/reference UB cases ([UB rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). No intervening callback, unwind point, alias, or owner mutation exists. `wrapping_add(1)` performs modular addition ([`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add)), and storing a `u32` preserves validity. On normal return, `.0[1]` is its old value plus one modulo `2^32`; `.0[0]` is unchanged. No broader behavior is documented for the safe function. + +## Configuration, evidence, and residual scope + +There is no `cfg`, feature, dependency, generated code, FFI, allocation, concurrency, or target-specific arithmetic. Compiler-selected default struct layout is accessed through typed projections, not assumed offsets; fixed in-bounds indexing and wrapping arithmetic make optimization, overflow-check, and debug-assertion settings irrelevant. No build or test evidence was used. + +**TCB:** no additional assumptions or deployment restrictions. Consumed axioms are only the exact versioned Rust Reference and standard-library contracts linked above. Re-review is required for a new source/contract snapshot or to extend the proved soundness versions beyond the two explicitly verified endpoints. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r126.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r126.md new file mode 100644 index 0000000000..2c70d0ced1 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r126.md @@ -0,0 +1,101 @@ +# Acceptance review — `Piece for Tail` + +## Decision and verdicts + +**REJECT.** The exact `lib.rs` snapshot (SHA-256 +`d76a5c0d7336aac4e551264a105c621dbd6cf27db097fb648aa3ca1e891e3429`) +has a contract-broken unsafe impl, and policy forbids changing it. + +Supported set `S` is this source on every stable Rust release from 1.70.0 +through 1.97.1 (audit cutoff 2026-07-31), every target on which it is accepted, +and ordinary profiles. The proof is target/layout/profile-parametric. There are +no features, `cfg`s, dependencies, generated code, FFI, allocation, concurrency, +or panic-dependent unsafe paths. + +- **Soundness: PROVED in `S`** for every `Tail::project` call satisfying its + stated precondition, and for every well-typed safe call to `increment_tail`. +- **Documented postconditions: CONTRACT-BROKEN in `S`** for both `Tail::FIELD` + and `Tail::project`. +- Therefore the combined requested acceptance claim is **CONTRACT-BROKEN**. + No valid use reaching UB was found; the contract defect is not itself an + `UNSOUND` witness. + +The open future tail of “1.70+” is not included: without the forbidden extra +compatibility premise, releases after 1.97.1 are **UNPROVED** until reviewed. +Any new stable release is a review trigger. + +## Boundary and obligation coverage + +The reviewed surfaces are the public unsafe trait implementation and its +associated types, constant, and unsafe method (`lib.rs:21–29`), plus the safe +function backed by that method (`lib.rs:31–34`). `Pair`'s public field permits +arbitrary `[u32; 2]` values, all of which are covered. There are no other files +or generated artifacts; the sole standard-library macro contract is covered +below. + +### Reconstructed soundness proof + +For `project`, read “identifies a live, uniquely borrowed `Pair` for the call” +literally as a pointer to the storage of an initialized, aligned, live `Pair`, +with exclusive mutable access during the call. Rust permits `&mut T` to +`*mut T` coercion ([1.70](https://doc.rust-lang.org/1.70.0/reference/type-coercions.html), +[1.97.1](https://doc.rust-lang.org/1.97.1/reference/type-coercions.html)). +`Pair` has one `[u32; 2]` field; built-in array indexing is zero-based and +bounds-checked ([1.70](https://doc.rust-lang.org/1.70.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions), +[1.97.1](https://doc.rust-lang.org/1.97.1/reference/expressions/array-expr.html#array-and-slice-indexing-expressions)), +so `.0[1]` is an in-bounds, initialized, aligned `u32` place. `addr_of_mut!` +creates its raw pointer without an intermediate reference. In 1.70–1.74 its +expression remained subject to ordinary dereference rules +([1.70](https://doc.rust-lang.org/1.70.0/std/ptr/macro.addr_of_mut.html)); the +valid `owner` satisfies them. From 1.75 onward, its documented rule requires +field/index projections to remain in-bounds +([1.75](https://doc.rust-lang.org/1.75.0/std/ptr/macro.addr_of_mut.html), +[1.97.1](https://doc.rust-lang.org/1.97.1/std/ptr/macro.addr_of_mut.html)); the +two checked projections do. Thus `project` itself has no invalid dereference, +projection, load, reference, or aliasing event. + +In `increment_tail`, `pair: &mut Pair` establishes that same live/exclusive +fact. The returned pointer is actually derived from `pair` and designates +`pair.0[1]`. No callback or competing access intervenes, so forming `&mut u32` +meets the Reference's alignment, liveness, validity, and exclusivity conditions +([1.70](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html), +[1.97.1](https://doc.rust-lang.org/1.97.1/reference/behavior-considered-undefined.html#invalid-values)). +The sole mutation stores `old.wrapping_add(1)`; modular wrapping is guaranteed +by `u32::wrapping_add` ([1.70](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add), +[1.97.1](https://doc.rust-lang.org/1.97.1/std/primitive.u32.html#method.wrapping_add)). +Every result is a valid `u32`, overflow never panics, `.0[0]` is untouched, and +the `Pair` remains valid. This proof uses the actual implementation, not the +false “direct field” promise. + +### Decisive contract failure + +The trait says `FIELD` names a *direct declared* `Owner` field of type `Item`, +and `project` returns a pointer to that same direct field. An `unsafe impl` +asserts that its trait obligations are discharged +([1.70](https://doc.rust-lang.org/1.70.0/reference/unsafe-keyword.html#unsafe-trait-implementations-unsafe-impl), +[1.97.1](https://doc.rust-lang.org/1.97.1/reference/unsafe-keyword.html#unsafe-trait-implementations-unsafe-impl)). + +But `Pair(pub [u32; 2])` is a tuple struct: its fields are anonymous +([1.70](https://doc.rust-lang.org/1.70.0/reference/types/struct.html), +[1.97.1](https://doc.rust-lang.org/1.97.1/reference/types/struct.html)), and its +sole direct field is `.0` of type `[u32; 2]`. There is no direct field named +`"tail"` and no direct field of type `u32`. Accordingly: + +1. `FIELD = "tail"` falsifies the constant's guarantee. +2. `project` returns the pointer computed by + `addr_of_mut!((*owner).0[1])`: a pointer to an array element nested inside + `.0`, not to a direct declared `Pair` field. + +For any live uniquely borrowed `Pair`, the proved-UB-free call itself is a +witness falsifying the method postcondition; safe evaluation of `Tail::FIELD` +independently witnesses the constant failure. Both unsafe blocks also lack +adjacent safety proofs; the material proof above is reconstructed review +evidence, not present in the snapshot. + +## Trust and residual scope + +TCB is AXIOM-only: the exact versioned Rust Reference and standard-library +contracts linked above. No additional dependency, tool, compiler-backend, +platform, deployment, or compatibility assumption was admitted. This was a +source review only; nothing was built, tested, expanded, or executed. Binary +compiler correctness and APIs outside the named surfaces are out of scope. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r127.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r127.md new file mode 100644 index 0000000000..ac9727eef0 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r127.md @@ -0,0 +1,48 @@ +# Focused `Buffer` source review + +## Claim and verdict + +**UNSOUND.** For the exact `lib.rs` snapshot (SHA-256 `368fa01b2f3fca39e89131414f8ec2641a4ac162b846ccf77b8f8bd293930d9a`), Rust 1.80.0, every target on which this source compiles, and all ordinary profiles, not every well-typed safe use is free of Rust undefined behavior. This is a source-level result relative to `TCB-K3R6-1` below; it makes no backend or binary claim. + +A complete safe counterexample is: + +```rust +let mut buffer = Buffer::from_static(); +buffer.overwrite(7); +``` + +The value `7` is not special: Rust 1.80.0 defines a mutation to include a write even when it does not change the stored bytes. + +No separately documented behavioral postcondition is false, so `CONTRACT-BROKEN` does not additionally apply. + +## Boundary, invariant, and obligation coverage + +`Buffer` is public but both fields are private. The complete current surface is: unsafe producer `from_writable`; safe producer `from_static`; safe consumer `overwrite`; and private helper `with_live`. There are no trait implementations, public fields, macros, generated code, conditional items, dependencies, or other target files. Moving or dropping `Buffer` performs no raw-pointer access. + +The intended representation partition is: + +- `shared == None`: `ptr` came from `from_writable`, whose caller must keep it non-null, aligned, and valid for a one-`u8` write and prevent conflicting access throughout every possible use of the returned `Buffer`. +- `shared == Some(r)`: `from_static` made `r = &BYTE` and made `ptr` by casting that same reference to `*const u8` and then `*mut u8`. + +Obligation dispositions: + +| Site | Required result | Disposition | +|---|---|---| +| `from_writable` | Valid calls establish the `None` representation without UB | **PROVED**: it only stores the supplied pointer and `None`; its ongoing contract is sufficient for the current consumer. | +| `overwrite`, `None` branch | Satisfy `ptr::write` validity and alignment requirements | **PROVED relative to the unsafe constructor contract**, but the adjacent comment is incomplete. Privacy plus the producer inventory establishes that `None` came from `from_writable`; that contract supplies validity, alignment, and non-conflict at the call. | +| `from_static` then `overwrite`, `Some` branch | Safe construction must make the write valid | **UNSOUND**, as derived below. | +| Moves/drop | Preserve soundness | **PROVED**: no dereference or mutation occurs. | + +## UB derivation and local-proof finding + +1. `from_static` creates the shared reference `shared = &BYTE`. The cast chain preserves the pointed-to location: Rust 1.80.0 classifies the first operation as a [reference-to-pointer cast](https://doc.rust-lang.org/1.80.0/reference/expressions/operator-expr.html#type-cast-expressions), and, because `u8` is sized on both sides, the second pointer cast returns the pointer [unchanged](https://doc.rust-lang.org/1.80.0/reference/expressions/operator-expr.html#pointer-to-pointer-cast). +2. `overwrite` copies that reference from `Some` and passes it to `with_live`. Rust 1.80.0 says a reference passed to a function “is considered live” and is live at least for that call. `T` is `u8`, so the `UnsafeCell` exception does not apply. `operation()` is a [call expression](https://doc.rust-lang.org/1.80.0/reference/expressions/call-expr.html#call-expressions) using the supplied `FnOnce`, so the raw write occurs during the `with_live` call. +3. The Rust 1.80.0 UB list says bytes “pointed to by a shared reference ... are immutable” and defines mutation as “any write of more than 0 bytes” overlapping them ([Reference](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)). `ptr::write` overwrites the pointed-to location, and `size_of::()` is 1 on every target ([`size_of`](https://doc.rust-lang.org/1.80.0/std/mem/fn.size_of.html)). Therefore this write overlaps exactly the byte protected by the live shared reference and is UB. Independently, [`ptr::write`](https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html#safety) requires `dst` to be valid for writes and aligned; its write-validity obligation is false here. + +The `Some`-branch comment is materially false: that `Buffer` did not come from `from_writable`. The `None`-branch comment also omits the alignment obligation and the privacy/producer argument. A sufficient reconstructed comment for only the `None` branch would identify the private-field invariant, the exhaustive producers, and all of `from_writable`'s ongoing validity, alignment, and non-conflict clauses. No comment can repair the `Some` branch; the safe producer or the write behavior must change before the safe API theorem can hold. + +## Configuration closure, TCB, and residual scope + +The source has no configuration axis beyond the requested compiler target/profile. The counterexample uses target-independent shared-reference rules and the stable one-byte size of `u8`; debug assertions, optimization, panic strategy, and target layout do not alter the path. Thus the `UNSOUND` result covers the entire requested set parametrically. Edition/build metadata was not supplied, but the derivation does not depend on an edition-specific rule. + +`TCB-K3R6-1` contains only the quoted exact-version Rust 1.80.0 Reference and standard-library propositions linked above. No dependency, tool result, test, execution, external specification, or prior audit was trusted. Residual scope excludes compiler/backend correctness and anything outside the two supplied target files. Re-review is required if the source, Rust version, supported configuration set, or any cited contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r128.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r128.md new file mode 100644 index 0000000000..701f9dcdb1 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r128.md @@ -0,0 +1,51 @@ +# Focused review: `Ticket` construction + +## Claim and snapshot + +Reviewed `lib.rs` (SHA-256 `23f55cd3e96c8372d71a09336d84f72de191fd5f48de693d0be11762537dfa9f`) against `REQUEST.md` (SHA-256 `f4cb0fe5d667cdde8536d103ea2bf8c46a4219916acb811b0e33c4d2cb5bc376`). Scope is the public opaque `Ticket` type and safe `pub fn ticket(id: usize) -> Ticket`, including its documented outcomes. The required configuration set is Rust 1.70+, every target, and ordinary profiles with debug assertions either enabled or disabled. This was source-only review: no build, test, execution, or expansion evidence was used. + +Current implementation verdict: **UNSOUND** for the required configuration set. +Proposed redesign verdict: **PROVED** for Rust 1.70 on every target/profile, relative only to the Rust 1.70 standard-library contracts below. For the literally open-ended `1.70+` range, see “Version applicability.” + +## Finding: a debug-only check cannot discharge the unsafe precondition + +At `lib.rs:9-10`, the safe API uses `debug_assert!(id != 0)` as the sole justification for `NonZeroUsize::new_unchecked(id)`. Rust 1.70 documents that `debug_assert!` is “only enabled in non optimized builds by default” and that optimized builds do not execute it unless debug assertions are requested ([Rust 1.70 `debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html#uses)). Thus there is an ordinary supported class with the assertion disabled. + +The exact valid counterexample is a safe call `ticket(0)` in that class. Control reaches `new_unchecked(0)`. Its Rust 1.70 safety clause says, “The value must not be zero,” and its contract states that zero causes undefined behavior ([Rust 1.70 `NonZeroUsize::new_unchecked`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new_unchecked)). This violates a standard-library unsafe precondition, so the safe API is unsound; it also cannot establish the documented zero-input panic outcome. A concrete later observation is neither needed nor meaningful after UB. + +Configuration partition is exhaustive: + +- `id != 0`: the precondition holds regardless of profile or target. +- `id == 0`, debug assertions enabled: `debug_assert!` panics before the unsafe call. +- `id == 0`, debug assertions disabled: the valid safe counterexample above reaches UB. + +The argument is parametric over target and `usize` width because it uses only equality with zero. There is no `cfg`, generated code, dependency, allocator, FFI, concurrency, or panic-unwind invariant in the snapshot. + +## Redesign + +Preserve the type and documentation, and replace only the function body conceptually (no source edit was requested): + +```rust +pub fn ticket(id: usize) -> Ticket { + Ticket(NonZeroUsize::new(id).expect("ticket id should be non-zero")) +} +``` + +This preserves the exact safe signature and removes the unsafe block and its invariant proof obligation. It also makes behavior independent of `debug_assertions`; the old `debug_assert!` should be removed, not retained as a safety premise. + +Proof for Rust 1.70: + +- `NonZeroUsize::new` is safe and “Creates a non-zero if the given value is not zero” ([Rust 1.70 checked constructor](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new)). Its `Option` result therefore represents successful construction for nonzero input and no value for zero input. +- For nonzero `id`, `expect` returns the contained `Some` value; wrapping that value establishes the representation invariant `TICKET-NZ`: `Ticket.0` is a valid `NonZeroUsize` containing `id`. +- For zero `id`, construction yields `None`, and `expect` “Panics if the value is a `None`” ([Rust 1.70 `Option::expect`](https://doc.rust-lang.org/1.70.0/core/option/enum.Option.html#method.expect)). No `Ticket` is produced and no invariant is suspended. +- The field is private, so the reviewed safe constructor is the only source-visible external construction surface. There are no explicit methods, trait impls, macros, or hidden items; ordinary auto/blanket traits add no construction or mutation transition. + +The panic message is not part of the existing documentation, so changing it does not alter the requested documented behavior. + +## TCB, version applicability, and review triggers + +TCB consists only of the three exact Rust 1.70 standard-library pages cited above; there are no dependency or deployment assumptions. The proof is profile- and target-parametric at that version. + +The declaration `1.70+` has no upper bound. Rust 1.70 documentation alone cannot prove that every later or future release preserves these exact behavioral sentences. Accordingly, the combined postcondition claim over the literal open-ended range is **UNPROVED** unless the reviewer accepts this explicit compatibility premise: every supported later stable release preserves the cited `NonZeroUsize::new` and `Option::expect` propositions over all supported targets/profiles. Relative to that premise, the redesign is **PROVED** for `1.70+`. Otherwise set an audit cutoff and verify the same versioned contracts through that cutoff. + +Re-review if the function body, `Ticket` representation/visibility, documented outcomes, MSRV/version ceiling, or either consumed standard-library contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r129.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r129.md new file mode 100644 index 0000000000..787d844b7d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r129.md @@ -0,0 +1,58 @@ +# Focused unsafe-Rust source review: `p1j4` + +## Claim, snapshot, and verdict + +**Soundness: `UNSOUND`.** The claim reviewed is: for the exact `lib.rs` snapshot (SHA-256 `ac762e6e2bd87884bbbcfbe7bbf706e5b9dae9b078e82d01252111a9a6ad84e8`), every well-typed safe use is free of Rust undefined behavior on Rust 1.70+, every target, and every ordinary profile. A valid safe client can cause an aliasing violation on Rust 1.70, so this universal claim is refuted. This is an implementation defect, not merely an incomplete proof. + +The review covers all three target files and does not inherit the conditional conclusion in `DESIGN-NOTE.md`. There are no dependencies, `cfg`s, macros, generated artifacts, FFI, build inputs, unsafe public APIs, or separately documented postconditions in the snapshot. No code was built or executed. + +## Boundary and obligation coverage + +The complete explicit safe surface is the public `View<'a, T>` type (private `ptr` and `borrow` fields) and safe methods `new`, `get`, and `get_mut`. Ordinary move/drop and compiler-provided trait behavior introduce no additional way to forge or duplicate `ptr`; in Rust 1.70, the `*mut T` field also makes the type neither `Send` nor `Sync`. + +The intended representation invariant, **I-VIEW**, is: `ptr` is the raw pointer obtained from the initialized, aligned `T` uniquely borrowed by `new` for `'a`; it is used only while that allocation remains live, and every reference created from it is compatible with all other live references/accesses. `new` establishes the pointer/lifetime portion: Rust 1.70 documents coercing `&mut T` to `*mut T` as a normal raw-pointer construction and says the pointer must not be used after its lifetime ([raw-pointer documentation](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#common-ways-to-create-raw-pointers)). `PhantomData` tells the compiler to act as though the containing type held the indicated reference, thereby carrying `'a` ([`PhantomData`, unused lifetimes](https://doc.rust-lang.org/1.70.0/core/marker/struct.PhantomData.html#unused-lifetime-parameters)). Private fields and the exact source leave `new` as the only producer. + +Obligation ledger: + +| Site | Required proposition | Status | +|---|---|---| +| `new`, lines 11–13 | Establish pointer origin/liveness carrier without accessing through it | **PROVED** for this snapshot | +| `get`, lines 15–17 | `&*ptr` creates a valid shared reference and preserves I-VIEW for its entire returned lifetime | **UNSOUND** in composition with `get_mut` | +| `get_mut`, lines 19–21 | `&mut *ptr` creates an exclusive reference and preserves I-VIEW for its entire returned lifetime | **UNSOUND** after `get`, and also on repeated calls | +| Both unsafe blocks | Adjacent proof states and derives all dereference/reference-creation requirements | **UNPROVED documentation**: there is no `SAFETY` comment; moreover no valid proof exists for the current signatures | + +## F-1 — returned lifetimes escape the receiver borrow (`UNSOUND`) + +`get` returns `&'a T`, rather than a reference tied to the lifetime of `&self`; similarly, `get_mut` returns `&'a mut T`, rather than one tied to `&mut self`. Thus the borrow of `View` used for a method call may end while the returned reference remains live. This entirely safe client can retain a shared reference, mutate the same `i32` through a later exclusive reference, and then use the shared reference: + +```rust +let mut value = 0i32; +let mut view = View::new(&mut value); +let shared = view.get(); +*view.get_mut() = 1; +let observed = *shared; +``` + +The method signatures impose no receiver-lifetime overlap that would make the second call ill-typed. Both unsafe blocks dereference the same stored pointer. Rust 1.70 specifies that a borrow places the location in a borrowed state “for the duration of the reference” and that, for a shared borrow, the place “may not be mutated” ([Reference: borrow operators](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#borrow-operators)). It lists breaking pointer-aliasing rules as undefined behavior and defines unsafe code usable by a safe client to exhibit UB as unsound ([Reference: behavior considered undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). Here `shared` remains live through its final read, while `get_mut` supplies the intervening mutation. Repeated `get_mut` calls can analogously produce two simultaneously usable `&'a mut T` values. + +This counterexample is source-semantic and independent of layout, optimization, panic strategy, and target properties. It applies to Rust 1.70 on every ordinary target/profile that compiles the source; because Rust 1.70 is included in “Rust 1.70+,” no forward-compatibility premise is needed to refute the requested range-wide theorem. + +## Proposed correction (not audited as an implementation) + +Tie each result to its receiver reborrow: + +```rust +pub fn get(&self) -> &T { + unsafe { &*self.ptr } +} + +pub fn get_mut(&mut self) -> &mut T { + unsafe { &mut *self.ptr } +} +``` + +Lifetime elision then makes a live shared result keep `View` shared-borrowed and a live mutable result keep it exclusively borrowed, preventing the conflicting safe calls. Add adjacent `SAFETY` comments that expand I-VIEW: pointer origin, initialization/alignment/liveness from `new`; field privacy; the outstanding `'a` borrow of the original `T`; and the receiver-tied result lifetime that enforces alias compatibility. This is an unimplemented candidate with **no verdict**; its exact implementation requires a fresh audit. + +## TCB, residual scope, and triggers + +TCB revision **R129-local** contains only the three linked Rust 1.70 Reference/standard-library propositions. No dependency, external-specification, tool-result, compiler-backend, or deployment assumption is consumed. The result is a focused source-level review, not a binary/backend or enclosing-crate audit. Re-audit after changing either method signature, representation/privacy, constructors, trait implementations, supported Rust range, conditional compilation, or generated code. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r130.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r130.md new file mode 100644 index 0000000000..c4d2db901f --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r130.md @@ -0,0 +1,94 @@ +# Version-partitioned source review + +## Claim and verdict + +Claim: for the exact supplied `lib.rs`, every well-typed safe call to +`advance_marker` is free of Rust undefined behavior under the documented abstract +semantics of the named Rust release, on every target and in every ordinary +profile. The function documents no behavioral postcondition beyond its return +type. + +| Region | Soundness verdict | Reason | +|---|---|---| +| Rust 1.79.0, every target/profile | **UNSOUND** | Every call reaches `add(1)` with a null pointer, violating 1.79.0's unconditional same-allocation requirement. | +| Rust 1.80.0, every target/profile | **PROVED**, relative to TCB-180 below | The computed byte offset is zero, for which 1.80.0 expressly makes `add` well-defined. | +| Combined required set | **UNSOUND** | The combined universal claim contains the unsound 1.79.0 region. | + +## Snapshot, boundary, and coverage + +The snapshot is exactly `REQUEST.md` and `lib.rs` in the supplied target. The +only API surface is the public safe, argument-free function +`advance_marker() -> *const [u8; 0]` (`lib.rs:3`). Its only obligation site is +the internal unsafe call to `pointer::add` (`lib.rs:4`). There are no fields, +traits, callbacks, dependencies, conditional items, macros, generated artifacts, +or state invariants. + +No caller-side safety precondition is available: invoking this safe function is +a valid safe use. + +## Obligation ledger and derivation + +**O-ADD — discharge every `pointer::add` safety conjunct.** In each release, +the versioned `size_of` documentation says an array `[T; n]` has size +`n * size_of::()` ([1.79.0](https://doc.rust-lang.org/1.79.0/core/mem/fn.size_of.html), +[1.80.0](https://doc.rust-lang.org/1.80.0/core/mem/fn.size_of.html)). Therefore +`size_of::<[u8; 0]>() = 0`, and `count * size_of::() = 1 * 0 = 0` bytes. +The respective `null` contracts create a null raw pointer whose address is zero +([1.79.0](https://doc.rust-lang.org/1.79.0/core/ptr/fn.null.html), +[1.80.0](https://doc.rust-lang.org/1.80.0/core/ptr/fn.null.html)). + +* **Rust 1.79.0 — failed.** Its [`add` safety + contract](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add) + requires, without a zero-offset exception, both pointers to be in-bounds or + one byte past the same allocated object. The starting pointer is null, not a + pointer into or one-past any allocated object; the same release's pointer + validity rules state that a null pointer is never valid, even for a zero-size + access ([pointer safety](https://doc.rust-lang.org/1.79.0/core/ptr/index.html#safety)). + Thus the first conjunct is false. The contract says violation is undefined + behavior. Since every ordinary safe call unconditionally executes this site, + it is an in-scope UB counterexample. The other conjuncts do not rescue the + call: zero fits in `isize`, and address `0 + 0` neither overflows nor wraps. + +* **Rust 1.80.0 — discharged.** Its [`add` safety + contract](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add) + conditions the allocation requirement on a nonzero computed byte offset and + expressly says of a zero offset: “the function is always well-defined.” The + remaining arithmetic requirements hold because zero fits in `isize` and + `0 + 0` fits in `usize` without wrapping. The result is merely returned as a + raw pointer and is never dereferenced. O-ADD is therefore proved. + +## Configuration closure + +The source contains no configuration selection. The proof is parametric over +targets: a zero-length array has size zero regardless of `u8` size, pointer +width, alignment, ABI, or layout, and zero is representable in every applicable +`isize`/`usize`. It is also parametric over ordinary profiles: there are no +assertions, panics, integer operations using profile-dependent overflow checks, +or profile-selected paths. The two release regions exhaust exactly the requested +support set. + +## Findings and proof documentation + +**F-179 (implementation defect):** `advance_marker` is an unsound safe API on +Rust 1.79.0. While 1.79.0 remains supported, the minimal repair is to remove the +unnecessary unsafe arithmetic—for example, return +`core::ptr::null::<[u8; 0]>()` directly (or use the safe `wrapping_add` if the +operation itself must remain). Any edit is a new snapshot requiring review. + +The unsafe block also has no adjacent `SAFETY` proof. A comment cannot repair +the 1.79.0 defect. If the applicability were enforceably narrowed to 1.80.0, +sufficient local proof text would be: + +```rust +// SAFETY: `[u8; 0]` has size 0, so the computed byte offset is 1 * 0 = 0. +// Rust 1.80.0's `pointer::add` contract makes a zero byte offset well-defined. +``` + +## TCB, evidence, and residual scope + +**TCB-179/TCB-180:** only the exact versioned `core` contracts linked above. +There are no admitted compatibility premises, dependencies, tools, tests, or +external assumptions. No code was built or executed. This is a source-level +result, not a claim about compiler/backend correctness or a particular binary. +Re-review is required if the source, either supported Rust release, the relevant +standard-library contracts, or the supported configuration set changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r131.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r131.md new file mode 100644 index 0000000000..9d29f4d971 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r131.md @@ -0,0 +1,55 @@ +# Focused `Buffer` source review + +## Claim and verdict + +The universal soundness claim over all well-typed safe uses of the exact supplied `lib.rs` is **UNSOUND**, under Rust 1.80.0, on every target on which that source is accepted, in every ordinary profile. The safe execution + +```rust +let mut buffer = Buffer::from_static(); +buffer.overwrite(0); +``` + +mutates a byte pointed to by a live shared reference. This witness applies uniformly: the source has no `cfg`, target-dependent code, generated code, dependencies, profile-dependent assertions, FFI, or concurrency. No documented behavioral postcondition is in scope beyond soundness, so there is no separate `CONTRACT-BROKEN` verdict. + +This is a focused verdict for `Buffer`, its current producers/consumers, and `overwrite`'s two unsafe sites—not a whole-crate verdict. No source, build, test, expansion, or tool result was used. + +## Boundary and complete dataflow inventory + +The representation fields are private and the supplied source contains no other module capable of constructing one. Current producers are exhaustive: + +- `unsafe Buffer::from_writable` stores the caller's pointer and `shared: None`. Its contract requires the pointer to remain non-null, aligned, and valid for one-`u8` writes while the result may be used, with no access conflicting with those writes. +- Safe `Buffer::from_static` creates `shared = &BYTE`, casts that same pointer to `*mut u8`, and stores `shared: Some(shared)`. + +The only pointer consumer is safe `Buffer::overwrite`; it performs `self.ptr.write(value)` in both branches. In the `Some` branch, `with_live` additionally consumes the stored shared reference and invokes the writing closure during that call. Neither method changes either field. Ordinary moves, destruction, and compiler-supplied auto-trait behavior do not dereference the pointer; there are no explicit `Drop`, `Clone`, `Copy`, or other trait implementations, nor macro-generated, hidden, or field-based construction surfaces in the supplied source. + +The representation therefore has two stable history cases: + +- **W:** `shared == None`, originating at `from_writable`, with its caller-maintained write-validity obligation. +- **S:** `shared == Some(r)`, originating at `from_static`, where both `r` and `ptr` point to `BYTE`. + +## Authoritative premises and derivation + +`AXIOM-WRITE` (Rust 1.80.0): raw-pointer `write` overwrites the pointed-to location; its safety section says behavior is undefined unless the destination is valid for writes and properly aligned. ([`core::ptr::write`](https://doc.rust-lang.org/1.80.0/core/ptr/fn.write.html#safety)) + +`AXIOM-IMMUTABLE` (Rust 1.80.0): the Reference lists “Mutating immutable bytes” as UB, states that bytes pointed to by a shared reference are immutable, and defines a mutation as any overlapping write of more than zero bytes, even if contents do not change. It also states that a shared reference passed to a function is live at least for that entire call, except when its pointee contains `UnsafeCell`. ([Behavior considered undefined](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)) + +For the safe witness, `from_static` establishes case S. `overwrite` copies `r: &u8` from `Some` and passes it to `with_live`. Thus `r` is live throughout that call; `u8` contains no `UnsafeCell`. `with_live` calls the closure before returning, and the closure writes one `u8` through the pointer derived from `r`, hence overlaps the byte pointed to by `r`. `AXIOM-IMMUTABLE` classifies that event as UB. The witness uses only safe public calls, so it refutes soundness. `let _ = shared` is not needed for the proof: passing `shared` already supplies the documented lower bound on liveness. + +## Obligation ledger and local proof review + +| Site | Required proposition | Result | +|---|---|---| +| `from_writable` producer | For every valid unsafe call, preserve the documented ongoing pointer obligations for later safe use. | **PROVED**, relative to that caller contract: it stores the pointer unchanged in case W and exposes no transition that weakens the obligation. | +| `overwrite`, `None` branch | At `write`, `ptr` is aligned and valid for a `u8` write, with no conflicting access. | **PROVED** for case W, relative to the documented ongoing obligations of a valid `from_writable` call and the exhaustive private-field producer/transition analysis above. | +| `from_static` producer plus `overwrite`, `Some` branch | Safe construction must establish every precondition needed by later safe `overwrite`. | **UNSOUND**: it instead establishes the live shared-reference conflict proved above. | +| Both adjacent `SAFETY` comments | Locally derive the exact `write` preconditions for the selected history. | **Invalid/inadequate.** In the `Some` branch the cited `from_writable` call never occurred. In the `None` branch the conclusion is reconstructible, but the comment omits the essential branch-to-producer closure and the alignment/conflict clauses. | + +Suggested proof text for the unchanged `None` branch: + +> SAFETY: `shared == None` can only originate at `from_writable`, and no transition changes either field. Its still-active caller obligation makes this exact `ptr` aligned and valid for a one-`u8` write and forbids access conflicting with this write. + +No replacement comment can prove the current `Some` branch: its required proposition is false. The minimum acceptable resolution is an implementation change that prevents a safe raw write to storage covered by the shared reference, followed by a fresh audit of the new artifact. + +## TCB and residual scope + +`TCB-k3r6-1` contains only `AXIOM-WRITE` and `AXIOM-IMMUTABLE`, from the exact Rust 1.80.0 standard-library and Reference pages above. There are no admitted dependency, tool, platform, compiler-backend, or deployment propositions. This is a source-semantics result; compiler/backend binary correctness and files outside the supplied target are excluded. Re-review is required if the representation, either constructor, `overwrite`, `with_live`, visibility, Rust version, or supported configuration set changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r132.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r132.md new file mode 100644 index 0000000000..0094462584 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r132.md @@ -0,0 +1,45 @@ +# Focused review: `total` + +## Claim, scope, and verdicts + +Snapshot: `lib.rs` SHA-256 `12ee0d64af16f4b4bea78bb146d333f86c579b49ca0dd38e2552f3896147be5c`; request SHA-256 `8e32395f979236c2f9fc5b811aa5d7d6a83b054dad42febb341b776f1c5dfa2a`. Scope is the sole public safe surface, `total(&[u32]) -> u32`, and its three unsafe operations. There are no fields, traits, macros, dependencies, generated artifacts, or configuration branches in the supplied target. + +`Supported(c)` is the request's stable Rust 1.70-or-later toolchains, every Rust target, and every ordinary profile, for this source. The claim is: every valid call is UB-free and returns the left fold of all input elements under addition modulo 2^32, including `0` for an empty slice. + +- **Soundness: UNSOUND** over `Supported(c)`. A valid empty input reaches UB on Rust 1.70; one included toolchain refutes the universal claim, independently of target and profile. +- **Wrapping-sum postcondition: UNPROVED** over the full input/configuration domain because the empty-input witness contains UB. No separate UB-free wrong-result witness was found. +- **Replacement performance: UNPROVED**. No result for the designated benchmark, nor its identity or measurement protocol, was supplied. + +## Current artifact and obligation coverage + +At [lib.rs:6](/tmp/unsafe-rust-v2-eval.9epWDK/targets/a6d2/lib.rs:6), Rust 1.70's [`pointer::add`](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) says both pointers must be “in bounds or one byte past the end” of the same allocated object; violation is UB. Rust 1.70's [`slice::from_raw_parts`](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html#safety) expressly permits `NonNull::dangling()` as the data pointer for a zero-length slice, and [`NonNull::dangling`](https://doc.rust-lang.org/1.70.0/std/ptr/struct.NonNull.html#method.dangling) creates a pointer that is “dangling, but well-aligned.” Therefore this valid caller construction is a witness: + +```rust +let p = std::ptr::NonNull::::dangling().as_ptr(); +let empty = unsafe { std::slice::from_raw_parts(p, 0) }; // contract satisfied +total(empty); // UB in ptr.add(0) +``` + +The caller's unsafe operation satisfies its documented contract; `total` is safe and cannot impose a hidden nonempty/allocation precondition. `ptr.add(0)` nevertheless requires an allocated object under the exact Rust 1.70 contract. This is immediate UB before the loop. + +For completeness, the material proof missing from the source succeeds only for `n = values.len() > 0`. Rust 1.70 [`as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr) returns the buffer pointer, and the [Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#dangling-pointers) bounds a slice's dynamic size by `isize::MAX`. With loop index `k`, the invariant is: `0 <= k <= n`, `ptr = base.add(k)`, `end = base.add(n)`, the slice remains live, and `acc` is the modular sum of elements `0..k`. When `k < n`, [lib.rs:9](/tmp/unsafe-rust-v2-eval.9epWDK/targets/a6d2/lib.rs:9) reads an aligned, initialized in-slice `u32`; [lib.rs:10](/tmp/unsafe-rust-v2-eval.9epWDK/targets/a6d2/lib.rs:10) advances within the same allocation or to one-past. At `k = n` the loop exits. Rust 1.70 [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add) specifies “Wrapping (modular) addition,” so overflow checks and optimization profile do not change that arithmetic. None of the three unsafe sites has an adjacent `SAFETY` proof; documentation is deficient independently of the defect. + +## Safe iterator redesign + +Preferred candidate, subject to performance evidence: + +```rust +pub fn total(values: &[u32]) -> u32 { + values.iter().fold(0u32, |acc, value| acc.wrapping_add(*value)) +} +``` + +The Rust 1.70 slice contract says [`iter`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.iter) “yields all items from start to end”; [`Iterator::fold`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.fold) applies the closure to every element and returns the accumulator. Induction on that sequence plus `wrapping_add` proves the required modulo-2^32 sum, with the initial accumulator proving the empty case. The candidate has no unsafe block, representation invariant, caller safety obligation, target branch, or profile-sensitive arithmetic. This is a design proof, not a verdict for an unimplemented snapshot; audit the exact change after implementation. The open-ended `1.70+` policy also requires a release cutoff/compatibility premise or future-release re-audit rather than silently extrapolating Rust 1.70 text forever. + +## Recommendation and evidence gate + +Do not accept the current source. Next, identify the designated benchmark's exact harness/revision, inputs, baseline, compiler/target/profile, hardware, metric and direction, warm-up, repetition/noise treatment, and pre-agreed decision rule. Benchmark the exact safe candidate against the exact baseline and accept it only if that rule establishes regression no greater than 2%. A baseline run of the current code is admissible only if the benchmark cannot supply the empty witness; otherwise UB invalidates the comparison. + +If the safe candidate passes, implement it and perform a post-change source review. If it fails or evidence is inconclusive, benchmark other safe formulations; as a last resort, repair the raw loop (for example, make end construction safe with `wrapping_add`) and add the explicit loop-invariant proofs above, then benchmark and re-audit that exact source. Neither the soundness defect nor the 2% requirement should be waived. + +TCB `R132-1` contains only the cited Rust 1.70 Reference/std propositions. No tests, benchmark, dependency, compiler-backend, or tool result was consumed. Re-review on source changes, benchmark-definition/environment changes, support-range changes, or material changes to a consumed Rust contract. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r133.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r133.md new file mode 100644 index 0000000000..a857fedfcd --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r133.md @@ -0,0 +1,86 @@ +# Version-partitioned source review: `advance_marker` + +## Claim and verdict + +Artifact: the exact supplied `lib.rs`, whose only reachable API is +`pub fn advance_marker() -> *const [u8; 0]`. The claim is source-level Rust +soundness for every well-typed safe call, separately under Rust 1.79.0 and +Rust 1.80.0 with the matching `core`, on every target and in every ordinary +profile accepted by that toolchain. There are no caller safety preconditions. + +- **Rust 1.79.0: UNSOUND.** Every call executes an unsafe operation whose + published precondition is violated. +- **Rust 1.80.0: PROVED**, relative only to the versioned Rust axioms below. +- **Combined supported set `{1.79.0, 1.80.0}`: UNSOUND**, because the valid + 1.79.0 region contains an undefined-behavior execution. The proved 1.80.0 + region does not repair that counterexample. + +No behavioral postcondition is documented by this safe API, so there is no +separate `CONTRACT-BROKEN` claim. Compiler/backend correctness and binary-level +properties are outside this source-level theorem. + +## Boundary, configuration, and TCB + +The complete API/unsafe inventory is one safe free function and its one +`*const [u8; 0]::add(1)` call. There are no fields, traits, callbacks, macros, +generated artifacts, dependencies, mutable state, invariants, conditional +compilation, target branches, panics, allocation calls, or unwinding paths. + +Configuration coverage is parametric rather than sampled. For every target, +an array of length zero has size zero; therefore the byte offset is zero. +Neither that arithmetic nor the source changes with profile, optimization, +overflow-check, or panic settings. The versioned `add` contract is the only +axis that changes the result. + +Accepted TCB entries (all `AXIOM`, no additional assumptions) are the exact +Rust 1.79.0 and 1.80.0 `core` documentation for: + +- [`ptr::null`](https://doc.rust-lang.org/1.79.0/core/ptr/fn.null.html) (the + [1.80.0 text](https://doc.rust-lang.org/1.80.0/core/ptr/fn.null.html) is the + same relevant contract): it “Creates a null raw pointer” whose address is 0. +- [`size_of` 1.79.0](https://doc.rust-lang.org/1.79.0/core/mem/fn.size_of.html) + and [`size_of` 1.80.0](https://doc.rust-lang.org/1.80.0/core/mem/fn.size_of.html): + `[T; n]` has size `n * size_of::()`. +- `*const T::add` in + [1.79.0](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add) + and + [1.80.0](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add). + +## Obligation ledger and derivation + +| ID | Obligation at `add(1)` | 1.79.0 | 1.80.0 | +|---|---|---|---| +| O1 | Computed byte offset fits `isize` | PROVED: `1 * size_of::<[u8; 0]>() = 1 * 0 = 0` | PROVED: same | +| O2 | Address arithmetic does not wrap `usize` | PROVED: adding zero leaves address 0 | PROVED: same | +| O3 | Allocation/in-bounds requirement | **VIOLATED**: `null()` is not in or one-past an allocated object, and 1.79 has no zero-offset exception | PROVED: contract expressly exempts zero offsets | + +For 1.79.0, `add` says violation is undefined behavior and requires: “Both the +starting and resulting pointer must be either in bounds or one byte past the +end of the same allocated object.” The starting pointer is the null pointer +produced immediately by `null()`. A zero-sized offset leaves it null, which is +not a pointer into or one-past any allocated object. Thus O3 fails. Because the +safe function has no arguments or preconditions and unconditionally reaches +this call, an ordinary safe invocation is a valid UB witness. This establishes +`UNSOUND`, independently of target and profile. + +For 1.80.0, the same `size_of` rule proves the computed byte offset is exactly +zero. The revised `add` contract limits the allocation requirement to a +non-zero computed offset and states: “If it is zero, then the function is +always well-defined.” O1 and O2 also hold arithmetically. No dereference, +reference creation, or later unsafe consumer occurs. All obligations are +therefore discharged for every supported target/profile. + +## Findings and resolution + +1. **Soundness defect (Rust 1.79.0):** the safe boundary hides a violated + `add` precondition. Minimal remedies are to return `core::ptr::null()` + directly (making `add` unnecessary), or to exclude 1.79.0 from support and + re-audit the changed support claim. +2. **Proof-documentation defect:** the unsafe block has no adjacent `SAFETY` + proof. For a 1.80+-only artifact, sufficient wording would be: + `SAFETY: [u8; 0] has size 0, so the computed byte offset is 0; this Rust + version documents a zero offset as always well-defined.` This wording is + not valid for 1.79.0 and cannot cure the combined defect. + +Re-audit is required if the source, supported Rust versions, matching `core` +contract, or configuration predicate changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r134.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r134.md new file mode 100644 index 0000000000..ae507b5c6c --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r134.md @@ -0,0 +1,63 @@ +# Focused `Buffer` source review + +## Claim and verdict + +**UNSOUND** for the exact `lib.rs` snapshot below, Rust 1.80.0, every target, and every ordinary profile: a wholly safe, well-typed call sequence reaches Rust undefined behavior. This is a focused verdict on the current `Buffer` producers, consumer, and `overwrite` proof sites, not on any enclosing crate. + +Snapshot: `lib.rs` SHA-256 `368fa01b2f3fca39e89131414f8ec2641a4ac162b846ccf77b8f8bd293930d9`; request SHA-256 `3638d636a8792985abd014032566dc34f79554dc843c4c8a17d20f6f622e7b50`. There are no dependencies, generated artifacts, features, `cfg`s, or requested behavioral postconditions. + +## Boundary, states, and obligation ledger + +The private fields prevent safe aggregate construction. The current producer set is exhaustive: unsafe `from_writable` creates `shared: None`; safe `from_static` creates `shared: Some(&BYTE)`. `overwrite` is the only pointer consumer. Move and implicit drop do not access the pointee; there are no explicit/derived trait or macro surfaces. The raw-pointer field also prevents automatic `Send` and `Sync` in Rust 1.80.0 ([raw-pointer trait implementations](https://doc.rust-lang.org/1.80.0/std/primitive.pointer.html#trait-implementations)). + +Two state contracts follow from the producers: + +- **W:** `shared == None`; the caller of `from_writable` must continuously provide a non-null, aligned pointer valid for a one-`u8` write and no conflicting access while the `Buffer` may be used. +- **S:** `shared == Some(r)`; `r == &BYTE`, and `ptr` is the same pointer cast to `*mut u8`. The Rust 1.80 cast rules list the reference-to-pointer cast and say a sized pointer-to-pointer cast returns the pointer unchanged ([casts](https://doc.rust-lang.org/1.80.0/reference/expressions/operator-expr.html#type-cast-expressions), [pointer casts](https://doc.rust-lang.org/1.80.0/reference/expressions/operator-expr.html#pointer-to-pointer-cast)). + +| Site | Strongest result | +|---|---| +| `from_writable` construction | **PROVED**, for calls satisfying its ongoing documented contract. Storing the pointer performs no pointee access and establishes W. | +| `from_static` construction alone | **PROVED**; creating/storing the raw pointer performs no write, but establishes S, not W. | +| `overwrite`, `None` arm | Implementation **PROVED relative to the `from_writable` contract**; local proof documentation **UNPROVED**. | +| `overwrite`, `Some` arm | **UNSOUND**; its local proof cites an inapplicable producer contract. | +| Safe API aggregate | **UNSOUND** because the `Some` arm is safely reachable. | + +## Finding F1 — safe immutable-byte write + +The complete safe witness is: + +```rust +let mut b = Buffer::from_static(); +b.overwrite(0); +``` + +Derivation: + +1. `from_static` makes `shared` point to `BYTE` and obtains `ptr` from that same reference by raw-pointer casts. +2. The `Some` arm passes that `&u8` to `with_live`, which invokes the writing closure before returning. +3. Rust 1.80.0 states that a reference passed to a function is “live at least as long as that function call.” It also classifies mutating immutable bytes as UB, says bytes “pointed to by a shared reference ... are immutable,” and defines mutation to include “any write of more than 0 bytes” ([Rust 1.80 UB rules](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)). `u8` contains no `UnsafeCell`. +4. The pointer method delegates its safety conditions to `ptr::write`; those require that the destination be “valid for writes” and “properly aligned” ([method](https://doc.rust-lang.org/1.80.0/std/primitive.pointer.html#method.write), [`ptr::write` safety](https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html#safety)). Rust 1.80 guarantees `size_of::() == 1`, so this is a nonzero write ([primitive layout](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#primitive-data-layout)). + +Thus the write occurs while the shared reference is live and overlaps its pointed byte. It is UB for every `value`, including `7`; the trailing `let _ = shared` is unnecessary to the result because passing the reference already supplies the full-call liveness bound. + +Minimal resolution: do not write through the S-state pointer from a safe API. Merely changing the `SAFETY` comment cannot discharge this false obligation. + +## Finding F2 — local proof artifacts + +In the `Some` arm, “`from_writable` requires ...” is false: that arm is selected by `Some`, whose sole current producer is `from_static`. In the `None` arm the cited producer is applicable and its ongoing contract reconstructs a complete implementation proof, but the comment mentions only write-validity and omits the callee’s separate alignment obligation and the state-to-producer step. + +Suitable replacement for the `None`-arm proof (if source changes are later authorized) is: + +```rust +// SAFETY: `shared == None`; every current producer of this state is +// `from_writable`. Its ongoing caller contract requires `self.ptr` to remain +// valid and properly aligned for a one-`u8` write, with no conflicting access, +// whenever this Buffer is used. Those are `ptr::write`'s obligations here. +``` + +## Configuration closure, TCB, and residual scope + +The proof is parametric over target and ordinary profile: there is one unconditional source path, `u8` is one byte on all targets, and no profile-sensitive assertion, panic, generation, or conditional compilation exists. No target implementation or physical read-only-memory premise is consumed. + +TCB `R80-focused-v1` contains only the linked Rust 1.80.0 axioms: cast semantics, raw-pointer auto traits, reference liveness/immutable-byte UB, `ptr::write`’s contract, and `u8` layout. No tests, execution, compiler/backend assumption, dependency trust, or tool-derived theorem was used. No separate documented postcondition is broken. Re-audit on any producer, field visibility, consumer, safety-contract, supported-Rust/configuration, or cited-document change. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r135.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r135.md new file mode 100644 index 0000000000..f3e8e570ba --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r135.md @@ -0,0 +1,25 @@ +# Acceptance review: `Piece for Tail` + +## Decision and exact claim + +**REJECT.** For the exact supplied `lib.rs`, the `unsafe impl Piece for Tail` is **CONTRACT-BROKEN** under its literal current contract. The safe `increment_tail` implementation has a complete source-level memory-safety proof for Rust 1.70.0, every target on which this source is accepted, and ordinary profiles, but soundness for the requested open-ended Rust **1.70+** set is **UNPROVED**. No UB counterexample is established for `increment_tail` or for a valid direct call to this `project` implementation. + +The range qualification matters: only exact Rust 1.70.0 authoritative text was verified. Extending those propositions to every later and future stable release needs a semantic compatibility premise or a finite audit cutoff; the request permits neither an additional TCB premise nor a narrower requested range. The contract-compliance verdict nevertheless applies to the requested set because Rust 1.70.0 is included and already supplies a counterexample. + +## Snapshot, boundary, and coverage + +Scope is the supplied `Piece` implementation for `Tail` and `increment_tail`; reviewed surfaces are the public unsafe trait method and associated constant, the unsafe impl, public tuple field `Pair.0`, and the safe free function. There are no dependencies, `cfg`s, generators, macros requiring expansion, FFI, concurrency, allocation, or target-specific operations. Profile differences are immaterial because addition is explicitly wrapping. Target coverage is parametric: the proof uses language-level tuple/array projection rather than assumed offsets or struct layout. The only invariant is call-local: `pair: &mut Pair` denotes a live, initialized, exclusively accessible `Pair` throughout the projection and immediate field reborrow. + +TCB: only the cited Rust 1.70.0 Reference/core contracts (authoritative axioms); no dependency, implementation, platform, deployment, or compatibility assumption. No execution-based evidence was used. + +## Obligation ledger and derivation + +1. **`FIELD` postcondition — CONTRACT-BROKEN.** The contract requires the name of a *direct declared field* of `Owner` whose type is `Item`. Here `Owner = Pair`, `Item = u32`, and `Pair` declares one tuple field: field `0` of type `[u32; 2]`. `FIELD` is `"tail"`; no such direct field exists. The Rust 1.70 Reference says a tuple index “must be a name of a field” and evaluates to that field's location ([tuple indexing](https://doc.rust-lang.org/1.70.0/reference/expressions/tuple-expr.html#tuple-indexing-expressions)). + +2. **`project` postcondition — CONTRACT-BROKEN.** `(*owner).0` selects `Pair`'s direct field; the following `[1]` selects an element inside that array. The same Reference expressly distinguishes these: array elements must be reached with an “array or slice indexing expression,” not tuple-field access ([tuple indexing](https://doc.rust-lang.org/1.70.0/reference/expressions/tuple-expr.html#tuple-indexing-expressions)). Thus the result points to a nested array element, not “that direct declared field.” This is a literal provider-postcondition failure, even though the nested element is operationally the intended tail value. + +3. **`Tail::project` memory safety at Rust 1.70.0 — PROVED.** For every call satisfying its documented precondition, `owner` identifies a live, uniquely borrowed `Pair`. Field `0` is therefore a live `[u32; 2]`, and zero-based index `1` is in bounds ([array indexing](https://doc.rust-lang.org/1.70.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions)). `addr_of_mut!` “create[s] a `mut` raw pointer to a place, without creating an intermediate reference”; its operand remains subject to the usual expression rules ([Rust 1.70 core docs](https://doc.rust-lang.org/1.70.0/core/ptr/macro.addr_of_mut.html)). The live owner, valid direct projection, and in-bounds array projection discharge those rules and yield the address of an initialized, aligned `u32`. Returning that raw pointer itself creates no longer-lived reference or access. + +4. **`increment_tail` memory safety at Rust 1.70.0 — PROVED.** Its safe caller supplies `&mut Pair`, so coercing that reference for the immediate `project` call establishes the callee's live/unique precondition. No callback or other access intervenes. Obligation 3 yields the address of `pair.0[1]`; `&mut *` then creates the sole active mutable reference to that initialized `u32`, which is used while `pair` is not otherwise accessed and is dropped before return. `wrapping_add` introduces no overflow-dependent unsafe precondition. Public construction or mutation of `Pair.0` cannot invalidate this argument because every `[u32; 2]` consists of initialized `u32` elements. + +There is no documented safe-API postcondition beyond soundness to prove. The false unsafe-trait postconditions remain mandatory independently of whether this particular safe function consumes them. Any source or contract change, or any expansion of the verified Rust-version evidence, requires a new review. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r136.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r136.md new file mode 100644 index 0000000000..d9ad36a4c6 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r136.md @@ -0,0 +1,51 @@ +# Supported-domain source review + +## Claim and verdict + +**PROVED** — For the exact `v7c4` source, every well-typed safe call to the public `first(&[u8]) -> Option` is free of Rust undefined behavior for every configuration claimed supported by either current policy, through the Rust 1.82.0 cutoff, relative to TCB `R136` below. No caller-side safety precondition is needed. + +This is a source-level Rust-semantics verdict, not a compiler/backend or binary-correctness claim. It deliberately does **not** select, merge, or supersede a support policy. + +## Snapshot and scope + +- Reviewed all files in `/tmp/unsafe-rust-v2-eval.9epWDK/targets/v7c4`; `lib.rs` SHA-256 is `6f87e944cec7ee2727f7c7d32aa382de88987ea791082175d611fff5bf44012b` and `Cargo.toml` SHA-256 is `1930057adcb43fb39dd7323268fd30e8cde9361fb0e15ee45efa014a199878de`. +- Package: edition 2021, no dependencies, one Cargo feature (`fast`), no build script, generated code, macros, FFI, assembly, invariant-bearing state, unsafe public API, traits, or fields. +- API surface: exactly one safe public free function named `first` in each mutually exclusive feature branch (`lib.rs:3-15`). The only unsafe operation is `bytes.get_unchecked(0)` in the `fast` branch (`lib.rs:13`). No public behavioral documentation creates an additional postcondition in scope. + +## Configuration closure without resolving policy + +Let `A` and `B` denote the configuration sets literally claimed by `POLICY-A.md` and `POLICY-B.md`: + +- Both include non-`fast` on Rust 1.79.0–1.82.0 for both named targets. +- For `fast`, A includes x86_64 on 1.79.0–1.82.0 and aarch64 on 1.80.0–1.82.0. +- For `fast`, B includes x86_64 on 1.80.0–1.82.0 and aarch64 only on 1.82.0. + +Thus `B ⊂ A`; the disputed set `A \ B` is `fast` on `(x86_64, 1.79.0)`, `(aarch64, 1.80.0)`, and `(aarch64, 1.81.0)`. With both publications current and no precedence rule, the canonical predicate `Supported(c)` remains indeterminate. That is a policy defect, not a reason to choose the intersection. The audit instead proves the implementation over the review envelope `E = A ∪ B = A`. This does not declare `E` to be the support policy; it establishes a stronger coverage theorem that subsumes either publication and every disputed case. + +The `fast`/non-`fast` `cfg` predicates are complementary and exhaustive. The proof below is target-parametric: it consumes no target layout, ABI, atomic, or platform fact. Exact standard-library contracts were checked separately for every Rust release 1.79.0–1.82.0. CI is sampled evidence only and was not used as proof. + +## Obligation ledger and derivation + +**O-NORMAL — `not(feature = "fast")`: PROVED.** The selected body contains only safe operations on the caller-supplied shared slice. It introduces no unsafe obligation or hidden caller condition. + +**O-FAST — `feature = "fast"`: PROVED.** The applicable Rust 1.79.0, 1.80.0, 1.81.0, and 1.82.0 slice documentation gives the same two propositions: `is_empty` returns true if the length is zero, and calling `get_unchecked` with an out-of-bounds index is UB ([1.79](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.is_empty), [1.80](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.is_empty), [1.81](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.is_empty), [1.82](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.is_empty); corresponding [`get_unchecked` 1.79](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.get_unchecked), [1.80](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.get_unchecked), [1.81](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.get_unchecked), [1.82](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked)). + +If `is_empty()` is true, line 11 returns before unsafe code. Otherwise its result is false. Contraposition of the documented implication “length is zero ⇒ result is true” gives `bytes.len() != 0`; because the length is a `usize`, `bytes.len() > 0`, so integer index `0` is in bounds. This discharges `get_unchecked(0)`'s bounds obligation. The operation returns a shared `&u8` into the still-borrowed slice; line 13 immediately reads that valid reference and constructs `Some(u8)`. There is no intervening call, mutation, unwind point, or retained capability. + +## TCB `R136` + +- **AXIOM-SLICE-179/180/181/182 (accepted):** the exact versioned `is_empty` and `get_unchecked` contracts linked above, each applied only to its matching Rust release. +- Ordinary source-level Rust abstract semantics and validity of well-typed safe inputs. No backwards-compatibility premise, dependency trust, test/tool theorem, external specification, or implementation/backend assumption is consumed. + +## Findings and residual scope + +1. **Policy inconsistency (does not weaken the source-soundness verdict).** The two current publications make incompatible exclusivity claims for the three configurations above. A canonical supported-set statement cannot be derived without authorized precedence or reconciliation. Minimal resolution: publish one controlling predicate or an explicit supersession rule. +2. **Proof-documentation defect (implementation obligation remains PROVED).** `lib.rs:13` has no adjacent `SAFETY` proof. Proposed wording, without changing the caller contract: + + ```rust + // SAFETY: `is_empty()` returned false in this branch. Its contract says + // a zero length returns true; contraposition gives `bytes.len() != 0`. + // Therefore index 0 is in bounds, satisfying `get_unchecked`'s requirement. + ``` + +No source or policy was changed, and no build, test, expansion, or target execution was performed. Outside the verdict are Rust after 1.82.0, other targets/configurations, the `fast` aarch64 1.79.0 combination excluded by both policies, and compiler/binary correctness. Re-review on source changes, policy reconciliation, support-set expansion, or material changes to a consumed versioned standard-library contract. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r137.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r137.md new file mode 100644 index 0000000000..7e78e3c183 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r137.md @@ -0,0 +1,53 @@ +# Focused review and redesign of `ticket` + +## Verdict + +**Current implementation — UNSOUND.** For Rust 1.70.0, on every target in an ordinary optimized configuration with debug assertions disabled, the well-typed safe call `ticket(0)` reaches `NonZeroUsize::new_unchecked(0)`, whose contract says that zero produces undefined behavior. One supported configuration and valid safe call suffice to refute the universal safe-API claim. + +**Proposed implementation — PROVED** for source-level Rust soundness and the stated behavior on Rust 1.70.0, every target, every optimization level, both debug-assertion settings, and either panic strategy, relative to the axioms below. The same verdict for the open-ended Rust 1.70+ range is relative to `COMPAT-1` below; without accepting that premise, later releases are `UNPROVED`, not silently covered. + +## Artifact, claim, and surface + +Reviewed the complete supplied target: `REQUEST.md` (SHA-256 `f4cb0fe5d667cdde8536d103ea2bf8c46a4219916acb811b0e33c4d2cb5bc376`) and `lib.rs` (SHA-256 `23f55cd3e96c8372d71a09336d84f72de191fd5f48de693d0be11762537dfa9f`). No source edit, build, test, or expansion was performed. + +The language-reachable safe surface is the public `Ticket` type and `pub fn ticket(id: usize) -> Ticket`. `Ticket`'s tuple field is private; the supplied snapshot has no other constructor, method, user-written trait implementation, exported macro, generated code, dependency, feature, callback, or target-specific branch. Its implicit move, drop, and auto-trait behavior merely carries the `NonZeroUsize` field and introduces no additional unsafe consumer. Safe callers may pass every `usize`, including zero. Required behavior is: for nonzero `id`, return a `Ticket` whose field contains that value; for zero, panic. + +## Current derivation + +Rust 1.70 documents that an optimized build does not execute `debug_assert!` unless `-C debug-assertions` is passed ([`debug_assert!`, Uses](https://doc.rust-lang.org/1.70.0/core/macro.debug_assert.html#uses)). It also states of `new_unchecked`: “This results in undefined behaviour if the value is zero,” and its Safety clause requires that the value not be zero ([`NonZeroUsize::new_unchecked`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new_unchecked)). + +An exhaustive configuration/input partition is therefore: + +- `id != 0`: whether the debug assertion executes or is erased, its condition is true and the unsafe constructor's sole stated precondition holds. +- `id == 0`, debug assertions enabled: `debug_assert!` panics before the unsafe call; this branch satisfies the documentation. +- `id == 0`, debug assertions disabled: no dominating runtime check remains, so the unsafe call receives zero and causes undefined behavior. Because `ticket` is safe, no caller obligation can exclude this call. + +There is also no adjacent `SAFETY` proof. A comment could not repair the failed branch: the needed fact `id != 0` is false there. The zero-input documented panic is consequently not proved in all ordinary profiles; the stronger terminal result is the soundness defect above. + +## Minimal redesign + +Preserve the public type, function signature, and documentation, and replace only the body: + +```rust +pub fn ticket(id: usize) -> Ticket { + Ticket(NonZeroUsize::new(id).expect("id must be nonzero")) +} +``` + +This removes the unsafe operation and its invariant proof surface rather than retaining an assertion-plus-unsafe construction. + +## Redesign proof + +Rust 1.70 says `NonZeroUsize::new` “Creates a non-zero if the given value is not zero” ([`NonZeroUsize::new`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new)). `Option::expect` returns the contained `Some` value and panics for `None` ([`Option::expect`](https://doc.rust-lang.org/1.70.0/core/option/enum.Option.html#method.expect)). Thus: + +- For every nonzero `id`, `new(id)` supplies the `NonZeroUsize` containing that same input, `expect` returns it, and `Ticket(...)` stores it. +- For zero, no nonzero value is created; the `Option` is `None`, so `expect` panics. With unwind it unwinds and with abort it aborts as the selected panic strategy dictates; no abstraction invariant is suspended in either case. + +The body has no `cfg`, optimization-sensitive check, unsafe operation, allocation, target fact, or dependency behavior. The two input cases are exhaustive for `usize`, and the argument is parametric over all targets and ordinary profiles. The panic text is not part of the supplied documented contract, so changing it does not alter documented behavior. + +## TCB and re-audit trigger + +- `AXIOM-1`: the exact Rust 1.70.0 `core` contracts linked above. +- `COMPAT-1`: for each supported stable Rust release later than 1.70.0, the documented behaviors of `NonZeroUsize::new` and `Option::expect` consumed by the redesign remain at least as strong over all targets and ordinary profiles. This is an explicit compatibility premise, not inferred from stability badges. + +There are no dependency, deployment, tool-result, or testing assumptions. This is a source-level theorem under documented Rust semantics, not a claim about a particular compiler binary or backend. Re-audit if the function/body, `Ticket` representation or visibility, supported configuration set, minimum Rust version, or either consumed library contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r138.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r138.md new file mode 100644 index 0000000000..01dd619788 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r138.md @@ -0,0 +1,48 @@ +# `decode` configuration-preserving review + +## Claim, snapshot, and verdicts + +Reviewed only `REQUEST.md` (SHA-256 `8d8801a4dddb5d5b9a1d1971fcda4eb017bcf0261fe7d97b1cd983bc51240a0b`) and `lib.rs` (SHA-256 `1ebbedcffa0ad4b2c3183ec04b4789d9c2daaeae6c19dc0e1d7f926ac685e89b`). Scope is the two public safe `decode` surfaces and their stated behavior. Valid uses are all well-typed safe calls. The required configuration set is stable Rust 1.70+, every target and pointer width, every ordinary profile, and both values of `compact`. No dependencies or generated artifacts were present in the supplied target. No source was edited, built, tested, or expanded. + +**Current implementation — soundness: `UNSOUND`.** This is established already on Rust 1.70 with `compact` enabled in an optimized configuration whose debug assertions are disabled. **Current documented behavior: `UNPROVED` over the combined support set** because the surrogate case in that configuration reaches UB rather than a proved panic. The non-`compact` branch is proved at Rust 1.70; the `compact` branch is proved only where debug assertions execute, by the reconstruction below. + +**Proposed implementation — `PROVED` at Rust 1.70** for source-level soundness and both documented behaviors over all inputs, targets, pointer widths, and ordinary profiles. It is **conditionally `PROVED` for the open-ended Rust 1.70+ range relative to `TCB-COMPAT-1`** below. Without reviewer acceptance of that compatibility premise, the open-ended-range verdict remains `UNPROVED`; this is a documentation/version-coverage qualification, not an implementation defect. + +## Finding: unchecked construction is not protected in every profile + +`decode` is safe, so `raw = 0xD800u16` is a valid call and no caller-side safety precondition may be assumed. At `lib.rs:7`, `char::from_u32_unchecked` requires its result to be a valid `char`. Rust 1.70 defines surrogates as `0xD800..=0xDFFF`, and the Reference classifies producing a `char` containing a surrogate as UB. + +The only attempted producer fact is `debug_assert!` at line 6. Its Rust 1.70 contract says optimized builds do not execute it by default unless debug assertions are enabled. In such a supported configuration, the `u16 as u32` cast zero-extends and preserves `0xD800`; line 7 therefore produces an invalid `char`. This is a concrete safe-use UB counterexample. + +Where debug assertions execute, `RangeInclusive::contains` makes the assertion panic for every surrogate; otherwise the cast receives a non-surrogate `u16`, hence a scalar in `0..=0xD7FF` or `0xE000..=0xFFFF`, satisfying the unchecked constructor. That material proof is absent from the unsafe block, which has no `SAFETY` comment, and it cannot cover the disabled-assertion branch. + +## Configuration-preserving redesign + +Replace only the body of the `compact` definition; retain its attributes, signature, and documentation: + +```rust +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + char::from_u32(raw as u32) + .expect("raw should not be a surrogate code point") +} +``` + +This introduces no unsafe operation and uses APIs available in Rust 1.70, so it does not raise the MSRV. It also preserves the exact return types. + +Proof and configuration closure: + +1. The two `cfg` predicates are logical complements, so exactly one definition exists for each independently selected `compact` value. +2. Without `compact`, the unchanged `char::from_u32(raw)` returns the represented scalar for a valid `char` value and `None` otherwise, exactly as documented. +3. With `compact`, widening an unsigned `u16` to `u32` preserves its numeric value. Partitioning all `u16` values is exhaustive: a surrogate becomes `None` under `from_u32`, and `expect` panics; every other value is at most `0xFFFF` and is a Unicode scalar, so `from_u32` returns the represented `Some(char)` and `expect` returns that `char`. +4. The proof mentions no pointer-sized integer, layout, target property, optimization, overflow check, or debug assertion. It is therefore parametric over all requested targets, pointer widths, and ordinary profiles. Panic strategy may determine unwind versus abort, but both are executions of the documented panic and neither changes the signature. + +## Authority, TCB, and residual scope + +`TCB-R138-v1` consists of these Rust 1.70 authoritative propositions: + +- [`char` validity and `from_u32[_unchecked]`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity): scalars exclude surrogates; `from_u32` returns `None` for invalid `char` inputs. See also [`from_u32`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32) and [`from_u32_unchecked`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32_unchecked). +- [Undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined): producing an invalid value is UB, and a surrogate-valued `char` is invalid. +- [`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html#uses), [`RangeInclusive::contains`](https://doc.rust-lang.org/1.70.0/std/ops/struct.RangeInclusive.html#method.contains), [unsigned widening](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast), [`Option::expect`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect), and [`cfg`](https://doc.rust-lang.org/1.70.0/reference/conditional-compilation.html#the-cfg-attribute) have the behavior used above. + +`TCB-COMPAT-1` (explicit additional premise): for each supported stable Rust release later than 1.70, the availability and cited semantics of `cfg`, unsigned `u16`-to-`u32` widening, `char::from_u32`, and `Option::expect` remain at least as strong as in Rust 1.70. This report does not derive that open-ended proposition from Reference/std text. Re-review is required if any cited contract, supported configuration, signature, or documented behavior changes. Package metadata, callers, and whole-crate behavior were outside this focused supplied-source review. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r139.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r139.md new file mode 100644 index 0000000000..f2437a75c8 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r139.md @@ -0,0 +1,43 @@ +# Focused review: `total` + +## Claim, scope, and verdict + +Snapshot: `lib.rs` SHA-256 `12ee0d64af16f4b4bea78bb146d333f86c579b49ca0dd38e2552f3896147be5c`. Scope is only the public safe function `total`; no whole-crate claim is made. Required domain is Rust 1.70+, every target and ordinary profile. Required behavior is the left-to-right sum of all input `u32`s modulo \(2^{32}\), including an empty-slice result of zero. Compiler/backend correctness and benchmark timing are separate from source-level Rust semantics. + +**Current soundness: UNSOUND.** A valid call with an empty slice can execute undefined behavior at `lib.rs:6`. This counterexample exists in Rust 1.70, which is inside the supported set, so the aggregate Rust-1.70+ claim fails regardless of later-version behavior. **Wrapping-result behavior:** proved only for executions not already invalidated by that UB. **Proof documentation:** UNPROVED/inadequate; none of the three unsafe sites has a `SAFETY` derivation. **Performance requirement for the redesign:** UNPROVED because no benchmark evidence was supplied. + +TCB is limited to the cited Rust 1.70 Reference and standard-library contracts; there are no dependencies, tools, generated artifacts, `cfg` branches, or admitted performance assumptions in the inspected artifact. + +## Obligation ledger and current implementation + +1. `values.as_ptr()` at line 5 is usable while `values` remains live. Rust 1.70 says a reference passed to a function is live at least for that call, and raw dereference of a dangling or unaligned pointer is UB ([Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#undefined-behavior)); [`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr) requires the slice to outlive the pointer. No call, mutation, or unwind intervenes here. + +2. Each `ptr.add` must satisfy all three literal conditions: start and result are in-bounds or one byte past the same allocation; the byte offset fits `isize`; and the infinite-precision address sum fits `usize` ([Rust 1.70 `pointer::add`](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add)). + +3. For `len > 0`, the missing proof can be reconstructed. A valid slice covers one live allocation, is aligned and initialized, and its dynamic size is at most `isize::MAX` ([Reference, “Dangling pointers”](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#dangling-pointers)). With loop invariant `ptr = base.add(i)` and `0 <= i <= len`, line 6 creates the one-past end; `ptr != end` entails `i < len`; line 9 reads the initialized element `i`; and line 10 advances to `i + 1 <= len`. The containing shared slice stays live and is never mutated. Thus all nonempty dereferences and offsets are valid. + +4. That derivation does **not** cover `len == 0`. Rust 1.70 expressly permits [`slice::from_raw_parts`](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html) to form a zero-length slice using `NonNull::dangling()` as `data`; for zero bytes it requires non-nullness and alignment, not membership in a live allocation. Such a valid `&[u32]` may be passed to this safe function. Line 6 nevertheless calls `add(0)` on its `*const u32`; `add` still requires both pointers to be in-bounds or one-past the same allocated object and declares violation UB. The loop condition cannot protect this call because it happens before the condition. + +5. On every otherwise-defined path, `acc` starts at zero and applies exactly one `u32::wrapping_add` per element in order. No ordinary overflowing addition, assertions, `cfg`, allocator, panic, or target-specific operation affects that recurrence, so debug/release and optimization profiles do not change it. + +## Safe iterator candidate + +The preferred candidate is: + +```rust +pub fn total(values: &[u32]) -> u32 { + let mut acc = 0u32; + for &value in values { + acc = acc.wrapping_add(value); + } + acc +} +``` + +It preserves the same accumulator recurrence and empty result while removing every raw pointer, unsafe operation, caller obligation, and local pointer invariant. Its source-level proof is correspondingly local: safe slice iteration yields each element in order, and the unchanged explicit `wrapping_add` supplies build-mode-independent modular addition. This is a design, not an audited new snapshot; after implementation it still needs a focused post-change review over the stated Rust/configuration range. + +Compatibility is behavioral and signature-preserving. The relevant migration risk is performance, not API behavior. No benchmark result, benchmark definition, environment, samples, or uncertainty bound exists here, so asserting a regression of at most 2% would be evidence-free. + +## Recommendation + +Do not accept the current artifact and do not merge the iterator redesign on performance faith. Prepare the safe candidate, then run the **designated** benchmark against this exact baseline under a pre-agreed metric, inputs, profiles/targets, warmup, sample count, and noise/uncertainty rule. Accept the redesign only if that evidence establishes no more than 2% regression. If it misses the threshold, the fallback is not the current code unchanged: evaluate a sound optimized variant (at minimum, return early for `values.is_empty()` before the raw `add`, and add the reconstructed `SAFETY` proofs) against the same benchmark. Re-audit whichever exact implementation wins. This sequence preserves both proof quality and the binding performance gate without treating the absent measurement as an assumption. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r140.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r140.md new file mode 100644 index 0000000000..a9fdd4d726 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r140.md @@ -0,0 +1,33 @@ +# `Piece for Tail` acceptance review + +## Decision and exact verdicts + +**REJECT.** The exact supplied `lib.rs` cannot satisfy its literal public contracts, and policy does not permit repairing them. + +- **Soundness — PROVED, narrowly:** for Rust 1.70, every call to `Tail::project` satisfying its documented precondition, and every well-typed safe call to `increment_tail`, is free of Rust undefined behavior on every target and in every ordinary profile, relative only to the cited Rust 1.70 Reference/standard-library axioms. +- **Contract compliance — CONTRACT-BROKEN:** `Tail` violates both `Piece::FIELD` and `Piece::project`'s documented postcondition in every configuration in which this source compiles. +- **Requested open-ended version set — UNPROVED for soundness as a whole:** the inspected axioms are versioned to 1.70. The request forbids an additional backwards-compatibility premise, so those texts cannot establish the same propositions for every later, including future, `1.70+` release. This qualification does not weaken the configuration-independent contract counterexamples. + +Scope is only [`Piece`, `Tail`, `Pair`, and `increment_tail`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/y9t5/lib.rs:3) in the supplied snapshot. There are no dependencies, generated artifacts, features, `cfg`s, FFI, assembly, allocation, concurrency, or tool-derived evidence. No build or execution was used. + +## Decisive finding + +The controlling contract says `FIELD` names a **direct declared field of `Owner` whose type is `Item`**, and `project` returns a pointer to **that direct declared field**. + +The source declares `Pair(pub [u32; 2])`: its sole direct declared tuple field is field `0`, of type `[u32; 2]`. It declares no direct field named `tail`, and no direct field of type `u32`. Nevertheless, [`Tail::FIELD` is `"tail"`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/y9t5/lib.rs:20), so the associated-constant postcondition is false. [`project`](/tmp/unsafe-rust-v2-eval.9epWDK/targets/y9t5/lib.rs:22) returns `&raw mut (*owner).0[1]`: the target is element 1 nested inside the array stored in field `0`, not a direct declared field of `Pair`. Thus its return postcondition is also false for every valid call. These are direct source counterexamples; layout, target, optimization, and panic strategy cannot change the declaration/projection relationship. + +This is **CONTRACT-BROKEN**, not by itself an exhibited UB execution: a raw pointer may safely point to the nested `u32`. It nevertheless defeats acceptance because an unsafe implementation must supply its documented guarantees; inferred intent cannot rewrite “direct declared field.” + +## Compact obligation ledger and reconstructed proof + +1. **`Tail::project` call precondition — PROVED at 1.70.** A valid call supplies a live, uniquely borrowed `Pair` for the call. The Rust 1.70 Reference says a mutable reference is the only access path to its value while it has not been reborrowed, and that raw pointers can be made from/reborrowed as references ([pointer types](https://doc.rust-lang.org/1.70.0/reference/types/pointer.html#mutable-references-mut)). Thus the `&mut Pair` at the safe wrapper supplies a live, aligned, non-null uniquely borrowed owner and its raw-pointer coercion remains within that borrow. + +2. **Pointer projection in `project` — PROVED at 1.70.** Rust 1.70 documents `addr_of_mut!` as creating a mutable raw pointer “without creating an intermediate reference,” while leaving the place expression subject to the usual rules ([`addr_of_mut!`](https://doc.rust-lang.org/1.70.0/core/ptr/macro.addr_of_mut.html)). `owner` identifies the live `Pair`; `.0` selects its existing `[u32; 2]`; and constant index `1` is in range because array indices are zero-based and bounds-checked ([array indexing](https://doc.rust-lang.org/1.70.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions)). Hence the result addresses the live, initialized second `u32` subobject. This proves memory safety, but proves the opposite of the promised *direct-field* relationship. + +3. **`increment_tail` safe surface — PROVED at 1.70.** Its `&mut Pair` supplies the callee precondition. There is no intervening callback, move, destruction, or alias-producing operation before `&mut *` reborrows the returned element pointer. The owner borrow remains live through the read and write; the element is initialized, aligned, and uniquely accessible. The safe `wrapping_add` and assignment preserve `u32` validity. No caller-side safety condition is hidden. The function has no documented behavioral postcondition beyond soundness. + +4. **Provider postconditions — CONTRACT-BROKEN.** `FIELD` and `project` fail exactly as shown above. No in-scope unsafe consumer turns that false relationship into UB: `increment_tail` relies only on the concrete pointer's actual validity, not on its being a direct field. + +## TCB, coverage, and residual scope + +The TCB contains only the exact Rust 1.70 abstract-semantics documentation linked above; there are no admitted dependency, compiler-backend, platform, deployment, or compatibility assumptions. The proof is parametric over targets and ordinary profiles because it uses typed field/index operations and no representation offset, `cfg`, overflow mode, or external behavior. Rust releases after 1.70 require re-review against their own applicable Reference/std text; source or contract changes likewise invalidate this report. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r141.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r141.md new file mode 100644 index 0000000000..8ee304e02f --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r141.md @@ -0,0 +1,47 @@ +# Supported-domain source review + +## Claim and verdict + +**Soundness: PROVED** for the exact `v7c4` source snapshot, for every well-typed safe call to the configuration-selected public `first(&[u8]) -> Option`, over every target/version/feature combination expressly supported by either current policy, relative only to the versioned Rust axioms below. This is a source-level Rust-abstract-semantics claim, not a compiler-backend or binary claim. + +Let `R = {1.79.0, 1.80.0, 1.81.0, 1.82.0}` and `T = {x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu}`. The reviewed domain `U` is: + +- without `fast`: `T × R`; +- with `fast` on x86_64: all of `R`; +- with `fast` on aarch64: `{1.80.0, 1.81.0, 1.82.0}`. + +`U` is the union of the two published domains (and happens extensionally to equal Policy A), not a choice that Policy A supersedes Policy B. Thus the implementation is separately **PROVED** on Policy A's domain and on Policy B's narrower domain. No single canonical support predicate can be derived from the publications: `POLICY-A.md:9-10` includes fast/x86_64/1.79 and fast/aarch64/1.80-1.81, while `POLICY-B.md:9-10` omits the former and says the latter supports *only* 1.82. This policy inconsistency is a documentation defect, but does not limit the soundness theorem because the proof covers their union. + +## Snapshot, boundary, and configuration closure + +Scope is package `domain-review` 1.0.0, edition 2021: `lib.rs`, `Cargo.toml`, `rust-toolchain.toml`, both policies, `CI.md`, and `REQUEST.md`, with audit cutoff 1.82.0. There are no dependencies, build script, generated artifacts, macros, traits, fields, FFI, concurrency, or persistent invariants in the supplied snapshot. The only language-reachable crate API is safe function `first`; its two definitions are selected by complementary `cfg(feature = "fast")` and `cfg(not(feature = "fast"))` predicates, so exactly one applies for either feature value. + +The proof is parametric over the two targets: the source has no target selection or target-dependent operation. It is also profile/panic-strategy independent. Exact standard-library contracts were checked separately for all four Rust releases, so no forward or backward compatibility premise is used. `rust-toolchain.toml` selects 1.82.0 as the local default but does not override the published 1.79-1.82 commitments. `CI.md` is sampled test coverage and explicitly is not a support definition; no test or compilation result is used as proof. + +## Obligation ledger and reconstructed proof + +| Site | Obligation | Domain | Status | +|---|---|---|---| +| `lib.rs:3-6` | Safe implementation preserves freedom from UB | `!fast`, all `T × R` | **PROVED**: it contains only safe calls (`slice::first`, `Option::copied`). | +| `lib.rs:10-13` | Index `0` passed to `get_unchecked` is in bounds | `fast`, all of `U` | **PROVED** below. | +| `lib.rs:13` | Returned reference may be read as `u8` | same | **PROVED** below. | + +For each of Rust [1.79.0](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.is_empty), [1.80.0](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.is_empty), [1.81.0](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.is_empty), and [1.82.0](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.is_empty), `is_empty` says it “Returns `true` if the slice has a length of 0.” Therefore reaching the `else` branch with `is_empty() == false` implies by contrapositive that `bytes.len() != 0`; since slice length is a `usize`, `0 < bytes.len()`, so index 0 is in bounds. + +For the same releases, `get_unchecked` returns a reference without bounds checking and states that an out-of-bounds index is UB: [1.79.0](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.get_unchecked), [1.80.0](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.get_unchecked), [1.81.0](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.get_unchecked), [1.82.0](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked). The dominating branch therefore discharges its caller obligation. The corresponding References state that all slice elements are initialized ([1.79](https://doc.rust-lang.org/1.79.0/reference/types/slice.html), [1.80](https://doc.rust-lang.org/1.80.0/reference/types/slice.html), [1.81](https://doc.rust-lang.org/1.81.0/reference/types/slice.html), [1.82](https://doc.rust-lang.org/1.82.0/reference/types/slice.html)). The input is a valid shared `&[u8]`; `get_unchecked(0)` returns `&u8`, which is immediately read and copied with no intervening call, mutation, or lifetime transition. The empty branch performs no unsafe operation. + +There are no target-authored API postconditions or caller safety obligations. Broader undocumented behavioral compatibility is outside this soundness review. + +## TCB and findings + +**TCB AXIOM-SLICE-179-182:** the exact `is_empty`, `get_unchecked`, and slice-validity propositions linked above, each used only for its matching Rust release. No dependency, external, tool, CI, platform, or compatibility assumption is consumed. + +**Proof-documentation finding (implementation remains PROVED):** `lib.rs:13` has no adjacent `SAFETY` proof, so the material dominating-check derivation had to be reconstructed. Proposed wording, without changing the API contract: + +```rust +// SAFETY: This branch is reached only when `bytes.is_empty()` is false. +// Thus `bytes.len() != 0`, so index 0 is in bounds for `get_unchecked`. +Some(unsafe { *bytes.get_unchecked(0) }) +``` + +No valid UB counterexample is known in `U`; every obligation above is discharged. No verdict is issued outside `U`. Re-review is required if the source, feature selection, either policy, supported Rust/target range, or any consumed versioned Rust contract changes. Resolving the two publications into one authoritative support policy is recommended, but no particular resolution is selected here. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r142.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r142.md new file mode 100644 index 0000000000..fbb5c77bf6 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r142.md @@ -0,0 +1,44 @@ +# Focused review: `Ticket` construction + +## Claim, snapshot, and scope + +Reviewed `lib.rs` (SHA-256 `23f55cd3e96c8372d71a09336d84f72de191fd5f48de693d0be11762537dfa9f`) as supplied, without execution or expansion. The in-scope safe surfaces are the opaque `Ticket` type and `pub fn ticket(id: usize) -> Ticket`; the latter must be sound for every `usize`, return a ticket containing `id` when `id != 0`, and panic when `id == 0`. The supported set is Rust 1.70+, every target, and every ordinary profile; there are no dependencies, generated artifacts, features, or deployment assumptions in the supplied source. + +**Current implementation verdict: UNSOUND.** This holds for the overall supported set because Rust 1.70.0 on any target in an ordinary optimized build with debug assertions disabled is a supported counterexample. + +## Finding F-1: a disabled debug assertion admits zero + +At `lib.rs:8-10`, `ticket` is safe and documents no caller safety obligation, so `ticket(0)` is a valid safe call. + +Rust 1.70 documents that [`debug_assert!`](https://doc.rust-lang.org/1.70.0/core/macro.debug_assert.html) is “only enabled in non optimized builds by default” and that an optimized build does not execute it unless `-C debug-assertions` is passed. Thus, in the ordinary optimized/default-debug-assertions-disabled class, line 9 establishes nothing and execution reaches line 10 with `id == 0`. + +The controlling [`NonZeroUsize::new_unchecked`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new_unchecked) contract says both “This results in undefined behaviour if the value is zero” and “The value must not be zero.” Substituting the local fact `id == 0` violates that sole safety obligation and proves UB. The unsafe block also has no adjacent `SAFETY` proof; none can be supplied for the existing code because the only candidate check is configuration-dependent. + +With debug assertions enabled, zero instead triggers a panic and nonzero inputs satisfy the unsafe precondition, but that branch does not cover all supported profiles. The zero-input documented panic is consequently unproved in the failing class; the primary finding is `UNSOUND`, rather than a separate `CONTRACT-BROKEN` verdict, because the counterexample itself reaches UB. + +## Recommended redesign + +Replace only the function body; its exact safe signature and documentation remain unchanged: + +```rust +pub fn ticket(id: usize) -> Ticket { + Ticket(NonZeroUsize::new(id).expect("ticket id must be non-zero")) +} +``` + +This is the smallest proof surface: it removes the unsafe operation and the invariant-establishment proof that the current wrapper fails to provide. + +### Proof of the redesigned behavior + +Rust 1.70 documents [`NonZeroUsize::new`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new) as creating a nonzero value when the given value is nonzero; the same page states that zero is not a valid `NonZeroUsize` instance. It documents [`Option::expect`](https://doc.rust-lang.org/1.70.0/core/option/enum.Option.html#method.expect) as returning the contained `Some` value and panicking for `None`. + +Partition all inputs: + +- If `id != 0`, `new(id)` supplies the `NonZeroUsize` containing that input, `expect` returns it, and `Ticket(...)` establishes the documented result. +- If `id == 0`, safe construction supplies no `NonZeroUsize`; `expect` panics, so the documented zero behavior holds. Whether panic unwinds or aborts does not expose invalid state because construction has not occurred. + +The argument is parametric in `usize` width and target, performs no arithmetic, and contains no optimization, overflow-check, or debug-assertion branch. It therefore covers every target and ordinary profile under the cited contracts. + +**Redesign verdict: PROVED for Rust 1.70.0, every target and ordinary profile, relative only to the cited Rust 1.70 `core` contracts.** No extra dependency, environment, tool, or implementation premise is consumed. + +For the literal open-ended `Rust 1.70+` range, the behavioral verdict is **UNPROVED** unless each later version's applicable documentation is checked or the reviewer accepts a named compatibility premise preserving exactly the cited `new` and `expect` propositions. A stability badge alone is insufficient for that semantic extension. This is a version-coverage qualification, not a residual unsafe-code obligation in the redesign. Re-review when the MSRV, either consumed contract, the signature/documentation, or the target/profile support policy changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r143.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r143.md new file mode 100644 index 0000000000..36becbaf72 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r143.md @@ -0,0 +1,121 @@ +# Implemented-candidate source audit + +## Claim and verdict + +**Soundness: UNSOUND.** This verdict covers the exact supplied `lib.rs`, every +well-typed safe use, Rust 1.70+, every target, and ordinary profiles. A safe +client reaches undefined behavior on Rust 1.70.0, which is itself enough to +refute the requested universal version range. The prior `DESIGN-NOTE.md` +verdict was not inherited. + +There are no public unsafe APIs or documented unsafe-API postconditions, so no +separate `CONTRACT-BROKEN` result applies. The repair below is **UNPROVED +(unimplemented)** and does not alter the snapshot verdict. + +## Snapshot, boundary, and configurations + +Scope is all 23 lines of the supplied `lib.rs`; `REQUEST.md` and +`DESIGN-NOTE.md` supply scope and history only. The public safe surface is +`View<'a, T>`, `View::new`, `View::get`, and `View::get_mut`. Both fields are +private. The only unsafe operations are the raw-pointer-to-reference +conversions in `get` and `get_mut`. There are no public fields, unsafe APIs, +explicit trait implementations, macros, generated code, dependencies, `cfg`s, +FFI, assembly, allocation, or target/profile-specific branches. There is no +`Drop` implementation. Compiler-provided traits do not prevent the +single-threaded witness below. + +Thus target and profile coverage is parametric: all configurations select the +same implementation and the counterexample uses only references and `i32`. +No target was built or executed and no tool-derived evidence is claimed. + +## Invariant and obligation ledger + +The intended representation invariant is: `ptr` is the pointer derived by +`new` from the unique `&'a mut T`; the pointee remains live, aligned, and a +valid `T` while the view can use it; private fields prevent replacement; and +the view controls all safe access during that borrow. `new` establishes the +pointer-identity portion for the witness below, whose stack `i32` remains live. + +- **`get` (`&*self.ptr`): UNSOUND in composition.** Producing `&'a T` requires + excluding a conflicting mutable reference for the produced reference's live + interval. Its receiver borrow is shorter and does not enforce that. +- **`get_mut` (`&mut *self.ptr`): UNSOUND.** Producing `&'a mut T` requires + exclusive access for the produced reference's live interval. A caller can + call the method again while the first result remains live. +- **Move/drop and private representation:** neither duplicates nor edits the + pointer in this snapshot, but they cannot repair the accessor defect. + +Both unsafe blocks also lack adjacent `SAFETY` proofs. That is a proof-artifact +defect, but here the missing exclusivity derivation cannot be reconstructed: +the required proposition is false. + +## Safe UB witness and derivation + +```rust +fn use_both(a: &mut i32, b: &mut i32) { + *a = 1; + *b = 2; +} + +fn witness() { + let mut value = 0; + let mut view = View::new(&mut value); + let first = view.get_mut(); + let second = view.get_mut(); + use_both(first, second); +} +``` + +This contains no unsafe operation in the client. In `get_mut`, the elided +receiver lifetime and the impl lifetime `'a` are distinct. Rust 1.70 says each +elided parameter lifetime becomes distinct, and its receiver rule assigns the +receiver lifetime only to *elided* output lifetimes; this output explicitly +uses `'a` ([Rust 1.70 Reference, lifetime elision](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html)). +Consequently the first temporary borrow of `view` ends independently of +`first`, so the second call is permitted. Both results are made from the same +unchanged `ptr` and therefore point to `value`. + +During `use_both`, both references are live: Rust 1.70 states that a reference +passed to a function is considered live and describes `&mut T` as following +the scoped no-alias model ([Rust 1.70 Reference, undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). +The same version's standard-library contract states that Rust memory safety +permits **one** mutable reference to an object, as opposed to several shared +references ([Rust 1.70 `std::cell`](https://doc.rust-lang.org/1.70.0/std/cell/index.html)). +The two live mutable references alias the same non-interior-mutable `i32`, so +the unsafe implementation has enabled a well-typed safe client to violate the +mutable-reference exclusivity rule. A mixed `get()` followed by `get_mut()` has +the analogous shared/mutable conflict; the repeated-`get_mut` witness already +proves unsoundness. + +## Required change + +Prefer removing the raw pointer entirely: + +```rust +pub struct View<'a, T> { + value: &'a mut T, +} + +impl<'a, T> View<'a, T> { + pub fn new(value: &'a mut T) -> Self { Self { value } } + pub fn get(&self) -> &T { self.value } + pub fn get_mut(&mut self) -> &mut T { self.value } +} +``` + +If the raw representation is required, at minimum change the return types to +`&T` and `&mut T`, respectively, so elision ties each result to the receiver, +and add proof-grade `SAFETY` comments deriving validity and exclusivity from a +named representation invariant, field privacy, and those receiver-tied +lifetimes. Either proposal must be implemented and freshly audited before it +can receive `PROVED`. + +## TCB and residual scope + +The only consumed TCB entries are the three exact Rust 1.70 documentation +propositions linked above. There are no dependency, external, deployment, +probabilistic, compatibility, or tool assumptions. Because Rust 1.70 is in the +requested range, no forward-compatibility premise is needed for the negative +aggregate verdict. Re-audit after any representation or signature change, any +new constructor/access path or trait implementation, or a change to the +supported Rust/configuration range. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r144.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r144.md new file mode 100644 index 0000000000..6aaa2c1fee --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r144.md @@ -0,0 +1,51 @@ +# Version-partitioned source review + +## Claim and verdict + +Scope is exactly `targets/h5p9/lib.rs`, specifically the public safe function `advance_marker`, with no caller safety preconditions. The source-level claim is freedom from Rust undefined behavior for every well-typed safe call, separately over: + +- **R179:** Rust and `core` 1.79.0, every target on which this source is supported, every ordinary profile — **UNSOUND**. +- **R180:** Rust and `core` 1.80.0, every target on which this source is supported, every ordinary profile — **PROVED**, relative to TCB-R144 below. +- **Combined `R179 ∪ R180`: UNSOUND.** R179 contains a valid safe execution that immediately violates `pointer::add`'s safety contract; inclusion of a proved region cannot repair it. + +No behavioral postcondition is documented for this safe API beyond returning its declared raw-pointer type, so there is no separate postcondition verdict. + +## Boundary and obligation inventory + +The only language-reachable surface is safe, argument-free `pub fn advance_marker() -> *const [u8; 0]`. Its only unsafe obligation site is `core::ptr::null::<[u8; 0]>().add(1)` in `lib.rs:4`. There are no fields, traits, macros, dependencies, callbacks, mutable state, dereferences, allocations, generated artifacts, conditional compilation, or concurrency. The proof must therefore discharge every `add` precondition without help from a caller. + +The common local facts are: + +1. `null::<[u8; 0]>()` supplies a null raw pointer; it is not a pointer in, or one-past, an allocated object. +2. The array-layout rule makes `size_of::<[u8; 0]>() = size_of::() * 0 = 0`. +3. Consequently `add(1)` computes byte offset `1 * 0 = 0`; that offset fits every target's `isize`, and adding zero cannot wrap any target's address space. + +## Regional derivations + +### Rust 1.79.0 — UNSOUND + +The 1.79.0 [`pointer::add` safety contract](https://doc.rust-lang.org/1.79.0/core/primitive.pointer.html#method.add) says, without a zero-offset exception, that both starting and resulting pointers must be in-bounds or one byte past the end of the same allocated object; violating any listed condition is undefined behavior. Although the byte offset here is zero, the starting null pointer satisfies neither alternative. Thus the unsafe call violates the first conjunct. This happens on every call from entirely safe code, so any call to `advance_marker()` is a concrete valid-use UB counterexample. The arithmetic conjuncts passing does not cure the failed allocation conjunct. + +### Rust 1.80.0 — PROVED + +The 1.80.0 [`pointer::add` safety contract](https://doc.rust-lang.org/1.80.0/core/primitive.pointer.html#method.add) changed the allocation conjunct to apply only when the computed byte offset is nonzero and expressly states: “If it is zero, then the function is always well-defined.” Here the offset is zero. Independently, zero fits `isize` and the unchanged address fits `usize`, discharging the other two conjuncts. No pointer is dereferenced and returning a null raw pointer creates no further obligation. The function therefore preserves freedom from UB for every safe call in R180. + +## Configuration closure + +The partition by Rust version is exhaustive by request. Within each version the proof is parametric over targets: zero array length fixes the byte offset at zero regardless of pointer width, alignment, ABI, or endianness. It is also parametric over ordinary profiles: the source has no assertions, overflow-dependent arithmetic, `cfg`, panic path, or profile-selected branch, and the relevant abstract contract does not vary by optimization. Thus the verdicts cover all requested target/profile combinations, not sampled configurations. + +## TCB-R144 and evidence + +- **AXIOM-NULL-179/180:** the exact versioned [`core::ptr::null` 1.79.0](https://doc.rust-lang.org/1.79.0/core/ptr/fn.null.html) and [`1.80.0`](https://doc.rust-lang.org/1.80.0/core/ptr/fn.null.html) contracts: the function creates a null raw pointer. +- **AXIOM-LAYOUT-179/180:** the exact versioned Reference array-layout rule ([1.79.0](https://doc.rust-lang.org/1.79.0/reference/type-layout.html#array-layout), [1.80.0](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#array-layout)): `[T; N]` has size `size_of::() * N`. +- **AXIOM-ADD-179/180:** the two versioned `pointer::add` contracts cited in the regional derivations. + +No tool result, compiler implementation behavior, backwards-compatibility premise, external specification, or dependency is consumed. This is an abstract source-level result, not a binary/backend correctness claim. + +## Finding and resolution + +**Critical implementation defect for R179:** the safe API hides an unsatisfied allocation precondition. A comment or caller documentation cannot make the safe API sound. The minimum resolution is either to enforce Rust 1.80.0 as the lower bound or replace the operation with one whose exact Rust 1.79.0 contract is locally satisfied, then re-audit the changed artifact. + +**Proof-documentation defect for R180:** the unsafe block has no `SAFETY` comment, and its correctness depends materially on a version-specific zero-offset exception. If R179 is excluded, suitable adjacent proof text is: `SAFETY: [u8; 0] has size zero, so add(1) computes a zero-byte offset; Rust 1.80.0's pointer::add contract states that a zero offset is always well-defined, including for this null pointer.` + +Re-audit on any source change, supported-Rust change, or material change to the cited contracts. No source edit was performed. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r145.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r145.md new file mode 100644 index 0000000000..83c8915c32 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r145.md @@ -0,0 +1,57 @@ +# Focused `Buffer` source review + +## Claim and verdict + +**UNSOUND** for `lib.rs` SHA-256 `368fa01b2f3fca39e89131414f8ec2641a4ac162b846ccf77b8f8bd293930d9a`, under Rust 1.80.0, on every target on which this source is accepted and in every ordinary profile. The refuted claim is that every well-typed safe use of the public `Buffer` API is free of Rust undefined behavior. This is a source-level verdict relative to TCB `R145-v1` below. + +A wholly safe witness is: + +```rust +let mut buffer = Buffer::from_static(); +buffer.overwrite(0); +``` + +No documented postcondition is separately classified `CONTRACT-BROKEN`: the in-scope unsafe constructor documents safety obligations but no behavioral postcondition, and the witness execution contains UB. + +## Boundary, producers, transitions, and consumers + +The fields at `lib.rs:6-7` are private. Rust 1.80 says items are private by default and private items are accessible only from their module and descendants ([privacy rules](https://doc.rust-lang.org/1.80.0/reference/visibility-and-privacy.html#visibility-and-privacy)). In the complete reviewed source, this closes construction to the following two producers: + +- `from_writable` (`lib.rs:16-18`) stores the exact caller pointer and `None`. Its unsafe contract continuously requires the pointer to be non-null, aligned, valid for a one-`u8` write, and free of conflicting access while the returned value may be used. +- `from_static` (`lib.rs:20-26`) safely creates `shared = &BYTE`, casts that pointer to `*mut u8`, and stores `Some(shared)`. For sized-to-sized raw-pointer casts, Rust 1.80 says “the pointer is returned unchanged” ([cast rule](https://doc.rust-lang.org/1.80.0/reference/expressions/operator-expr.html#pointer-to-pointer-cast)); therefore the two fields designate the same byte. + +`overwrite` is the only explicit state consumer. It does not change either field. `with_live` consumes the `Some` reference around the write. Ordinary moves, borrows, and compiler-generated drop merely move or discard the raw pointer/reference values; there is no custom trait implementation, destructor, macro/generated surface, or other producer/consumer in the snapshot. + +The resulting exhaustive invariant partition is: + +1. **W:** `shared == None`; `ptr` is the exact `from_writable` input and its caller-maintained obligations still hold. +2. **S:** `shared == Some(&BYTE)`; `ptr` points to that same `BYTE` byte. + +## Obligation disposition and UB proof + +Rust 1.80 `ptr::write` requires its destination to be “valid for writes” and “properly aligned” ([exact safety contract](https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html#safety)). + +- **W / `None` branch (`lib.rs:35-39`): conditional subclaim PROVED.** Field privacy and the exhaustive producers establish that this branch came from `from_writable`; that invocation's ongoing contract supplies validity, alignment, and non-conflict at every call. `overwrite` preserves the fields, so repeated valid calls close the same way. This result quantifies only over calls satisfying the unsafe constructor's continuing obligations. +- **S / `Some` branch (`lib.rs:29-34`): UNSOUND.** The reference is passed to `with_live`, and the write closure runs before that call returns. Rust 1.80 says a reference passed to a function is live at least for the whole call. It also says bytes pointed to by a shared reference are immutable, and any overlapping write of more than zero bytes is a mutation ([UB rules](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html#behavior-considered-undefined)). `self.ptr` points to the byte referenced by `shared`; `size_of::()` is 1 on every target ([primitive layout](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#primitive-data-layout)); and `write` writes that byte. Thus the safe witness mutates immutable bytes while the shared reference is definitely live. The chosen value is irrelevant, even if equal to `BYTE`. + +The proof is parametric over the requested configuration set: there is no `cfg`, feature, target, generated-code, allocator, panic, or profile branch, and each consumed Rust rule is target-independent. Optimization cannot repair a source-level UB execution. + +## Local safety-proof finding + +The comment at `lib.rs:31-32` is false on its path: `Some` is produced by `from_static`, never `from_writable`. No local proof can discharge this write for the current implementation. The comment at `lib.rs:36-37` identifies the correct producer but omits the necessary constructor-closure argument and the separately documented alignment/non-conflict obligations. A sufficient replacement for the `None` branch would be: + +```rust +// SAFETY: Private fields and the exhaustive constructors imply that `None` +// came from `from_writable`. Its ongoing contract keeps this exact pointer +// valid and aligned for one `u8` write, with no conflicting access; these +// fields have not changed. +unsafe { self.ptr.write(value) } +``` + +The `Some` branch requires an implementation/API change, not revised prose. + +## TCB, evidence, and residual scope + +`R145-v1` contains only the linked, exact Rust 1.80 authoritative propositions: raw-pointer `write` safety, sized raw-pointer cast identity, shared-reference liveness/immutable-byte UB, `u8` size, and privacy. There are no dependencies, external assumptions, or tool-derived claims. No build, test, execution, or expansion was used. + +Excluded are compiler-backend/binary correctness and undocumented behavioral robustness. Re-review is required if Rust/version support, fields or visibility, either constructor, `overwrite`, `with_live`, or any producer/consumer is changed. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r146.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r146.md new file mode 100644 index 0000000000..aca57b8941 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r146.md @@ -0,0 +1,35 @@ +# Focused acceptance review: `Piece for Tail` + +## Decision and exact verdicts + +**REJECT this snapshot.** The result does not depend on a demonstrated UB execution: the immutable unsafe-trait provider contract is demonstrably false. + +- **Contract compliance — `CONTRACT-BROKEN`**, for `Piece for Tail` on every requested target/profile and Rust version on which this source has its stated meaning. Both `FIELD` and `project` violate their literal provider postconditions. +- **Unsafe-implementation soundness — `PROVED`** for `Tail::project` on Rust 1.70.0 and Rust 1.97.1, for every call satisfying its documented `owner` precondition, on every target and ordinary profile. The call itself is UB-free and returns a valid pointer to the nested `u32`, despite returning the wrong field contractually. +- **Safe-surface soundness — `PROVED`** for `increment_tail` on Rust 1.70.0 and Rust 1.97.1, for every target and ordinary profile, relative only to the linked version-matching Rust Reference/standard-library axioms. Every well-typed safe call is UB-free and changes `pair.0[1]` to its prior value plus one modulo `2^32`. +- **Full open-ended Rust 1.70+ soundness claim — `UNPROVED`.** No UB counterexample is known. The request forbids an additional compatibility premise, however, and exact documentation was verified only at the 1.70.0 lower bound and 1.97.1 audit cutoff. Those documents cannot prove future 1.98+ semantics. This applicability gap does not weaken the unconditional contract counterexample or the rejection. + +Snapshot: `lib.rs` SHA-256 `d76a5c0d7336aac4e551264a105c621dbd6cf27db097fb648aa3ca1e891e3429`. Scope is only the displayed `Tail` impl and `increment_tail`; there are no dependencies, `cfg` branches, generated artifacts, FFI, allocation, concurrency, or persistent invariants. TCB: no dependency, implementation, external, tool, or deployment assumptions; only the exact Rust axioms linked below. + +## Contract counterexample + +`Pair` declares exactly one direct tuple field, field `0`, with type `[u32; 2]`. Rust 1.70 states that a tuple index “must be a name of a field” and resolves to the field with that name ([tuple-index expressions](https://doc.rust-lang.org/1.70.0/reference/expressions/tuple-expr.html)); tuple-struct declarations contain their declared field types ([struct items](https://doc.rust-lang.org/1.70.0/reference/items/structs.html)). Therefore: + +1. `Tail::FIELD == "tail"` is not the name of a direct field of `Pair`, and no direct field has `Tail::Item = u32`. +2. For every valid `owner`, `Tail::project` returns the address of array element `(*owner).0[1]`. That element is nested inside direct field `0`; it is not a direct declared field of `Pair`. + +Thus the associated-constant assertion and “Returns a pointer to that direct declared field” postcondition are false. This is also a failed `unsafe impl` assertion. The returned pointer is nevertheless valid for every call satisfying the stated `owner` precondition, so the mismatch alone proves `CONTRACT-BROKEN`, not `UNSOUND`. + +## Reconstructed soundness proof + +The source contains no adjacent `SAFETY` proof for either unsafe block; the following material derivation is therefore review evidence, not proof documentation present in the snapshot. + +At `increment_tail`, safe-call validity supplies a live, initialized, exclusively borrowed `Pair` through `&mut Pair`. Function arguments are coercion sites and `&mut T` coerces to `*mut T` ([Rust 1.70 coercions](https://doc.rust-lang.org/1.70.0/reference/type-coercions.html)); hence the exact documented precondition of `Tail::project` holds. + +Inside `project`, `.0` selects the sole real field and `[1]` is in bounds for `[u32; 2]`. Arrays are fixed-size, all elements are initialized, and safe indexing is bounds-checked ([array types](https://doc.rust-lang.org/1.70.0/reference/types/array.html); [index expressions](https://doc.rust-lang.org/1.70.0/reference/expressions/array-expr.html)). Rust layout guarantees properly aligned struct fields and that an array has `T`'s alignment with element `n` at `n * size_of::()` ([type layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html)). Thus the typed projections stay within the live `Pair` and produce an aligned, initialized `u32` place on every target. `addr_of_mut!` creates its raw pointer without an intermediate reference while leaving the place expression subject to the usual rules ([macro contract](https://doc.rust-lang.org/1.70.0/core/ptr/macro.addr_of_mut.html)); those rules are met. + +Back in `increment_tail`, `&mut *` is the documented raw-pointer-to-reference reborrow form ([pointer types](https://doc.rust-lang.org/1.70.0/reference/types/pointer.html)). The pointer is non-null, aligned, live, initialized, derived from the still-exclusive input borrow, and neither function performs an intervening or overlapping access. This satisfies the dangling/alignment/validity and alias obligations ([Rust 1.70 UB rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). `wrapping_add` is modular addition ([`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add)), so overflow and profile settings cannot panic or create an invalid `u32`; the write ends before return and preserves `Pair` validity. + +The same derivation was independently checked against the changed current wording for [Rust 1.97.1 `addr_of_mut!`](https://doc.rust-lang.org/1.97.1/core/ptr/macro.addr_of_mut.html), [place projection/aliasing/validity](https://doc.rust-lang.org/1.97.1/reference/behavior-considered-undefined.html), and [layout](https://doc.rust-lang.org/1.97.1/reference/type-layout.html). No target, optimization, overflow-check, or panic-strategy branch changes any premise. + +`increment_tail` has no written behavioral postcondition beyond its safe signature; its name is not used to invent one. The proved modular update above records actual behavior. Whole-crate behavior, other `Piece` implementations/consumers, compiler-binary correctness, and future Rust releases are outside the bounded proof. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r147.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r147.md new file mode 100644 index 0000000000..05efc3c8bd --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r147.md @@ -0,0 +1,96 @@ +# Focused review: `decode` + +## Claim and verdict + +Scope is exactly the supplied `lib.rs`: both mutually exclusive public safe +`decode` functions and their stated behavior. The supported set is Rust 1.70+, +all targets and pointer widths, all ordinary profiles, and both values of +`feature = "compact"`. Because these are safe APIs, callers have no safety +precondition beyond passing a value of the declared integer type. + +**Current-artifact soundness: `UNSOUND`.** With `compact` enabled in an +optimized configuration that does not enable debug assertions, the valid safe +call `decode(0xD800)` reaches `char::from_u32_unchecked(0xD800)`. This constructs +an invalid `char`, violating the unsafe constructor's validity requirement. +Rust 1.70 is itself in the support set, so this single supported configuration +refutes the universal claim without any premise about later Rust releases. + +**Documented behavior over the full support set: `UNPROVED`.** The promised +surrogate panic is established only when debug assertions execute. On the +counterexample configuration the path instead reaches undefined behavior, so +Rust semantics cannot establish the promised outcome. This is not a separate +`CONTRACT-BROKEN` finding based on a defined execution; `UNSOUND` is the primary +defect. + +## Obligation and configuration coverage + +| Configuration region | Derivation | Status | +|---|---|---| +| `compact`, debug assertions enabled | For a surrogate, the range test is true, its negation is false, and `debug_assert!` panics before the unsafe call. For a non-surrogate, `raw: u16` is at most `0xFFFF`; widening preserves its value, so it is a Unicode scalar and satisfies the unchecked constructor. | `PROVED` at Rust 1.70 | +| `compact`, debug assertions disabled | The assertion is not executed. `raw = 0xD800` widens unchanged and reaches the unchecked constructor despite being a surrogate. | `UNSOUND` at Rust 1.70 | +| not `compact` | `char::from_u32(raw)` is safe and returns `None` exactly when `raw` is not a valid `char`; otherwise it returns the represented scalar. | `PROVED` at Rust 1.70 | + +The two `cfg` predicates are mutually exclusive and exhaustive. Target, +pointer-width, optimization, overflow-check, and panic-strategy differences do +not alter the fixed-width integer argument or the `char` validity partition; +only whether the debug assertion executes affects the current proof. There is +no generated code, dependency, stateful invariant, callback, allocation, FFI, +or concurrency surface in scope. + +The existing unsafe block has no `SAFETY` comment. A complete local proof would +have to establish that `raw as u32` is never in `0xD800..=0xDFFF`; the source +cannot establish that in every supported profile. Documentation alone cannot +repair a hidden precondition on this safe function. + +## Authoritative premises and TCB + +- **AXIOM-CHAR-1 (Rust 1.70):** [`char` validity and constructors](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity) + defines scalar values as excluding surrogates and says, “No `char` may be + constructed ... that is not a Unicode scalar value.” The same page documents + [`from_u32`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32) + as returning `None` for an invalid `char` input and + [`from_u32_unchecked`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32_unchecked) + as ignoring validity and “possibly creating an invalid one.” +- **AXIOM-DBG-1 (Rust 1.70):** [`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html) + states: “An optimized build will not execute `debug_assert!` statements + unless `-C debug-assertions` is passed.” +- **AXIOM-CAST-1 (Rust 1.70):** the Reference's + [numeric-cast semantics](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast) + says widening an unsigned integer will “zero-extend,” hence `0xD800_u16 as + u32 == 0xD800_u32`. + +No compiler/backend correctness or external implementation premise is consumed +by this source-level counterexample. Claims extending the proved safe regions +or the redesign plan beyond Rust 1.70 require **COMPAT-1**: later supported Rust +releases retain these exact standard-library and language propositions. That is +an explicit compatibility assumption, not inferred from stability badges. + +## Recommended configuration-preserving redesign + +Replace only the body of the `compact` function conceptually as follows; leave +the non-`compact` function unchanged: + +```rust +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + match char::from_u32(raw as u32) { + Some(c) => c, + None => panic!("surrogate code point"), + } +} +``` + +This preserves the `u16 -> char` signature and public behavior: every +non-surrogate `u16` becomes the scalar with the same value, while every +surrogate produces `None` and invokes +[`panic!`](https://doc.rust-lang.org/1.70.0/std/macro.panic.html), which Rust 1.70 +documents as panicking the current thread. Both APIs used were already stable +by Rust 1.70. The design contains no unsafe operation and is independent of +debug assertions, optimization, target, and pointer width. It therefore removes +the failed proof obligation instead of moving it to callers. Panic payload and +unwind-versus-abort behavior were not promised and need not change the return +types. + +This is a design proof plan, not a verdict on an implemented snapshot. After +implementation, re-review the exact source for signature/documentation +preservation and both feature branches. No source edit was made. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r148.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r148.md new file mode 100644 index 0000000000..31b704675a --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r148.md @@ -0,0 +1,58 @@ +# Focused `Buffer` source review + +## Claim and verdict + +**UNSOUND.** For the exact `lib.rs` snapshot identified below, under Rust and standard library 1.80.0, on every target and in every ordinary profile, the claim that every well-typed safe use of the current public `Buffer` API is free from Rust undefined behavior is false. This entirely safe use is a counterexample: + +```rust +let mut buffer = Buffer::from_static(); +buffer.overwrite(9); +``` + +This is a source-level verdict under documented Rust abstract semantics, not a claim about whether a particular binary traps or visibly changes `BYTE`. + +## Snapshot, scope, and coverage + +- Audited source: `lib.rs`, SHA-256 `368fa01b2f3fca39e89131414f8ec2641a4ac162b846ccf77b8f8bd293930d9a`. +- Request: `REQUEST.md`, SHA-256 `3638d636a8792985abd014032566dc34f79554dc843c4c8a17d20f6f622e7b50`. +- Scope: every current producer, transition, and consumer of `Buffer`, plus both local `SAFETY` comments in `overwrite`. There are no target conditionals, dependencies, macros, generated artifacts, FFI, allocation, or panic-sensitive transitions in the supplied files. +- Configuration closure is parametric: the same source and one-byte `u8` access occur in all requested configurations. The Rust 1.80 layout table gives `size_of::() == 1` on all targets. + +The private fields establish the complete current-producer partition: + +| Site | State/obligation | Result | +|---|---|---| +| `from_writable`, lines 11–18 | Produces `shared == None`; its unsafe caller must keep `ptr` aligned and valid for a one-`u8` write throughout use and exclude conflicting access. | **PROVED relative to that documented caller contract.** Construction only stores values. | +| `from_static`, lines 20–26 | Safely produces `shared == Some(&BYTE)` and `ptr` with the same address, merely cast to `*mut u8`. | Construction itself performs no access, but it creates the state behind the unsound safe consumer. | +| `overwrite`, `None`, lines 35–39 | Current field privacy and producer coverage identify `from_writable` as the producer; its ongoing obligations entail `write`'s validity and alignment preconditions. | **PROVED** for valid uses of the unsafe constructor. | +| `overwrite`, `Some`, lines 29–34; `with_live`, lines 43–46 | Passes the shared reference to `with_live`, then writes through the same-address raw pointer during that call. | **UNSOUND.** | +| Move, ordinary drop, or forget | No pointer dereference, memory access, or custom destructor occurs. | No additional unsafe consumer. | + +No public field, trait implementation, conversion, constant, macro, or other constructor supplies another state. + +## Proof of the finding + +Rust 1.80's raw-pointer method documentation says `*mut T::write` uses the safety conditions of `ptr::write`. The latter requires that “`dst` must be valid for writes” and “`dst` must be properly aligned.” It overwrites the pointed-to location. [Rust 1.80 `*mut T::write`](https://doc.rust-lang.org/1.80.0/std/primitive.pointer.html#method.write); [Rust 1.80 `ptr::write` safety contract](https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html#safety). + +In the counterexample, `from_static` makes both fields point to `BYTE`. Pattern matching copies the `&'static u8` into `shared`; passing it to `with_live` makes that reference live for at least the entire function call under the Rust 1.80 Reference. The closure executes within that call and `self.ptr.write(value)` writes one byte at the address the reference points to. The Reference states that “bytes pointed to by a shared reference ... are immutable” and defines any overlapping write of more than zero bytes as a mutation. Thus this write is explicitly listed undefined behavior, irrespective of the value written. [Rust 1.80 undefined-behavior rules](https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html); [Rust 1.80 primitive layout](https://doc.rust-lang.org/1.80.0/reference/type-layout.html#primitive-data-layout). + +The counterexample uses neither `unsafe` nor an unenforced caller promise. It therefore refutes safe-surface soundness on every requested configuration. + +## Local proof review and resolution + +The `SAFETY` comment at lines 31–32 is false: that branch's producer is `from_static`, not `from_writable`, and its `ptr` is not valid for this write. No replacement proof can justify the current operation. + +The identical comment at lines 36–37 reaches the right conclusion but omits the material producer/privacy bridge and alignment obligation. Suitable proof text for that branch would be: + +```rust +// SAFETY: Private-field producer coverage makes `shared == None` possible +// only for a Buffer returned by `from_writable`. Its caller must keep `ptr` +// aligned and valid for writing one u8 throughout every possible use of this +// Buffer and prevent conflicting access, satisfying `ptr::write` here. +``` + +Minimum soundness resolution: prevent the safe `from_static` state from reaching this raw write (or make the storage and operation genuinely support mutation). Changing only the comment cannot resolve the defect. + +## TCB, evidence, and residual scope + +Inline TCB `K3R6-TCB-1` contains only the exact Rust 1.80 Reference and standard-library propositions cited above; no additional assumptions or dependency trust are consumed. No build, test, execution, expansion, or analysis-tool result was used. No broader safe-API behavior was documented or requested. Re-review is required if the source, Rust version, public/private boundary, producers, write operation, or supported configuration set changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r149.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r149.md new file mode 100644 index 0000000000..3aabceaeb6 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r149.md @@ -0,0 +1,50 @@ +# Supported-domain source review + +## Claim and verdict + +**PROVED** for the exact `domain-review` source snapshot: every well-typed safe call to the configuration-selected public `first(&[u8]) -> Option` is free from Rust undefined behavior throughout the conservative candidate domain `U` below, relative to TCB `R149-inline-v1`. There are no caller safety preconditions. + +Let `V = {1.79.0, 1.80.0, 1.81.0, 1.82.0}` and `T = {x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu}`. Without selecting either conflicting policy as controlling, + +`U = (!fast && rust in V && target in T) + || (fast && rust in V && target == x86_64-unknown-linux-gnu) + || (fast && rust in {1.80.0,1.81.0,1.82.0} + && target == aarch64-unknown-linux-gnu)`. + +This is `POLICY-A ∪ POLICY-B` (indeed, B's domain is a subset of A's), not a newly inferred support promise. Thus both published candidate policies are individually covered, as is their union. Which policy controls remains unresolved, but that ambiguity leaves no candidate soundness region uncovered through the requested 1.82.0 cutoff. + +## Snapshot and coverage + +Reviewed all seven supplied target files. The source is `lib.rs` SHA-256 `6f87e944cec7ee2727f7c7d32aa382de88987ea791082175d611fff5bf44012b`, package 1.0.0, edition 2021, with only the Boolean `fast` feature. There are no dependencies, build scripts, generated artifacts, macros, FFI, unsafe declarations/traits/impls, or invariant-bearing state. The only external safe surface is `first`; the only unsafe operation is `bytes.get_unchecked(0)` at `lib.rs:13` when `fast` is enabled. + +`rust-toolchain.toml` selects 1.82.0 as a default and `CI.md` records samples; neither claims to define support and neither resolves the two published policies. Profiles and the two named targets do not alter the proof: the source branches only on `fast`, and the bounds argument is target- and profile-parametric. + +## Configuration closure and proof + +The exact 1.79.0, 1.80.0, 1.81.0, and 1.82.0 References each state that a configuration option is true when set and false when unset, `not(...)` reverses that truth value, and `#[cfg]` includes its item exactly when its predicate is true: [1.79.0](https://doc.rust-lang.org/1.79.0/reference/conditional-compilation.html), [1.80.0](https://doc.rust-lang.org/1.80.0/reference/conditional-compilation.html), [1.81.0](https://doc.rust-lang.org/1.81.0/reference/conditional-compilation.html), [1.82.0](https://doc.rust-lang.org/1.82.0/reference/conditional-compilation.html). Therefore the two definitions are mutually exclusive and exhaustive for every member of `U`. + +- Without `fast`, the selected body contains only safe standard-library operations. It introduces no unsafe obligation. + +- With `fast`, an empty slice returns `None` before reaching unsafe code. On the other path, `bytes.is_empty()` returned false. Each exact version's slice documentation says `is_empty` returns true when length is zero; contraposition gives `bytes.len() != 0`, hence `bytes.len() > 0`. The corresponding References state that slice indices are zero-based: [1.79.0](https://doc.rust-lang.org/1.79.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions), [1.80.0](https://doc.rust-lang.org/1.80.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions), [1.81.0](https://doc.rust-lang.org/1.81.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions), [1.82.0](https://doc.rust-lang.org/1.82.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions). Thus index `0` is in bounds. Each exact version's `get_unchecked` contract says an out-of-bounds call is UB and that the method returns a reference to the selected element: [1.79.0](https://doc.rust-lang.org/1.79.0/std/primitive.slice.html#method.get_unchecked), [1.80.0](https://doc.rust-lang.org/1.80.0/std/primitive.slice.html#method.get_unchecked), [1.81.0](https://doc.rust-lang.org/1.81.0/std/primitive.slice.html#method.get_unchecked), [1.82.0](https://doc.rust-lang.org/1.82.0/std/primitive.slice.html#method.get_unchecked). The sole safety precondition is therefore satisfied. Dereferencing the returned shared `&u8` merely copies that valid element; no call, mutation, unwind, or interference occurs between production and consumption. + +No later-version compatibility premise was used: every authoritative clause was checked separately for all four releases. No tests, compilation, or other tool result is proof evidence. + +## TCB `R149-inline-v1` + +- `AX-CFG`: the exact versioned Reference clauses cited above govern configuration evaluation and zero-based slice indexing. +- `AX-SLICE`: the exact versioned standard-library contracts cited above govern `is_empty`, `get_unchecked`, and the returned reference. + +These are source-level Rust axioms. No compiler backend, binary, platform implementation, dependency, or external specification is trusted or claimed. + +## Finding: missing local proof documentation + +Implementation soundness is proved, but the unsafe block has no adjacent `SAFETY` comment; the material bounds derivation had to be reconstructed. Proposed wording (no edit requested): + +```rust +// SAFETY: This branch is reached only when `bytes.is_empty()` is false, +// so `bytes.len() > 0`. Slice indices are zero-based, hence index 0 is +// in bounds and satisfies `get_unchecked`'s safety contract. +Some(unsafe { *bytes.get_unchecked(0) }) +``` + +This is proof-documentation debt, not an implementation defect. The target documents no broader behavioral postcondition, so none was added by inference. Re-review is required if the source, either support policy, the supported cutoff/targets/features, or any consumed versioned Rust contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r150.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r150.md new file mode 100644 index 0000000000..77d323f693 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/reports/r150.md @@ -0,0 +1,71 @@ +# Focused unsafe-Rust source review + +## Claim and verdicts + +**Snapshot.** This review covers exactly `targets/m2q8/lib.rs` (SHA-256 +`42388bb1a03741a7725c9836cc134c205af3061efef7b8ee00ce38d2dc51aeeb`) under +Rust and `core` 1.80.0, on every target and ordinary profile requested by +`REQUEST.md`. The only language-reachable API surface is the safe free function +`pub fn classify(u8) -> u8`; its two documentation clauses are both mandatory. + +**Soundness — UNSOUND.** A well-typed safe call `classify(0)` reaches +`core::hint::unreachable_unchecked()`, which is undefined behavior. This +counterexample applies on every in-scope target and profile; the API has no +caller-side safety condition that could exclude it. + +**Documented behavior — CONTRACT-BROKEN.** The normal-return guarantee is +false without requiring undefined behavior: `classify(1)` returns `2`, although +the documented result is the input, `1`. The separate promise to panic for +input `0` is also not established: that path reaches undefined behavior rather +than a defined panic. Thus the combined soundness-and-behavior claim is not +proved. + +## Authoritative premises and derivation + +TCB `R150-inline-v1` contains only these Rust 1.80.0 axioms; there are no +dependencies or additional assumptions: + +- **AXIOM-UU:** [`unreachable_unchecked` safety](https://doc.rust-lang.org/1.80.0/core/hint/fn.unreachable_unchecked.html#safety): + “Reaching this function is Undefined Behavior.” +- **AXIOM-MATCH:** [match expressions](https://doc.rust-lang.org/1.80.0/reference/expressions/match-expr.html): + “The first arm with a matching pattern is chosen as the branch target of the + `match`”. +- **AXIOM-PATTERN:** [literal patterns](https://doc.rust-lang.org/1.80.0/reference/patterns.html#literal-patterns) + “match exactly the same value as what is created by the literal”; the + [wildcard pattern](https://doc.rust-lang.org/1.80.0/reference/patterns.html#wildcard-pattern) + “matches any value.” + +The input domain partitions exhaustively into `0`, `1`, and values unequal to +both: + +1. For `0`, the first literal pattern matches, so control enters line 8 and + reaches `unreachable_unchecked`. AXIOM-UU yields UB. Its required local + proposition—this call site is unreachable—is therefore not merely + undocumented but false. +2. For `1`, the `0` pattern does not match and the `1` pattern does. Line 9 + evaluates to `2`, so the function normally returns `2 != input`. This is a + defined postcondition counterexample. +3. For every other `u8`, neither literal matches, `_` matches, and line 10 + returns `input`; the normal-return clause holds in this region. + +## Coverage and findings + +There are no other fields, constructors, methods, traits, impls, statics, +macros, hidden items, callbacks, generated artifacts, invariants, or unsafe +sites in the supplied snapshot. The sole unsafe call has no adjacent `SAFETY` +proof; none can prove its current call-site obligation because the dominating +`0` arm establishes reachability. + +There is no `cfg`, target-specific code, feature selection, dependency, FFI, +assembly, allocation, concurrency, arithmetic, assertion, or profile-sensitive +operation. The three-case source derivation is therefore parametric over every +requested target, optimization level, overflow-check setting, and panic +strategy. No build, execution, test, expansion, or tool-derived semantic +evidence was used. + +Two implementation defects require correction before either theorem can be +re-audited: the `0` case must have defined panicking behavior without reaching +`unreachable_unchecked`, and the `1` case must return its input. This is a +review-only result; no source change or redesign was performed. Any source or +documentation change, Rust-version change, or expansion of the supported +configuration set invalidates this snapshot result. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/results.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/results.md new file mode 100644 index 0000000000..146eb98558 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/results.md @@ -0,0 +1,207 @@ +# V2 Forward Evaluation: Unblinded Aggregate + +Each cell is a pass count out of five independent reports. Hard errors are +reported per mode and condition; heterogeneous modes are not pooled. + +## Per-mode condition results + +| Mode | Condition | Atom pass counts | Hard errors | +|---|---|---|---:| +| U | V2 | U1 5/5; U2 5/5; U3 5/5 | 0 | +| U | V1 | U1 5/5; U2 4/5; U3 5/5 | 1 | +| U | Core | U1 5/5; U2 5/5; U3 5/5 | 0 | +| D | V2 | D1 1/5; D2 1/5; D3 1/5 | 4 | +| D | V1 | D1 2/5; D2 0/5; D3 0/5 | 4 | +| D | Core | D1 0/5; D2 0/5; D3 0/5 | 5 | +| V | V2 | V1 5/5; V2 5/5; V3 5/5; V4 5/5 | 0 | +| V | V1 | V1 5/5; V2 5/5; V3 5/5; V4 5/5 | 0 | +| V | Core | V1 5/5; V2 5/5; V3 5/5; V4 5/5 | 0 | +| I | V2 | I1 5/5; I2 5/5; I3 5/5 | 0 | +| I | V1 | I1 5/5; I2 5/5; I3 5/5 | 0 | +| I | Core | I1 5/5; I2 5/5; I3 5/5 | 0 | +| T | V2 | T1 5/5; T2 5/5; T3 5/5 | 0 | +| T | V1 | T1 5/5; T2 4/5; T3 5/5 | 1 | +| T | Core | T1 5/5; T2 5/5; T3 0/5 | 5 | +| C | V2 | C1 5/5; C2 5/5; C3 5/5 | 0 | +| C | V1 | C1 5/5; C2 5/5; C3 5/5 | 0 | +| C | Core | C1 4/5; C2 5/5; C3 0/5 | 5 | +| H | V2 | H1 4/5; H2 5/5; H3 5/5 | 1 | +| H | V1 | H1 3/5; H2 5/5; H3 5/5 | 2 | +| H | Core | H1 5/5; H2 5/5; H3 5/5 | 0 | +| A | V2 | A1 5/5; A2 3/5; A3 5/5 | 0 | +| A | V1 | A1 5/5; A2 5/5; A3 5/5 | 0 | +| A | Core | A1 5/5; A2 3/5; A3 5/5 | 0 | +| P | V2 | P1 5/5; P2 5/5; P3 5/5 | 0 | +| P | V1 | P1 4/5; P2 5/5; P3 5/5 | 0 | +| P | Core | P1 5/5; P2 5/5; P3 5/5 | 0 | +| N | V2 | N1 4/5; N2 5/5; N3 5/5 | 0 | +| N | V1 | N1 5/5; N2 5/5; N3 5/5 | 0 | +| N | Core | N1 4/5; N2 5/5; N3 5/5 | 0 | + +## Condition differences + +Deltas use atom order shown in the final column. + +| Mode | Atoms | V2−V1 | V1−Core | +|---|---|---|---| +| U | U1, U2, U3 | 0, +1, 0 | 0, -1, 0 | +| D | D1, D2, D3 | -1, +1, +1 | +2, 0, 0 | +| V | V1, V2, V3, V4 | 0, 0, 0, 0 | 0, 0, 0, 0 | +| I | I1, I2, I3 | 0, 0, 0 | 0, 0, 0 | +| T | T1, T2, T3 | 0, +1, 0 | 0, -1, +5 | +| C | C1, C2, C3 | 0, 0, 0 | +1, 0, +5 | +| H | H1, H2, H3 | +1, 0, 0 | -2, 0, 0 | +| A | A1, A2, A3 | 0, -2, 0 | 0, +2, 0 | +| P | P1, P2, P3 | +1, 0, 0 | -1, 0, 0 | +| N | N1, N2, N3 | -1, 0, 0 | +1, 0, 0 | + +## Preregistered V2 gates + +**Overall gate result: FAIL.** + +- V2 atom failures: 16 +- V2 hard errors: 5 +- V2 proposal-laundering reports: 0 + +| Gate | Result | +|---|---| +| Zero V2 hard errors | FAIL | +| Every atom passes in all five V2 reports | FAIL | +| No V2 proposal laundering | PASS | +| U2, T2, and C1 apply the UB/postcondition rule 5/5 | PASS | +| V1–V4 and H1 close exact-version reasoning 5/5 | FAIL | +| D1–D3 recover and audit the ambiguous union 5/5 | FAIL | +| I1–I3 reject producer-premise promotion 5/5 | PASS | +| Every A, P, and N control atom passes 5/5 | FAIL | + +### V2 failed atom cells + +| Mode | Atom | Run | Replicate | Blind label | +|---|---|---|---:|---| +| D | D1 | r123 | 3 | D | +| D | D2 | r123 | 3 | D | +| D | D3 | r123 | 3 | D | +| D | D1 | r149 | 4 | E | +| D | D2 | r149 | 4 | E | +| D | D3 | r149 | 4 | E | +| D | D1 | r058 | 2 | I | +| D | D2 | r058 | 2 | I | +| D | D3 | r058 | 2 | I | +| D | D1 | r026 | 1 | O | +| D | D2 | r026 | 1 | O | +| D | D3 | r026 | 1 | O | +| H | H1 | r021 | 5 | M | +| A | A2 | r019 | 4 | G | +| A | A2 | r126 | 3 | O | +| N | N1 | r067 | 5 | H | + +### V2 hard errors + +| Mode | Run | Blind label | Decision | +|---|---|---|---| +| D | r123 | D | Yes — contracts 1.80.1 and asserts closure | +| D | r149 | E | Yes — contracts 1.80.1 and asserts closure | +| D | r058 | I | Yes — contracts 1.80.1 and asserts closure | +| D | r026 | O | Yes — contracts 1.80.1 and asserts closure | +| H | r021 | M | Incorrect exact-Rust-version result | + +## All non-passing atom cells + +| Mode | Condition | Atom | Run | Replicate | Blind label | +|---|---|---|---|---:|---| +| U | V1 | U2 | r029 | 4 | D | +| D | V2 | D1 | r026 | 1 | O | +| D | V2 | D2 | r026 | 1 | O | +| D | V2 | D3 | r026 | 1 | O | +| D | V2 | D1 | r058 | 2 | I | +| D | V2 | D2 | r058 | 2 | I | +| D | V2 | D3 | r058 | 2 | I | +| D | V2 | D1 | r123 | 3 | D | +| D | V2 | D2 | r123 | 3 | D | +| D | V2 | D3 | r123 | 3 | D | +| D | V2 | D1 | r149 | 4 | E | +| D | V2 | D2 | r149 | 4 | E | +| D | V2 | D3 | r149 | 4 | E | +| D | V1 | D1 | r141 | 1 | H | +| D | V1 | D2 | r141 | 1 | H | +| D | V1 | D3 | r141 | 1 | H | +| D | V1 | D2 | r057 | 2 | M | +| D | V1 | D3 | r057 | 2 | M | +| D | V1 | D1 | r074 | 3 | J | +| D | V1 | D2 | r074 | 3 | J | +| D | V1 | D3 | r074 | 3 | J | +| D | V1 | D1 | r136 | 4 | K | +| D | V1 | D2 | r136 | 4 | K | +| D | V1 | D3 | r136 | 4 | K | +| D | V1 | D2 | r004 | 5 | F | +| D | V1 | D3 | r004 | 5 | F | +| D | Core | D1 | r035 | 1 | L | +| D | Core | D2 | r035 | 1 | L | +| D | Core | D3 | r035 | 1 | L | +| D | Core | D1 | r073 | 2 | G | +| D | Core | D2 | r073 | 2 | G | +| D | Core | D3 | r073 | 2 | G | +| D | Core | D1 | r014 | 3 | C | +| D | Core | D2 | r014 | 3 | C | +| D | Core | D3 | r014 | 3 | C | +| D | Core | D1 | r102 | 4 | B | +| D | Core | D2 | r102 | 4 | B | +| D | Core | D3 | r102 | 4 | B | +| D | Core | D1 | r093 | 5 | N | +| D | Core | D2 | r093 | 5 | N | +| D | Core | D3 | r093 | 5 | N | +| T | V1 | T2 | r031 | 4 | J | +| T | Core | T3 | r115 | 1 | M | +| T | Core | T3 | r017 | 2 | F | +| T | Core | T3 | r128 | 3 | B | +| T | Core | T3 | r137 | 4 | H | +| T | Core | T3 | r142 | 5 | G | +| C | Core | C3 | r080 | 1 | H | +| C | Core | C3 | r138 | 2 | O | +| C | Core | C3 | r103 | 3 | L | +| C | Core | C1 | r059 | 4 | J | +| C | Core | C3 | r059 | 4 | J | +| C | Core | C3 | r086 | 5 | F | +| H | V2 | H1 | r021 | 5 | M | +| H | V1 | H1 | r039 | 1 | H | +| H | V1 | H1 | r055 | 2 | O | +| A | V2 | A2 | r126 | 3 | O | +| A | V2 | A2 | r019 | 4 | G | +| A | Core | A2 | r046 | 2 | I | +| A | Core | A2 | r118 | 4 | J | +| P | V1 | P1 | r003 | 3 | G | +| N | V2 | N1 | r067 | 5 | H | +| N | Core | N1 | r083 | 1 | D | + +## All hard errors + +| Mode | Condition | Run | Blind label | Decision | +|---|---|---|---|---| +| U | V1 | r029 | D | Yes — uses the input-zero UB execution as a behavioral refutation | +| D | V2 | r123 | D | Yes — contracts 1.80.1 and asserts closure | +| D | V2 | r149 | E | Yes — contracts 1.80.1 and asserts closure | +| D | V2 | r058 | I | Yes — contracts 1.80.1 and asserts closure | +| D | V2 | r026 | O | Yes — contracts 1.80.1 and asserts closure | +| D | V1 | r141 | H | Yes — contracts 1.80.1 and asserts closure | +| D | V1 | r074 | J | Yes — contracts/does not cover 1.80.1, yet asserts closure | +| D | V1 | r136 | K | Yes — contracts/does not cover 1.80.1, yet asserts closure | +| D | V1 | r057 | M | Yes — asserts closure without 1.80.1 evidence | +| D | Core | r102 | B | Yes — contracts 1.80.1 and asserts closure | +| D | Core | r014 | C | Yes — contracts 1.80.1 and asserts closure | +| D | Core | r073 | G | Yes — contracts 1.80.1 and asserts closure | +| D | Core | r035 | L | Yes — contracts 1.80.1 and asserts closure | +| D | Core | r093 | N | Yes — contracts 1.80.1 and asserts closure | +| T | V1 | r031 | J | UB execution used as a behavioral counterexample | +| T | Core | r128 | B | Proposal laundering | +| T | Core | r017 | F | Proposal laundering | +| T | Core | r142 | G | Proposal laundering | +| T | Core | r137 | H | Proposal laundering | +| T | Core | r115 | M | Proposal laundering | +| C | Core | r086 | F | Proposal laundering | +| C | Core | r080 | H | Proposal laundering | +| C | Core | r059 | J | Proposal laundering | +| C | Core | r103 | L | Proposal laundering | +| C | Core | r138 | O | Proposal laundering | +| H | V2 | r021 | M | Incorrect exact-Rust-version result | +| H | V1 | r039 | H | Incorrect exact-Rust-version result | +| H | V1 | r055 | O | Incorrect exact-Rust-version result | diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-ledgers/README.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-ledgers/README.md new file mode 100644 index 0000000000..31f6ba3b3d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-ledgers/README.md @@ -0,0 +1,37 @@ +# Blind-scoring orchestration reconciliation + +The three shard ledgers in this directory are byte-for-byte copies of the +temporary orchestration ledgers. They record each orchestrator's local view at +the time it stopped; statuses such as `owned_by_other_orchestrator` and the +Shard 3 count of 19 present scores are therefore intermediate ownership +snapshots, not final missing-data findings. + +## Shard contributions + +| Ledger | Valid scores produced by that orchestrator | Ledger SHA-256 | +|---|---|---| +| [`shard-1.tsv`](shard-1.tsv) | P-s2, H-s1 | `bc6840381dcdc6a7228f4142d7e31fc41724f24061d4914f1db8b233426f6c0e` | +| [`shard-2.md`](shard-2.md) | C-s1, V-s1, U-s2, D-s1, U-s1, P-s1 | `760225868e5a36380c634802055065da4541de136718aad33d2dbe18f992c4f0` | +| [`shard-3.jsonl`](shard-3.jsonl) | C-s2, D-s2, H-s2, I-s2, A-s1, T-s1 | `9b3d78629dd5e76180f8904137b386eca23a5bae79d29e428efe237e715ad2ca` | + +All fourteen score digests and word counts recorded by these ledgers match the +corresponding files in [`../blind-scores/raw/`](../blind-scores/raw/). The +remaining six schedule tasks—N-s1, I-s1, A-s2, V-s2, N-s2, and T-s2—were +atomically owned elsewhere and are also present in that final archive. + +## T-s2 chronology + +Shard 3 completed while T-s2 was owned by another orchestrator and before its +output existed, producing its accurate intermediate count of 19. The first +T-s2 attempt later consulted a Rust release-blog page outside the frozen +allowed-source set. It was excluded and preserved as +[`../blind-scores/invalid/T-s2-attempt-1.md`](../blind-scores/invalid/T-s2-attempt-1.md). +A fresh retry produced the valid +[`../blind-scores/raw/T-s2.md`](../blind-scores/raw/T-s2.md). The invalid and +valid score SHA-256 values are respectively +`47fdd0495848b4d4a2c5447673819fee629eb2482e39328a014d5a06edd0adab` +and `ed792bc0b5c714992401b342396dd24b89fade7cb112684fd9c991f40284937f`. + +After that retry, all 20 runtime valid-score files were compared byte-for-byte +with the 20 archived raw scores and matched. The shard reports therefore add +operational provenance but change no score, adjudication, aggregate, or gate. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-ledgers/shard-1.tsv b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-ledgers/shard-1.tsv new file mode 100644 index 0000000000..adffd0c7f4 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-ledgers/shard-1.tsv @@ -0,0 +1,25 @@ +task scorer_identity attempt status score_sha256 word_count reminders interruptions deviations +N-s1 none 0 claim_owned_elsewhere - - 0 none claim mkdir returned File exists; no output inspected +I-s1 none 0 claim_owned_elsewhere - - 0 none claim mkdir returned File exists; no output inspected +A-s2 none 0 claim_owned_elsewhere - - 0 none claim mkdir returned File exists; no output inspected +C-s1 none 0 claim_owned_elsewhere - - 0 none claim mkdir returned File exists; no output inspected +V-s1 none 0 claim_owned_elsewhere - - 0 none claim mkdir returned File exists; no output inspected +U-s2 none 0 claim_owned_elsewhere - - 0 none claim mkdir returned File exists; no output inspected +C-s2 none 0 claim_owned_elsewhere - - 0 none claim mkdir returned File exists; no output inspected +D-s2 none 0 claim_owned_elsewhere - - 0 none claim mkdir returned File exists; no output inspected +H-s2 none 0 claim_owned_elsewhere - - 0 none claim mkdir returned File exists; no output inspected +V-s2 none 0 claim_owned_elsewhere - - 0 none claim mkdir returned File exists; no output inspected +P-s2 /root/score_p_s2_attempt1 1 claimed_running - - 0 none atomic claim created; fresh scorer launched +H-s1 none 0 claimed_dispatch_deferred - - 0 none atomic claim succeeded, but fresh-agent spawn was rejected by thread limit; output untouched +H-s1 none 0 dispatch_deferred - - 0 none second fresh-agent spawn rejected by thread limit; claim retained and output untouched +P-s2 /root/score_p_s2_attempt1 1 score_preserved 29ed1e2d6749b91f3b1d299ad0d774399631c9a7cd91c720256517541a4aab9b 2238 0 none none +H-s1 /root/score_h_s1_attempt1 1 claimed_running - - 0 none launched after two recorded thread-limit deferrals; same frozen prompt +H-s1 /root/score_h_s1_attempt1 1 score_preserved 7a6202b94ab93a00faf0d2a8e884d8e7601c83e8eefd9c552201c1af58587b42 2100 1 none reminder sent at completion boundary; see prior launch deferrals +N-s2 none 0 skipped_existing_score - - 0 none score existence only; another scorer's output not inspected +T-s2 none 0 claim_owned_elsewhere - - 0 none claim mkdir returned File exists; no output inspected +D-s1 none 0 skipped_existing_score - - 0 none score existence only; another scorer's output not inspected +U-s1 none 0 skipped_existing_score - - 0 none score existence only; another scorer's output not inspected +P-s1 none 0 skipped_existing_score - - 0 none score existence only; another scorer's output not inspected +I-s2 none 0 skipped_existing_score - - 0 none score existence only; another scorer's output not inspected +A-s1 none 0 skipped_existing_score - - 0 none score existence only; another scorer's output not inspected +T-s1 none 0 skipped_existing_score - - 0 none score existence only; another scorer's output not inspected diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-ledgers/shard-2.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-ledgers/shard-2.md new file mode 100644 index 0000000000..028187ff40 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-ledgers/shard-2.md @@ -0,0 +1,34 @@ +# Blind-scoring operational ledger + +- Existing collection shard claim: `/tmp/unsafe-rust-v2-eval.9epWDK/coord/shard-2.claim` +- Shared scoring runtime: `/tmp/unsafe-rust-v2-score.IpMWrc` +- Frozen task order: `N-s1 I-s1 A-s2 C-s1 V-s1 U-s2 C-s2 D-s2 H-s2 V-s2 P-s2 H-s1 N-s2 T-s2 D-s1 U-s1 P-s1 I-s2 A-s1 T-s1` + +## Attempts + +- `N-s1` | claim scan: `atomic mkdir returned File exists` | ownership: `another orchestrator` | scorer launched: `no` +- `I-s1` | claim scan: `atomic mkdir returned File exists` | ownership: `another orchestrator` | scorer launched: `no` +- `A-s2` | claim scan: `atomic mkdir returned File exists` | ownership: `another orchestrator` | scorer launched: `no` +- `C-s1` | claim: `successful` | scorer: `/root/score_c_s1_attempt1` | attempt: `1` | event: `launched` | reminders: `0` | interruptions: `none` | deviations: `none` +- `V-s1` | claim: `successful` | scorer: `/root/score_v_s1_attempt1` | attempt: `1` | event: `launched` | reminders: `0` | interruptions: `none` | deviations: `none` +- `U-s2` | claim: `successful` | scorer: `/root/score_u_s2_attempt1` | attempt: `1` | event: `launched` | reminders: `0` | interruptions: `none` | deviations: `none` +- `V-s1` | scorer: `/root/score_v_s1_attempt1` | attempt: `1` | status: `score preserved` | SHA-256: `0c4b96b3e7ed95063cc0eb5b506e888d348853421c867e87fdad1294f8b111d9` | words: `1948` | reminders: `0` | interruptions: `none` | deviations: `none` +- `U-s2` | scorer: `/root/score_u_s2_attempt1` | attempt: `1` | status: `score preserved` | SHA-256: `fbed9920f0f731611a3f008f57e69899879fde1c806c360ec94c381054f364ef` | words: `1778` | reminders: `0` | interruptions: `none` | deviations: `none` +- `C-s1` | scorer: `/root/score_c_s1_attempt1` | attempt: `1` | status: `score preserved` | SHA-256: `882d852cf151d31f26128171cd4ccda19d009809efb52820b443b0c01718052a` | words: `2306` | reminders: `0` | interruptions: `none` | deviations: `none` +- `C-s2` | claim scan: `skipped; score.md already existed` | scorer launched: `no` +- `D-s2` | claim scan: `atomic mkdir returned File exists` | ownership: `another orchestrator` | scorer launched: `no` +- `H-s2` | claim scan: `atomic mkdir returned File exists` | ownership: `another orchestrator` | scorer launched: `no` +- `V-s2` | claim scan: `atomic mkdir returned File exists` | ownership: `another orchestrator` | scorer launched: `no` +- `P-s2` | claim scan: `atomic mkdir returned File exists` | ownership: `another orchestrator` | scorer launched: `no` +- `H-s1` | claim scan: `atomic mkdir returned File exists` | ownership: `another orchestrator` | scorer launched: `no` +- `N-s2` | claim scan: `atomic mkdir returned File exists` | ownership: `another orchestrator` | scorer launched: `no` +- `T-s2` | claim scan: `atomic mkdir returned File exists` | ownership: `another orchestrator` | scorer launched: `no` +- `D-s1` | claim: `successful` | scorer: `/root/score_d_s1_attempt1` | attempt: `1` | event: `launched` | reminders: `0` | interruptions: `none` | deviations: `none` +- `U-s1` | claim: `successful` | scorer: `/root/score_u_s1_attempt1` | attempt: `1` | event: `launched` | reminders: `0` | interruptions: `none` | deviations: `none` +- `P-s1` | claim: `successful` | scorer: `/root/score_p_s1_attempt1` | attempt: `1` | event: `launched` | reminders: `0` | interruptions: `none` | deviations: `none` +- `D-s1` | scorer: `/root/score_d_s1_attempt1` | attempt: `1` | status: `score preserved` | SHA-256: `d9acf7549df118095a35b2d442f28d4045b2c5a27b515be73d34a46d52839d19` | words: `2094` | reminders: `0` | interruptions: `none` | deviations: `none` +- `I-s2` | claim scan: `skipped; score.md already existed` | scorer launched: `no` +- `A-s1` | claim scan: `atomic mkdir returned File exists` | ownership: `another orchestrator` | scorer launched: `no` +- `T-s1` | claim scan: `atomic mkdir returned File exists` | ownership: `another orchestrator` | scorer launched: `no` +- `U-s1` | scorer: `/root/score_u_s1_attempt1` | attempt: `1` | status: `score preserved` | SHA-256: `ba7bd842e511cc777d5c0fdd4d0b102161711d57dcf5884e094de2590b90bf68` | words: `1902` | reminders: `0` | interruptions: `none` | deviations: `none` +- `P-s1` | scorer: `/root/score_p_s1_attempt1` | attempt: `1` | status: `score preserved` | SHA-256: `e6a4f02e64b6a0cc5e4827ec6bcc3165e0db7f120c2861263780b7d6226ceb43` | words: `2386` | reminders: `0` | interruptions: `none` | deviations: `none` diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-ledgers/shard-3.jsonl b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-ledgers/shard-3.jsonl new file mode 100644 index 0000000000..cc7f679d44 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-ledgers/shard-3.jsonl @@ -0,0 +1,35 @@ +{"event":"blind_scoring_started","runtime":"/tmp/unsafe-rust-v2-score.IpMWrc","task_order":"N-s1 I-s1 A-s2 C-s1 V-s1 U-s2 C-s2 D-s2 H-s2 V-s2 P-s2 H-s1 N-s2 T-s2 D-s1 U-s1 P-s1 I-s2 A-s1 T-s1","interruptions":0,"deviations":"none"} +{"event":"claim_attempt","task":"N-s1","score_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"claim_attempt","task":"I-s1","score_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"claim_attempt","task":"A-s2","score_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"claim_attempt","task":"C-s1","score_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"claim_attempt","task":"V-s1","score_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"claim_attempt","task":"U-s2","score_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"claim_attempt","task":"C-s2","score_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","task":"C-s2","scorer_identity":"/root/score_c_s2_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"claim_attempt","task":"D-s2","score_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","task":"D-s2","scorer_identity":"/root/score_d_s2_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"claim_attempt","task":"H-s2","score_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","task":"H-s2","scorer_identity":"/root/score_h_s2_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"score_preserved","task":"C-s2","scorer_identity":"/root/score_c_s2_attempt1","attempt_count":1,"score_sha256":"fea252b503175f62457625a303db9449cd0792bf102a603e74ed34138e38de1e","word_count":1978,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"claim_attempt","task":"V-s2","score_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"claim_attempt","task":"P-s2","score_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"claim_attempt","task":"H-s1","score_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"claim_attempt","task":"N-s2","score_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"claim_attempt","task":"T-s2","score_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"claim_attempt","task":"D-s1","score_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"claim_attempt","task":"U-s1","score_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"claim_attempt","task":"P-s1","score_preexisted":false,"claim_result":"owned_by_other_orchestrator","deviations":"none"} +{"event":"claim_attempt","task":"I-s2","score_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","task":"I-s2","scorer_identity":"/root/score_i_s2_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"score_preserved","task":"H-s2","scorer_identity":"/root/score_h_s2_attempt1","attempt_count":1,"score_sha256":"92bc78a1e3af45ad683f39e95496c4d200a062db827e134fb288a91598958ac8","word_count":2140,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"claim_attempt","task":"A-s1","score_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","task":"A-s1","scorer_identity":"/root/score_a_s1_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"completion_reminder","task":"D-s2","scorer_identity":"/root/score_d_s2_attempt1","attempt":1,"text":"Complete now within the frozen word limit using only material already inspected; do not widen scope."} +{"event":"score_preserved","task":"D-s2","scorer_identity":"/root/score_d_s2_attempt1","attempt_count":1,"score_sha256":"83a9477f1b4a33ec47e05e595fbb8d4f9d10be6d005a0457ed6a10f1c634a47f","word_count":1769,"reminders":1,"interruptions":0,"deviations":"none"} +{"event":"claim_attempt","task":"T-s1","score_preexisted":false,"claim_result":"claimed_by_this_orchestrator","deviations":"none"} +{"event":"attempt_started","task":"T-s1","scorer_identity":"/root/score_t_s1_attempt1","attempt":1,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"score_preserved","task":"I-s2","scorer_identity":"/root/score_i_s2_attempt1","attempt_count":1,"score_sha256":"21981b43c59dba36a651d43ed22c950596fb95677a26ee843a58fe6611f5855f","word_count":1895,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"score_preserved","task":"A-s1","scorer_identity":"/root/score_a_s1_attempt1","attempt_count":1,"score_sha256":"b753b88d8c03da2406b8e8de21e88a2b2d1f8fc1f61424fb24f1039e3947edd3","word_count":1988,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"score_preserved","task":"T-s1","scorer_identity":"/root/score_t_s1_attempt1","attempt_count":1,"score_sha256":"16ab5655130e681d3108d4ce151fb7fcf1cede8e4fd1d60dde7a6fd589172603","word_count":1905,"reminders":0,"interruptions":0,"deviations":"none"} +{"event":"blind_scoring_orchestration_complete","schedule_task_count":20,"scores_present_at_final_audit":19,"claimed_by_other_orchestrator_without_score_at_final_audit":["T-s2"],"missing_unclaimed_tasks":0,"tasks_completed_by_this_orchestrator":["C-s2","D-s2","H-s2","I-s2","A-s1","T-s1"],"local_attempts":6,"local_scores_preserved":6,"local_reminders":1,"local_interruptions":0,"local_retries":0,"deviations":"none","ledger_json_lines_valid":true} diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-prompt.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-prompt.md new file mode 100644 index 0000000000..8e0423aade --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-prompt.md @@ -0,0 +1,25 @@ +# Frozen Blind-Scorer Prompt + +Each scorer receives byte-identical text except resolved `[PACKET]` and unique +empty `[OUTPUT]` paths: + +```text +Act as a fresh blind evaluator. Read every file under [PACKET], including the +source, SCORER.md, RUBRIC.md, and all fifteen anonymous reports A–O. Follow the +scoring instructions exactly and score every report independently. + +Inspect only [PACKET] and exact versioned official Rust Reference or +standard-library documentation needed to resolve a material claim. Do not +inspect sibling directories, the enclosing repository, manifests, skill +packages, condition maps, prior scores, or another scorer's output. Do not +identify, cluster, or speculate about generating conditions. Do not modify the +packet and do not spawn helper agents. + +Write the complete score to [OUTPUT]/score.md using apply_patch, then return +the same score in your final response. Include the required A–O atom table, +hard-error decisions, compact evidence for every judgment, and any genuine +rubric ambiguity. Keep the result no longer than 6,000 words. +``` + +No substantive steering is permitted. One neutral reminder to finish from +material already inspected is permitted and must be recorded. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/A.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/A.md new file mode 100644 index 0000000000..5791bdbc0d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/A.md @@ -0,0 +1,11 @@ +# Mode A Rubric + +- **A1:** The literal `Piece` contract is false: `"tail"` is not a direct + declared field, and the projected array element is not a direct field. +- **A2:** Separate that contract failure from soundness; the shown projection + reaches a valid `u32`, so no UB counterexample is established. +- **A3:** Do not replace the literal contract with inferred intent or redesign + an acceptance-only immutable snapshot. + +Hard error: certifying an unimplemented proposal. Other departures from A1–A3 +are atom failures unless another frozen rule expressly classifies them as hard. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/C.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/C.md new file mode 100644 index 0000000000..95fa63b42e --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/C.md @@ -0,0 +1,13 @@ +# Mode C Rubric + +- **C1:** In compact builds with debug assertions disabled, a surrogate input + reaches UB. That region is `UNSOUND`; its panic promise is only `UNPROVED` + from that execution. +- **C2:** The noncompact branch is separately sound and behaviorally correct. +- **C3:** A checked compact conversion can preserve both signatures, behavior, + and the complete support set, but the unimplemented candidate is not + `PROVED`. + +Hard error: proposal laundering; using a UB-containing execution as a defined +behavioral counterexample; or giving an incorrect exact-Rust-version result. + diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/D.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/D.md new file mode 100644 index 0000000000..ceeb0dbda5 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/D.md @@ -0,0 +1,19 @@ +# Mode D Rubric + +- **D1:** State both current published support predicates and the disputed + regions without inventing precedence. Their differences are `fast` x86_64 + on Rust 1.79 and `fast` aarch64 on Rust 1.80–1.81. +- **D2:** Audit the conservative union while distinguishing that review domain + from the unresolved actual project promise. The developer toolchain pin and + sampled CI matrix neither resolve the conflict nor exhaust support. +- **D3:** Prove both source branches over the entire union, parametrically or + by exhaustive partition. The union is non-`fast` on both targets for + 1.79–1.82; `fast` x86_64 for 1.79–1.82; and `fast` aarch64 for 1.80–1.82. + The source is sound over this union even though the controlling project + predicate remains unresolved. + +Hard error: silently choosing a policy; calling the union the recovered +project promise; contracting any union region; treating CI or the developer +pin as exhaustive; asserting closure without covering the union; or promoting +project/Cargo policy to a Rust axiom. + diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/H.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/H.md new file mode 100644 index 0000000000..df7c5363c6 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/H.md @@ -0,0 +1,13 @@ +# Mode H Rubric + +- **H1:** Rust 1.70's `add(0)` contract plus a valid dangling empty slice makes + the current Rust-1.70+ API `UNSOUND`. +- **H2:** The safe wrapping-iterator candidate preserves source behavior, but + benchmark compliance remains `UNPROVED` without evidence. +- **H3:** Keep current-source verdict, candidate proof plan, and performance + evidence separate. The unsound current loop is not an acceptable fallback + if the candidate misses its benchmark; the candidate is not yet `PROVED`. + +Hard error: proposal laundering; using a UB-containing execution as a defined +behavioral counterexample; or giving an incorrect exact-Rust-version result. + diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/I.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/I.md new file mode 100644 index 0000000000..c0527714ec --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/I.md @@ -0,0 +1,17 @@ +# Mode I Rubric + +- **I1:** Enumerate both producers. `from_writable`'s precondition concerns + values produced by valid invocations of that constructor; it is not a + universal `Buffer` invariant. +- **I2:** `overwrite` needs `ptr` valid for writes. `from_static` does not + establish that fact, so both copies of the local comment fail despite field + privacy. +- **I3:** The fully safe `Buffer::from_static().overwrite(0)` execution writes + while the shared reference to the same immutable byte is live for the + `with_live` call. The Reference immutable-byte rule and `ptr::write` + contract establish UB; the safe API is `UNSOUND`. + +Hard error: using `from_writable` to close all values; treating privacy as +proof that it is the only producer; missing the safe witness; or stopping at +vague proof debt despite the direct authoritative UB derivation. + diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/N.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/N.md new file mode 100644 index 0000000000..e1e028b695 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/N.md @@ -0,0 +1,11 @@ +# Mode N Rubric + +- **N1:** Freshly derive both safe alias witnesses and report the current + snapshot `UNSOUND`. +- **N2:** Identify receiver-unbound `'a` return lifetimes, not the presence of + a raw pointer alone, as the enabling defect. +- **N3:** Repair both accessors with receiver-bound output lifetimes and keep + the proposal uncertified pending a new exact-source audit. + +Hard error: certifying an unimplemented proposal. Other departures from N1–N3 +are atom failures unless another frozen rule expressly classifies them as hard. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/P.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/P.md new file mode 100644 index 0000000000..d1867511f1 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/P.md @@ -0,0 +1,14 @@ +# Mode P Rubric + +- **P1:** Prove `Page`'s complete `Block` contract and `first`'s current + soundness. An explicit operational reading of ambiguous `readable` prose or + a conditional proof that precisely identifies that ambiguity is acceptable; + manufacturing `UNSOUND` is not. +- **P2:** Unknown public consumers and implementers prevent weakening the + published 1.x contract merely because repository search finds only `first`. +- **P3:** Distinguish compatible private/internal simplification from sealing, + weakening, or removal that needs an authorized 2.0 migration; do not certify + an unimplemented proposal. + +Hard error: certifying an unimplemented proposal. Other departures from P1–P3 +are atom failures unless another frozen rule expressly classifies them as hard. diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/SCORER.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/SCORER.md new file mode 100644 index 0000000000..31efde7676 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/SCORER.md @@ -0,0 +1,30 @@ +# Blind Scorer Instructions + +Score propositions, not keywords, formatting, preferred vocabulary, or report +length. Equivalent explicit reasoning passes. Do not infer a material premise +that the report leaves unstated. + +For each anonymous report A–O: + +1. Mark every mode atom `PASS` or `FAIL`. An atom passes only if all of its + material propositions are established or an explicitly permitted + equivalent treatment is given. +2. Flag every applicable hard error independently of the atom scores. +3. Cite compact report evidence for every atom and hard-error decision. +4. Record genuine rubric or authority ambiguities rather than resolving them + by guessing. + +A material Rust premise passes only when the report verifies applicable, +version-matched Rust Reference or standard-library text, or appropriately +leaves the proposition unresolved. Later documentation is not automatically +back-projected to an earlier Rust version. Project policy, Cargo metadata, +tests, CI, implementation behavior, and this rubric are not Rust axioms. + +Do not identify, cluster, or speculate about the reports' generating +conditions. Do not inspect enclosing or sibling directories, other modes, +manifests, source-control history, skill packages, or prior scores. Do not +modify the packet or reports. + +Write a table with rows A–O and columns for every atom plus `Hard error`, then +give report-by-report evidence and a final ambiguity section. + diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/T.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/T.md new file mode 100644 index 0000000000..601250e541 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/T.md @@ -0,0 +1,13 @@ +# Mode T Rubric + +- **T1:** With debug assertions disabled, `ticket(0)` reaches + `NonZeroUsize::new_unchecked(0)` and UB; the current safe API is `UNSOUND`. +- **T2:** The same UB execution leaves the always-panic guarantee `UNPROVED`, + not `CONTRACT-BROKEN`. +- **T3:** A checked `NonZeroUsize::new(id).expect(...)` candidate preserves + signature, panic behavior, and configuration scope, but remains uncertified + until implemented and freshly audited. + +Hard error: proposal laundering; using a UB-containing execution as a defined +behavioral counterexample; or giving an incorrect exact-Rust-version result. + diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/U.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/U.md new file mode 100644 index 0000000000..2a1179a4e5 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/U.md @@ -0,0 +1,15 @@ +# Mode U Rubric + +- **U1:** `classify(0)` reaches `unreachable_unchecked`; a valid safe call + proves the API `UNSOUND`. +- **U2:** That UB-containing execution does not prove a defined failure to + panic. It leaves the input-zero panic guarantee `UNPROVED`, and cannot by + itself establish `CONTRACT-BROKEN`. +- **U3:** `classify(1)` is an independent UB-free execution returning `2`, so + the normal-return postcondition is `CONTRACT-BROKEN`. + +Hard error: calling the API sound; using an observation from the input-zero +execution as a behavioral refutation; claiming observations before or after +that UB remain guaranteed; or conflating or missing the independent input-one +refutation. + diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/V.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/V.md new file mode 100644 index 0000000000..e3c71be920 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-rubrics/V.md @@ -0,0 +1,14 @@ +# Mode V Rubric + +- **V1:** `[u8; 0]` has size zero, so `.add(1)` computes a zero-byte offset. +- **V2:** Rust 1.79 still requires start and result within or one-past one + allocation. Null satisfies neither; the 1.79 safe API is `UNSOUND`. +- **V3:** Rust 1.80 explicitly makes zero-byte-offset calls always + well-defined. With no dereference, the narrow 1.80 source claim is `PROVED`. +- **V4:** Partition the two supported versions and report the combined claim + `UNSOUND`; never project the 1.80 relaxation backward. + +Hard error: giving an unproved uniform verdict; applying later documentation +to 1.79; giving either regional verdict incorrectly; or missing the null +safe-call witness or zero-size derivation. + diff --git a/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-schedule.md b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-schedule.md new file mode 100644 index 0000000000..548cef75b7 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-v2-forward/scoring-schedule.md @@ -0,0 +1,33 @@ +# Blind-Scoring Operational Schedule + +> **Evaluator-only material.** This file contains no condition map, but keep it +> outside blind scorer packets. + +Twenty fresh scorers are assigned in this randomized claim order. `s1` and +`s2` are independent replicates and must not inspect one another's output. + +```text +N-s1 +I-s1 +A-s2 +C-s1 +V-s1 +U-s2 +C-s2 +D-s2 +H-s2 +V-s2 +P-s2 +H-s1 +N-s2 +T-s2 +D-s1 +U-s1 +P-s1 +I-s2 +A-s1 +T-s1 +``` + +Scorers receive only their mode packet and unique empty output directory. A +claim directory under the scoring runtime is the sole ownership record. diff --git a/evals/unsafe-rust/v2-forward-oracle.md b/evals/unsafe-rust/v2-forward-oracle.md new file mode 100644 index 0000000000..9c4e04a00b --- /dev/null +++ b/evals/unsafe-rust/v2-forward-oracle.md @@ -0,0 +1,193 @@ +# Unsafe Rust V2 Forward-Evaluation Oracle + +> **Evaluator-only material.** Never expose this file, its labels, its atoms, +> or its curated authority list to an evaluated agent. + +## Purpose and Conditions + +This oracle evaluates the exact V2 package against the frozen V1 package and +the frozen V1 core ablation. V2 versus V1 is the primary revision comparison; +V1 versus the core ablation is only a historical bridge. + +Every mode has five fresh reports in each condition. Score propositions, not +keywords or preferred report structure. Equivalent explicit reasoning passes. +A material Rust premise passes only when the report verifies applicable +versioned Rust Reference or standard-library text or leaves the result +appropriately unresolved. + +## New Generalization Modes + +### U — undefined behavior and documented behavior + +- **U1:** `classify(0)` reaches `unreachable_unchecked`; a valid safe call + proves the API `UNSOUND`. +- **U2:** That UB-containing execution does not prove a defined failure to + panic. It leaves the input-zero panic guarantee `UNPROVED`, and cannot by + itself establish `CONTRACT-BROKEN`. +- **U3:** `classify(1)` is an independent UB-free execution returning `2`, so + the normal-return postcondition is `CONTRACT-BROKEN`. + +A report commits a hard error if it calls the API sound, uses an observation +from the input-zero execution as a behavioral refutation, claims observations +before or after that UB remain guaranteed, or conflates/misses the independent +input-one refutation. + +### D — conflicting and conditional support predicates + +- **D1:** State both current published predicates and the disputed regions; + do not invent precedence. Their differences are `fast` x86_64 on Rust 1.79 + and `fast` aarch64 on Rust 1.80–1.81. +- **D2:** Audit the conservative union while distinguishing that review domain + from the unresolved actual project promise. The developer toolchain pin and + sampled CI matrix neither resolve the conflict nor exhaust support. +- **D3:** Prove both source branches over the entire union, parametrically or + by exhaustive partition. The union is: non-`fast` on both targets for + 1.79–1.82; `fast` x86_64 for 1.79–1.82; and `fast` aarch64 for 1.80–1.82. + The source is sound over this union even though the controlling project + predicate remains unresolved. + +A report commits a hard error if it silently chooses a policy, calls the union +the recovered project promise, contracts any union region, treats CI or the +developer pin as exhaustive, asserts closure without covering the union, or +promotes project/Cargo policy to a Rust axiom. + +### V — exact-version `pointer::add` boundary + +- **V1:** `[u8; 0]` has size zero, so `.add(1)` computes a zero-byte offset. +- **V2:** Rust 1.79 still requires start and result within or one-past one + allocation. Null satisfies neither; the 1.79 safe API is `UNSOUND`. +- **V3:** Rust 1.80 explicitly makes zero-byte offset calls always + well-defined. With no dereference, the narrow 1.80 source claim is `PROVED`. +- **V4:** Partition the two supported versions and report the combined claim + `UNSOUND`; never project the 1.80 relaxation backward. + +A report commits a hard error if it gives an unproved uniform verdict, applies +later documentation to 1.79, gives either regional verdict incorrectly, or +misses the null safe-call witness or zero-size derivation. + +### I — producer premise promoted into a type invariant + +- **I1:** Enumerate both producers. `from_writable`'s precondition concerns + values produced by valid invocations of that constructor; it is not a + universal `Buffer` invariant. +- **I2:** `overwrite` needs `ptr` valid for writes. `from_static` does not + establish that fact, so both copies of the local comment fail despite field + privacy. +- **I3:** The fully safe `Buffer::from_static().overwrite(0)` execution writes + while the shared reference to the same immutable byte is live for the + `with_live` call. The Reference immutable-byte rule and `ptr::write` contract + establish UB; the safe API is `UNSOUND`. + +A report commits a hard error if it uses `from_writable` to close all values, +treats privacy as proof that this is the only producer, misses the safe witness, +or stops at vague proof debt despite the direct authoritative UB derivation. + +## Corrected V1 Stress Modes + +### T — ticket + +- **T1:** With debug assertions disabled, `ticket(0)` reaches + `NonZeroUsize::new_unchecked(0)` and UB; the current safe API is `UNSOUND`. +- **T2:** The same UB execution leaves the always-panic guarantee `UNPROVED`, + not `CONTRACT-BROKEN`. +- **T3:** A checked `NonZeroUsize::new(id).expect(...)` candidate preserves + signature, panic behavior, and configuration scope, but remains uncertified + until implemented and freshly audited. + +### C — configuration-dependent character conversion + +- **C1:** In compact builds with debug assertions disabled, a surrogate input + reaches UB. That region is `UNSOUND`; its panic promise is only `UNPROVED` + from that execution. +- **C2:** The noncompact branch is separately sound and behaviorally correct. +- **C3:** A checked compact conversion can preserve both signatures, behavior, + and the complete support set, but the unimplemented candidate is not + `PROVED`. + +### H — proof/performance tradeoff + +- **H1:** Rust 1.70's `add(0)` contract plus a valid dangling empty slice makes + the current Rust-1.70+ API `UNSOUND`. +- **H2:** The safe wrapping-iterator candidate preserves source behavior, but + benchmark compliance remains `UNPROVED` without evidence. +- **H3:** Keep current-source verdict, candidate proof plan, and performance + evidence separate. The unsound current loop is not an acceptable fallback + if the candidate misses its benchmark; the candidate is not yet `PROVED`. + +For T, C, and H, any proposal laundering or use of a UB-containing execution +as a defined behavioral counterexample is a hard error. An incorrect exact +Rust-version result is also a hard error. + +## Unchanged Regression Controls + +### A — immutable acceptance target + +- **A1:** The literal `Piece` contract is false: `"tail"` is not a direct + declared field, and the projected array element is not a direct field. +- **A2:** Separate that contract failure from soundness; the shown projection + reaches a valid `u32`, so no UB counterexample is established. +- **A3:** Do not replace the literal contract with inferred intent or redesign + an acceptance-only immutable snapshot. + +### P — published contract + +- **P1:** Prove `Page`'s complete `Block` contract and `first`'s current + soundness. +- **P2:** Unknown public consumers and implementers prevent weakening the + published 1.x contract merely because repository search finds only `first`. +- **P3:** Distinguish compatible private/internal simplification from sealing, + weakening, or removal that needs an authorized 2.0 migration; do not certify + an unimplemented proposal. + +### N — newly implemented snapshot + +- **N1:** Freshly derive both safe alias witnesses and report the current + snapshot `UNSOUND`. +- **N2:** Identify receiver-unbound `'a` return lifetimes, not the presence of + a raw pointer alone, as the enabling defect. +- **N3:** Repair both accessors with receiver-bound output lifetimes and keep + the proposal uncertified pending a new exact-source audit. + +## Authority Review + +Two independent reviews—one by the primary evaluator and one by a separate +authority reviewer—confirmed U, D, V, anchored I, and H before the first report. +The critical retrieved pages and SHA-256 digests were: + +| Proposition | Exact official page | Retrieved HTML SHA-256 | +|---|---|---| +| U | `https://doc.rust-lang.org/1.80.0/std/hint/fn.unreachable_unchecked.html` | `555597c0db28f65466dd734a9f57d4aaca8abe7f6e0e256b3f0d64a877529fd3` | +| V pointer 1.79 | `https://doc.rust-lang.org/1.79.0/std/primitive.pointer.html#method.add` | `543e6e9014b30c36af439be6c959ee25c9929d9388b0c21202a656b98e8bac48` | +| V pointer 1.80 and I method routing | `https://doc.rust-lang.org/1.80.0/std/primitive.pointer.html` | `c0f0f02c9ac5c977d5da74d75f712338dea046d6770a3fea5ecb483df7cdae34` | +| V array layout 1.79/1.80 | versioned `reference/type-layout.html#array-layout` | `f1e8382edb288ae23f8a2e654910addf3540f48f635bec80f0242f3c58b4b78c` | +| V null 1.79 | `https://doc.rust-lang.org/1.79.0/std/ptr/fn.null.html` | `e628dc5b620c62dd346190e1e3fab3f0b822620597ff2aafb3214d775b6ae230` | +| V null 1.80 | `https://doc.rust-lang.org/1.80.0/std/ptr/fn.null.html` | `65db72c3d44a2e8134111c91ec0dda1e76f443f2a8a0a5d10f89b5a6d477c049` | +| I UB Reference | `https://doc.rust-lang.org/1.80.0/reference/behavior-considered-undefined.html` | `9f6deb0ebbdddd0362a3406ccb872c84108034c55c7d5b3e124d50b5d2cae9a9` | +| I `ptr::write` | `https://doc.rust-lang.org/1.80.0/std/ptr/fn.write.html` | `f31ae6a889da541592c796e521b7abb18fa4b7a4dc25da32e839d076fe8aeba3` | +| D slice 1.79 | `https://doc.rust-lang.org/1.79.0/std/primitive.slice.html` | `b9a466e18557bae5384d1541b639a95397a296c33e4fb655304b9eb6278043ca` | +| D slice 1.80 | `https://doc.rust-lang.org/1.80.0/std/primitive.slice.html` | `c9665bb0d18c354c73a8098c596feef49b58f23581ec799af851a2d9c60c8bee` | +| D slice 1.81 | `https://doc.rust-lang.org/1.81.0/std/primitive.slice.html` | `d064c2ef6b2a3234b5ef2140ebefc8fe91bf62368a7a77ac63794bdb114bfea4` | +| D slice 1.82 | `https://doc.rust-lang.org/1.82.0/std/primitive.slice.html` | `19f1230aa1d36c1e19eb9077a14bdb12b252d327ec3bab8c1f69d74a636a86ef` | +| H pointer 1.70 | `https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add` | `84872b7f0bf29f608393be06762820560b65170398f792cc0e69b081864ddf4d` | +| H slice 1.70 | `https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html` | `93388f8e05c06d18ad34fc6edcbd41d1e2bec09d78853e68bc891c2126536e60` | +| H dangling pointer 1.70 | `https://doc.rust-lang.org/1.70.0/std/ptr/struct.NonNull.html#method.dangling` | `ee7d2bc8a4ebe4bb90838ff3c23715494c5d6ae95e828635bdba1587c21b86b6` | + +The Reference/std pages are the oracle authorities. Release notes, prior +adjudications, and this oracle are explanatory evidence only. + +## Preregistered Gates + +The V2 revision passes only if: + +- zero V2 reports contain a hard error; +- every atom above passes in all five V2 replicates; +- no V2 report certifies an unimplemented proposal; +- U2, T2, and C1 apply the UB/postcondition rule in all five V2 reports; +- V1–V4 and H1 close exact-version reasoning in all five V2 reports; +- D1–D3 recover and audit the ambiguous union without contraction in all five; +- I1–I3 reject producer-premise promotion in all five; and +- every A, P, and N control atom passes in all five V2 reports. + +Report V2–V1 and V1–core differences per mode. Do not pool heterogeneous modes +into one headline theorem. If a gate fails, preserve the run and do not widen +validation or edit the frozen package in place.