diff --git a/evals/unsafe-rust/abstraction-design-plan.md b/evals/unsafe-rust/abstraction-design-plan.md new file mode 100644 index 0000000000..e2af50705d --- /dev/null +++ b/evals/unsafe-rust/abstraction-design-plan.md @@ -0,0 +1,153 @@ +# Unsafe Rust Abstraction-Design Evaluation Plan + +> **Evaluator-only material.** Do not expose this plan, fixture sibling names, +> or expected atoms to evaluated agents. + +## Objective and honest scope + +Determine whether the conditional abstraction-design workflow causes fresh +agents to produce better proof-oriented unsafe-Rust designs without laundering +the current artifact's obligations. + +“Exhaustive” here means every behavior required by the new design reference has +an explicit fixture atom and disposition. It does not mean that nine snippets +represent every possible unsafe abstraction, model, or codebase. + +## Frozen treatment and comparator + +- **Treatment:** the complete frozen `unsafe-rust` package, including + `references/abstraction-design.md`. +- **Core ablation:** the same frozen package with the abstraction-design + workflow, its activation/routing, its report-template section, and its + cross-references removed. All ordinary unsafe-Rust proof, authority, TCB, + API, configuration, and verdict guidance remains byte-for-byte identical. + +This comparator isolates the added workflow better than a no-skill baseline. +It is not a prior released skill and must not be described as one. + +Freeze and record both package digests before the first run. Do not edit either +package after observing results. + +## Capability-closure matrix + +| Mode | Capability under test | Fixture | +|---|---|---| +| A | Immutable third-party acceptance; literal current verdict; no unsolicited redesign | `a_acceptance` | +| R | Intent discovery; minimum capability; parsimony; current/proposal firewall | `b_projection_redesign` | +| T | Local enforcement versus hidden/transferred caller obligations | `c_ticket` | +| P | Unknown public consumers; unsafe-trait commitments; SemVer directionality | `d_published_contract` | +| C | Rust/feature/target/profile domain closure; no silent support contraction | `e_configuration_domain` | +| S | Sealing and module privacy; smallest invariant boundary | `f_sealed_boundary` | +| G | Greenfield specialization and safe reuse; no invented generic abstraction | `g_greenfield` | +| H | Incomparable performance/proof tradeoffs; no invented score | `h_tradeoff` | +| N | Implemented candidate is a new snapshot requiring a fresh audit | `i_new_snapshot` | + +The evaluator-only fixture registry in +[`fixtures/abstraction-design-v1/README.md`](fixtures/abstraction-design-v1/README.md) +defines every exact atom and accepted alternative. + +## Conditions, repetitions, and order + +Run three independent fresh agents for each `(mode, condition)` cell: + +```text +9 modes × 2 conditions × 3 replicates = 54 evaluated runs +``` + +Randomize cells across waves. Each agent sees one opaque target copy, one opaque +skill package, the neutral request, exact official versioned Rust documentation +as needed, and its own output path. It must not see another mode, replicate, +condition, report, oracle, plan, package, history, or evaluator file. + +Use `fork_turns="none"`; do not reuse agents and forbid evaluated agents from +spawning helpers. The two conditions receive byte-identical prompts except for +the opaque skill-package path. + +## Frozen prompt + +```text +Act as a fresh source-review and design 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 the other files in +[TARGET], and complete exactly the requested review/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] 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 review/design report, not a persistent whole-crate audit; +provide the equivalent proof material compactly. +``` + +Target-neutral requests to conclude without widening scope are permitted and +must be recorded. Any substantive hint invalidates and reruns the cell. + +## Scoring + +Score each preregistered atom as pass/fail. Equivalent reasoning counts; +headings, vocabulary, candidate names, and preferred prose do not. + +An atom passes only if the report makes the required reasoning explicit. An +omission, ambiguous implication, or contradiction fails it. Do not infer credit +from a generally sophisticated report. + +For each mode report: + +- atom recovery and hard errors; +- current-artifact verdict accuracy; +- requirement and consumer-proposition extraction; +- candidate validity and proof plan; +- proof-surface/TCB/invariant reduction; +- public-contract and compatibility analysis; +- supported-domain coverage; +- current/candidate/post-change output separation; and +- unsupported certainty or obligation transfer. + +After all evaluated runs finish, normalize reports under random labels and use +fresh blind scorers which know the mode oracle but not condition identity. A +separate adjudicator resolves disputed semantic claims against raw source and +applicable official documentation. + +## Hard errors + +Any one fails the run regardless of atom count: + +- inferred intent, tests, known consumers, or a proposal narrows or discharges + a current obligation; +- a proposal erases/downgrades a current finding or receives `PROVED` before + implementation and fresh audit; +- an enforceable obligation is recommended as a hidden or unnecessary unsafe + caller/implementer obligation; +- repository-visible consumers are treated as exhaustive for a public API; +- a binding public, SemVer, behavioral, MSRV, target, feature, profile, or + performance constraint is silently weakened; +- an immutable acceptance-only task receives redesign, migration, or source + changes; +- incomparable candidates are collapsed into an invented numerical score or + unsupported priority; +- a new implementation inherits the design sketch's proof instead of receiving + an exact-source audit; +- current-artifact, candidate, compatibility, and post-change conclusions are + conflated; +- a fixed/sound current artifact is condemned without proof, or an unsound one + receives unsupported `PROVED`; or +- forbidden oracle, paired-side, condition, or prior-report material is read. + +## Preregistered gates + +- Zero treatment hard errors. +- Every atom passes in at least `2/3` treatment replicates. +- The verdict-firewall atoms A1–A3 and R1, support-preservation atoms P2–P4 and + C3–C4, and fresh-audit atoms N1–N4 pass `3/3` treatment replicates. +- No treatment replicate launders T's obligation or calls G's sketch proved. +- Report per-atom treatment/ablation rates and paired differences without + pooling modes into a misleading single theorem. + +The suite is exploratory if physical isolation, model identity, seed control, +offline documentation, or independent multi-scorer adjudication is missing, +even when all semantic gates pass. diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/README.md b/evals/unsafe-rust/fixtures/abstraction-design-v1/README.md new file mode 100644 index 0000000000..26df459b01 --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/README.md @@ -0,0 +1,140 @@ +# Abstraction Design V1 Fixture Registry + +> **Evaluator-only oracle.** Never place this file or sibling fixtures in an +> evaluated agent's accessible target directory. + +All fixtures are source-only. No build, test, expansion, or target execution is +permitted. + +## A — Immutable acceptance + +- **A1:** Apply the literal `Piece` contract. `Tail` does not name or project a + direct declared field called `tail`; the nominal contract is false. +- **A2:** Preserve a scoped current-artifact rejection/`UNPROVED` or + contract-broken result without manufacturing a UB witness; the operational + nested projection can be sound independently. +- **A3:** Treat names, comments, tests, and the one known consumer only as intent + evidence, never as a replacement contract. +- **A4:** Respect the immutable accept/reject-only scope: no redesign, + candidate, migration, or edit. + +## R — Projection redesign + +- **R1:** Preserve A's current literal result before and independently of any + proposal. +- **R2:** Extract the exact required behavior: increment `Pair.0[1]` with + wrapping arithmetic; internal-only API; Rust 1.70+; all targets/profiles; no + allocation; no demonstrated generic reuse. +- **R3:** Identify nested projection as the operational capability and separate + it from unused nominal direct-field metadata. +- **R4:** Prefer the safe specialized implementation because it eliminates the + unsafe trait and is strictly simpler under the stated requirements. A + narrower projection-only abstraction is acceptable only as a conditional + future-reuse alternative. +- **R5:** Reject cosmetic rename/fabricated-field fixes and unnecessary caller + obligations. +- **R6:** State contract/invariant/proof simplification, behavior and support + preservation, compatibility (internal-only), and re-audit needs. +- **R7:** Give no unimplemented candidate a `PROVED` verdict; keep current, + requirements, proposal, and post-change audit separate. + +## T — Ticket construction + +- **T1:** Current safe `ticket(0)` is `UNSOUND` without debug assertions and + violates the mandatory always-panic behavior. +- **T2:** Prefer `NonZeroUsize::new(id).expect(...)` or an equivalent safe + standard primitive with an unconditional zero check. +- **T3:** Preserve the safe signature, exact nonzero value, panic behavior, all + profiles, and support domain. +- **T4:** Reject making the function unsafe, adding a hidden caller precondition, + retaining `debug_assert!`, or using unchecked construction when the safe + primitive matches. +- **T5:** Keep current finding, proposal proof plan, and fresh-audit requirement + separate. + +## P — Published contract + +- **P1:** The current `Page` implementation satisfies all published `Block` + clauses and `first` is sound for every valid unsafe implementation; do not + invent a current defect. +- **P2:** Known repository consumers do not exhaust downstream consumers of the + public unsafe trait. +- **P3:** Do not weaken alignment/readable-extent guarantees, seal the trait, or + strengthen implementer obligations within the promised compatible 1.x line. +- **P4:** A private narrower helper may simplify known internal code but cannot + erase the public trait/impl audit. A breaking split/seal belongs to an + explicitly authorized 2.0 plan. +- **P5:** State provider/implementer/consumer contract directions, migration, + proof consequences, and any human release decision. + +## C — Configuration domain + +- **C1:** `compact` plus disabled debug assertions is `UNSOUND` for surrogate + inputs and breaks the documented panic postcondition. +- **C2:** The non-`compact` branch is a separate safe, correct region. +- **C3:** Redesign compact mode with an unconditional checked conversion which + preserves its signature and panic behavior throughout Rust 1.70+. +- **C4:** Cover both feature values, both assertion states, every supported + target/pointer width, and the MSRV without silently dropping/raising any. +- **C5:** Keep current regional verdicts, proposed proof, compatibility, and + future exact-source audit separate. + +## S — Sealed boundary + +- **S1:** Current safe downstream `Bytes` implementations can return + dangling/inconsistent raw parts, making safe `last` unsound. +- **S2:** `#[doc(hidden)]` is not sealing and behavioral prose on the safe method + cannot establish the invariant. +- **S3:** Because external implementations are unnecessary and sealing is + authorized, prefer a genuinely private leaf-module sealing/representation + boundary and audit each controlled implementation. Merely `pub(crate)` + invariant-bearing access is proof-surface debt under the stated discipline. +- **S4:** Making the trait unsafe is viable but exposes a larger implementer + obligation and is dominated under these requirements; do not prefer it + without another constraint. +- **S5:** Preserve `last`: empty built-ins return `None`, otherwise the final + byte; specify local contracts, controlled producers/consumer, and re-audit. + +## G — Greenfield + +- **G1:** Give no current-artifact verdict; no implementation exists. +- **G2:** Preserve exact order: `None` for equal/out-of-range indices, otherwise + return references corresponding to `(i, j)`. +- **G3:** Prefer a safe `split_at_mut` design, reversing tuple assembly when + `j < i`; no unsafe trait, raw-pointer abstraction, or caller token is needed. +- **G4:** State the conditional proof plan, bounds/distinctness reasoning, and + support/behavior scope. +- **G5:** Do not call the sketch `PROVED`; require audit of exact implemented + source. + +## H — Incomparable tradeoff + +- **H1:** Do not condemn the current pointer loop without a failed proof; state + its relevant one-past/end, dereference, progression, overflow, empty-slice, + and wrapping-add obligations. +- **H2:** Identify `iter().copied().fold(0, u32::wrapping_add)` or equivalent as + the safe behavior-preserving candidate; reject plain `sum()` as changing + debug overflow behavior. +- **H3:** Do not claim the safe candidate meets the 2% benchmark without the + designated measurement. +- **H4:** Present safe-candidate implementation/audit plus benchmark as the next + action, retaining the proved raw loop conditionally if the binding benchmark + rejects it. +- **H5:** Treat proof surface and measured performance as incomparable until + evidence resolves the hard performance constraint; use no invented score. + +## N — New snapshot + +- **N1:** Treat supplied code as a new exact artifact; do not inherit the + approved sketch or alter any prior verdict. +- **N2:** Find a fully safe witness retaining two mutable references from two + `get_mut` calls; `get` followed by `get_mut` also permits shared/mutable + overlap. Classify the implemented candidate `UNSOUND`. +- **N3:** Explain that returned `'a` is not tied to the receiver borrow; privacy, + `PhantomData`, and the safe constructor do not discharge method aliasing. +- **N4:** Propose receiver-bound output lifetimes for both methods, not only + `get_mut`, and withhold a verdict until that exact source is implemented and + audited. +- **N5:** Report current new-snapshot finding, proposed fix, and post-fix audit + as separate artifacts. + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/a_acceptance/REQUEST.md b/evals/unsafe-rust/fixtures/abstraction-design-v1/a_acceptance/REQUEST.md new file mode 100644 index 0000000000..e4610b85c2 --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/a_acceptance/REQUEST.md @@ -0,0 +1,13 @@ +# Acceptance request + +This exact third-party snapshot is proposed for vendoring. Policy permits only +accepting or rejecting it; its source and public contracts cannot be changed. + +Review the `Piece for Tail` implementation and the safe `increment_tail` +surface under the literal current contracts. State the strongest scoped +soundness and contract-compliance result. Do not propose redesigns, patches, +migrations, or alternative APIs. + +Scope: Rust 1.70+, all targets and ordinary profiles, no additional TCB or +deployment restriction. + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/a_acceptance/lib.rs b/evals/unsafe-rust/fixtures/abstraction-design-v1/a_acceptance/lib.rs new file mode 100644 index 0000000000..ee9f5e67ff --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/a_acceptance/lib.rs @@ -0,0 +1,35 @@ +#![allow(dead_code)] + +pub unsafe trait Piece { + type Owner; + type Item; + + /// The name of a direct declared field of `Owner` whose type is `Item`. + const FIELD: &'static str; + + /// Returns a pointer to that direct declared field. + /// + /// # Safety + /// + /// `owner` must identify a live, uniquely borrowed `Owner` for the call. + unsafe fn project(owner: *mut Self::Owner) -> *mut Self::Item; +} + +pub struct Pair(pub [u32; 2]); +pub struct Tail; + +unsafe impl Piece for Tail { + type Owner = Pair; + type Item = u32; + const FIELD: &'static str = "tail"; + + unsafe fn project(owner: *mut Pair) -> *mut u32 { + unsafe { core::ptr::addr_of_mut!((*owner).0[1]) } + } +} + +pub fn increment_tail(pair: &mut Pair) { + let value = unsafe { &mut *Tail::project(pair) }; + *value = value.wrapping_add(1); +} + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/b_projection_redesign/REQUEST.md b/evals/unsafe-rust/fixtures/abstraction-design-v1/b_projection_redesign/REQUEST.md new file mode 100644 index 0000000000..bc800b10a3 --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/b_projection_redesign/REQUEST.md @@ -0,0 +1,12 @@ +# Redesign request + +We own this unpublished internal abstraction and may change it freely. First +review the current implementation under its literal contract. Then propose the +simplest proof-oriented redesign. + +The only required behavior is that `increment_tail(&mut Pair)` increments +`Pair.0[1]` with wrapping arithmetic. It must remain allocation-free, support +Rust 1.70+, every target, and every ordinary profile. Repository search found +no other consumer and no planned generic reuse. No source edit is requested in +this task; provide the design and proof plan. + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/b_projection_redesign/lib.rs b/evals/unsafe-rust/fixtures/abstraction-design-v1/b_projection_redesign/lib.rs new file mode 100644 index 0000000000..ee9f5e67ff --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/b_projection_redesign/lib.rs @@ -0,0 +1,35 @@ +#![allow(dead_code)] + +pub unsafe trait Piece { + type Owner; + type Item; + + /// The name of a direct declared field of `Owner` whose type is `Item`. + const FIELD: &'static str; + + /// Returns a pointer to that direct declared field. + /// + /// # Safety + /// + /// `owner` must identify a live, uniquely borrowed `Owner` for the call. + unsafe fn project(owner: *mut Self::Owner) -> *mut Self::Item; +} + +pub struct Pair(pub [u32; 2]); +pub struct Tail; + +unsafe impl Piece for Tail { + type Owner = Pair; + type Item = u32; + const FIELD: &'static str = "tail"; + + unsafe fn project(owner: *mut Pair) -> *mut u32 { + unsafe { core::ptr::addr_of_mut!((*owner).0[1]) } + } +} + +pub fn increment_tail(pair: &mut Pair) { + let value = unsafe { &mut *Tail::project(pair) }; + *value = value.wrapping_add(1); +} + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/c_ticket/REQUEST.md b/evals/unsafe-rust/fixtures/abstraction-design-v1/c_ticket/REQUEST.md new file mode 100644 index 0000000000..0c234f5dc1 --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/c_ticket/REQUEST.md @@ -0,0 +1,7 @@ +# Redesign request + +Review the current safe API and redesign it while preserving its exact safe +signature and documented behavior in every ordinary build profile. The crate +supports Rust 1.70+ on every target. It has no dependencies or deployment +restrictions. Prefer the smallest proof surface; no source edit is requested. + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/c_ticket/lib.rs b/evals/unsafe-rust/fixtures/abstraction-design-v1/c_ticket/lib.rs new file mode 100644 index 0000000000..d5ba887f62 --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/c_ticket/lib.rs @@ -0,0 +1,12 @@ +#![allow(dead_code)] + +use core::num::NonZeroUsize; + +pub struct Ticket(NonZeroUsize); + +/// Returns a ticket containing `id`; panics when `id == 0`. +pub fn ticket(id: usize) -> Ticket { + debug_assert!(id != 0); + Ticket(unsafe { NonZeroUsize::new_unchecked(id) }) +} + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/d_published_contract/REQUEST.md b/evals/unsafe-rust/fixtures/abstraction-design-v1/d_published_contract/REQUEST.md new file mode 100644 index 0000000000..d1eaf31127 --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/d_published_contract/REQUEST.md @@ -0,0 +1,12 @@ +# Published-contract redesign request + +`Block`, its safety contract, and downstream implementations are published 1.x +commitments. The project promises ordinary SemVer compatibility throughout 1.x. +Repository search finds only `first`, which consumes less than the full +contract, but downstream consumers and implementations are not enumerable. + +Review the current `Page` implementation and `first`, then explain what +proof-oriented simplification is possible in 1.x and what would require an +explicitly authorized 2.0 migration. No source edit or major release has been +authorized. Scope: Rust 1.70+, all targets/profiles. + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/d_published_contract/lib.rs b/evals/unsafe-rust/fixtures/abstraction-design-v1/d_published_contract/lib.rs new file mode 100644 index 0000000000..a411d25a60 --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/d_published_contract/lib.rs @@ -0,0 +1,25 @@ +#![allow(dead_code)] + +pub unsafe trait Block { + /// A nonzero power of two. + const ALIGN: usize; + + /// During the borrow, the result is non-null, `ALIGN`-aligned, and readable + /// for 16 bytes. + fn base(&self) -> *const u8; +} + +#[repr(C, align(16))] +pub struct Page([u8; 16]); + +unsafe impl Block for Page { + const ALIGN: usize = 16; + + fn base(&self) -> *const u8 { + self.0.as_ptr() + } +} + +pub fn first(block: &B) -> u8 { + unsafe { *block.base() } +} diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/e_configuration_domain/REQUEST.md b/evals/unsafe-rust/fixtures/abstraction-design-v1/e_configuration_domain/REQUEST.md new file mode 100644 index 0000000000..cc3ae78fab --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/e_configuration_domain/REQUEST.md @@ -0,0 +1,8 @@ +# Configuration-preserving redesign + +Review and redesign `decode` without changing either configuration-specific +signature or documented behavior. The published support set is Rust 1.70+, +every target and pointer width, every ordinary profile, and both independently +selectable values of feature `compact`. Dropping a configuration, raising the +MSRV, or changing a return type is not authorized. No source edit is requested. + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/e_configuration_domain/lib.rs b/evals/unsafe-rust/fixtures/abstraction-design-v1/e_configuration_domain/lib.rs new file mode 100644 index 0000000000..6681059b3d --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/e_configuration_domain/lib.rs @@ -0,0 +1,15 @@ +#![allow(dead_code)] + +/// With `compact`, returns the represented scalar and panics for a surrogate. +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + debug_assert!(!(0xD800..=0xDFFF).contains(&raw)); + unsafe { char::from_u32_unchecked(raw as u32) } +} + +/// Without `compact`, returns the represented scalar or `None`. +#[cfg(not(feature = "compact"))] +pub fn decode(raw: u32) -> Option { + char::from_u32(raw) +} + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/f_sealed_boundary/REQUEST.md b/evals/unsafe-rust/fixtures/abstraction-design-v1/f_sealed_boundary/REQUEST.md new file mode 100644 index 0000000000..853c4cdb0b --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/f_sealed_boundary/REQUEST.md @@ -0,0 +1,9 @@ +# Invariant-boundary redesign + +Review the current safe API, then redesign its invariant boundary. External +implementations of `Bytes` are not required, and genuine sealing is authorized. +Preserve safe `last` behavior for supported built-in types: empty values return +`None`; nonempty values return the final byte. New code should use the smallest +practical leaf-module proof boundary and treat safe code outside it as +untrusted. Scope: Rust 1.70+, `std`, all targets/profiles. No edit is requested. + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/f_sealed_boundary/lib.rs b/evals/unsafe-rust/fixtures/abstraction-design-v1/f_sealed_boundary/lib.rs new file mode 100644 index 0000000000..a481daea5a --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/f_sealed_boundary/lib.rs @@ -0,0 +1,20 @@ +#![allow(dead_code)] + +pub trait Bytes { + #[doc(hidden)] + fn raw_parts(&self) -> (*const u8, usize); +} + +pub struct Owned(Vec); + +impl Bytes for Owned { + fn raw_parts(&self) -> (*const u8, usize) { + (self.0.as_ptr(), self.0.len()) + } +} + +pub fn last(bytes: &B) -> Option { + let (ptr, len) = bytes.raw_parts(); + (len != 0).then(|| unsafe { *ptr.add(len - 1) }) +} + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/g_greenfield/REQUEST.md b/evals/unsafe-rust/fixtures/abstraction-design-v1/g_greenfield/REQUEST.md new file mode 100644 index 0000000000..7e50360993 --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/g_greenfield/REQUEST.md @@ -0,0 +1,11 @@ +# Greenfield design request + +The function body is a placeholder, not an existing implementation to audit. +Design it conceptually. Return `None` if either index is out of bounds or if +`i == j`; otherwise return mutable references to the two corresponding elements +in `(i, j)` order. There is no performance requirement beyond ordinary +linear-memory access and no demonstrated need for a reusable abstraction. + +The crate is `no_std`, supports Rust 1.70+ and all targets, and has no +dependencies. Do not edit the source; provide a design and proof plan. + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/g_greenfield/lib.rs b/evals/unsafe-rust/fixtures/abstraction-design-v1/g_greenfield/lib.rs new file mode 100644 index 0000000000..6e47755284 --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/g_greenfield/lib.rs @@ -0,0 +1,10 @@ +#![allow(dead_code)] + +pub fn two_mut( + values: &mut [T], + i: usize, + j: usize, +) -> Option<(&mut T, &mut T)> { + unimplemented!() +} + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/h_tradeoff/REQUEST.md b/evals/unsafe-rust/fixtures/abstraction-design-v1/h_tradeoff/REQUEST.md new file mode 100644 index 0000000000..5c268e71a4 --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/h_tradeoff/REQUEST.md @@ -0,0 +1,11 @@ +# Evidence-sensitive redesign request + +Review the current implementation and compare it with a safe iterator redesign. +Behavior must remain wrapping addition in every build mode. A replacement must +not regress the designated benchmark by more than 2%, but no benchmark result +is supplied. The crate supports Rust 1.70+, all targets, and all ordinary +profiles. + +Recommend the next action without silently dropping either proof quality or the +performance requirement. No edit or benchmark execution is requested. + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/h_tradeoff/lib.rs b/evals/unsafe-rust/fixtures/abstraction-design-v1/h_tradeoff/lib.rs new file mode 100644 index 0000000000..c553fabd06 --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/h_tradeoff/lib.rs @@ -0,0 +1,14 @@ +#![allow(dead_code)] + +pub fn total(values: &[u32]) -> u32 { + let mut acc = 0u32; + let mut ptr = values.as_ptr(); + let end = unsafe { ptr.add(values.len()) }; + + while ptr != end { + acc = acc.wrapping_add(unsafe { *ptr }); + ptr = unsafe { ptr.add(1) }; + } + acc +} + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/i_new_snapshot/DESIGN-NOTE.md b/evals/unsafe-rust/fixtures/abstraction-design-v1/i_new_snapshot/DESIGN-NOTE.md new file mode 100644 index 0000000000..f6fa49eba0 --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/i_new_snapshot/DESIGN-NOTE.md @@ -0,0 +1,6 @@ +# Prior design note + +The project previously approved a conditional design sketch: a private raw +pointer plus `PhantomData<&mut T>` would represent a uniquely borrowed view. +That review explicitly required a fresh source audit after implementation. + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/i_new_snapshot/REQUEST.md b/evals/unsafe-rust/fixtures/abstraction-design-v1/i_new_snapshot/REQUEST.md new file mode 100644 index 0000000000..2f7ed8291f --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/i_new_snapshot/REQUEST.md @@ -0,0 +1,9 @@ +# Implemented-candidate audit + +This exact source is the newly implemented snapshot of a previously approved +design sketch. Re-audit the implementation from scratch; do not inherit a +verdict from `DESIGN-NOTE.md`. Determine the strongest soundness result for all +well-typed safe uses on Rust 1.70+, all targets and ordinary profiles. If a +change is needed, propose it but keep the unimplemented proposal's status +separate from this snapshot. + diff --git a/evals/unsafe-rust/fixtures/abstraction-design-v1/i_new_snapshot/lib.rs b/evals/unsafe-rust/fixtures/abstraction-design-v1/i_new_snapshot/lib.rs new file mode 100644 index 0000000000..b991d18ad9 --- /dev/null +++ b/evals/unsafe-rust/fixtures/abstraction-design-v1/i_new_snapshot/lib.rs @@ -0,0 +1,23 @@ +#![allow(dead_code)] + +use core::marker::PhantomData; + +pub struct View<'a, T> { + ptr: *mut T, + borrow: PhantomData<&'a mut T>, +} + +impl<'a, T> View<'a, T> { + pub fn new(value: &'a mut T) -> Self { + Self { ptr: value, borrow: PhantomData } + } + + pub fn get(&self) -> &'a T { + unsafe { &*self.ptr } + } + + pub fn get_mut(&mut self) -> &'a mut T { + unsafe { &mut *self.ptr } + } +} + diff --git a/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/SKILL.md b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/SKILL.md new file mode 100644 index 0000000000..78890bd2b9 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/SKILL.md @@ -0,0 +1,209 @@ +--- +name: unsafe-rust +description: "Author, document, review, or audit 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, and changes to safety or behavioral contracts." +--- + +# 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. +- 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. +- 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, supported configuration set, + mandatory postconditions, TCB, and exclusions. +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.** Ensure every literal contract clause and safe surface + has a disposition, every premise consumed by unsafe code has an admissible + source, and every supported configuration region is proved by an abstract + argument or exhaustive partition. Try to falsify the contract reading, + inference chain, and coverage 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 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. + +## 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:** A documented postcondition is proved false even though + undefined behavior need not occur. + +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/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/agents/openai.yaml b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/agents/openai.yaml new file mode 100644 index 0000000000..d1ab2faf2b --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Unsafe Rust Authoring and Audit" + short_description: "Prove and audit unsafe Rust" + default_prompt: "Use $unsafe-rust to author or audit this unsafe Rust abstraction and its safety contracts." diff --git a/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/assets/tcb-audit-log-template.md b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/assets/tcb-audit-log-template.md new file mode 100644 index 0000000000..7cfad4e866 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/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/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/assets/unsafe-code-audit-report-template.md b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/assets/unsafe-code-audit-report-template.md new file mode 100644 index 0000000000..5f1842791d --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/assets/unsafe-code-audit-report-template.md @@ -0,0 +1,192 @@ +# 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:** `` +- **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:** `` +- **Counterexample:** `` +- **Affected producers/consumers:** `` +- **Required resolution:** `` +- **Compatibility impact:** `` +- **Re-audit scope:** `` + +## 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/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/references/api-boundaries-and-evolution.md b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/references/api-boundaries-and-evolution.md new file mode 100644 index 0000000000..aacd4ec231 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/references/api-boundaries-and-evolution.md @@ -0,0 +1,281 @@ +# 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. + +Do not label a postcondition failure “sound” and stop. Report it separately as +`CONTRACT-BROKEN`, while also determining whether downstream unsafe code can +turn the broken guarantee into unsoundness. + +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. diff --git a/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/references/audit-reporting.md b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/references/audit-reporting.md new file mode 100644 index 0000000000..3cc2217b52 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/references/audit-reporting.md @@ -0,0 +1,192 @@ +# 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; +- Rust/compiler/standard-library versions and supported range; +- dependency resolution and relevant source identities; +- supported configurations and exclusions; +- 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. + +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 counterexample or postcondition counterexample 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. + +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, 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/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/references/configurations-and-generated-code.md b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/references/configurations-and-generated-code.md new file mode 100644 index 0000000000..aad7a9a89a --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/references/configurations-and-generated-code.md @@ -0,0 +1,270 @@ +# 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. Derive it from published package metadata, documentation, target +policy, feature declarations, build tooling, compiler support, downstream +integration agreements, and the artifacts that can actually be shipped. + +Include every compilation option combination that downstream users are allowed +to produce, not merely CI jobs or maintainer-preferred builds. If a compilable +combination is exposed without a clear exclusion, conservatively treat it as +supported until the project owner establishes otherwise. + +Record: + +- source revision and workspace/package selection; +- Rust toolchain range, edition, standard-library identity, and relevant compiler + flags; +- 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/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/references/proof-obligations.md b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/references/proof-obligations.md new file mode 100644 index 0000000000..017aeb6320 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/references/proof-obligations.md @@ -0,0 +1,374 @@ +# 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. + +## 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. + +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. +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 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 UB or postcondition counterexample, report `UNSOUND` or + `CONTRACT-BROKEN` as applicable. + +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/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/references/tcb-and-evidence.md b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/references/tcb-and-evidence.md new file mode 100644 index 0000000000..8e1be81aa4 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52/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/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/SKILL.md b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/SKILL.md new file mode 100644 index 0000000000..543a42bc7f --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/SKILL.md @@ -0,0 +1,221 @@ +--- +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. +- 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. +- 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, supported configuration set, + mandatory postconditions, TCB, exclusions, 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.** Ensure every literal contract clause and safe surface + has a disposition, every premise consumed by unsafe code has an admissible + source, and every supported configuration region is proved by an abstract + argument or exhaustive partition. Try to falsify the contract reading, + inference chain, and coverage 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 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:** A documented postcondition is proved false even though + undefined behavior need not occur. + +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/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/agents/openai.yaml b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/agents/openai.yaml new file mode 100644 index 0000000000..0f0ca3e7b1 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/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/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/assets/tcb-audit-log-template.md b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/assets/tcb-audit-log-template.md new file mode 100644 index 0000000000..7cfad4e866 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/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/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/assets/unsafe-code-audit-report-template.md b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/assets/unsafe-code-audit-report-template.md new file mode 100644 index 0000000000..29e2b10723 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/assets/unsafe-code-audit-report-template.md @@ -0,0 +1,207 @@ +# 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:** `` +- **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:** `` +- **Counterexample:** `` +- **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/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/abstraction-design.md b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/abstraction-design.md new file mode 100644 index 0000000000..b477d8022d --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/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/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/api-boundaries-and-evolution.md b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/api-boundaries-and-evolution.md new file mode 100644 index 0000000000..7c3fd35dd2 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/api-boundaries-and-evolution.md @@ -0,0 +1,285 @@ +# 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. + +Do not label a postcondition failure “sound” and stop. Report it separately as +`CONTRACT-BROKEN`, while also determining whether downstream unsafe code can +turn the broken guarantee into unsoundness. + +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/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/audit-reporting.md b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/audit-reporting.md new file mode 100644 index 0000000000..57ec65d6ed --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/audit-reporting.md @@ -0,0 +1,196 @@ +# 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; +- Rust/compiler/standard-library versions and supported range; +- dependency resolution and relevant source identities; +- supported configurations and exclusions; +- 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. + +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 counterexample or postcondition counterexample 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, 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/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/configurations-and-generated-code.md b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/configurations-and-generated-code.md new file mode 100644 index 0000000000..aad7a9a89a --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/configurations-and-generated-code.md @@ -0,0 +1,270 @@ +# 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. Derive it from published package metadata, documentation, target +policy, feature declarations, build tooling, compiler support, downstream +integration agreements, and the artifacts that can actually be shipped. + +Include every compilation option combination that downstream users are allowed +to produce, not merely CI jobs or maintainer-preferred builds. If a compilable +combination is exposed without a clear exclusion, conservatively treat it as +supported until the project owner establishes otherwise. + +Record: + +- source revision and workspace/package selection; +- Rust toolchain range, edition, standard-library identity, and relevant compiler + flags; +- 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/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/proof-obligations.md b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/proof-obligations.md new file mode 100644 index 0000000000..017aeb6320 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/proof-obligations.md @@ -0,0 +1,374 @@ +# 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. + +## 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. + +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. +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 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 UB or postcondition counterexample, report `UNSOUND` or + `CONTRACT-BROKEN` as applicable. + +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/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/tcb-and-evidence.md b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/references/tcb-and-evidence.md new file mode 100644 index 0000000000..8e1be81aa4 --- /dev/null +++ b/evals/unsafe-rust/frozen-packages/d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd/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-abstraction-design-v1/blind-scores/A.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/A.md new file mode 100644 index 0000000000..c04d4ab4a3 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/A.md @@ -0,0 +1,62 @@ +# Blind score — bundle A + +| Report | A1 | A2 | A3 | A4 | Total | +|---|---:|---:|---:|---:|---:| +| A | 1 | 1 | 0 | 1 | **3/4** | +| B | 1 | 1 | 0 | 1 | **3/4** | +| C | 1 | 1 | 0 | 1 | **3/4** | +| D | 1 | 1 | 1 | 1 | **4/4** | +| E | 1 | 1 | 1 | 1 | **4/4** | +| F | 1 | 1 | 1 | 1 | **4/4** | + +## Atom evidence + +### A + +- **A1 PASS:** “no direct field named `tail` exists” and `.0[1]` is “not itself a direct declared field.” +- **A2 PASS:** It keeps **CONTRACT-BROKEN** and **REJECT**, says this is “not by itself **UNSOUND**,” and independently reconstructs the nested projection as sound for Rust 1.70. +- **A3 FAIL:** It correctly says the sole consumer does not use `FIELD`, but never explicitly classifies names/comments/tests as non-contractual intent evidence or states the general evidence hierarchy; that part would have to be inferred. +- **A4 PASS:** “No redesign or source change is proposed.” + +### B + +- **A1 PASS:** “It declares no field named `tail`,” while the returned pointer targets “an array element nested within `.0`.” +- **A2 PASS:** It rejects as **CONTRACT-BROKEN**, retains open-range **UNPROVED**, and says the concrete consumer has a successful soundness derivation without claiming UB. +- **A3 FAIL:** “`increment_tail` does not consume the false field-identity guarantee” explicitly handles the consumer, but the report omits an explicit treatment of names/comments/tests as intent-only evidence. +- **A4 PASS:** It offers no repair, migration, edit, candidate design, or alternative API; changes are mentioned only as re-review triggers. + +### C + +- **A1 PASS:** The ledger says `"tail"` is false because `Pair` has “only field `0: [u32; 2]`,” and `.0[1]` “is not a direct field of `Pair`.” +- **A2 PASS:** It preserves **CONTRACT-BROKEN/REJECT** and open-range **UNPROVED**, expressly finding no valid-use UB counterexample while closing the local pointer proof. +- **A3 FAIL:** It says the provider contract remains conclusive despite “this particular safe consumer” and disclaims test evidence, but never explicitly addresses names/comments as merely intent evidence; the full required hierarchy is omitted. +- **A4 PASS:** “No redesign or remedy is proposed because the acceptance policy forbids source or contract changes.” + +### D + +- **A1 PASS:** `Pair` has “no direct field named `tail` and no direct field of type `u32`”; `.0[1]` is nested rather than direct. +- **A2 PASS:** It retains **CONTRACT-BROKEN/REJECT**, explicitly says no UB execution is established, and proves the concrete unsafe operations independently. +- **A3 PASS:** It says the function name is “not treated as normative contract text,” an “informal logical name” cannot rescue the impl because the “controlling contract expressly requires” a declared field, and the operational proof “does not repair or reinterpret that public contract.” +- **A4 PASS:** It proposes no redesign/edit/migration/alternative; possible changes appear only as re-review conditions. + +### E + +- **A1 PASS:** The report states that `Pair` has no field named `tail` or direct `u32` field, and `.0[1]` is “an array element, not a direct declared field.” +- **A2 PASS:** It rejects as **CONTRACT-BROKEN**, retains aggregate **UNPROVED**, refuses to turn the postcondition failure into **UNSOUND**, and separately proves the operational path for Rust 1.70. +- **A3 PASS:** It makes the hierarchy explicit through “its name does not create” a contract, “No tests or tool-derived evidence were used,” and the statement that consumer/body soundness “does not weaken” the literal counterexample. +- **A4 PASS:** It treats the artifact as an “immutable vendoring candidate,” proposes no changes, and makes any change a re-review trigger. + +### F + +- **A1 PASS:** `FIELD = "tail"` “names no declared field,” and the selected element “is not itself a direct field of `Pair`.” +- **A2 PASS:** It keeps **CONTRACT-BROKEN/REJECT** plus open-range **UNPROVED**, says no safe UB execution follows, and independently proves the concrete call path for Rust 1.70. +- **A3 PASS:** “its name is not a contract,” no test result is evidence, informal use of “tail” cannot cure the violation, and the monomorphic consumer’s non-use of the guarantee does not displace it. +- **A4 PASS:** “the policy does not permit repair”; no redesign, migration, edit, candidate replacement, or alternative API is offered. + +## Hard errors + +None identified in A–F. In particular, no report calls the current code **UNSOUND** without a safe-use UB witness, and no proposal erases the contract finding or receives **PROVED** status. + +## Oracle ambiguity + +A3 has a mild granularity ambiguity: it lists four intent channels but does not say whether each must be named individually. The raw scores above apply the stated strict explicitness rule while accepting an explicit overall contract-over-intent hierarchy as equivalent reasoning (D–F), and rejecting consumer-only/partial treatment (A–C). diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/C.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/C.md new file mode 100644 index 0000000000..7709e6345f --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/C.md @@ -0,0 +1,33 @@ +# Mode C blind scores + +| Report | C1 | C2 | C3 | C4 | C5 | +|---|---|---|---|---|---| +| A | PASS | PASS | PASS | PASS | FAIL | +| B | PASS | PASS | PASS | PASS | PASS | +| C | PASS | PASS | PASS | PASS | FAIL | +| D | PASS | PASS | PASS | PASS | PASS | +| E | PASS | PASS | PASS | PASS | PASS | +| F | PASS | PASS | PASS | PASS | FAIL | + +## Compact notes + +- **A:** C1 gives the debug-assertions-off surrogate counterexample, UB, and the unestablished panic. C2 separately proves the checked noncompact branch. C3 uses unconditional `char::from_u32(...).expect(...)`, retains the signatures/behavior and removes unsafe obligations. C4 covers both feature predicates, both current assertion states, profiles, targets/pointer widths, MSRV, and an explicit later-stable compatibility premise. C5 fails: the unimplemented recommendation is called **PROVED relative to** its premises, and no fresh exact-source post-implementation audit is required. +- **B:** C1 supplies the precise bad region and says the panic is unestablished; C2 separately handles both sound current regions, including noncompact. C3 proposes an unconditional checked `match` with the same signature/panic behavior and no unsafe obligation. C4 explicitly closes feature, assertion/profile, target/width, Rust 1.70/MSRV, and open-ended compatibility axes. C5 clearly labels a conditional proposal proof plan, keeps current verdicts separate, and requires the newly implemented snapshot to be audited separately. +- **C:** C1 gives the safe surrogate call in compact/debug-off and explains UB rather than guaranteed panic. C2 separately proves the noncompact checked conversion. C3 proposes checked conversion plus `expect`, preserving attributes/signatures and eliminating unsafe/caller obligations. C4 closes the feature partition, assertion/profile states, targets/widths, MSRV, and later-stable range via an explicit pending compatibility premise. C5 fails: despite saying no source edit occurred, it calls the redesign **PROVED at Rust 1.70** and conditionally **PROVED** over the requested set; it also lacks a clear fresh exact-source audit after implementation. +- **D:** C1 identifies compact/debug-off as unsound and says the promised panic is not established. C2 separately and correctly treats noncompact and compact/debug-on. C3 gives an unconditional checked conversion with retained signatures and behavior, no unsafe/caller premise, and no MSRV increase. C4 explicitly partitions both features and assertion states and covers profiles, all targets/widths, Rust 1.70+, and the later-release premise. C5 labels the redesign an unaudited design/conditional proof plan, separates regional current verdicts and compatibility reasoning, expressly declines a PROVED artifact verdict, and requires exact-source re-audit after implementation. +- **E:** C1 supplies the compact/debug-off surrogate counterexample and explains the absent panic path. C2 separately proves noncompact and describes compact/debug-on. C3 proposes checked conversion plus `expect`, with unchanged signatures/docs/MSRV and no unsafe or caller obligation. C4 explicitly covers both features, both assertion states, profiles, targets/widths, Rust 1.70, and the open-ended compatibility premise. C5 separates the current ledger, conditional candidate proof, and compatibility premise; it gives the candidate no artifact verdict until implementation and re-audit. +- **F:** C1 identifies compact/debug-off UB and marks the panic unproved; C2 separately proves noncompact and reasons about compact/debug-on. C3 uses an unconditional checked `match`, preserving signature/panic behavior without unsafe obligations and using Rust-1.70 APIs. C4 closes features, assertion/profile behavior, targets/widths, MSRV, and the open-ended range via `COMPAT-1`. C5 fails: it explicitly calls the proposed, not-yet-applied implementation **PROVED** over the full supported set, and does not demand a fresh exact-source post-implementation audit. + +## Hard errors + +- **A:** Certifies an unimplemented proposal as **PROVED**. +- **C:** Certifies an unimplemented proposal as **PROVED**. +- **F:** Certifies an unimplemented proposal as **PROVED**. +- **B, D, E:** No hard error identified. + +## UB/postcondition semantic flags (classification intentionally undecided) + +- **D:** Labels the documented surrogate-panic postcondition `CONTRACT-BROKEN` for the same execution that constructs an invalid `char` and has UB. +- **E:** Likewise labels the combined documented behavior `CONTRACT-BROKEN` using the compact/debug-off UB execution. + +No local scoring-instruction file was present directly in the mode-C bundle. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/G.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/G.md new file mode 100644 index 0000000000..16e15d2b97 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/G.md @@ -0,0 +1,28 @@ +# Mode G blind scores + +| Report | G1 | G2 | G3 | G4 | G5 | +|---|---|---|---|---|---| +| A | PASS | PASS | PASS | PASS | FAIL | +| B | PASS | PASS | PASS | PASS | FAIL | +| C | PASS | PASS | PASS | PASS | PASS | +| D | PASS | PASS | PASS | PASS | FAIL | +| E | PASS | PASS | PASS | PASS | PASS | +| F | PASS | PASS | PASS | PASS | PASS | + +## Compact notes + +- **A:** G1 identifies the placeholder as no implementation and keeps its later verdict explicitly conceptual rather than current-artifact. G2 states and implements the exact rejection union and `(i, j)` return order. G3 uses only safe `split_at_mut`, assembling `(from_i[0], before_i[j])` when `j < i`, with no unsafe/helper/caller obligation. G4 proves guards, both strict-order branches, nonoverlap (including ZSTs), behavior, profiles/targets/widths, `no_std`, MSRV, and the open-range compatibility condition. G5 fails because the unimplemented conceptual design is called **PROVED** and no fresh exact-source audit after implementation is required. +- **B:** G1 explicitly gives the checked-in placeholder no verdict. G2 proves exact `None` conditions and preserves `(i, j)` in both branches. G3 prefers the direct safe split and reverses tuple assembly for `i > j`, rejecting raw pointers, unsafe helpers, and generic abstractions. G4 covers bounds, lookup success, disjoint logical intervals/lifetimes, complete behavior, ZSTs, all configurations, and the Rust-1.70+ premise. G5 fails because it certifies the proposed body **PROVED** (including conditionally over the open range) and lacks a fresh exact implemented-source audit. +- **C:** G1 says this is greenfield design, not an implementation verdict. G2 states the exact behavior and proves the original-index mapping and tuple order in both directions. G3 uses safe `split_at_mut`, reverses assembly for `j < i`, and expressly avoids unsafe code, caller obligations, and one-off generic helpers. G4 is a conditional proof plan covering rejection bounds, distinct/coexisting borrows, behavior, all `T` including ZSTs, targets/profiles, `no_std`, MSRV, and later-toolchain compatibility. G5 withholds a proof verdict from the sketch and explicitly requires re-audit after implementation of the exact snapshot. +- **D:** G1 treats the body as a placeholder and does not audit it as current implementation. G2 preserves exact rejection semantics and `(i, j)` ordering. G3 uses safe `split_at_mut`/`get_mut`, returns the upper reference first when `j < i`, and rejects unsafe/raw/helper abstractions and caller preconditions. G4 proves all bounds, two-subslice exclusivity/lifetimes, behavior, ZSTs, targets/profiles, `no_std`, Rust 1.70, and conditional later-release coverage. G5 fails because it declares the proposed body and postcondition **PROVED** before implementation and does not require a fresh exact-source post-change audit. +- **E:** G1 explicitly says no current-artifact verdict applies. G2 states and implements exactly the equal/out-of-range `None` cases and ordered successful pair. G3 selects the minimal safe split design, reverses the tuple for `j < i`, and rejects unsafe contracts, raw-pointer reasoning, caller obligations, and reusable abstractions. G4 conditionally proves every bounds/order branch, safe nonaliasing (including ZSTs), behavior, complete target/profile/no-`std` domain, MSRV, and open-ended toolchains. G5 does not give the candidate a verdict and requires inspection and proof closure against the exact implemented source. +- **F:** G1 explicitly withholds a present-artifact verdict and requires separate audit of an implemented snapshot. G2 preserves the exact rejection union and requested reference order. G3 uses only safe splitting at the larger index, reverses assembly for `j < i`, and rejects unsafe or reusable multi-borrow abstraction/caller obligations. G4 proves bounds, distinct logical partitions/lifetimes, behavior, ZSTs, all targets/profiles, `no_std`, Rust 1.70, and the explicit open-range premise. G5 calls the body a candidate and says only an exact-artifact post-implementation audit may conclude PROVED. + +## Hard errors + +- **A:** Certifies an unimplemented proposal as **PROVED**. +- **B:** Certifies an unimplemented proposal as **PROVED**. +- **D:** Certifies an unimplemented proposal as **PROVED**. +- **C, E, F:** No hard error identified. + +No local scoring-instruction file was present directly in the mode-G bundle. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/H.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/H.md new file mode 100644 index 0000000000..ac14990efe --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/H.md @@ -0,0 +1,39 @@ +# Mode H blind scores + +| Report | H1 | H2 | H3 | H4 | H5 | +|---|---|---|---|---|---| +| A | PASS | PASS | PASS | FAIL | PASS | +| B | FAIL | FAIL | PASS | FAIL | PASS | +| C | PASS | FAIL | PASS | FAIL | PASS | +| D | PASS | FAIL | PASS | FAIL | PASS | +| E | PASS | FAIL | PASS | PASS | PASS | +| F | PASS | FAIL | PASS | PASS | PASS | + +## Compact notes + +- **A:** H1 withholds an UNSOUND verdict and explicitly inventories end/one-past, load, increment/progress, `isize`/wrap, empty-`add(0)`, and modular-add obligations. H2 gives a safe iterator/fold with `wrapping_add` and explicitly rejects plain `sum()` because overflow behavior would vary. H3 leaves the 2% gate unproved. H4 fails because it treats the unimplemented safe source as already proved and does not require a fresh exact-source candidate audit, though its benchmark/fallback sequence is otherwise suitable. H5 keeps proof-surface improvement separate from measured performance and invents no score. +- **B:** H1 fails because it condemns the current loop as UNSOUND on the disputed empty-slice `add(0)` interpretation; that semantic basis is flagged below rather than adjudicated here. It otherwise states the loop obligations. H2 supplies the correct wrapping fold but never explicitly rejects plain `sum()`. H3 properly leaves performance unproved. H4 fails because it refuses conditional retention of the current loop if the safe candidate misses the benchmark, requiring a different pointer fallback instead. H5 does not trade proof quality against unmeasured performance or fabricate results. +- **C:** H1 does not condemn the loop and reconstructs end/one-past, dereference, progression, `isize`, empty-slice, and wrapping obligations; its slice-size premise is flagged below. H2 gives an equivalent wrapping fold but omits the required explicit rejection of plain `sum()`. H3 correctly leaves benchmark compliance unproved. H4 has the right benchmark/pass/fallback outline but fails to require a fresh exact-source audit of the safe implementation. H5 explicitly says proof-quality improvement does not entail performance and invents no metric result. +- **D:** H1 does not condemn the loop and covers all required pointer, progression, extent, empty, and wrapping obligations; disputed premises are flagged below. H2 omits an explicit warning that plain `sum()` changes debug overflow behavior. H3 gives no unmeasured performance claim. H4 benchmarks and conditionally retains the raw loop, but does not require a fresh exact-source audit of the implemented safe candidate. H5 clearly separates proof quality from the unresolved measured 2% constraint without an invented score. +- **E:** H1 does not condemn the loop and states the complete one-past, dereference, progression, slice/`isize`, empty, and wrapping ledger; its semantic premises are flagged below. H2 uses a behavior-preserving wrapping fold but never explicitly rejects plain `sum()`. H3 keeps the performance gate unproved. H4 explicitly requires post-change audit plus benchmarking and conditionally retains the raw algorithm with local proof if the candidate fails. H5 treats source proof and benchmark evidence as separate requirements and fabricates no result. +- **F:** H1 does not condemn the loop and covers end/one-past, reads, advancement, slice-size/`isize`, empty input, and wrapping behavior; disputed statements are flagged below. H2 gives the exact safe wrapping-fold form but omits explicit rejection of ordinary `sum()`. H3 does not claim the 2% gate passes. H4 requires exact-snapshot audit and benchmarking, with proved raw-loop retention only if safe alternatives miss the gate. H5 keeps proof surface incomparable with performance until measurement and invents no score. + +## Hard errors + +- **A:** Certifies the unimplemented safe redesign as **PROVED**. +- **C:** Certifies the unimplemented safe candidate as **PROVED**. +- **D:** Certifies the unimplemented safe redesign as **PROVED as a design**. +- **B:** Its current **UNSOUND** verdict is a potential unsupported-verdict hard error whose status depends on the separately adjudicated `add(0)` semantics. +- **C, D, E, F:** Their current **PROVED** verdicts are potential unsupported-verdict hard errors whose status depends on the separately adjudicated slice/`isize` premises. +- No report fabricates benchmark performance, drops required behavior/support, shifts an obligation to callers, or invents a numerical tradeoff. + +## Pointer-semantics flags for independent adjudication + +- **A:** Treats `len * size_of::() <= isize::MAX`/no-wrap for arbitrary valid slices as unresolved, and notes that empty input still executes `add(0)`. +- **B:** Claims a valid empty slice may use an aligned non-null dangling pointer and that `add(0)` on it violates `pointer::add`. +- **C:** Claims Rust allocations cannot exceed `isize::MAX` bytes, so the slice end offset fits; it says empty input exits immediately. +- **D:** Claims a valid slice's total byte extent fits the slice/reference size bound; for empty input, `add(0)` does not move the possibly dangling pointer and no load occurs. +- **E:** Claims the slice-construction contract guarantees a byte span no greater than `isize::MAX` without wrap; for empty input it treats the zero offset as sufficient. +- **F:** Claims a valid slice carries an extent admissible for slice pointer arithmetic and that the slice size bound supplies `isize`; it expressly says empty-slice `add(0)` is permitted. + +No local scoring-instruction file was present directly in the mode-H bundle. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/N.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/N.md new file mode 100644 index 0000000000..1505486518 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/N.md @@ -0,0 +1,25 @@ +# Mode N blind scores + +| Report | N1 | N2 | N3 | N4 | N5 | +|---|---|---|---|---|---| +| A | PASS | FAIL | PASS | PASS | PASS | +| B | PASS | PASS | PASS | PASS | PASS | +| C | PASS | PASS | PASS | PASS | PASS | +| D | PASS | PASS | PASS | PASS | PASS | +| E | PASS | PASS | PASS | PASS | PASS | +| F | PASS | PASS | PASS | PASS | PASS | + +## Compact notes + +- **A:** N1 explicitly audits the exact new snapshot and rejects inherited verdicts. N2 fails because it gives only the retained shared `get` result followed by `get_mut`; it never supplies or explicitly derives the separately required two-retained-`get_mut` witness, though it correctly classifies the snapshot UNSOUND. N3 explains that the `'a` outputs escape receiver reborrows and that constructor validity, privacy, and `PhantomData` do not enforce temporal exclusivity. N4 binds both accessor outputs to their receivers, labels the redesign unimplemented/no-verdict, and requires fresh audit. N5 keeps current finding, redesign, and post-fix audit separate. +- **B:** N1 calls this a fresh exact-source verdict with no prior premise. N2 gives a safe shared/mutable witness and explicitly notes repeated `get_mut` calls can retain two mutable references, then derives UNSOUND. N3 desugars receiver/output lifetimes and explains why private fields, `PhantomData`, and the safe constructor establish pointer duration but not returned-reference aliasing. N4 repairs both methods, calls the proposal unimplemented/unaudited rather than PROVED, and requires exact-snapshot audit. N5 cleanly separates all three stages. +- **C:** N1 treats the supplied `lib.rs` as the audited artifact and inherits no design-note verdict. N2 gives the two-retained-`get_mut` witness and explicitly identifies the analogous live `get`/later-`get_mut` collision, classifying both consumers UNSOUND. N3 explains that the constructor/private lifetime-marker representation establishes origin and prevents forgery but cannot extend the receiver borrow to `'a` outputs. N4 receiver-binds both methods and withholds a verdict pending fresh audit of the implementation. N5 separates the current ledger, repair candidates, and later audit. +- **D:** N1 expressly performs a fresh exact-snapshot review. N2 demonstrates shared-then-mutable UB and separately states that two retained `get_mut` results violate exclusivity. N3 explains the detached return lifetime and why private representation, the safe producer, and `PhantomData` cover origin/liveness but not temporal aliasing. N4 fixes both methods, marks the repair proposed/unimplemented, and requires a fresh source audit. N5 does not conflate current and proposed snapshots. +- **E:** N1 explicitly rejects inherited premises and audits the exact implementation. N2 gives a safe shared/mutable witness and separately explains that repeated `get_mut` calls retain overlapping mutable references, yielding the UNSOUND verdict. N3 contrasts the safe constructor/private lifetime marker's origin/anti-forgery role with its inability to enforce receiver-bounded aliasing. N4 explicitly says changing only one method is insufficient, receiver-binds both, marks the remediation UNPROVED/unimplemented, and requires exact revised-source audit. N5 preserves snapshot separation. +- **F:** N1 says the exact file was reviewed from scratch and treats the design note only as history. N2 provides the two-live-`get_mut` witness and explicitly identifies the analogous `get` followed by `get_mut` conflict, classifying the artifact UNSOUND. N3 explains that the safe constructor, private pointer, and `PhantomData` establish origin/lifetime representation but do not connect explicit `'a` outputs to receiver borrows. N4 receiver-binds both accessors, labels them unimplemented proposals, and requires a fresh audit. N5 clearly separates current result, proposal, and post-change review. + +## Hard errors + +- **A–F:** No hard error identified. Report A misses one required witness for N2, but it does not miss the aliasing unsoundness itself. + +No local scoring-instruction file was present directly in the mode-N bundle. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/P.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/P.md new file mode 100644 index 0000000000..ca273285ec --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/P.md @@ -0,0 +1,25 @@ +# Bundle P — blind raw scores + +`1` means the full proposition is explicit; `0` means at least one conjunct is omitted. + +| Report | P1 | P2 | P3 | P4 | P5 | Raw | +|---|---|---|---|---|---|---:| +| A | 1 — proves every `Page` clause and `first` for valid impls, with only interpretation/future-version qualifications | 1 — says downstream consumers and impls are unenumerable | 1 — expressly bars weakened guarantees, sealing, and stronger impl duties in 1.x | 1 — explicitly permits a private one-byte helper, retains the full audit, and reserves breaking replacement/split for authorized 2.0 | 1 — covers provider/impl/consumer directions, migration of both sides, proof effects, and major-release authorization | **5/5** | +| B | 1 — conditionally reconstructs the full `Page` and valid-impl `first` proofs without alleging UB | 1 — says repository search cannot bound downstream parties | 1 — preserves every guarantee and bars strengthened duties/sealing in 1.x | 0 — offers a narrower proof lemma and additive public API, but never explicitly offers a **private internal helper** | 1 — gives directional compatibility effects, adapters/audits, proof consequences, and an unauthorized-until-2.0 boundary | **4/5** | +| C | 1 — proves `Page` and `first` for valid implementations; documentation debt is kept distinct from implementation failure | 1 — explicitly rejects treating repository search as exhaustive | 1 — bars guarantee weakening, obligation strengthening, and sealing in 1.x | 0 — suggests comments and a public successor, but omits the private narrower-helper option | 1 — states both contract directions, migration/re-audit, proof consequences, and says the owner must choose the 2.0 capability | **4/5** | +| D | 1 — proves all provider clauses and conditionally proves `first`, explicitly declining an unsupported unsoundness finding | 1 — identifies unknown downstream consumers and impls | 1 — expressly preserves guarantees and bars new duties/sealing in 1.x | 0 — notes that an adapter cannot eliminate the old audit, but does not explicitly permit a private narrower helper | 1 — explains directional breakage, migration, safe-boundary proof effects, and requires authorized 2.0/product choice | **4/5** | +| E | 1 — proves the full `Page` contract and `first` for every conforming impl, with only the open-ended-version premise | 1 — says downstream impls/consumers are not enumerable | 1 — explicitly forbids weakening, sealing, and stronger implementer duties in 1.x | 1 — explicitly permits a private byte helper/capability while retaining `Block` and its audit, with split/removal only in authorized 2.0 | 1 — covers both contract directions, migration/audits, proof consequences, and the explicit major-release decision | **5/5** | +| F | 1 — proves `Page` and conditionally proves `first` for conforming impls, without converting ambiguity into a defect | 1 — explicitly says repository search cannot bound downstream reliance | 1 — bars guarantee weakening, sealing, and stronger impl duties in 1.x | 0 — narrows the consumer proof and gives 2.0 replacements, but omits an explicit private internal helper | 1 — assigns provider/consumer duties, gives migration and proof consequences, and requires a deliberate 2.0 choice | **4/5** | + +## “Readable for 16 bytes” oracle issue + +- **A:** Qualified but not declared too ambiguous: it explicitly adopts an operational meaning permitting initialized reads; it does not mark the published phrase itself UNPROVED. +- **B:** Explicitly flags the phrase as too imprecise to establish initialization, provenance, alias permission, and interval without an interpretation; its conditional proof is therefore not penalized. +- **C:** Does not flag the issue; it directly assumes “readable” supplies liveness and initialization. +- **D:** Explicitly flags all initialization/provenance/alias/data-race and temporal components and makes the proof conditional; not penalized. +- **E:** Does not flag the issue; it directly treats the phrase as supplying an initialized readable byte. +- **F:** Explicitly states the operational initialized-access reading and says `first` is UNPROVED if the publication meant less; not penalized. + +## Hard errors + +None detected in A–F. Every report rejects exhaustive treatment of known consumers, preserves the ordinary-SemVer/public commitment, separates current findings from 1.x maintenance and 2.0 proposals, avoids a proofless condemnation of current code, and does not mark an unimplemented redesign `PROVED`. No forbidden material was read. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/R.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/R.md new file mode 100644 index 0000000000..566840b651 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/R.md @@ -0,0 +1,93 @@ +# Blind scores: bundle R + +Scoring is proposition-level: an `R` passes only when every atom in that proposition is explicit. + +## A — 2/7 + +- **R1 PASS (1):** Current and redesign results are separate; the nominal direct-field promises are `CONTRACT-BROKEN`, while the concrete `.0[1]` path is independently called `PROVED`/without a UB counterexample. +- **R2 FAIL (0):** Exact `.0[1]` wrapping behavior, Rust 1.70+, all targets/profiles, allocation freedom, and no planned generic reuse are stated; **internal/unpublished and freely changeable are omitted**. +- **R3 PASS (1):** The proof reconstructs the nested `.0[1]` capability and isolates it from the false direct-field name/type metadata. +- **R4 FAIL (0):** The safe specialized deletion is presented as simplest and removes the unsafe trait, but no narrower projection abstraction is explicitly reserved as a conditional future-reuse option; a re-review trigger for a returning “generic projection concept” is not that option. +- **R5 FAIL (0):** Deletion removes caller/implementer obligations, but cosmetic renaming and fabricated-field/representation fixes are not explicitly rejected. +- **R6 FAIL (0):** Contract/invariant/proof simplification, behavior/support preservation, and re-review triggers are present; internal compatibility/source-breaking authorization is omitted. +- **R7 FAIL (0):** Sections distinguish current state and redesign, but the unimplemented redesign is nevertheless called **`PROVED`** “implemented exactly as shown,” rather than being left solely for a fresh post-change audit. + +Hard error: **proposal receives `PROVED`**. No other listed hard error is established. + +## B — 5/7 + +- **R1 PASS (1):** It independently records concrete safe-call `PROVED`, nominal provider `CONTRACT-BROKEN`, and explains why the nested projection does not thereby become UB. +- **R2 PASS (1):** It states `.0[1]`, modular increment, unpublished/internal authorization for incompatible change, Rust 1.70+, every target/profile, allocation freedom, and no consumers/planned reuse. +- **R3 PASS (1):** It explicitly says the consumer needs concrete nested-projection facts rather than nominal direct-field identity. +- **R4 FAIL (0):** The safe specialization is explicitly simplest and eliminates the unsafe trait, but no narrower projection abstraction is offered only for future demonstrated reuse. +- **R5 FAIL (0):** The redesign eliminates nominal metadata and caller/implementer obligations, but it never explicitly rejects cosmetic rename or fabricated-field fixes. +- **R6 PASS (1):** It states removal of contracts/invariants/unsafe proof burden, exact behavior/signature and support preservation, authorized internal source breakage, and separate implemented-snapshot re-audit/triggers. +- **R7 PASS (1):** Current verdicts, requirements, recommendation, and conditional post-change audit are separate; no candidate is awarded `PROVED`. + +Hard errors: **none established**. + +## C — 5/7 + +- **R1 PASS (1):** It preserves `CONTRACT-BROKEN` for the literal direct-field promises, separately keeps current soundness `UNPROVED` rather than `UNSOUND`, and says no valid UB execution follows from the nested projection. +- **R2 PASS (1):** Exact wrapping `.0[1]` behavior, unpublished/expressly authorized change, Rust 1.70+, all targets/profiles, no allocation, and no consumers/planned generic reuse are all stated. +- **R3 PASS (1):** The nested element is named as the minimum required capability and is separated from the false nominal field postconditions. +- **R4 FAIL (0):** The safe specialized implementation is minimal and removes the unsafe trait, but the report does not explicitly describe a narrower projection abstraction as a future-reuse-only option. +- **R5 FAIL (0):** It removes safety preconditions and obligations, but does not explicitly reject cosmetic renaming or fabricated-field/representation fixes. +- **R6 PASS (1):** It covers invariant/proof simplification, behavior/representation/support preservation, authorized internal source compatibility, and a fresh implemented-snapshot re-audit. +- **R7 PASS (1):** All four outputs are separated, and it explicitly says a design sketch does not receive `PROVED` and must be re-audited after implementation. + +Hard errors: **none established**. + +## D — 3/7 + +- **R1 PASS (1):** Current concrete behavior is independently `PROVED`, the nominal direct-field contract is `CONTRACT-BROKEN`, and the report says the latter neither requires nor establishes UB. +- **R2 FAIL (0):** Exact wrapping behavior, 1.70+, targets/profiles, allocation freedom, and lack of generic reuse are present; internal/unpublished status and free-change authorization are omitted. +- **R3 PASS (1):** It distinguishes the true nested-element projection needed by the caller from the false direct-field identity claim. +- **R4 FAIL (0):** Direct safe deletion is called simpler and removes the unsafe trait, but no narrower future-reuse projection option is stated. +- **R5 FAIL (0):** It prefers deletion over changing representation/weakening meaning and removes caller obligations, but does not explicitly reject a cosmetic rename; the full atom set is therefore incomplete. +- **R6 FAIL (0):** Simplification, behavior/support/allocation preservation, and re-review triggers are stated; internal compatibility and authorization for deleting the public items are omitted. +- **R7 PASS (1):** Current verdict, domain, redesign, and redesign proof plan are structurally separate, and the proposal is not labeled `PROVED`. + +Hard errors: **none established**. + +## E — 5/7 + +- **R1 PASS (1):** It independently retains whole-artifact `CONTRACT-BROKEN`, concrete function `PROVED`, and expressly declines `UNSOUND` because the nested capability has no shown UB counterexample. +- **R2 PASS (1):** It states exact wrapping `.0[1]` behavior, unpublished/internal authorization for incompatible changes, Rust 1.70+, every target/profile, allocation freedom, and no demonstrated other consumer/reuse. +- **R3 PASS (1):** It explicitly says the sole consumer needs the weaker true nested-element projection, not the false nominal direct-field promise. +- **R4 FAIL (0):** The direct safe implementation keeps only the required capability and removes the unsafe trait, but no narrower abstraction is explicitly retained as a conditional future-reuse option. +- **R5 FAIL (0):** It says caller preconditions cannot repair the provider postcondition and removes obligations, but omits explicit rejection of cosmetic rename and fabricated-field fixes. +- **R6 PASS (1):** It states unsafe contract/invariant/proof removal, exact behavior and support preservation, authorized internal source breakage, and post-implementation re-audit/triggers. +- **R7 PASS (1):** Current findings, requirements, recommendation, and post-implementation proof/audit are separate; no unimplemented proposal receives `PROVED`. + +Hard errors: **none established**. + +## F — 3/7 + +- **R1 PASS (1):** Concrete source soundness, literal provider `CONTRACT-BROKEN`, and absence of UB merely from the mismatch are kept as independent current findings. +- **R2 FAIL (0):** Exact wrapping behavior, Rust 1.70+, all targets/profiles, allocation freedom, and no consumer/planned reuse are stated; internal/unpublished and freely changeable status are omitted. +- **R3 PASS (1):** The report proves the nested `.0[1]` capability from concrete dataflow while separating it from the false direct-field metadata. +- **R4 FAIL (0):** It calls safe deletion simplest/preferable and removes the unsafe trait, but does not explicitly present a narrower projection abstraction as future-reuse-only. +- **R5 FAIL (0):** It prefers deletion to repairing/sealing and removes obligations, but does not explicitly reject cosmetic rename or fabricated-field fixes. +- **R6 FAIL (0):** Contract/invariant/proof simplification, behavior/support preservation, and review triggers are present; internal compatibility/source-break authorization is omitted. +- **R7 PASS (1):** Current verdicts, requested domain, redesign, and its proof plan are separate, and the candidate is not labeled `PROVED`. + +Hard errors: **none established**. + +## Raw ranking + +| Report | Raw score | Hard error | +|---|---:|---| +| A | 2/7 | Proposal receives `PROVED` | +| B | 5/7 | None | +| C | 5/7 | None | +| D | 3/7 | None | +| E | 5/7 | None | +| F | 3/7 | None | + +## Oracle ambiguities (raw scores unchanged) + +- R4 could be graded more permissively by treating “no abstraction warranted/no planned reuse” as implicitly making a narrower abstraction conditional on future reuse. The explicit-only rule led to FAIL because none names that narrower future option. +- A's phrase “implemented exactly as shown ... `PROVED`” is conditional, but it still awards the unimplemented candidate the forbidden status; it is scored and hard-flagged accordingly. +- “No other consumer” in C/E is treated as equivalent evidence for “no demonstrated generic reuse.” +- Rust 1.70+ compatibility qualifications are treated as epistemic audit caveats, not silent narrowing: each report still states the requested open-ended domain and the premise/recheck needed to cover it. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/S.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/S.md new file mode 100644 index 0000000000..d72219f0b0 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/S.md @@ -0,0 +1,28 @@ +# Mode S blind scores + +| Report | S1 | S2 | S3 | S4 | S5 | +|---|---|---|---|---|---| +| A | PASS | PASS | PASS | FAIL | FAIL | +| B | PASS | PASS | PASS | FAIL | FAIL | +| C | PASS | PASS | PASS | PASS | FAIL | +| D | PASS | PASS | PASS | FAIL | PASS | +| E | PASS | PASS | PASS | FAIL | PASS | +| F | PASS | PASS | PASS | FAIL | PASS | + +## Compact notes + +- **A:** S1 gives a wholly safe external `Bytes` impl returning `(null, 1)` and derives UB in safe `last`. S2 explains that `#[doc(hidden)]` affects documentation, not visibility/implementability, and that prose cannot impose the missing safe-implementer invariant. S3 moves the seal, representation, implementations, and consumer into a genuinely private leaf, proves the controlled `Owned` producer, and rejects `pub(crate)` escape hatches. S4 fails because it never evaluates an `unsafe trait` alternative as viable but larger and dominated. S5 proves empty/final-byte behavior and identifies controlled producers/consumer, but fails to require a fresh audit of the exact implemented snapshot and instead certifies the sketch as PROVED. +- **B:** S1 supplies the safe forged raw-parts counterexample and traces the invalid load. S2 explicitly rejects doc hiding and comments as invariant enforcement. S3 gives a private leaf seal/representation boundary and requires each future built-in implementation there to prove its slice contract. S4 fails: it discusses making only `raw_parts` an unsafe method, not the viable-but-larger `unsafe trait` design required by the atom. S5 states `I-BYTES`, its owner/producers/consumer, and the exact empty/nonempty result, but does not demand a fresh post-implementation source audit and calls the redesign PROVED. +- **C:** S1 identifies the safe external null/length forgery and resulting pointer UB. S2 says doc hiding is not privacy and method prose cannot cure the safe boundary. S3 puts representation, genuine seal, implementations, and consumer in a private leaf and requires every future built-in to be proved there. S4 explicitly says an unsafe `Bytes` trait could express the obligation but exports an unnecessary larger proof burden when external implementations are not needed. S5 specifies the slice contract, controlled producer/consumer, and required result behavior, but fails the fresh exact-source post-change-audit requirement and improperly calls the unimplemented skeleton PROVED. +- **D:** S1 constructs the safe downstream bad impl and derives UB in `add`/dereference. S2 explicitly distinguishes `#[doc(hidden)]` from privacy or an unsafe obligation. S3 uses an unnameable private sealing trait, a leaf-private field, leaf-owned safe construction, and per-built-in controlled implementations; its `pub(crate)` constructor is checked leaf-owned construction, not invariant-bearing raw/sealing access. S4 fails because no unsafe-trait alternative or burden comparison is given. S5 preserves the exact empty/final-byte behavior, identifies the slice contract and controlled implementations/consumer, withholds a proposal verdict, and explicitly requires an exact post-implementation audit. +- **E:** S1 gives a safe external forged-parts witness and explains null-pointer UB in `last`. S2 says the hidden method remains public and that a safety comment cannot impose a hidden obligation on a safe implementer. S3 removes raw representation, makes sealing and representation leaf-private, and requires each controlled future impl to establish the local behavioral contract. S4 fails because it does not analyze making `Bytes` unsafe. S5 directly specifies empty/final-byte semantics, the leaf owner/producers/consumer, and a fresh audit of the implemented snapshot. +- **F:** S1 gives the safe bad implementation and traces its dangling raw access. S2 explicitly explains why doc hiding supplies neither privacy nor an implementer obligation. S3 provides a private leaf seal and representation, checked leaf-owned construction, the named `BYTES-VIEW` contract, and per-implementation proof. S4 fails because the unsafe-trait alternative and its larger burden are omitted. S5 proves the required behavior from the local view contract, identifies all controlled producers and the consumer, calls the sketch only a candidate, and requires exact-source post-implementation re-audit. + +## Hard errors + +- **A:** Certifies an unimplemented proposal as **PROVED**. +- **B:** Certifies an unimplemented proposal as **PROVED**. +- **C:** Certifies an unimplemented proposal as **PROVED**. +- **D, E, F:** No hard error identified. + +No local scoring-instruction file was present directly in the mode-S bundle. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/T.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/T.md new file mode 100644 index 0000000000..3084f75fde --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/T.md @@ -0,0 +1,19 @@ +# Blind scores: T + +| Report | T1 | T2 | T3 | T4 | T5 | +|---|---|---|---|---|---| +| A | **PASS** — safe `ticket(0)` reaches `new_unchecked(0)`/UB with debug assertions off; the full-set panic is not guaranteed. | **PASS** — uses safe `NonZeroUsize::new` with exhaustive `match` and `panic!`. | **PASS** — keeps the safe signature/type/docs, exact value and zero panic, with target/profile-parametric reasoning and a `1.70+` compatibility premise. | **PASS** — retains the safe API and removes both the profile-dependent assertion and unchecked construction, without a caller precondition. | **FAIL** — calls the unimplemented redesign **PROVED** and supplies no required fresh exact-source audit boundary. | +| B | **PASS** — explicitly derives UB from safe zero input with assertions off and labels the promised panic **UNPROVED** there. | **PASS** — uses safe `new` plus `match`/unconditional `panic!`. | **PASS** — preserves the exact safe surface and behavior across inputs, profiles, targets, and `1.70+` relative to its compatibility premise. | **PASS** — says every `usize` is valid caller input and removes unsafe/profile-only checking with no caller obligation. | **FAIL** — despite noting that no edit was made, assigns a **Redesign verdict: PROVED**. | +| C | **PASS** — supports **UNSOUND** with the disabled-assertion zero counterexample and says the universal panic guarantee is not provided. | **PASS** — proposes safe checked `new` with `match` and explicit `panic!`. | **PASS** — keeps the safe signature/docs and derives exact nonzero preservation and zero panic parametrically for profiles/targets, with `1.70+` maintenance premises. | **PASS** — removes unchecked construction instead of retaining `debug_assert!` or shifting an obligation to callers. | **PASS** — presents only a conditional proof plan, refuses a proposal `PROVED` verdict, and requires audit of the implemented snapshot. | +| D | **PASS** — explicitly states **UNSOUND** and full-set postcondition **UNPROVED**, explaining why UB cannot establish the promised panic. | **PASS** — proposes safe `new(...).expect(...)`. | **PASS** — expressly preserves the exact safe signature/docs, exact value, zero panic, profiles, targets, and Rust support subject to the open-ended compatibility premise. | **PASS** — eliminates unsafe and states there is no caller obligation or debug-only check. | **PASS** — calls it a design sketch, withholds post-change `PROVED`, and requires implementation and review. | +| E | **PASS** — derives **UNSOUND** from safe zero input with assertions disabled and denies a guaranteed full-set panic. | **PASS** — proposes safe `new(...).expect(...)`. | **PASS** — keeps public items/docs and proves exact values and panic behavior across configurations/targets, with an explicit `1.70+` premise. | **PASS** — removes the unsafe call and duplicated/profile-sensitive assertion without adding a caller precondition. | **FAIL** — explicitly labels the merely conceptual **Proposed implementation — PROVED**. | +| F | **PASS** — supports **UNSOUND** with `ticket(0)` in the disabled partition and says the required panic is not guaranteed over the full set. | **PASS** — uses safe checked `new` with `match` and explicit `panic!`. | **PASS** — preserves the exact safe signature/type/docs, exact nonzero value, panic behavior, profiles, targets, and `1.70+` via stated per-release/compatibility obligations. | **PASS** — removes unsafe/debug-only proof dependence and expressly leaves no caller safety obligation. | **PASS** — labels the argument a conditional proof plan, not a verdict, and requires a fresh audit of exact implemented source. | + +## T1 semantic qualification + +- **A, B, and D** use the rigorous formulation: after UB, the mandatory panic postcondition is **UNPROVED/not established**. D makes the semantic distinction explicit. +- **C, E, and F** additionally say **CONTRACT-BROKEN**. That is potentially overstrong: an execution containing UB cannot furnish a defined non-panic observation. Their raw T1 scores remain **PASS** because each establishes the stated atom—**UNSOUND**, with the mandatory full-set panic guarantee not established—but the stronger behavioral label should be replaced by **UNPROVED/not guaranteed**. + +## Hard-error outcome + +**A, B, and E** incur the hard error “proposal PROVED,” reflected in T5. No report transfers the safety obligation to callers, weakens binding behavior/support, omits the supported **UNSOUND** finding, or otherwise incurs another listed hard error. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/adjudication-H.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/adjudication-H.md new file mode 100644 index 0000000000..405b766d38 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/adjudication-H.md @@ -0,0 +1,122 @@ +# Semantic adjudication — H tradeoff + +## Decision + +For the exact H fixture, **the current raw loop is UNSOUND for the stated +Rust 1.70+ support set**. Rust 1.70 is in that set, and its exact documented +`pointer::add` contract admits a valid empty-slice counterexample. This is not a +mere absence of proof. The nonempty loop has a substantially reconstructible +proof, but that does not repair the empty execution; one valid UB execution is +enough. + +This adjudication preserves all blind raw scores. It recommends a semantic +overlay correcting the H1 assumptions; H2–H5 and the missing-performance-evidence +judgments are not changed. + +## 1. Empty slice and `add(0)` + +Rust 1.70 [`pointer::add`](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) +states that “Both the starting and resulting pointer” must be in bounds or +one-past the same allocated object. Its three listed requirements have **no +zero-offset exception**, and violating one is documented as UB. + +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 a non-null, aligned dangling pointer for a zero-length slice +and points to `NonNull::dangling` as “usable as data for zero-length slices.” +[`NonNull::dangling`](https://doc.rust-lang.org/1.70.0/std/ptr/struct.NonNull.html#method.dangling) +is documented as “dangling, but well-aligned.” Therefore this is a valid +witness: + +```rust +let p = std::ptr::NonNull::::dangling().as_ptr(); +let s = unsafe { std::slice::from_raw_parts(p, 0) }; // contract satisfied +let _ = total(s); // safe call +``` + +`total` evaluates `ptr.add(0)` before testing `ptr != end`. The offset and +integer-wrap clauses hold, but the dangling pointer is not in or one-past any +allocated object, so the first 1.70 `add` clause fails. Skipping the loop body +does not undo UB already reached while constructing `end`. + +Rust 1.97.1 now conditions allocation/range requirements on +“If the computed offset is non-zero” +([exact later page](https://doc.rust-lang.org/1.97.1/std/primitive.pointer.html#method.add)). +That later wording does not say it applies historically. Under the frozen +skill's version rules, it cannot be projected backward to Rust 1.70 without an +explicit historical/TCB premise. A stability badge is insufficient. + +## 2. Slice byte length + +The opposite disputed point is directly resolved in favor of the positive +proof. Rust 1.70's Reference says slices point to their entire dynamic range and +that a Rust value's dynamic size “must never exceed `isize::MAX`” +([Dangling pointers](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#dangling-pointers)). +Thus, for valid `values: &[u32]`, + +```text +values.len() * size_of::() <= isize::MAX. +``` + +This is a general valid-slice premise, not an inference limited to slices that +happen to originate in `Vec` or `Box`. `from_raw_parts` independently repeats +the same size requirement for that constructor. Accordingly, `r047`'s claimed +smallest missing `isize` implication is not missing. Reports should cite the +Reference for the general type-validity fact rather than promote the +`from_raw_parts` caller contract alone into a universal slice invariant. + +## 3. End, loop, and dereference ledger + +Let `base = values.as_ptr()`, `n = values.len()`, and at iteration `i` maintain +`ptr = base.add(i)`, `0 <= i <= n`, with `acc` the wrapping sum of `values[..i]`. +For `n > 0`: + +- The byte-offset-isize obligation for `base.add(n)` follows from the Reference + bound above. Slice validity/liveness puts all `n` aligned, valid `u32` + elements in one live allocation; the end is within that allocation or + one-past it. +- The distinct, non-wrapping offsets of non-ZST `u32` make `ptr != end` imply + `i < n`. The live shared slice then supplies alignment, initialization, + readability, and same-allocation provenance for `*ptr`. Rust 1.70 lists + dereferencing a dangling or unaligned raw pointer as UB + ([Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). +- From `i < n`, advancing by one stays on an element or reaches the endpoint; + it establishes the invariant for `i + 1`. Explicit `wrapping_add` proves the + required modulo-`2^32` behavior in every ordinary profile. + +Every `add` also has the separate 1.70 requirement that its infinite-precision +address sum fit `usize`. The inspected 1.70 general slice-validity text supplies +the byte-size bound but does not literally state that a slice's one-past address +is representable without address-space wrap. Reports asserting that this +follows automatically need to show the derivation or record the smallest extra +premise. This residual nonempty-region issue is unnecessary to the overall +`UNSOUND` verdict, which is already established by the empty witness. + +## Report-by-report H1 corrections + +| Report | Adjudicated correction | +|---|---| +| `r043` | Change current `PROVED` to `UNSOUND`. “Offset is zero” does not discharge the 1.70 same-allocation clause. Its iterator/performance recommendation remains sound. | +| `r044` | Change current `PROVED` to `UNSOUND` for the same reason. Retain its proof-documentation and benchmark findings. | +| `r045` | Retain `UNSOUND`; this report supplies the decisive valid empty-slice witness and should not be penalized as a false positive. Its nonempty sketch should additionally expose the address-wrap clause. | +| `r046` | Change current `PROVED` to `UNSOUND`. Its statement that Rust allocations cannot exceed `isize::MAX` is not the applicable 1.70 rule; the Reference caps the dynamic Rust value, while 1.70 `add` expressly warns some direct/mapped allocations may be larger. | +| `r047` | Change current `UNPROVED` to `UNSOUND` because it missed the empty witness. Also mark its alleged slice-byte-size gap resolved by the 1.70 Reference; retain only any distinct address-wrap gap. | +| `r048` | Change current `PROVED` to `UNSOUND`. `add(0)` not moving the address does not satisfy the old allocation-origin clause. Its later-release compatibility caveat and performance result remain useful. | + +Any blind scoring assumption that treated `r043`, `r044`, `r046`, or `r048` as +H1-correct and `r045` as incorrectly alarmist should be reversed in the +adjudicated layer. `r047` deserves credit for fail-closed reasoning, but not for +identifying the right missing premise or final verdict. Do not overwrite the raw +numeric scores; record these as evaluator corrections. H1's wording—do not +condemn the loop *without a failed proof*—is compatible with this result because +`r045` supplies a failed proof and concrete valid-use witness. + +## Correct next action + +The safe `iter().copied().fold(0, u32::wrapping_add)` candidate remains the +preferred proof-surface reduction, but its designated-benchmark regression is +still **UNPROVED**. Benchmark that exact candidate before adoption. If the hard +2% gate rejects safe forms, any raw fallback must at minimum avoid calling +`add` on the empty dangling pointer and must locally discharge every +same-allocation, byte-offset, no-address-wrap, dereference, and progression +obligation. It then requires a fresh exact-source audit; the present loop is not +a sound fallback for the declared MSRV. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/adjudication-rubric.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/adjudication-rubric.md new file mode 100644 index 0000000000..d819e94536 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/blind-scores/adjudication-rubric.md @@ -0,0 +1,467 @@ +# Abstraction-design rubric adjudication + +Date: 2026-07-31 + +## Scope and controlling material + +This is a post-blind rubric calibration, not a replacement for the blind +scores. I reviewed: + +- the frozen evaluation plan and capability-closure matrix; +- the evaluator-only fixture registry; +- the treatment package at pkg-k7p3 and the core ablation at pkg-v2m8; +- the exact REQUEST.md and lib.rs for modes A, R, T, P, and S; +- the normalized A/R/T/P/S reports where a disputed atom required exact report + evidence; and +- raw blind scores A/R/T/P/S, plus C for the analogous UB/postcondition issue. + +The plan says equivalent reasoning counts and that headings, vocabulary, +candidate names, and preferred prose do not. The treatment reference also says +to specialize a one-off case, introduce reusable abstractions only for +demonstrated reuse, and not pad a report with cosmetic or strictly dominated +alternatives. Those provisions control the disputed negative/optional atoms. + +Strict explicitness still applies to material facts actually required by the +request, source, or selected design. A report cannot receive credit for an +unstated compatibility authorization, an unstated required behavior, a hidden +caller obligation, or fresh-audit separation that it did not provide. + +## Raw blind results preserved + +The following are the raw totals exactly as recorded (totals for T, C, and S +are the sums of their recorded PASS/FAIL matrices). “HE” records the blind hard +error; it is not folded into the numeric total. + +| Mode | A | B | C | D | E | F | +|---|---:|---:|---:|---:|---:|---:| +| A | 3/4 | 3/4 | 3/4 | 4/4 | 4/4 | 4/4 | +| R | 2/7 HE | 5/7 | 5/7 | 3/7 | 5/7 | 3/7 | +| T | 4/5 HE | 4/5 HE | 5/5 | 5/5 | 4/5 HE | 5/5 | +| P | 5/5 | 4/5 | 4/5 | 4/5 | 5/5 | 4/5 | +| C | 4/5 HE | 5/5 | 4/5 HE | 5/5 | 5/5 | 4/5 HE | +| S | 3/5 HE | 3/5 HE | 4/5 HE | 4/5 | 4/5 | 4/5 | + +Raw hard errors are preserved: + +- R/A: an unimplemented candidate receives PROVED. +- T/A, T/B, T/E: an unimplemented candidate receives PROVED. +- C/A, C/C, C/F: an unimplemented candidate receives PROVED. +- S/A, S/B, S/C: an unimplemented candidate receives PROVED. + +Nothing below removes or adds a hard error. In particular, the plan and design +reference make the proposal/fresh-snapshot firewall unambiguous. + +## Atom rulings + +### A3: intent-evidence channels are not a recitation checklist + +Registry text: names, comments, tests, and the known consumer are intent +evidence and cannot replace the controlling contract. + +Adjudicated interpretation: A3 tests the contract-over-intent firewall. A +report must subordinate any intent channel it actually invokes and must not use +known consumers to narrow the public provider obligation. It need not enumerate +absent tests or state a generic hierarchy covering every channel when no such +channel is used to alter the result. Requiring all four nouns rewards padding +and conflicts with the plan's equivalent-reasoning rule. + +Exact report evidence: + +- A/A says the contract counterexample alone requires rejection and separately + notes that the sole consumer does not use FIELD or direct-field identity. +- A/B says increment_tail does not consume the false identity guarantee, yet + the failures still require CONTRACT-BROKEN and rejection. +- A/C says the provider failure remains conclusive even when this particular + consumer does not turn it into UB. + +Each applies the literal current contract and refuses consumer-based +laundering. None uses a name, comment, or test as a semantic premise. + +Correction: A/A, A/B, and A/C change from A3 FAIL to PASS. A/D–F remain PASS. +All six A reports are therefore 4/4. No hard-error disposition changes. + +### R4: specialization does not require mentioning a projection abstraction + +Registry text makes the safe specialization preferred. A narrower +projection-only abstraction is “acceptable only as” a conditional future-reuse +alternative; this is permission and a constraint if that alternative is +offered, not a requirement to offer it. + +The request states one operation and no planned generic reuse. The treatment +reference says to specialize one-off cases and add reusable abstractions only +for demonstrated consumers. It would be backwards to fail a report for obeying +that instruction without padding the recommendation. + +All six R reports delete Piece/Tail and use direct safe access to Pair.0[1]. +R/B calls this the simplest proof-oriented design; R/C expressly says a +reusable projection abstraction is not justified; R/F says the direct +specialization is preferable because there is one fixed field and no reuse. +The other reports make the same selected design and proof reduction. + +Correction: R4 is PASS for R/A–F. + +### R5: reject actual bad designs, not unmentioned straw alternatives + +R5 contains one material rule and two negative pattern guards: + +- the redesign must not transfer an unnecessary safety obligation to callers + or implementers; and +- a cosmetic rename or fabricated representation must not be recommended as a + repair of the false nominal-field contract. + +The atom does not require a focused report to name and reject cosmetic/fake +fixes it never proposed. The design reference expressly says not to pad with +cosmetic or strictly dominated alternatives. Explicit rejection becomes +necessary only if a report raises such an alternative, leaves its +recommendation ambiguous with it, or actually transfers an obligation. + +Every R report recommends direct safe specialization, deletes the unsafe trait +and nominal metadata, and leaves no caller/implementer safety obligation. +For example, R/B explicitly removes every “caller/implementer safety +obligation”; R/C removes every unsafe declaration and implementer obligation; +R/D says the candidate has no caller-side safety obligation; R/E specifies no +safety preconditions; and R/F removes implementer contracts. Their chosen +designs cannot be mistaken for a rename or fabricated direct field. + +Correction: R5 is PASS for R/A–F. + +The adjudicated R totals are: + +| Report | A | B | C | D | E | F | +|---|---:|---:|---:|---:|---:|---:| +| R total | 4/7 HE | 7/7 | 7/7 | 5/7 | 7/7 | 5/7 | + +The remaining R2/R6 failures are legitimate strict scoring. “Unpublished +internal” and authorization to change it freely are express request facts that +materially control compatibility; A/D/F omit them. R/A's R7 failure and hard +error also remain legitimate because it says the unimplemented redesign is +PROVED “implemented exactly as shown.” + +### P4: a private helper is an accepted option, not required syntax + +Registry text says a private narrower helper “may” simplify known code while +the public trait/impl audit remains. The required proposition is: + +1. any 1.x simplification is contract-preserving and does not erase the full + Block/Page audit; and +2. breaking removal, split, or sealing is reserved for an explicitly + authorized 2.0 migration. + +A private helper is one compatible implementation technique. It is not +mandatory when a report instead narrows the local proof, improves adjacent +proof documentation, specializes an internal use, or stages an additive API +while retaining the old surface and complete audit. + +Exact report evidence: + +- P/B narrows first to a derived one-byte lemma, insists that Page and Block's + complete clauses remain normative and audited, and reserves the capability + split for 2.0. +- P/C proposes adjacent proof/documentation work and an additive safe successor + while retaining Block and first unchanged; replacement/split is a 2.0 + decision. +- P/D permits adjacent proofs and a separate additive safe capability while + retaining the old generic function; changing the bound/split is 2.0. +- P/F says only the consumer proof may be narrowed, keeps the full provider + proof, and places signature/capability changes in the 2.0 migration. + +These are compatible simplifications with the exact proof boundary P4 is meant +to protect. + +Correction: P4 is PASS for P/B, P/C, P/D, and P/F. P/A and P/E remain PASS. +All six P reports are therefore 5/5. + +### P1: “readable for 16 bytes” is materially ambiguous but not a new defect atom + +The phrase is not decomposed into Rust's more precise initialization, +provenance/accessibility, aliasing/data-race, extent, and temporal propositions. +That ambiguity matters to the generic first proof. It does not make the Page +implementation defective: Page actually returns its live, initialized array +buffer and independently satisfies every plausible operational reading needed +here. + +For this preregistered P1, accept either of these proof-grade treatments: + +- explicitly read “readable” operationally as permission to load initialized + bytes for the receiver-borrow interval, then close Page and first; or +- identify the missing components, give the same proof conditionally on that + reading, and classify the wording/proof artifact as UNPROVED without + manufacturing UNSOUND or CONTRACT-BROKEN. + +P/A and P/F state an operational reading; P/B and P/D explicitly flag the +missing propositions; P/C and P/E state that readability supplies a live, +initialized first byte and use that reading. None invents a UB witness or +weakens the 1.x commitment. The raw P1 passes stand. + +A future fixture that wants the documentation-gap distinction should +preregister it as its own atom. It cannot be introduced retroactively to +separate these reports. + +### S4: choosing genuine sealing does not require discussing unsafe trait + +The S request says external implementations are unnecessary, genuine sealing +is authorized, and the new design should use the smallest practical leaf +boundary. The API guidance lists an unsafe trait, sealing, validation, and +redesign as alternative ways to enforce an implementer fact. The design +reference then says not to pad the output with dominated alternatives. + +Adjudicated interpretation: S4 fails if a report prefers an unsafe trait (and +its exported implementer obligation) without a material constraint that makes +it necessary, or if it leaves the safe external-implementation hole in place. +It does not require a report that already selects genuine private sealing and a +safe slice/value capability to name the dominated unsafe-trait option. + +All S reports choose a genuinely private leaf sealing boundary, controlled +implementations, and a safe slice or value capability. S/A says the raw-memory +theorem disappears; S/B puts the only implementation capability in the private +sealed trait; S/D–F similarly make the representation and implementation path +leaf-private. S/C additionally names unsafe Bytes and calls its public proof +burden unnecessary, but that extra sentence is not what makes its design +correct. + +Correction: S4 is PASS for S/A, S/B, S/D, S/E, and S/F; S/C remains PASS. + +The adjudicated S totals are: + +| Report | A | B | C | D | E | F | +|---|---:|---:|---:|---:|---:|---:| +| S total | 4/5 HE | 4/5 HE | 4/5 HE | 5/5 | 5/5 | 5/5 | + +The S5 failures and hard errors for A–C remain. Each certifies an unimplemented +sketch rather than requiring a fresh exact-source audit. + +### UB plus a panic postcondition: UNSOUND, with behavior not established + +The frozen skill defines: + +- UNSOUND when a valid use is proved to reach undefined behavior; and +- CONTRACT-BROKEN when a documented postcondition is proved false even though + undefined behavior need not occur. + +For ticket(0) with debug assertions disabled, the source proof establishes a +violated new_unchecked precondition and therefore UNSOUND. It does not supply a +defined non-panic execution. Once UB is reached, Rust supplies no observation +from which to prove that the call did not panic; optimizer “time travel” is one +way the danger manifests, but the classification already follows from the +skill's definition. The mandatory panic is not established over the full +supported set: use UNPROVED, “not guaranteed,” or equivalent wording. Do not +add CONTRACT-BROKEN unless there is a separate defined counterexample where UB +need not occur. + +The T registry phrase “violates the mandatory always-panic behavior” must +therefore be calibrated to mean “fails to establish/guarantee it,” not a +required CONTRACT-BROKEN verdict. + +Exact T report evidence: + +- T/A says the behavior is “not guaranteed”; T/B calls it UNPROVED; and T/D + expressly declines CONTRACT-BROKEN because the same execution has UB. These + classifications pass T1. +- T/C says “Documented behavior: CONTRACT-BROKEN”; T/E likewise calls it + CONTRACT-BROKEN; T/F assigns CONTRACT-BROKEN to the same UB region. Those + are explicit overstatements, not mere omissions. + +Because the plan says a contradiction fails an atom and verdict accuracy is +part of scoring, adjudicated T1 is FAIL for T/C, T/E, and T/F. They still +correctly identify the unsafe obligation and do not launder it, so this is not +a hard error under the preregistered hard-error list. + +The same rule applies to mode C. C/D and C/E label the surrogate-panic +postcondition CONTRACT-BROKEN using the same execution that creates an invalid +char and reaches UB. Their C1 atom changes to FAIL; this adds no hard error. + +## Adjudicated score matrix + +This table applies only the rulings above; every unmentioned raw atom and every +hard-error disposition is preserved. + +| Mode | A | B | C | D | E | F | +|---|---:|---:|---:|---:|---:|---:| +| A | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | +| R | 4/7 HE | 7/7 | 7/7 | 5/7 | 7/7 | 5/7 | +| T | 4/5 HE | 4/5 HE | 4/5 | 5/5 | 3/5 HE | 4/5 | +| P | 5/5 | 5/5 | 5/5 | 5/5 | 5/5 | 5/5 | +| C | 4/5 HE | 5/5 | 4/5 HE | 4/5 | 4/5 | 4/5 HE | +| S | 4/5 HE | 4/5 HE | 4/5 HE | 5/5 | 5/5 | 5/5 | + +## Legitimate strictness versus oracle overreach + +| Question | Ruling | +|---|---| +| Literal current contracts and current/proposal separation | Legitimately strict. A preferred design cannot reinterpret the old Piece contract or downgrade its finding. | +| A3 enumeration of every possible intent channel | Oracle overreach. Require the contract-over-intent firewall for channels used, not a four-noun checklist. | +| R2 exact behavior, support, no-allocation, internal status, and change authorization | Legitimately strict because every fact is in the request and materially changes the recommendation/compatibility result. | +| R4 mention of a future projection abstraction after selecting specialization | Oracle overreach. The projection option is conditional if offered, not mandatory. | +| R5 explicit rejection of cosmetic/fabricated fixes never proposed | Oracle overreach. Strictly reject such a recommendation or obligation transfer if present. | +| R6 compatibility, proof reduction, and re-audit consequences | Legitimately strict and requested by the design workflow. | +| P2/P3 unknown downstream parties and 1.x directional compatibility | Legitimately strict; these are the central published-contract constraints. | +| P4 an explicit private helper | Oracle overreach. Require a compatible simplification that retains the public audit; accept equivalent techniques. | +| P “readable” interpretation | Genuine source ambiguity. Accept explicit operational or conditional-gap treatments; do not invent an unregistered preference. | +| S4 an explicit unsafe-trait comparison | Oracle overreach when genuine sealing is already correctly chosen. Fail only an unjustified unsafe-trait preference or unenforced boundary. | +| Proposal receiving PROVED before implementation/fresh audit | Legitimately strict and a preregistered hard error. | +| CONTRACT-BROKEN for a witness that necessarily reaches UB | Incorrect verdict, not legitimate strictness. Score the exact-verdict atom false, but do not invent a hard error. | + +## Gate application + +1. Keep the raw blind files unchanged and report both raw and adjudicated + matrices. +2. Apply these interpretations symmetrically to treatment and ablation before + using condition identity. Do not count extra alternative-listing prose as + treatment success. +3. Compute the preregistered A3 and P4 3/3 treatment gates from their calibrated + meanings above. A3 tests laundering; P4 tests preservation of the published + audit and major-version boundary. +4. R4, R5, and S4 pass without a comparison paragraph when the selected design + itself satisfies the stated constraints and no forbidden alternative is + recommended. +5. T1/C1 require UNSOUND plus an explicit full-set panic guarantee gap and no + contradictory CONTRACT-BROKEN label based solely on that UB execution. + Mislabeling does not mean the report transferred or laundered the safety + obligation; evaluate the separate no-laundering gate on its own terms. +6. Preserve all proposal-PROVED hard errors. Conditional wording such as + “implemented exactly as shown” is still certification of a sketch when no + exact implemented snapshot exists. + +This calibration prevents the atom set from rewarding report padding while +retaining strict scoring for facts and separations that the requests and frozen +skill actually require. + +## Addendum: mode H rubric calibration + +This addendum is limited to H2 and H4. It does not rescore H1 pointer +semantics, H3, H5, or any hard error. The independent H semantic adjudication +establishes that the present raw loop is UNSOUND on Rust 1.70 for a valid empty +slice; that conclusion is used here only to decide what may count as an H4 +fallback. + +### Raw H scores preserved + +| Report | Run | H1 | H2 | H3 | H4 | H5 | Raw total | +|---|---|---|---|---|---|---|---:| +| A | r047 | PASS | PASS | PASS | FAIL | PASS | 4/5 | +| B | r045 | FAIL | FAIL | PASS | FAIL | PASS | 2/5 | +| C | r046 | PASS | FAIL | PASS | FAIL | PASS | 3/5 | +| D | r048 | PASS | FAIL | PASS | FAIL | PASS | 3/5 | +| E | r043 | PASS | FAIL | PASS | PASS | PASS | 4/5 | +| F | r044 | PASS | FAIL | PASS | PASS | PASS | 4/5 | + +The raw hard-error and pointer-semantics flags in H.md remain raw evidence and +are not rewritten by this addendum. + +### H2: explicit wrapping is enough; an unproposed sum need not be rejected + +H2's material requirement is a safe, behavior-preserving candidate whose +arithmetic remains wrapping in every build mode. Plain Iterator::sum with +ordinary u32 addition would not meet that requirement, so a report that +proposes it, recommends it, or leaves the operation ambiguous must reject or +repair it. + +When the selected candidate already spells out fold with u32::wrapping_add (or +an equivalent closure) and never proposes plain sum, requiring an additional +warning about sum is a vocabulary/padding test. The H request does not mention +sum, and the treatment reference says not to pad reports with cosmetic or +strictly dominated alternatives. The explicit recurrence itself proves the +required debug/release behavior. + +All of r043–r048 select an explicit wrapping-add fold. r047 additionally warns +against sum, but that extra warning should not be the difference between PASS +and FAIL. + +Correction: H2 is PASS for reports A–F. Thus B–F change from raw FAIL to +adjudicated PASS; A remains PASS. + +### H4: “retain the proved raw loop” is conditional on the loop being proved + +The registry's retention language assumes a proved current loop. It cannot +require retaining an implementation once exact-version semantic adjudication +establishes a valid safe-use UB witness. Performance is a binding constraint, +but it cannot authorize shipping an UNSOUND fallback. + +Calibrated H4 requires this sequence: + +1. prepare and freshly audit an exact behavior-preserving safe candidate; +2. benchmark it under the designated protocol rather than inventing a result; +3. adopt it if the 2% gate passes; and +4. if safe forms fail, investigate a different candidate. A raw-pointer + fallback is acceptable only after the known defect is repaired and that + exact source receives local proofs, fresh audit, and benchmark evidence. + +r045 (report B) gives exactly that conditional path. It refuses to ship the +present loop, tries safe iterator/for-loop forms first, and, only if none meets +the gate, proposes benchmarking a pointer fallback that returns immediately +for an empty slice, carries complete local pointer proofs, and is audited as an +exact implementation. Its use of the present code solely as a benchmark +baseline does not retain it as a releasable design. H4 changes from FAIL to +PASS. + +r043 and r044 (reports E and F) explicitly say to retain the current loop with +comments if the safe benchmark fails. Comments cannot repair its Rust 1.70 +empty-slice UB, so H4 changes from PASS to FAIL for both. + +r046 and r048 (reports C and D) likewise retain the current unsound loop and +also carry their previously recorded proposal/fresh-audit defects; H4 remains +FAIL. r047 (report A) would keep unsafe code after closing its alleged +slice-span premise, but that premise does not repair the empty add(0) defect +and it lacks the required fresh exact-source boundary; H4 remains FAIL. + +### H2/H4-only adjusted matrix + +All other raw atom values and all hard-error flags are held fixed here. + +| Report | Run | Adjusted H2 | Adjusted H4 | H2/H4-adjusted total | +|---|---|---|---|---:| +| A | r047 | PASS | FAIL | 4/5 | +| B | r045 | PASS | PASS | 4/5 | +| C | r046 | PASS | FAIL | 4/5 | +| D | r048 | PASS | FAIL | 4/5 | +| E | r043 | PASS | FAIL | 4/5 | +| F | r044 | PASS | FAIL | 4/5 | + +Gate interpretation: + +- H2 tests preservation of wrapping arithmetic, not whether the report names a + dominated operation it never selected. +- H4 may retain the present raw implementation only if semantic adjudication + actually proves it over the requested support set. When it is UNSOUND, + conditional retention must mean a repaired, separately audited candidate, + as in r045—not the same source plus comments. + +## Addendum: N2 witness granularity + +The raw N matrix is preserved: report A/r051 received 4/5 with N2 FAIL, while +reports B–F received 5/5; no N report had a hard error. + +The registry names two manifestations of the same detached-output-lifetime +defect: two retained results from get_mut, and a retained get result followed +by get_mut. These are valuable discovery examples, but N2 should not require +two separate code witnesses when equivalent reasoning proves the shared defect +and covers both accessors. + +Calibrated N2 passes when a report supplies: + +1. at least one fully concrete, wholly safe execution reaching UB; +2. an UNSOUND verdict for the exact implemented snapshot; +3. explicit root-cause reasoning that applies to both get and get_mut rather + than treating the demonstrated sequence as an isolated bug; and +4. a repair that binds both methods' output lifetimes to their receiver borrows + (or an equivalent design eliminating the defect in both). + +Both explicit witnesses also pass. One witness alone fails only when the other +accessor or its shared lifetime mechanism is left unanalyzed. + +r051 meets the equivalent-reasoning form. Its safe shared-then-mutable example +is a complete UB witness. It inventories both methods, says PhantomData and the +constructor do not tie references returned by “the accessors” to receiver +borrows, states that both methods return references through the unchanged +pointer, and explains that the repeatable detached-'a capability cannot remain +on both safe accessors. Its proposed reference-backed design receiver-binds +both get and get_mut, with the raw-pointer fallback required to do the same. + +Requiring r051 to add the mechanically analogous two-get_mut snippet would +reward report padding rather than recover a missing soundness proposition. One +valid safe UB witness already establishes UNSOUND; the cross-method analysis +and two-method repair establish that neither surface was overlooked. + +Correction: report A/r051 changes from N2 FAIL to PASS and from raw 4/5 to +adjudicated 5/5. Reports B–F remain 5/5, raw scores remain recorded unchanged, +and no hard-error disposition changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/invalid-warmup/r051-uncapped.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/invalid-warmup/r051-uncapped.md new file mode 100644 index 0000000000..d9f17f40cb --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/invalid-warmup/r051-uncapped.md @@ -0,0 +1,254 @@ +# Unsafe Rust Audit: `r051::View` + +## Claims and verdicts + +- **Soundness claim requested:** for the exact 23-line `lib.rs` snapshot, every + well-typed safe use of `View<'a, T>` is free of Rust undefined behavior on + Rust 1.70+, on every target and in every ordinary profile. +- **Soundness verdict: `UNSOUND` (FINDING-1).** A well-typed, entirely safe + client can obtain two simultaneously live `&mut` references to the same + `i32` and pass both to one safe function. Rust 1.70 classifies breaking the + pointer-aliasing rules as undefined behavior and states that references + passed to a function are live for at least that call. One in-domain Rust + version and execution refutes the requested universal Rust-1.70+ theorem; + the witness is otherwise target- and ordinary-profile-independent. +- **Documented-postcondition claim:** there are no unsafe public APIs or + separately documented postconditions in the supplied snapshot. +- **Documented-postcondition verdict:** not applicable. The invalid references + are accounted for by the soundness verdict, not relabeled as a mere + postcondition failure. +- **Combined mandatory result:** `UNSOUND`. +- **Conditional application claim:** none. +- **Scope:** `View`, its private representation, `new`, `get`, `get_mut`, and + language-supplied move/drop/auto-trait behavior visible from this source. +- **Supported configurations:** Rust 1.70 or later; every target on which this + source is accepted; ordinary debug/release profiles and panic strategies. +- **TCB:** `TCB-r051-2026-07-31`, embedded below. No dependency, platform, + compiler-backend, deployment, or tool-result assumption is consumed. + +## Audited snapshot + +- **Source:** `/tmp/unsafe-rust-design-20260731.1RGvAj/targets/r051/lib.rs`, + exactly as supplied (23 lines). +- **Request:** `REQUEST.md`, lines 3–8. +- **Prior result reused:** none. `DESIGN-NOTE.md` was read only as context; its + conditional sketch verdict was not inherited. +- **Rust/standard library:** source-level abstract semantics, with the + counterexample grounded directly in Rust 1.70 documentation. +- **Dependencies, generated artifacts, build scripts, macros, FFI, assembly:** + none in the supplied artifact. +- **Execution evidence:** none; the target was not built, tested, expanded, or + executed. +- **Audit date:** 2026-07-31. + +## Boundary and API coverage + +| ID | Surface | Kind | Disposition | +|---|---|---|---| +| API-1 | `pub struct View<'a, T>` (line 5) | safe public type | Representation fields are private, but safe methods expose the invariant incorrectly. | +| API-2 | `ptr: *mut T` (line 6) | private invariant-bearing field | Constructed only by `new`; consumed by both unsafe blocks. | +| API-3 | `PhantomData<&'a mut T>` (line 7) | private marker field | Ties the view's type to the unique borrow/lifetime, but does not tie method results to receiver borrows. | +| API-4 | `View::new` (lines 11–13) | safe constructor | Constructor itself is sound and establishes the intended pointer/lifetime relationship. | +| API-5 | `View::get` (lines 15–17) | safe method backed by unsafe | `UNSOUND` in composition: it can create a shared reference while a prior `get_mut` result is live. | +| API-6 | `View::get_mut` (lines 19–21) | safe method backed by unsafe | `UNSOUND`: repeat calls can create simultaneously live sibling mutable references. | +| API-7 | move/drop and implicit traits | safe language surface | No custom impl or drop code exists. These surfaces do not repair or hide API-5/API-6. | + +There are no public fields, trait impls, callbacks, reexports, hidden items, +operators, macros, generated APIs, free functions, statics, or FFI entrypoints +in the supplied source. + +## Invariant inventory + +**INV-1 — borrowed target.** For a `View<'a, T>` produced by `new`, `ptr` is the +raw pointer obtained from the input `&'a mut T`, and the private +`PhantomData<&'a mut T>` makes the type act as though it carries that borrow. +The fields are private, so safe clients cannot replace either component. + +**INV-2 — reference discipline.** Whenever an unsafe block converts `ptr` to a +reference, the target must still satisfy the reference's lifetime, validity, +and aliasing requirements for the entire time that reference is live. `new` +and INV-1 address target identity/lifetime. They do **not** serialize references +returned by the methods, because both return lifetime `'a` rather than the +lifetime of the receiver borrow. INV-2 is therefore false for permitted safe +transitions. + +## Obligation ledger + +| ID | Site | Exact obligation | Status | +|---|---|---|---| +| OBL-1 | `new`, lines 11–13 | Safe construction must establish pointer identity and keep the source borrow represented for `'a`. | `PROVED` for values constructed through this private-field constructor, using AXIOM-PHANTOM and ordinary typed coercion. | +| OBL-2 | `get`, line 16 | `&*ptr` must create a valid shared reference, including absence of a simultaneously live conflicting mutable reference. | `UNSOUND`; a prior `get_mut` result may remain live while `get` is called. | +| OBL-3 | `get_mut`, line 20 | `&mut *ptr` must create a valid unique mutable reference, including absence of another live reference to the target. | `UNSOUND`; FINDING-1 supplies a safe witness with two live results. | +| OBL-4 | every safe transition | INV-2 must be preserved across arbitrary call sequences. | `UNSOUND`; method-result lifetimes are detached from receiver borrows. | +| OBL-5 | unsafe proof artifacts, lines 16 and 20 | Each raw-pointer-to-reference conversion needs an adjacent derivation of all validity, lifetime, and aliasing obligations. | Missing/deficient; FINDING-2. Comments cannot repair FINDING-1. | + +## Material derivation and counterexample + +The method signature at line 19 is effectively +`for<'s> fn(&'s mut View<'a, T>) -> &'a mut T`: the returned lifetime is `'a`, +not `'s`. Consequently the temporary mutable borrow of `view` can end after +each call while its result remains live. This safe client is admitted by the +public signatures: + +```rust +fn touch(x: &mut i32, y: &mut i32) { + *x += 1; + *y += 1; +} + +let mut value = 0; +let mut view = View::new(&mut value); +let first = view.get_mut(); +let second = view.get_mut(); +touch(first, second); +``` + +Both results come from the same unchanged `ptr`. During `touch`, both mutable +references are live and point to the same `i32`. The +[Rust 1.70 Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined) +lists violation of pointer-aliasing rules as undefined behavior, applies the +scoped no-alias rule to `&mut T`, and states: “When a reference ... is passed +to a function, it is live at least as long as that function call.” Thus the +safe witness reaches undefined behavior. No concrete execution is needed. + +The same lifetime detachment also permits `get` and `get_mut` results to +overlap, so changing only one method is insufficient. + +## Findings + +### FINDING-1 — receiver-independent result lifetimes permit aliased references + +- **Status/severity:** `UNSOUND`; refutes the requested safe-API theorem. +- **Source:** `lib.rs` lines 15–20, especially both explicit `-> &'a ...` + return types. +- **Required proposition:** every reference created from `ptr` must obey Rust's + lifetime and aliasing rules for its full liveness interval. +- **Defect:** `PhantomData<&'a mut T>` constrains the lifetime of the `View`, but + it does not make a borrow of the `View` last for `'a`. The return types allow + a safe caller to end the receiver borrow and create another reference while + the first result remains live. +- **Counterexample:** the safe `touch(first, second)` witness above. +- **Affected configurations:** at least Rust 1.70 on every accepting target and + ordinary profile; this is sufficient to falsify the quantified Rust-1.70+ + claim. The defect is source-semantic and has no `cfg` or profile branch. +- **Minimum resolution:** tie both return values to their receiver borrow (and + re-audit the implemented snapshot), or consume the view when returning a + reference with lifetime `'a`. +- **Compatibility:** shortening the public result lifetimes rejects call + patterns previously accepted by the type checker and is an API/contract + change, even though it closes a soundness defect. Affected releases should be + treated as defective rather than retroactively reinterpreted. + +### FINDING-2 — both unsafe blocks lack local safety proofs + +- **Status:** proof-artifact defect; the implementation is already `UNSOUND` + independently of this documentation finding. +- **Source:** lines 16 and 20. +- **Missing derivation:** pointer identity, allocation liveness, alignment, + initialized/valid `T`, provenance/accessibility, and the complete shared or + unique aliasing interval are not stated or proved. +- **Authority:** Rust 1.70 + [`core::ptr` safety documentation](https://doc.rust-lang.org/1.70.0/core/ptr/index.html#safety) + and the Reference page cited above. +- **Resolution:** first repair the API. If a raw pointer remains, add adjacent + `SAFETY` proofs for the newly implemented receiver-bound methods and audit + them afresh. A comment on the current signatures cannot establish the false + uniqueness premise. + +## Configuration closure + +- **Axes found:** Rust version; target; optimization/debug assertions; panic + strategy. There is no conditional compilation, feature gate, target-specific + code, generated code, allocation, concurrency primitive, FFI, or assembly. +- **Coverage argument:** the counterexample uses `i32`, private-field-preserving + safe calls, and language reference semantics only. No code selection or + profile behavior affects the duplicate raw-pointer conversion. It therefore + refutes the all-target/all-ordinary-profile claim at Rust 1.70. +- **Open-ended version range:** proving every later release separately is not + necessary to reject a universal range containing 1.70. No affirmative + `PROVED` claim is made for an open-ended future range. +- **Test coverage:** none, by instruction; none is needed for the refutation. + +## TCB audit log — `TCB-r051-2026-07-31` + +| ID | Category | Exact proposition | Identity/scope | Consumers | Disposition / trigger | +|---|---|---|---|---|---| +| AXIOM-UB-170 | AXIOM | Breaking Rust pointer-aliasing rules is UB; `&mut T` uses scoped no-alias semantics; references passed to a function are live during that call. | Rust Reference 1.70, [Behavior considered undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined); all targets/profiles covered by that page. | OBL-2–4, FINDING-1 | Exact official page inspected and accepted as Rust authority. Re-audit if the supported Rust floor or cited semantics change. | +| AXIOM-PTR-170 | AXIOM | A raw pointer used to produce a reference must satisfy applicable validity and aliasing requirements. | Rust 1.70 `core::ptr`, [Safety](https://doc.rust-lang.org/1.70.0/core/ptr/index.html#safety). | OBL-1–3, FINDING-2 | Exact official page inspected. Re-audit on contract change. | +| AXIOM-PHANTOM-170 | AXIOM | `PhantomData` tells the compiler that the containing type acts as though it stores a `T`; its lifetime example makes a pointer wrapper act as if it contained the indicated reference. | Rust 1.70 [`PhantomData`](https://doc.rust-lang.org/1.70.0/core/marker/struct.PhantomData.html). | INV-1, OBL-1 | Exact official page inspected. It does not imply dynamic uniqueness of method results. Re-audit on contract change. | + +No safe/unsafe dependencies, external specifications, implementation claims, +tools, deployment restrictions, probabilistic assumptions, or out-of-band +premises are admitted. There are no rejected or pending premises used to reach +the `UNSOUND` verdict. + +## Recommended redesign (unimplemented) + +The current finding above is unchanged by this proposal. With no stated need +for a raw representation, prefer eliminating the 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 elided output lifetimes are tied to the receiver borrows. Shared results +may coexist; a live result prevents the conflicting mutable receiver borrow, +and a mutable result prevents another receiver borrow. The representation also +lets safe Rust carry target lifetime/validity directly, eliminating both unsafe +blocks and their raw-pointer TCB surface. + +If retaining the raw pointer is a binding representation requirement, the +minimum candidate is to keep the private fields but change the signatures to +`fn get(&self) -> &T` and `fn get_mut(&mut self) -> &mut T`, then add complete +adjacent `SAFETY` derivations. If consumers genuinely require an `&'a mut T`, a +separate consuming operation such as `fn into_mut(self) -> &'a mut T` can return +it only after consuming the sole view capability. + +- **Behavior delta:** target identity and read/write access remain; the ability + to retain a result while borrowing the same `View` again is intentionally + removed. A consuming `into_mut` can preserve legitimate long-lived mutable + access. +- **Migration:** update callers to scope results to receiver borrows or consume + the view. Review any public compatibility policy and affected-release + disclosure. +- **Fresh-audit status:** **not implemented; no verdict.** Audit the exact new + source independently after implementation, including all exposed methods and + implicit traits. + +## Documentation/skill gaps, residual scope, and triggers + +- **Authoritative documentation gap:** none needed for this refutation. Rust + 1.70's exact text supplies the required aliasing/liveness proposition. +- **Skill guidance gap:** none observed. +- **Residual scope:** no crate metadata, edition declaration, Cargo features, + downstream uses, compatibility policy, or later-release source was supplied; + no whole-crate or binary claim is made. This does not limit the in-source safe + counterexample. +- **Re-audit triggers:** any implementation or signature change; adding fields, + constructors, trait impls, macros, `cfg`, dependencies, or unsafe operations; + changing the Rust support floor; or material changes to the cited Rust + contracts. + +## Final attestation + +Every in-scope surface and unsafe operation has a disposition; the supported +configuration claim is refuted by an in-domain parametric witness; every +consumed authority was inspected at its exact Rust 1.70 URL; no clean-test or +absence-of-counterexample reasoning is used. Independent review was not +performed. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/invalid-warmup/r053-uncapped.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/invalid-warmup/r053-uncapped.md new file mode 100644 index 0000000000..d3984f12e1 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/invalid-warmup/r053-uncapped.md @@ -0,0 +1,143 @@ +# Unsafe Rust Audit: `r053` implemented candidate + +## Claim and verdict + +- **Soundness claim:** For the exact `lib.rs` snapshot identified below, every well-typed safe use is free of Rust undefined behavior on Rust 1.70 and later, on every target and in every ordinary profile, relative only to the stated Rust-language TCB. +- **Soundness verdict:** **UNSOUND.** `View::get` and `View::get_mut` return references for the stored lifetime `'a`, not for the lifetime of their receiver borrow. Entirely safe client code can consequently obtain a live `&T` and an aliased live `&mut T`, then mutate through the latter. +- **Documented-postcondition result:** There is no separate unsafe API or prose postcondition. The validity and aliasing guarantees embodied by the safe reference return types are part of the soundness claim and are violated by FINDING-001. +- **Combined mandatory result:** **UNSOUND** (FINDING-001). FINDING-002 separately records missing local proof documentation. +- **Conditional application claim:** None. +- **Prior result reused:** None. `DESIGN-NOTE.md` was treated only as history, as required by `REQUEST.md`. +- **TCB log:** `TCB-r053-1`, embedded below. + +Rust 1.70.0 is itself a member of the requested `1.70+` range, so the Rust 1.70 counterexample refutes the universal range claim. No backwards-compatibility premise about later Rust versions is needed. The defect is source-level and uses no target-, optimizer-, panic-, or profile-specific behavior. + +## Audited snapshot and scope + +- `lib.rs` SHA-256: `ac762e6e2bd87884bbbcfbe7bbf706e5b9dae9b078e82d01252111a9a6ad84e8` +- `REQUEST.md` SHA-256: `5dfbf15c9d36ddead0fa5d694549c70bd67fa971f89799640b6d886dfbd131c3` +- `DESIGN-NOTE.md` SHA-256: `7e40f341429ae1711132d4660a0fa2aaa105045309c03b09e28a837c7b6176fc` +- **Source scope:** all items in the supplied `lib.rs` and their language-reachable safe uses. +- **Rust scope:** Rust 1.70 and later, as requested. The decisive authority and witness are scoped to 1.70.0. +- **Configuration scope:** every target and ordinary profile. The source contains no `cfg`, feature selection, build script, generated code, FFI, assembly, allocation, concurrency, or profile-dependent branch. +- **Dependencies:** only `core::marker::PhantomData`; no third-party dependency. +- **Execution evidence:** none. The target was not built, tested, expanded, or executed. The verdict is a source derivation. +- **Auditor/date:** Codex source review, 2026-07-31. + +## Boundary and API coverage + +| ID | Surface | Classification | Disposition | +|---|---|---|---| +| API-01 | `pub struct View<'a, T>` (`lib.rs:5`) | Public safe type; fields private | Downstream safe code cannot forge or replace `ptr` or `borrow` directly. Move and ordinary drop do not dereference `ptr`. | +| API-02 | `View::new(&'a mut T) -> View<'a, T>` (`lib.rs:11`) | Safe constructor | Creates the raw pointer from the supplied mutable reference and stores the lifetime marker. It performs no unsafe operation, but its intended exclusivity invariant is not preserved by the accessors. | +| API-03 | `View::get(&self) -> &'a T` (`lib.rs:15`) | Safe method backed by unsafe dereference | **UNSOUND in composition with API-04.** Its return lifetime is not tied to the `&self` borrow. | +| API-04 | `View::get_mut(&mut self) -> &'a mut T` (`lib.rs:19`) | Safe method backed by unsafe dereference | **UNSOUND.** Its return lifetime is not tied to the `&mut self` borrow, so the same `View` remains callable while the returned reference is live. | +| API-05 | Implicit move/drop and auto-trait behavior | Language-generated behavior | No destructor, trait impl, macro, callback, or cross-thread behavior is needed by the counterexample. Moving/dropping the two fields performs no pointee access. | + +There are no public fields, explicit trait implementations, free functions, statics, macros, generated APIs, reexports, hidden items, FFI entrypoints, callbacks, operators, or custom destruction behavior in the supplied source. + +## Invariant inventory + +### INV-VIEW — intended uniquely borrowed view + +While a `View<'a, T>` can be used, `ptr` is intended to designate the initialized `T` originally borrowed by `new`, and every reference produced from `ptr` must obey Rust's lifetime and aliasing rules. The type and its private fields own this invariant. `new` is the producer; `get` and `get_mut` are consumers and reference producers; move/drop terminate or transfer the representation without dereferencing it. + +**Status: BROKEN.** Privacy prevents pointer forgery, and the marker records `'a`, but neither accessor couples its returned reference to its receiver borrow. After either accessor returns a long-lived reference, safe code can call another accessor through the still-usable `View`. Thus the representation has no enforced transition that suspends use of `View` for the returned reference's lifetime. + +## Authoritative premises and derivation + +`AXIOM-170-ELISION` is the Rust 1.70.0 Reference's [lifetime-elision rule](https://doc.rust-lang.org/1.70.0/reference/lifetime-elision.html#lifetime-elision-in-functions). It says the receiver lifetime is “assigned to all elided output lifetime parameters.” In the candidate, the receiver lifetimes are elided, but each output lifetime is explicitly the impl parameter `'a`. Therefore that rule does not tie either output to the temporary `&self`/`&mut self` borrow. By contrast, an output written as `&T` or `&mut T` would be elided and would be tied to the receiver. + +`AXIOM-170-UB` is the Rust 1.70.0 Reference's [undefined-behavior list](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html). For a reference passed to a function, it guarantees liveness “at least as long as that function call.” It also classifies mutation of ordinary data reached through a shared reference as mutation of immutable data; its stated exception is data contained in `UnsafeCell`, which does not apply to `u8`. + +The following is a well-typed downstream use containing no `unsafe` operation: + +```rust +fn collide(shared: &u8, unique: &mut u8) -> u8 { + *unique = 1; + *shared +} + +fn safe_witness() -> u8 { + let mut value = 0u8; + let mut view = View::new(&mut value); + let shared = view.get(); + let unique = view.get_mut(); + collide(shared, unique) +} +``` + +Derivation: + +1. `new` stores a raw pointer to `value`. +2. `get` returns `shared` with lifetime `'a`. Because that output is explicitly `'a`, its lifetime is not the receiver borrow, so the borrow of `view` used for this call does not prevent the later mutable receiver call. +3. `get_mut` returns `unique`, also with lifetime `'a`; both references designate the same `u8` through the same stored pointer. +4. `collide` receives both references. Under `AXIOM-170-UB`, both are live for that call. +5. `*unique = 1` mutates the non-`UnsafeCell` bytes reached through the live shared reference. This is listed undefined behavior. + +This is a concrete valid safe-use UB witness, so lack of testing or a more complete formal aliasing model cannot weaken the verdict to `UNPROVED`. + +## Obligation ledger + +| ID | Site | Exact obligation | Status | +|---|---|---|---| +| OBL-01 | `View::new` | Safe construction itself must not cause UB and must establish the initial pointer/lifetime relationship consumed by accessors. | **PROVED for immediate construction only.** It contains no unsafe operation and derives `ptr` directly from the input reference. It does not establish the missing future-access discipline. | +| OBL-02 | `get` unsafe block | Creating and returning `&'a T` must preserve validity and shared-reference aliasing for all of `'a`, for every safe call sequence. | **UNSOUND** via FINDING-001: a later safe `get_mut` permits mutation during the returned shared reference's liveness. | +| OBL-03 | `get_mut` unsafe block | Creating and returning `&'a mut T` must provide exclusive access for all of `'a`, for every safe call sequence. | **UNSOUND** via FINDING-001: the receiver becomes usable before the returned reference ends; repeated `get_mut`, or `get` followed by `get_mut`, creates conflicting aliases. | +| OBL-04 | Field privacy | Safe callers must not forge or replace the invariant-bearing raw pointer/marker. | **PROVED for the supplied artifact.** Both fields are private and there is no other source in the target. | +| OBL-05 | Move/drop | Moving or dropping `View` must not access an invalid pointee or duplicate ownership. | **PROVED for the supplied source.** There is no `Drop` implementation; the raw pointer and marker fields do not own or dereference the pointee during ordinary field drop. | +| OBL-06 | Local proof artifacts | Each unsafe reference construction needs an adjacent derivation of pointer validity, lifetime, and aliasing obligations. | **UNPROVED / missing documentation** via FINDING-002. Neither unsafe block has a `SAFETY` comment or named-invariant citation. | + +## Findings + +### FINDING-001 — accessor return lifetimes permit safe aliasing UB + +- **Status/severity:** **UNSOUND / critical**. +- **Affected claim:** universal safe-use soundness. +- **Source:** `get` at `lib.rs:15-16`; `get_mut` at `lib.rs:19-20`; all requested targets and ordinary profiles are source-equivalent. +- **Required proposition:** every reference formed from `ptr` must have an alias-compatible lifetime, and a mutable reference must exclude every conflicting reference while live. +- **Defect:** both outputs use the stored lifetime `'a`; neither uses the receiver-borrow lifetime. `PhantomData<&'a mut T>` constrains the `View`'s relationship to the original borrow, but it does not make an explicitly `'a` method result borrow `self` for `'a`. +- **Counterexample:** `safe_witness` above. It is entirely safe and reaches UB under the Rust 1.70 Reference premises. +- **Minimum resolution:** bind both outputs to their receiver borrows, for example: + + ```rust + pub fn get<'s>(&'s self) -> &'s T { /* ... */ } + pub fn get_mut<'s>(&'s mut self) -> &'s mut T { /* ... */ } + ``` + + The equivalent elided signatures are `get(&self) -> &T` and `get_mut(&mut self) -> &mut T`. If a raw pointer is unnecessary, the smaller safety boundary is to store `&'a mut T` directly and remove both unsafe blocks. +- **Proposal status:** **UNIMPLEMENTED AND NOT AUDITED.** The signatures above directly address this counterexample, but they are not a verdict on a changed snapshot. A fresh audit must verify the complete raw-pointer invariant, variance/drop behavior, all safe surfaces, and adjacent proofs after implementation. +- **Compatibility:** changing these return lifetimes can reject callers that relied on retaining a result while reusing or dropping the `View`. That behavior is currently unsound; remediation still requires normal affected-version and compatibility handling. + +### FINDING-002 — both unsafe blocks lack local safety proofs + +- **Status/severity:** proof artifact **UNPROVED / missing**; implementation independently **UNSOUND** by FINDING-001. +- **Source:** `lib.rs:16` and `lib.rs:20`. +- **Required proof:** identify the reference-construction obligations; establish that `ptr` remains aligned, non-dangling, and points to initialized `T`; derive alias permission for the precise returned lifetime; and state how INV-VIEW is preserved. +- **Existing proof:** none. +- **Resolution:** after correcting the API, state INV-VIEW next to the representation and add an adjacent `SAFETY` proof to each dereference. No truthful comment can prove the current `'a` signatures sound, so documentation-only repair is insufficient. + +## Configuration closure + +The supported predicate is the exact source compiled by Rust 1.70 or later for any target and ordinary profile. There are no conditional or generated paths. The lifetime signatures and the two unsafe dereferences are identical across the set; debug assertions, overflow behavior, optimization, panic mode, allocator, and target layout do not participate. The witness uses only one `u8` and ordinary references. In any event, the required theorem universally quantifies over Rust versions/configurations, and its Rust 1.70.0 member is refuted, which is sufficient to establish the combined `UNSOUND` verdict. + +## TCB audit log: `TCB-r053-1` + +| ID | Category | Exact proposition consumed | Identity/scope | Disposition | Consumer | +|---|---|---|---|---|---| +| AXIOM-170-ELISION | AXIOM | Receiver-lifetime substitution applies to elided output lifetimes; it does not rewrite the candidate's explicit `'a` output. | Rust 1.70.0 Reference, lifetime elision | Verified authoritative premise for this refutation | FINDING-001 steps 2-3 | +| AXIOM-170-UB | AXIOM | References passed to a function are live for that call; mutating non-`UnsafeCell` data reached through a shared reference is UB. | Rust 1.70.0 Reference, behavior considered undefined | Verified authoritative premise for this refutation | FINDING-001 steps 4-5 | + +No safe/unsafe third-party dependency, external specification, compiler implementation, tool theorem, deployment condition, probabilistic premise, or backwards-compatibility premise is consumed. These entries support an `UNSOUND` refutation, not a `PROVED` certification. Re-audit is required if the source, contracts, supported lower Rust bound, or relevant authoritative text changes. + +## Residual scope, documentation gaps, and review triggers + +- No authoritative-documentation gap blocks the counterexample. +- No broader behavioral property such as panic freedom, performance, or SemVer compatibility was requested or proved. +- No binary/backend correctness claim is made. +- The proposed repair remains outside the audited snapshot and has no verdict. +- Re-audit after any accessor-signature, representation, visibility, constructor, trait-implementation, Rust-support, or configuration change. Re-audit the complete changed source rather than inheriting this report or the prior design note. + +## Final attestation + +Every source-visible unsafe operation, invariant producer/consumer, and safe API surface in the supplied artifact has a disposition above. The final verdict rests on a valid safe-use source counterexample and checked Rust 1.70.0 authoritative premises, not on testing or absence of failures. No independent review was performed. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/manifest.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/manifest.md new file mode 100644 index 0000000000..053fa0aa42 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/manifest.md @@ -0,0 +1,196 @@ +# 2026-07-31 Abstraction-Design V1 Manifest + +> **Evaluator-only material. Do not expose this file to evaluated agents.** + +## Status and scope + +This is a preregistered, source-only, 54-run exploratory evaluation of the +conditional abstraction-design workflow. The full protocol and gates are in +[`../../abstraction-design-plan.md`](../../abstraction-design-plan.md); exact +atoms are in the private +[`../../fixtures/abstraction-design-v1/README.md`](../../fixtures/abstraction-design-v1/README.md). + +“Exhaustive” means capability-closure over the nine behaviors named in that +plan, not coverage of every possible unsafe-Rust abstraction. + +The skill and comparator were frozen before the first valid evaluated run. No +target may be modified, built, tested, expanded by execution, or otherwise +executed. + +## Frozen package identities + +- Treatment tree digest: + `d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd` +- Treatment `SKILL.md` digest: + `2b063ad7d8c6a3f5051294e3c9ed49c8850397645b46772cd40ec6ae7136531e` +- Core-ablation tree digest: + `7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52` +- Core-ablation `SKILL.md` digest: + `c2f07d263ce89d758985d6ff388ca344e038c1db111f2298cc5ddef051697595` + +The treatment is a byte-for-byte copy of `skills/unsafe-rust`. The comparator +removes the abstraction-design reference, its activation/routing paragraphs, +its optional report-template section, and its design-only cross-references; +ordinary proof, API, configuration, authority, TCB, and verdict content is +unchanged. Both packages passed the skill static validator. + +## Frozen fixture identities + +| Mode | Source directory | Deterministic tree digest | +|---|---|---| +| A | `a_acceptance` | `f3564d5af0704da33cf13e6bb01711677ccf1746f7c2dbc822385573ed1d1f55` | +| R | `b_projection_redesign` | `8f5357f4a57900a8009c9fc9c732b04ed84715adfface9aeae9d072a547cef2c` | +| T | `c_ticket` | `df348015164c68d626b79e9c9a4625a3f7163377b2ad6783baa5f42ff27ec388` | +| P | `d_published_contract` | `0ad42022c041cbd4cc6dd555ae605cafd4e49ffaa6724baeeff142388961ed54` | +| C | `e_configuration_domain` | `5c5cdc7430571e055d7a6b1ddf281c83ed62f6f224872a158dd4ff775d07fdf9` | +| S | `f_sealed_boundary` | `cb45d50017c290dd84bc9485b6e2b33a0259b27d73582d802fd347d8d47ef78a` | +| G | `g_greenfield` | `f7679319733257b582d62744483c263cbbcaaa4b366f408e014caa01169cea30` | +| H | `h_tradeoff` | `06c138336064a80f8069feb9b6e41b92c5eb53623a07c0c1033033cd35f03d59` | +| N | `i_new_snapshot` | `eef6b621a5b74e23613d3c67679470744651a192546eb63fa03986865b275bff` | + +Digests use GNU tar streams rooted at `.`, sorted names, timestamp zero, +numeric owner/group zero, and preserved contents/modes. Every opaque runtime +copy was verified against its mode digest before the first run. + +## Frozen cells + +Each row has three fresh treatment replicates followed by three fresh +core-ablation replicates. + +| Mode | Treatment targets | Core-ablation targets | +|---|---|---| +| A | `r001`, `r002`, `r003` | `r004`, `r005`, `r006` | +| R | `r007`, `r008`, `r009` | `r010`, `r011`, `r012` | +| T | `r013`, `r014`, `r015` | `r016`, `r017`, `r018` | +| P | `r019`, `r020`, `r021` | `r022`, `r023`, `r024` | +| C | `r025`, `r026`, `r027` | `r028`, `r029`, `r030` | +| S | `r031`, `r032`, `r033` | `r034`, `r035`, `r036` | +| G | `r037`, `r038`, `r039` | `r040`, `r041`, `r042` | +| H | `r043`, `r044`, `r045` | `r046`, `r047`, `r048` | +| N | `r049`, `r050`, `r051` | `r052`, `r053`, `r054` | + +Randomized launch order, fixed before the first run: + +```text +r051 r053 r007 r015 r008 r025 r039 r046 r024 r011 r023 r030 r034 r038 +r043 r016 r052 r033 r045 r005 r021 r054 r048 r029 r019 r003 r037 r002 +r047 r044 r018 r004 r031 r040 r050 r036 r022 r001 r026 r032 r028 r017 +r006 r009 r041 r012 r020 r010 r013 r014 r035 r049 r042 r027 +``` + +## Frozen evaluated-agent prompt + +The two conditions receive byte-identical text except the resolved opaque +`[PACKAGE]` path; every cell receives its own resolved opaque `[TARGET]` and +`[OUTPUT]` paths. + +```text +Act as a fresh source-review and design 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 the other files in +[TARGET], and complete exactly the requested review/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] 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 review/design report, not a persistent whole-crate audit; +provide the equivalent proof material compactly. +``` + +No substantive steering is permitted. A neutral instruction to finish within +the current source-only scope is permitted and must be recorded. + +### Invalidated uncapped warm-up + +Before the word-limit clarification above was frozen, `r051` treatment and +`r053` core-ablation warm-ups ran with the same prompt minus its final two +sentences. Each expanded the 23-line target into a persistent-audit-scale +report and took roughly ten minutes. They were invalidated before scoring; no +semantic result motivated any change. Their raw outputs are preserved as +procedural evidence but are excluded from every score and gate. Both cells are +rerun fresh under the exact prompt above, and every other valid cell uses that +prompt from its first attempt. + +## Isolation and reproducibility limits + +Fresh collaboration agents use `fork_turns="none"`, but share a host +filesystem. Path restrictions are procedural rather than a hardened mount. +The collaboration API exposes neither a fixed seed nor a precise hosted model +identity. A pinned offline Rust-documentation mirror is unavailable. These +limitations make the suite exploratory even if all semantic gates pass. + +## Run ledger + +Raw reports will be copied byte-for-byte into `reports/rNNN.md` only after all +evaluated runs finish. Agent identity, report digest, deviations, blind score, +and adjudication will then be appended without changing the frozen material +above. + +## Collection result + +All 54 valid cells completed. Their byte-for-byte reports are in +[`reports/`](reports/); the deterministic report-tree digest is +`5e4adb0ccddb368282c95116278b980b0b1fc859f50116310e440f79a18fb649`. +Reports contain 527–907 words (mean 728.65; total 39,347), so every report +satisfied the frozen 1,400-word cap. + +Every valid cell received the same target-neutral reminder after approximately +two minutes: “Complete now within the frozen word limit using only material +already inspected; do not widen scope.” A few slow cells received a semantically +equivalent second request to stop lookup and write from material already +inspected. No finding, contract interpretation, candidate, verdict, or expected +result was supplied. + +One `r035` core-ablation agent reported that its first command ignored the +requested temporary working directory and a follow-up `find .` enumerated +enclosing-workspace filenames. It stopped immediately, opened no sibling file, +and used none of the listing. This procedural-isolation deviation is retained +and the report remains valid; the physical-isolation limitation was already +preregistered. + +The invalid uncapped warm-up report digests are: + +- `r051`: `81380cef54b5d9ba7c202478e9df4db7c1b05e004f313f8a98f2930e84a5e267` +- `r053`: `d54f0e77500ad740f2751f56d73e5e08afa316a268d6d8a6810e9c135f45abc5` + +They remain excluded from scoring. + +## Blind-scoring map + +Before scoring, each mode's reports were randomly copied under labels A–F. +Scorers receive one mode, its exact atoms, and these labels; they receive no +condition identity, source, skill, sibling mode, or map below. + +| Mode | A | B | C | D | E | F | +|---|---|---|---|---|---|---| +| Acceptance | `r005` | `r004` | `r001` | `r003` | `r006` | `r002` | +| Projection | `r011` | `r008` | `r009` | `r010` | `r007` | `r012` | +| Ticket | `r017` | `r018` | `r013` | `r015` | `r016` | `r014` | +| Published | `r023` | `r024` | `r021` | `r020` | `r019` | `r022` | +| Configuration | `r029` | `r026` | `r030` | `r027` | `r025` | `r028` | +| Sealing | `r036` | `r034` | `r035` | `r032` | `r033` | `r031` | +| Greenfield | `r040` | `r042` | `r039` | `r041` | `r037` | `r038` | +| Tradeoff | `r047` | `r045` | `r046` | `r048` | `r043` | `r044` | +| New snapshot | `r051` | `r050` | `r049` | `r054` | `r052` | `r053` | + +## Scoring and adjudication result + +All nine anonymous mode bundles received blind atom-level scores. Raw score +files were preserved unchanged, then independent adjudicators reviewed rubric +granularity and the disputed Rust-1.70 pointer semantics. The resulting +score/adjudication tree is in [`blind-scores/`](blind-scores/) and has +deterministic digest +`a0772c8375ff573c6e571676b63411d2370b735c9a0a17cb431d94852b325240`. + +The final condition-level analysis and preregistered gate disposition are in +[`result.md`](result.md). The treatment materially improved the +proposal/new-snapshot firewall but did not pass the complete gate set: two H +treatment reports incorrectly proved an unsound Rust-1.70 pointer loop, and +four treatment atoms fell below the required 2/3 rate after adjudication. No +skill/package file was changed during collection, scoring, or adjudication. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r001.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r001.md new file mode 100644 index 0000000000..8e09066bb0 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r001.md @@ -0,0 +1,61 @@ +# Acceptance review: **REJECT** + +## Claim and verdict + +Artifact: the exact `REQUEST.md` and `lib.rs` supplied in target `r001`, reviewed +2026-07-31. Scope is only `unsafe impl Piece for Tail`, its `project` +implementation, and the safe `increment_tail` surface. The requested supported +set is every Rust 1.70+ toolchain, every target, and every ordinary profile, +with no dependency, deployment, or non-authoritative TCB assumption. + +- **Contract compliance: CONTRACT-BROKEN** for `Piece for Tail`, on every + configuration where this source has the stated declarations. This result is + established directly from the source and is enough to reject the snapshot. +- **Soundness: UNPROVED** for the complete requested, open-ended Rust 1.70+ + set. No valid-use UB counterexample was found in the implementation itself; + the local pointer proof closes under the inspected current documented + semantics. But current documentation cannot establish the same propositions + retrospectively for 1.70 through every intervening release, nor prospectively + for all future releases, and the request forbids the additional compatibility + premise needed to bridge that range. + +These verdicts are deliberately separate: violating an unsafe trait's provider +contract is conclusive even when this particular safe consumer does not turn +the false guarantee into UB. + +## Boundary and invariant inventory + +`Pair(pub [u32; 2])` is safely constructible and its only declared field is the +tuple field `0`, of type `[u32; 2]`. `Tail` is safely constructible but has no +state. `Piece` is an unsafe implementer boundary: `FIELD` must name a direct +declared `Owner` field of type `Item`, and `project` promises a pointer to that +field when given a live, uniquely borrowed owner. `increment_tail(&mut Pair)` +is a safe caller boundary and may impose no extra caller safety condition. +There is no representation invariant beyond ordinary `Pair`/array validity. + +## Obligation ledger and derivation + +| Site | Required proposition | Disposition | +|---|---|---| +| `Tail::FIELD` | Names a direct declared `Pair` field whose type is `u32` | **False.** It is `"tail"`; `Pair` has only field `0: [u32; 2]`. The Reference grammar likewise distinguishes named struct fields from tuple fields, which contain a type but no identifier ([struct items](https://doc.rust-lang.org/reference/items/structs.html#tuple-structs)). | +| `Tail::project` postcondition | Returns a pointer to *that direct declared field* | **False.** `(*owner).0[1]` denotes element 1 nested inside field `0`; it is not a direct field of `Pair`. Indeed no direct `Pair` field of type `u32` exists. Thus both provider guarantees fail for every valid call, without needing a layout assumption or UB execution. | +| `Tail::project` operation | Its place projections are valid for every owner satisfying its safety precondition | Locally established under the inspected semantics: a live uniquely borrowed `Pair` supplies an in-bounds `Pair`; `.0` is its declared array field and index `1` is in `[u32; 2]`. `addr_of_mut!` creates no intermediate reference but requires field/index projections to be in-bounds ([`addr_of_mut!`, core 1.97.1](https://doc.rust-lang.org/core/ptr/macro.addr_of_mut.html#safety)); those conditions hold. Arrays place element `n` at `n * size_of::()` ([array layout](https://doc.rust-lang.org/stable/reference/type-layout.html#array-layout)). | +| `increment_tail` call | `project` receives a live, uniquely borrowed `Pair` | Locally established by the `&mut Pair` parameter and its reborrow/coercion for the call. | +| `&mut *returned_pointer` | Pointer is aligned, non-dangling, points to a valid initialized `u32`, and is exclusive while the reference is live | Established from the actual implementation, not its false direct-field postcondition: it points to initialized element 1 within the exclusively borrowed live `Pair`. The Reference identifies dangling/misaligned access, alias violations, and invalid references as UB conditions ([UB rules](https://doc.rust-lang.org/stable/reference/behavior-considered-undefined.html#undefined-pointer-access)); none is introduced on this straight-line path. `pair` is not used through a competing access afterward. | +| update | Read and write remain defined | `u32` is valid and initialized; `wrapping_add(1)` is total, and the write stays within the same exclusively borrowed element. | +| Rust-version closure | Above Rust premises apply to every Rust 1.70+ release | **Missing.** The inspected current pages do not themselves assert that their exact propositions held in every earlier supported release or will hold in every future one. With no admitted compatibility premise and no finite audit cutoff covering versioned documents, the requested universal soundness theorem cannot be `PROVED`. | + +## Configuration closure, TCB, and residual scope + +There are no `cfg`s, macros generating APIs, dependencies, allocation, FFI, +concurrency, target features, layout casts, panic-dependent cleanup, or +profile-dependent checks. Target and ordinary-profile axes are therefore +covered parametrically by typed field/array projection and straight-line +control flow; Rust-version semantics are the sole coverage gap. Evidence is +source inspection plus the linked official Rust documentation; no build, test, +expansion, external specification, tool theorem, or additional TCB premise was +used. + +No redesign or remedy is proposed because the acceptance policy forbids source +or contract changes. Any source/contract change, supported-version cutoff, or +new authoritative compatibility premise requires a new review. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r002.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r002.md new file mode 100644 index 0000000000..c5650fa391 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r002.md @@ -0,0 +1,45 @@ +# Acceptance review: `Piece for Tail` + +## Decision + +**REJECT.** The exact snapshot is **CONTRACT-BROKEN**: both `Tail::FIELD` and `Tail::project` violate literal, mandatory guarantees of `Piece`. This conclusion holds in every requested configuration in which this source compiles and requires no extra TCB or deployment restriction. The reviewed safe call path is not thereby shown to reach undefined behavior, but contract compliance is independently mandatory and the policy does not permit repair. + +## Claim and scope + +Artifact: the exact `REQUEST.md` and `lib.rs` supplied for `r002`. In scope are the `unsafe impl Piece for Tail`, its associated constant and method, and every well-typed safe call to `increment_tail`. Requested configurations are Rust 1.70+, all standard targets, and ordinary profiles. There are no dependencies, conditional compilation branches, FFI, concurrency, allocator operations, or target-specific operations. No build, test, or expansion result is evidence here. + +TCB: only the applicable Rust abstract semantics and Rust standard-library contracts; no additional premise is admitted. The source-level configuration argument is parametric over target layout and profile because it uses declared field/element containment and borrow facts, never byte offsets, representation attributes, overflow behavior, or assertions. + +## Surface and controlling obligations + +`Piece` supplies three literal provider obligations for this impl: + +1. `FIELD` names a *direct declared field* of `Owner`. +2. That field's type is `Item`. +3. Given a pointer identifying a live, uniquely borrowed `Owner` for the call, `project` avoids UB and returns a pointer to *that direct declared field*. + +`increment_tail` is safe, so it may impose no caller safety precondition. It has no documented behavioral postcondition; its name is not a contract. + +## Finding: the unsafe impl breaks its contract + +`Pair(pub [u32; 2])` declares exactly one direct field, `.0`, of type `[u32; 2]`. With `Owner = Pair` and `Item = u32`: + +- `FIELD = "tail"` names no declared field, and the sole direct field is not of type `Item`. +- `project` evaluates `(*owner).0[1]`. The selected `u32` is element 1 nested inside the direct `[u32; 2]` field; it is not itself a direct field of `Pair`, and it cannot be the nonexistent field named `"tail"`. + +These are direct counterexamples to all three provider clauses. Verdict for associated-constant compliance: **CONTRACT-BROKEN**. Verdict for the method postcondition and therefore the `unsafe impl`: **CONTRACT-BROKEN**. This is not an ambiguity about intent and cannot be cured by observing that “tail” informally describes the second element. + +## Soundness derivation for the concrete call path + +For Rust 1.70, the implementation's UB obligations are **PROVED** for valid `project` calls, and `increment_tail` is **PROVED sound**, despite the postcondition failure: + +- In `increment_tail`, `pair: &mut Pair` supplies a live, aligned, initialized `Pair` and exclusive access throughout the call; coercing it for `project` therefore satisfies the stated unsafe precondition. +- `.0` is within that `Pair`; index `1` is within `[u32; 2]`. Thus the projections identify an initialized, aligned `u32` inside the same live object, with exclusivity inherited from `pair`. +- Rust 1.70 documents that [`addr_of_mut!` creates a raw pointer without an intermediate reference and that its expression remains subject to the usual rules](https://doc.rust-lang.org/1.70.0/std/ptr/macro.addr_of_mut.html). Those rules are met by the preceding facts. +- The resulting raw pointer is non-null, aligned, points to a valid initialized `u32`, and remains exclusively accessible. These discharge the requirements for `&mut *`; Rust 1.70 describes a [reference as aligned, non-null, and pointing to valid `T`](https://doc.rust-lang.org/1.70.0/std/primitive.reference.html). The reference is used only to read, compute `wrapping_add(1)`, and write before the original borrow ends. No intervening callback, panic-producing index, or alias exists. + +Consequently, a normal call stores the old second array element plus one modulo `u32`; this observed effect is not a documented safe-API promise. The false direct-field guarantees are not consumed by this monomorphic path, so no in-scope safe UB execution follows from their failure. + +## Rust-version coverage + +The strongest unconditional proof from the inspected versioned authority is the Rust 1.70 result above. The aggregate open-ended **Rust 1.70+ soundness claim is UNPROVED**: applying the 1.70 documentation to every later and future release requires a Rust semantic-compatibility premise, and the request forbids additional TCB. This coverage gap does not weaken the version-independent contract counterexamples or the rejection decision. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r003.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r003.md new file mode 100644 index 0000000000..94a4b117e7 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r003.md @@ -0,0 +1,50 @@ +# Acceptance review: `Piece for Tail` + +## Verdict + +**REJECT this snapshot.** For the exact `lib.rs` supplied, the strongest split result is: + +- **Soundness — PROVED** for the concrete body of `Tail::project` on every call satisfying its documented safety precondition, and for every well-typed safe call to `increment_tail`, under the Rust abstract semantics on Rust 1.70+, all targets, and ordinary profiles. This is a source-level result, not a compiler/backend claim. +- **Documented contract compliance — CONTRACT-BROKEN** for `unsafe impl Piece for Tail`. Both `FIELD` and `project` contradict the literal `Piece` provider contract. +- Consequently, the combined acceptance claim (soundness plus all in-scope documented postconditions) is **CONTRACT-BROKEN**, even though no in-scope UB execution is established. + +The TCB contains only the normative Rust abstract semantics applicable to each supported Rust version; there is no dependency, tool, deployment, platform, or other additional assumption. + +## Exact claim and surface + +Snapshot: the supplied 35-line `lib.rs`; no generated artifacts or dependencies are present. In scope are the public unsafe trait contract as instantiated by `unsafe impl Piece for Tail`, its associated types and constant, `Tail::project`, and the public safe function `increment_tail`. `Pair`'s public field is included because safe callers can freely construct and mutate every valid `[u32; 2]` value. + +The literal obligations are: + +1. `Tail::FIELD` names a direct declared field of `Pair` whose type is `u32`. +2. Given a raw pointer identifying a live, uniquely borrowed `Pair` for the call, `Tail::project` executes without UB and returns a pointer to *that* direct declared field. +3. `increment_tail` is sound for every well-typed safe call, without an unstated caller safety condition. + +There is no documented functional postcondition for `increment_tail`; its name is not treated as normative contract text. + +## Contract counterexample + +`Pair` is declared as `pub struct Pair(pub [u32; 2]);`. It has one direct tuple field, selected as `.0`, and that field's type is `[u32; 2]`. It has no direct field named `tail` and no direct field of type `u32`. + +Nevertheless, the impl sets `Item = u32` and `FIELD = "tail"`. Obligation 1 is therefore false for every value of `Pair`. + +The method returns `addr_of_mut!((*owner).0[1])`. This points to element 1 nested inside the array stored in `.0`; it is not a pointer to a direct declared field of `Pair`. Thus obligation 2's provider postcondition is also false. The phrase “that direct declared field” cannot be rescued by interpreting `tail` as an informal logical name: the controlling contract expressly requires a declared field, and the declared representation supplies none matching either the name or `Item` type. + +This is a concrete postcondition counterexample, so contract compliance is **CONTRACT-BROKEN**, not merely unproved. The false metadata/directness guarantee does not itself exhibit UB in either reviewed function, so this review does not upgrade the finding to **UNSOUND**. + +## Reconstructed soundness proof + +The source has no adjacent `SAFETY` proof for either unsafe block; the material derivation is reconstructed here. + +- A well-typed call to `increment_tail` supplies `pair: &mut Pair`. During the call this denotes a live, initialized, suitably aligned `Pair` under exclusive mutable access. Its coercion/reborrow as `*mut Pair` therefore satisfies `project`'s entire stated caller safety precondition. +- In `project`, `.0` selects the sole `[u32; 2]` field and `[1]` selects an in-bounds element. Raw address formation therefore produces a pointer to a live, aligned, initialized `u32` within the same `Pair`; it does not escape this invocation of `increment_tail`. +- Forming `&mut *` from that pointer is valid: the owner remains live, the element is initialized and aligned, and the originating `&mut Pair` supplies exclusivity. No competing access occurs while `value` is used. +- `wrapping_add(1)` is defined for every `u32`; storing the resulting `u32` preserves value validity. The pointer/reference is not retained after the owner borrow. + +This proves the concrete unsafe operations despite the stronger false “direct field” postcondition. It does not repair or reinterpret that public contract. + +## Configuration closure and residual scope + +The proof is parametric over target layout and ordinary profile: it uses semantic tuple/array projection and an in-bounds fixed index, not byte offsets, endianness, pointer width, optimization, overflow checks, or debug assertions. There is no `cfg`, feature branch, allocator, FFI, assembly, concurrency, atomics, build script, macro-generated public surface, or panic-dependent invariant. No configuration partition remains uncovered. + +Excluded from the claim are compiler/backend correctness, nonordinary or unstable compiler modes, custom semantics outside the applicable Rust abstract contract, and any code not supplied in this target. Any source/contract change or material change to the governing Rust semantics requires re-review. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r004.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r004.md new file mode 100644 index 0000000000..fde615d7db --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r004.md @@ -0,0 +1,98 @@ +# `Piece for Tail` acceptance review + +## Decision and exact verdicts + +**REJECT this snapshot.** Its concrete safe wrapper has a successful source-level +soundness derivation, but the `unsafe impl Piece for Tail` falsifies two literal +public guarantees. Policy does not permit accepting a contract-broken unsafe +implementation. + +- **Soundness — `increment_tail`: PROVED** for the exact shown source on Rust + 1.70.0, every target and ordinary profile, relative only to the applicable + Rust Reference/core axioms described below. Every well-typed safe call is + free of Rust undefined behavior. The same derivation is parametric for any + later Rust release whose own authoritative documentation supplies the same + propositions. +- **Soundness — the requested unbounded `Rust 1.70+` set: UNPROVED.** No + additional TCB is permitted, and no authoritative premise inspected here + commits every future Rust version to preserve those exact semantic + propositions. The exact-1.70.0 result above is the strongest closed positive + version scope; a later release requires revalidation against its applicable + documentation. +- **Documented postconditions — `unsafe impl Piece for Tail`: + CONTRACT-BROKEN** for every target/profile and every Rust version on which + this source has its ordinary meaning. This is a direct source counterexample, + independent of UB. +- **Combined in-scope claim: CONTRACT-BROKEN** (and open-ended-version + soundness remains `UNPROVED`). + +## Boundary and obligation coverage + +The reviewed boundary comprises public `Piece::{Owner, Item, FIELD, project}`; +the `unsafe impl` for `Tail`; constructible `Pair` and its public tuple field; +constructible `Tail`; and safe `increment_tail`. There is no conditional code, +generation, dependency, FFI, allocation, concurrency, target feature, or +profile-sensitive assertion in the snapshot. + +The controlling trait contracts decompose as follows: + +1. `FIELD` must name a **direct declared field** of `Owner`, and that field's + type must be `Item`. +2. For an `owner` identifying a live, uniquely borrowed `Owner` for the call, + `project` must avoid UB and return a pointer to **that direct declared + field**. +3. Safe `increment_tail` may impose no hidden caller safety requirement. + +## Reconstructed soundness proof + +At entry to `increment_tail`, `pair: &mut Pair` supplies a live, initialized, +aligned `Pair` and exclusive access for the borrow. Converting/reborrowing it +for `Tail::project` therefore establishes the method's sole caller +precondition. + +Inside `project`, `Pair`'s only declared field is `.0: [u32; 2]`; index `1` is +always in bounds. Thus `(*owner).0[1]` denotes its live, initialized second +`u32` element. `addr_of_mut!` takes the address of that place without loading +it, so the returned raw pointer designates that element with the alignment and +provenance inherited from the live `Pair`. + +Back in `increment_tail`, no intervening callback or access can invalidate that +fact. `&mut *` creates a reference only to the second element while the +originating `&mut Pair` remains unused; no competing access occurs during the +derived borrow. The read, `u32::wrapping_add(1)`, and write are therefore valid. +The derived borrow ends locally before control returns. This also establishes +the concrete (though undocumented) effect: `.0[1]` advances modulo `2^32` and +`.0[0]` is unchanged. + +The material Rust premises are the Rust 1.70.0 core contract that +[`addr_of_mut!` creates a mutable raw pointer to a place without an intermediate +reference](https://doc.rust-lang.org/1.70.0/core/ptr/macro.addr_of_mut.html), +the Reference's [borrow-operator rules](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#borrow-operators), +and the standard-library contract for +[`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add). +The source contains no adjacent `SAFETY` derivation; the proof above is the +material reconstruction needed to validate the two unsafe blocks. + +## Contract counterexample + +With `Owner = Pair` and `Item = u32`, the declaration +`Pair(pub [u32; 2])` has exactly one direct field: tuple field `.0`, whose type +is `[u32; 2]`. It declares no field named `tail`, so `FIELD = "tail"` violates +the associated-constant guarantee. Even treating tuple index `0` as a field +name cannot help because its type is not `u32`. + +For every valid call, `project` returns `addr_of_mut!((*owner).0[1])`, a pointer +to an **array element nested within** `.0`, not to a direct declared field of +`Pair`. Its return postcondition is therefore false as well. `increment_tail` +does not consume the false field-identity guarantee, so these failures do not +invalidate the concrete UB-freedom proof; they do require the separate +`CONTRACT-BROKEN` and rejection verdicts. + +## Trust, exclusions, and review trigger + +TCB: only version-applicable normative Rust abstract semantics; no dependency, +tool, compiler-backend, environment, deployment, or out-of-band assumption. +This is a focused review of the named implementation and wrapper, not a +whole-crate or binary claim. Any source/public-contract change, supported +configuration expansion, or Rust version beyond the exactly validated scope +requires review; none can cure this snapshot's literal contract counterexample. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r005.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r005.md new file mode 100644 index 0000000000..dff3221324 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r005.md @@ -0,0 +1,89 @@ +# r005 focused review — **REJECT** + +## Claim and verdicts + +Snapshot: the exact `lib.rs` supplied with this request. Scope is only the +`unsafe impl Piece for Tail` and the safe `increment_tail` surface. The requested +configuration set is every target and ordinary profile on Rust 1.70 and later. +There are no dependencies, generated artifacts, `cfg` branches, or admitted +TCB premises; only exact applicable Rust Reference/standard-library axioms are +permitted. + +| Claim | Verdict | +|---|---| +| `Tail` satisfies the literal `Piece` contract | **CONTRACT-BROKEN** on every configuration that accepts this source | +| `increment_tail` is source-level sound on Rust 1.70.0, all targets and ordinary profiles | **PROVED**, relative only to the cited Rust 1.70 axioms | +| `increment_tail` is sound for the full open-ended Rust `1.70+` set | **UNPROVED**: later-release applicability is not established without a compatibility premise | + +The contract counterexample alone requires rejection under the accept/reject +policy. No redesign or source change is proposed. + +## Contract counterexample + +`Pair(pub [u32; 2])` declares exactly one direct tuple-struct field: field `0`, +whose type is `[u32; 2]`. `Tail` instead sets `Item = u32` and `FIELD = "tail"`. +Thus `FIELD` is not “the name of a direct declared field of `Owner` whose type is +`Item`.” This is a source-level contradiction, independent of layout, target, +profile, or input. + +For every valid call, `project` computes `addr_of_mut!((*owner).0[1])`: a pointer +to array element 1 inside direct field `0`. An array element is not itself a +direct declared field of `Pair`, and no direct field named `tail` exists. +Consequently the documented postcondition “Returns a pointer to that direct +declared field” is also false. Any live uniquely borrowed `Pair` is a concrete +valid-call counterexample to both promises. The `unsafe impl` is an assertion +of the unsafe trait contract, so both failures are provider defects, not caller +precondition failures. Rust 1.70 describes tuple structs as nominal tuple types +whose declaration contains `TupleField` entries +([Reference: structs](https://doc.rust-lang.org/1.70.0/reference/items/structs.html#tuple-structs)). + +This establishes **CONTRACT-BROKEN**, not by itself **UNSOUND**: the scoped code +contains no execution proved to reach UB from the false direct-field identity. +The only scoped safe consumer does not use `FIELD` or rely on direct-field +identity. + +## Reconstructed `increment_tail` proof (Rust 1.70.0) + +The safe function has no caller-side safety precondition. Its `&mut Pair` +argument identifies a live, initialized, aligned `Pair` and supplies exclusive +access during the call; passing it as `*mut Pair` therefore satisfies +`Piece::project`'s stated precondition. Rust's aliasing outline says an `&mut T` +must not have competing access or another reference while live +([Reference: undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#undefined-behavior)). + +Projection `.0[1]` is in bounds because field `0` is exactly `[u32; 2]`. +Rust 1.70 specifies that array element `n` is at offset +`n * size_of::()`, and Rust-layout fields are properly aligned +([Reference: array and Rust layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)). +The `addr_of_mut!` contract says it creates a mutable raw pointer “without +creating an intermediate reference” +([std 1.70 `addr_of_mut!`](https://doc.rust-lang.org/1.70.0/std/ptr/macro.addr_of_mut.html)). +Here its raw-pointer dereference and both projections remain within the live +`Pair`, and the resulting `u32` place is initialized and aligned. + +No call or competing access intervenes before `&mut *` creates `value`; that +reference is derived from the still-exclusive `pair` borrow and points to the +live second `u32`. Reading it is valid. `wrapping_add(1)` performs modular +addition and therefore has defined overflow behavior +([std 1.70 `u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add)). +Writing the resulting valid `u32` through the exclusive reference preserves +`Pair` validity. The argument is parametric over target layout and unaffected +by optimization, overflow-check, debug-assertion, or panic profile choices. + +The source contains no adjacent safety proof, but the reconstruction closes the +implementation obligation for Rust 1.70.0. Its resulting behavior is that +`pair.0[1]` is replaced by its value plus one modulo `2^32`; no broader safe-API +postcondition is documented. + +## Version closure and residual scope + +The request's `1.70+` range is open-ended. Rust 1.70 documentation cannot prove +that every cited semantic proposition applies unchanged to each later release, +and API stability alone does not preserve every behavioral sentence. The +request permits no backwards-compatibility TCB premise. Therefore the aggregate +soundness claim over all `1.70+` versions is **UNPROVED**, with the smallest +missing proposition being: *every later supported Rust release preserves all +cited 1.70 pointer, layout, aliasing, and arithmetic guarantees over this +program's domain*. This is an applicability gap, not an identified UB +counterexample. Any change to the source/contracts or extension to another +Rust release requires rechecking the corresponding versioned axioms. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r006.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r006.md new file mode 100644 index 0000000000..618e0b6cf7 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r006.md @@ -0,0 +1,51 @@ +# `r006` acceptance review + +## Decision and scoped verdicts + +**REJECT.** The exact `Piece for Tail` implementation is **CONTRACT-BROKEN** under the literal published contract, for every requested target/profile and independently of Rust-version semantic variation. This is enough to reject the immutable vendoring candidate. + +Separately, no execution of the reviewed code is shown to reach undefined behavior. For Rust 1.70, the implementation-body soundness of `Tail::project` for calls satisfying its stated precondition, and the safe-surface soundness of `increment_tail`, are **PROVED** at source level for all targets and ordinary profiles. The requested open-ended Rust `1.70+` soundness theorem is **UNPROVED** with no additional TCB: carrying Rust 1.70 semantic documentation to every later and future release requires an applicable compatibility premise, which the request disallows. This version-coverage gap does not weaken the version-independent contract counterexample. + +These verdicts are deliberately separate: the counterexample establishes a provider-postcondition failure, not by itself a valid execution reaching UB, so **CONTRACT-BROKEN**, not **UNSOUND**, is the strongest justified defect verdict. + +## Claim, snapshot, and boundary + +Scope is exactly `targets/r006/lib.rs`: the public unsafe trait `Piece`; its associated types, safe associated constant `FIELD`, and unsafe method `project`; public `Pair` and its public tuple field; public `Tail`; `unsafe impl Piece for Tail`; and safe function `increment_tail`. There are no dependencies, conditional items, generators, FFI, assembly, allocators, callbacks, concurrency, or other target files. TCB: no additional assumptions; ordinary Rust abstract semantics only. No tests or tool-derived evidence were used. + +The literal provider obligations are: + +1. `FIELD` is the name of a *direct declared field* of `Owner`, and that field's type is `Item`. +2. For an `owner` identifying a live, uniquely borrowed `Owner` for the call, `project` avoids UB and returns a pointer to *that* direct field. +3. `increment_tail` is safe, so every well-typed safe call must avoid UB without an extra caller obligation. It has no documented behavioral postcondition; its name does not create one. + +## Contract counterexample + +For this impl, `Owner = Pair` and `Item = u32`. The declaration + +```rust +pub struct Pair(pub [u32; 2]); +``` + +has one direct declared field: tuple field `0`, whose type is `[u32; 2]`. It has no direct field named `tail`, and no direct field of type `u32`. Therefore + +```rust +const FIELD: &'static str = "tail"; +``` + +falsifies obligation 1. + +For every valid `owner`, `project` computes `addr_of_mut!((*owner).0[1])`. The designated place is element `1` of the array stored in field `0`; it is an array element, not a direct declared field of `Pair`. Thus normal return falsifies obligation 2's postcondition as well. The counterexample needs no layout assumption: source-level place structure alone distinguishes `Pair`'s direct field `.0` from its nested element `.0[1]`. + +## Reconstructed memory-safety derivation + +For a valid `Tail::project` call, the precondition supplies a live, uniquely borrowed `Pair` for the duration of the call. Its `.0` is an initialized `[u32; 2]`; index `1` is in bounds. Consequently the address expression designates the initialized, properly aligned second `u32` subobject inside that live `Pair`, without depending on a hard-coded offset or field order. Forming and returning its raw pointer performs no access through an invalid place. This closes the implementation-body UB obligation while leaving the promised *direct-field* identity false. + +For any safe call `increment_tail(pair: &mut Pair)`, the argument itself establishes `project`'s live/unique precondition. No caller-controlled code intervenes. The returned raw pointer actually designates `.0[1]`; while the original exclusive borrow remains in force and unused elsewhere, converting that pointer to `&mut u32` therefore satisfies lifetime, alignment, initialization, dereferenceability, and exclusivity requirements. `wrapping_add(1)` is modular `u32` addition, and the subsequent store writes a valid `u32` through that exclusive reference. Normal return leaves `Pair` valid; there are no alternative exits relevant to the unsafe operation. + +This derivation is parametric over target layout and ordinary profile choices: field/index projection uses the compiler's actual layout; the index and array length are constants; wrapping arithmetic does not rely on overflow-check settings; and no configuration selects different code. + +## Coverage and residual scope + +All in-scope unsafe/provider sites are covered: the `unsafe impl` assertion, `FIELD`, the body and postcondition of `project`, the call and raw-pointer dereference in `increment_tail`, and the safe public construction/mutation surface of `Pair`. The public field permits arbitrary `[u32; 2]` values, all initialized valid `u32`s, so it adds no hidden unsafe state. + +The only unresolved soundness coverage is the open-ended release range: without a permitted compatibility axiom or a finite audited release cutoff, Rust 1.70 documentation cannot establish the same propositions for all `1.70+` releases. No broader whole-crate, binary/backend, robustness, or downstream-consumer claim is made. Any source/contract change, configuration expansion, or Rust-range clarification requires re-review. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r007.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r007.md new file mode 100644 index 0000000000..92718ee8ea --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r007.md @@ -0,0 +1,42 @@ +# r007 focused review and redesign + +## Claim, snapshot, and verdict + +Reviewed only `lib.rs` (SHA-256 `d76a5c0d7336aac4e551264a105c621dbd6cf27db097fb648aa3ca1e891e3429`) and the stated request, without execution. In scope are the public surfaces `Piece`, `Pair`, `Tail`, and `increment_tail`, the `Tail: Piece` implementation, and the required behavior. There are no dependencies, `cfg` branches, generated artifacts, allocation operations, concurrency, FFI, or target-specific operations. + +- **`CONTRACT-BROKEN` — `unsafe impl Piece for Tail`, every target/profile and Rust version on which this source compiles.** `Pair` declares exactly one direct tuple field, field `0`, whose type is `[u32; 2]`. `FIELD = "tail"` names no direct declared `Pair` field of type `u32`; `project` returns a pointer to the nested array element `Pair.0[1]`, not to a direct declared field. Thus both documented provider postconditions at lines 7 and 10 are false. The caller precondition at line 14 cannot repair an unrelated false postcondition. +- **`PROVED` — the concrete safe function `increment_tail`, for Rust 1.70 and, parametrically, later releases retaining the cited contracts, on every target and ordinary profile, relative only to AX-1 through AX-3 below.** Every well-typed safe call is free of Rust UB and changes only `Pair.0[1]` to its prior value plus one modulo `2^32`. No valid UB execution of this concrete function was found. +- The combined reviewed artifact is therefore **`CONTRACT-BROKEN`**, not `PROVED`. I do not label it `UNSOUND`: the false unsafe-impl promise is serious, but this snapshot's sole consumer needs only the weaker, actually true nested-element projection, and no valid safe-use UB counterexample was established. + +The public `Pair.0` permits safe construction and mutation of any `[u32; 2]`, but no hidden invariant depends on its contents. `Tail` is a constructible unit type. Third-party `Piece` implementations require `unsafe impl`; they are outside the concrete `Tail` proof and cannot affect `increment_tail`'s monomorphic call. + +## Obligation proof and documentation finding + +For the operation at lines 27 and 32: the `&mut Pair` argument denotes a live exclusively borrowed `Pair` for the entire function. Its raw-pointer coercion therefore satisfies `project`'s call precondition. Field `0` exists; index `1` is in bounds for length two and contains an initialized `u32`. `addr_of_mut!` consequently produces the address of that element. No call or aliasing transition intervenes before `&mut *` creates the element reference, and the original exclusive borrow remains active. AX-3 then proves the assigned value. Fixed in-bounds indexing and explicit wrapping make target layout, optimization, debug overflow checks, and panic strategy irrelevant. + +This material derivation is absent: neither unsafe block has a `SAFETY` comment, and the method contract's phrase “identify a live, uniquely borrowed” does not spell out provenance, alignment, accessibility, or the returned pointer's usable interval. Implementation correctness for this one call does not cure that proof-documentation defect or the false trait postconditions. + +TCB/axioms (Rust 1.70 documentation): + +- **AX-1:** [`addr_of_mut!`](https://doc.rust-lang.org/1.70.0/std/ptr/macro.addr_of_mut.html) “creates a `mut` raw pointer to a place, without creating an intermediate reference”; its place projection remains subject to the documented validity/in-bounds rules. +- **AX-2:** [arrays](https://doc.rust-lang.org/1.70.0/reference/types/array.html) are fixed-size sequences; “all elements of arrays are always initialized, and access to an array is always bounds-checked in safe methods and operators.” +- **AX-3:** [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add) is “wrapping (modular) addition” and wraps at the type boundary. + +No dependency, tool-result, platform, or implementation assumption is consumed. The links establish the MSRV endpoint; an open-ended `1.70+` promise should recheck these propositions when admitting a new Rust release rather than treating an availability badge as a forward semantic proof. + +## Recommended proof-oriented redesign + +Delete `Piece` and `Tail`; repository search found no other consumer and the owner authorizes incompatible changes. Keep only the capability actually required: + +```rust +pub struct Pair(pub [u32; 2]); + +pub fn increment_tail(pair: &mut Pair) { + let tail = &mut pair.0[1]; + *tail = tail.wrapping_add(1); +} +``` + +Exact contract: for every `old: [u32; 2]`, normal return leaves `pair.0[0] == old[0]` and `pair.0[1] == old[1].wrapping_add(1)`. It has no safety preconditions, unsafe surface, representation invariant, panic path, or allocation. + +Post-implementation proof plan: (1) confirm the exact source contains no remaining unsafe/generated/configured path; (2) use the `&mut Pair` type and safe field projection to obtain the exclusive element reference; (3) discharge bounds with `1 < 2`; (4) apply AX-3 and verify the first element is untouched; (5) check compilation at MSRV 1.70 separately as availability evidence; and (6) re-audit the implemented snapshot. The proof is parametric over targets and ordinary profiles because it uses only safe fixed-array access and explicit wrapping. Removing the public trait and `Tail` is source-breaking, but expressly authorized for this unpublished abstraction. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r008.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r008.md new file mode 100644 index 0000000000..3fb5e94616 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r008.md @@ -0,0 +1,47 @@ +# Focused unsafe-Rust review and redesign: r008 + +## Claim and scope + +Snapshot: the supplied `lib.rs` only. Scope is `Piece`, `Tail`, `Pair`, and `increment_tail`; no generated code, dependencies, features, FFI, concurrency, or allocation are present. Required domain is stable Rust 1.70+, every Rust target, and ordinary profiles. The current-artifact claim is: every well-typed safe call to `increment_tail` is free of Rust UB and increments `pair.0[1]` modulo `2^32`; the public unsafe API must additionally meet its literal documented contract. + +## Current-artifact verdicts + +- **`PROVED` (source-level, Rust 1.70 abstract semantics):** every safe call to the concrete `increment_tail` is free of UB and performs the required wrapping increment. +- **`CONTRACT-BROKEN`:** `unsafe impl Piece for Tail` does not satisfy either field-identification postcondition. `Pair` has one direct declared field, tuple field `0`, of type `[u32; 2]`. There is no direct field named `"tail"` of type `u32`; `project` returns the address of nested array element `.0[1]`, not a direct field of `Pair`. +- **`UNPROVED` for the public `Piece::project` contract as a reusable abstraction:** its safety clause uses undefined shorthand (“identify”, “live”, “uniquely borrowed”), limits exclusivity to “for the call”, and says neither the returned pointer's usable interval nor the precise alignment, provenance, initialization, accessibility, and aliasing guarantees. No generic consumer exists here, but the public contract is not proof-grade. +- **`UNPROVED` for the literal open-ended `1.70+` audit domain unless TCB-COMPAT below is accepted.** Rust 1.70 documentation cannot by itself establish unchanged semantics for every later and future stable release. + +These statuses are independent: the false trait postcondition does not make this concrete safe function unsound, because its needed facts are derivable from the inspected `Tail` implementation rather than from nominal direct-field identity. + +## Compact obligation ledger and reconstructed proof + +Safe surfaces are `Pair` and its public tuple field, the unit construction of `Tail`, reads of `Piece::FIELD`, and `increment_tail`; unsafe surfaces are downstream `Piece` implementations and `Piece::project` calls. Arbitrary safe construction/mutation of `[u32; 2]` cannot invalidate the concrete proof. + +At `addr_of_mut!((*owner).0[1])`, the caller-supplied pointer is produced directly from the live `&mut Pair`; `.0` exists, `[1]` is in bounds for `[u32; 2]`, and the resulting `u32` subobject is initialized, aligned, accessible, and within the same exclusive borrow. `addr_of_mut!` forms its raw address without loading the value. The pointer remains derived from `pair` and the `Pair` remains live and exclusively borrowed when `&mut *` immediately reborrows that subobject. Thus the reference creation satisfies the raw-pointer dereference requirements. The fixed in-bounds index cannot panic. `u32::wrapping_add(1)` computes addition modulo the type boundary, and assignment writes that result to element 1. + +This material derivation is absent from both unsafe blocks; neither has an adjacent `SAFETY` proof. That is a proof-documentation defect even though the concrete implementation obligation closes. + +Authoritative Rust 1.70 premises to pin in the implementation review are the standard-library contracts for [`addr_of_mut!`](https://doc.rust-lang.org/1.70.0/core/ptr/macro.addr_of_mut.html) and [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add), plus the Reference rules for [undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html), [arrays](https://doc.rust-lang.org/1.70.0/reference/types/array.html), [indexing](https://doc.rust-lang.org/1.70.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions), and [unsafe traits](https://doc.rust-lang.org/1.70.0/reference/items/traits.html#unsafe-traits). + +## Recommended redesign + +Delete `Piece` and `Tail` and express the one demonstrated capability directly: + +```rust +pub struct Pair(pub [u32; 2]); + +pub fn increment_tail(pair: &mut Pair) { + let tail = &mut pair.0[1]; + *tail = tail.wrapping_add(1); +} +``` + +This is the simplest proof-oriented design: no raw pointer, unsafe block, unsafe trait/impl, nominal field metadata, hidden invariant, caller/implementer safety obligation, or prospective genericity remains. It is allocation-free and retains the exact required signature and behavior. + +### Conditional post-change proof plan + +Audit the implemented snapshot separately. Check that the only index is constant `1` into length `2`; therefore safe indexing is total here. Record old `pair.0[1] = x`; the documented `wrapping_add` contract gives the assigned value `(x + 1) mod 2^32`. The exclusive safe borrow permits the write and all other storage is untouched. No allocation operation or panic path exists. The same source and argument are parametric over target layout, optimization, overflow-check, debug-assertion, and panic strategy: no `cfg`, layout assumption, generated artifact, or ordinary-profile-dependent operation occurs. Confirm compilation support at the 1.70 MSRV separately; compilation is evidence of availability/type checking, not the semantic proof. + +TCB-R170 consists only of the cited Rust 1.70 Reference/std propositions. Proposed **TCB-COMPAT** is: each later supported stable Rust release preserves those exact safe-indexing and `wrapping_add` propositions over all supported targets/profiles. Obtain an accepted compatibility authority or use a finite audit cutoff and recheck later releases; otherwise retain the open-ended-domain `UNPROVED` qualification. + +Removing public `Piece` and `Tail` is an API-breaking change in isolation, but this is authorized for an unpublished internal abstraction and the request records no consumers or planned reuse. Re-audit if the source, required behavior, MSRV, public compatibility commitment, supported configuration set, or consumed Rust contracts change. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r009.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r009.md new file mode 100644 index 0000000000..4b001429ce --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r009.md @@ -0,0 +1,108 @@ +# Focused review and redesign: r009 + +## Claim and snapshot + +Scope is exactly `targets/r009/lib.rs` as supplied, with the behavior in +`REQUEST.md`: Rust 1.70+, every target and ordinary profile. The current claim +is freedom from Rust UB for every well-typed safe use and every unsafe use +satisfying the literal documented obligations, plus the documented `Piece` +postconditions and the requested behavior of `increment_tail`. The file has no +dependencies, `cfg`, generated code, allocation, FFI, concurrency, or panic +configuration. No source execution or modification was performed. + +**Current soundness verdict: UNPROVED.** The public unsafe contract uses the +undefined phrase “identify a live, uniquely borrowed `Owner` for the call.” It +does not state the pointer's required alignment, provenance/accessibility and +initialization properties, nor precisely delimit exclusivity or the returned +pointer's usable interval. The smallest missing implication is: + +> the stated “identifies/live/uniquely borrowed” condition implies every raw +> pointer property required to evaluate the projection and makes the resulting +> pointer suitable for the promised field access for the relevant interval. + +No valid UB execution is established here, so this is not an `UNSOUND` +verdict. For the concrete safe caller, a material source-level reconstruction +is available: `pair: &mut Pair` supplies the owned object and exclusive access; +the implementation selects constant index 1 of a two-element initialized +array; the resulting pointer is immediately converted to a mutable reference, +used without an intervening access through `pair`, and discarded before +return. That supports the intended implementation, but the existing safety +documentation neither states the needed raw-pointer propositions nor cites an +applicable Rust axiom set over the open-ended 1.70+ range. It therefore cannot +close the declared theorem. + +**Documented-postcondition verdict: CONTRACT-BROKEN.** This follows entirely +from the source and does not depend on the ambiguous safety precondition: + +- `Piece::FIELD` promises the name of a *direct declared field* of `Owner` + having type `Item`. `Tail` sets `Owner = Pair`, `Item = u32`, and + `FIELD = "tail"`. `Pair` has only tuple field `0`, whose type is `[u32; 2]`; + no direct field named `tail` exists. +- `Piece::project` promises a pointer to “that direct declared field.” It + returns the address of `Pair.0[1]`, a nested array element, not a direct field + of `Pair`. + +Thus both literal promises are false for every configuration. This is a +behavioral contract defect; by itself it is not a UB counterexample because no +in-scope unsafe consumer relies on the false nominal-field claim. + +## Boundary and obligation coverage + +The complete surface is: public construction and mutation through `Pair.0`; +construction of unit struct `Tail`; the public unsafe trait and its associated +types, safe constant, and unsafe `project`; its sole `unsafe impl`; and safe +`increment_tail`. There are no representation invariants: every `[u32; 2]` +value is accepted. The unsafe obligation sites are the impl assertion, raw +projection in `project`, and raw-pointer-to-reference conversion in +`increment_tail`. The two latter sites have no adjacent proof comment, and the +trait has no explicit implementer-level `# Safety` contract. The reconstructed +argument above is material proof missing from the artifact. + +Configuration coverage is parametric in target and ordinary profile at the +source level: one unconditional implementation is selected, the index and +array length do not vary, and explicit `wrapping_add` avoids overflow-check +profile differences. The complete current verdict nevertheless remains +`UNPROVED` because the pointer-contract and exact Rust-version premises are not +closed. The TCB for this report contains no admitted dependency, tool, or +external assumptions; exact versioned Reference/std axioms were not admitted +to paper over the ambiguous source contract. + +## Recommended proof-oriented redesign + +Delete `Piece` and `Tail` and implement the one required operation directly: + +```rust +pub struct Pair(pub [u32; 2]); + +pub fn increment_tail(pair: &mut Pair) { + let tail = &mut pair.0[1]; + *tail = tail.wrapping_add(1); +} +``` + +This is the minimum capability actually required. It retains the function +signature and `Pair` representation, allocates nothing, and removes every +unsafe declaration, operation, implementer obligation, nominal-field promise, +raw pointer, and invariant boundary. A reusable projection abstraction is not +justified by the supplied consumer information. + +Conditional proof plan for the implemented replacement: + +1. Inventory confirms only the public tuple field and safe function remain; + safe callers have no safety precondition. +2. From `Pair`'s representation, `.0` is `[u32; 2]`; index `1` is in bounds in + every execution, so `tail` denotes exactly `Pair.0[1]` and no indexing panic + is reachable. +3. Verify in the exact Rust 1.70 standard-library documentation that + `u32::wrapping_add(1)` yields the old value plus one modulo `2^32`; assignment + then establishes the required postcondition and changes no other element. +4. Close 1.70+ applicability either release-by-release or with an explicitly + accepted Rust semantic-compatibility premise; re-audit if the supported + range, representation, function body, or consumed documentation changes. +5. Re-audit the implemented snapshot separately. A design sketch itself does + not receive `PROVED`. + +Removing public `Piece` and `Tail` is source-breaking in isolation, but the +abstraction is unpublished, change is expressly authorized, and the supplied +repository-search result reports no consumers or planned generic reuse. No +caller migration is therefore required beyond deleting any stale imports. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r010.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r010.md new file mode 100644 index 0000000000..a79d5a449d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r010.md @@ -0,0 +1,43 @@ +# `r010` focused unsafe-Rust review and redesign + +## Claim, snapshot, and verdict + +Scope is exactly `lib.rs` (SHA-256 `d76a5c0d7336aac4e551264a105c621dbd6cf27db097fb648aa3ca1e891e3429`) under the literal item documentation, plus the requested redesign. Surfaces are the public tuple field/constructor of `Pair`, unit construction of `Tail`, unsafe trait `Piece` and its associated items, `unsafe impl Piece for Tail`, unsafe `project`, and safe `increment_tail`. There are no dependencies, `cfg`s, generators, allocation, concurrency, FFI, or assembly. + +- **Current source soundness: PROVED** for Rust 1.70.0, every Rust-supported target and ordinary profile, relative to the axioms below. Every well-typed safe call to `increment_tail` is free of Rust UB and performs the requested wrapping increment. +- **`Piece for Tail` documented postconditions: CONTRACT-BROKEN.** This does not require or establish a UB counterexample. +- **Proof documentation: UNPROVED as written.** The unsafe trait has no implementer `# Safety` contract and neither unsafe block has an adjacent derivation. The implementation proof can be reconstructed, but the false public postconditions cannot be repaired by a comment. +- An unconditional, open-ended **Rust 1.70+** verdict needs either per-release verification of the cited semantics or reviewer acceptance of `COMPAT`: later stable Rust releases preserve these exact 1.70 semantic propositions. A stability badge alone does not prove that premise. The derivations below are parametric for every release where the propositions hold. + +No target was executed, expanded, built, or tested. + +## Findings and reconstructed proof + +**F1 — false field identity and projection (contract broken, lines 7–10, 18, 24, 27).** `Pair` declares one direct tuple field, field `0`, whose type is `[u32; 2]`. The Rust Reference says tuple indexing evaluates to the location of the field bearing that numeric name, while array elements require array indexing. Thus `"tail"` names no declared `Pair` field, and `(*owner).0[1]` is a nested array element, not a direct declared field of `Pair` of type `u32`. Both `FIELD` and `project`'s “that direct declared field” postcondition are false. See the 1.70 [tuple-indexing contract](https://doc.rust-lang.org/1.70.0/reference/expressions/tuple-expr.html#tuple-indexing-expressions) and [array/index contract](https://doc.rust-lang.org/1.70.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions). + +**O1 — `project` operation, proved for valid calls.** Its caller precondition decomposes into: `owner` identifies a live `Pair`, and that `Pair` is uniquely borrowed during the call. These facts make the `.0` projection in-bounds; `[u32; 2]` has exactly two initialized `u32` elements, so zero-based index `1` is in-bounds, initialized, and correctly aligned. `addr_of_mut!` “creates a `mut` raw pointer to a place, without creating an intermediate reference” ([Rust 1.70 `addr_of_mut!`](https://doc.rust-lang.org/1.70.0/core/ptr/macro.addr_of_mut.html)). Therefore line 27 returns a pointer valid for the unique borrow's element access. It proves that concrete fact, but not F1's documented direct-field claim. + +**O2 — safe wrapper, proved (lines 31–33).** The input `&mut Pair` supplies O1's live/exclusive facts. No intervening code can invalidate them. O1's result points to initialized element `pair.0[1]`, so the derived `&mut u32` is aligned, dereferenceable, live, and exclusive for its use. The assignment changes that element to its old value plus one modulo `2^32`; [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/core/primitive.u32.html#method.wrapping_add) is documented as modular addition wrapping at the type boundary. No other place is written. + +The materially missing local proof is O1 plus the transfer from the input borrow through O1 to line 32. Adding it would fix proof documentation only. The smallest resolution of F1 would require changing the representation or weakening the abstraction's published meaning; deletion is simpler because generic reuse is expressly out of scope. + +## Proof-oriented redesign + +Delete `Piece`, its impl, and `Tail`. Retain `Pair` and implement only the required operation: + +```rust +pub struct Pair(pub [u32; 2]); + +pub fn increment_tail(pair: &mut Pair) { + let tail = &mut pair.0[1]; + *tail = (*tail).wrapping_add(1); +} +``` + +There is no representation invariant: every `[u32; 2]` is admissible, so the public field is not a safety boundary. There is no unsafe API, raw pointer, implementer contract, or caller-side safety obligation. + +**Redesign proof plan.** At entry let `old = pair.0[1]`. The array-type proposition—“a fixed-size sequence of `N` elements,” with all elements initialized and safe access bounds-checked—applies to `[u32; 2]` ([Rust 1.70 array types](https://doc.rust-lang.org/1.70.0/reference/types/array.html)). Hence index `1` denotes an initialized `u32` and the safe mutable borrow is valid. `wrapping_add(1)` yields `(old + 1) mod 2^32`; assignment establishes that value at `Pair.0[1]`, and syntactic place analysis shows `.0[0]` is unchanged. The index cannot fail and wrapping addition cannot overflow-panic. + +Configuration closure is parametric: there is one unconditional source path; it assumes no layout, pointer width, endianness, allocator, panic strategy, overflow-check setting, optimization, or target feature. The code invokes no allocation API and creates only a borrow to existing storage, establishing allocation freedom at Rust source level. All used syntax and `wrapping_add` are available in Rust 1.70. Re-review is triggered by a change to `Pair`, the function/postcondition, supported configuration policy, MSRV, or any consumed Rust contract. + +**TCB `TCB-r010-2026-07-31`:** the linked Rust 1.70 Reference/core propositions only. `COMPAT` is pending unless explicitly accepted; there are no dependency, tool, compiler-backend, or environmental assumptions in the source-level theorem. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r011.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r011.md new file mode 100644 index 0000000000..0b7341dd33 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r011.md @@ -0,0 +1,49 @@ +# Focused review and redesign: `r011/lib.rs` + +## Claim and scope + +Snapshot: the supplied `lib.rs` only; no dependencies, generated code, `cfg`, FFI, concurrency, or allocation code. The requested domain is stable Rust 1.70+, every target on which the source is accepted, and every ordinary profile. The current safe-API claim is: every well-typed call to `increment_tail(&mut Pair)` is free of Rust UB and changes only `Pair.0[1]`, from `v` to `v + 1 mod 2^32`. + +## Current implementation + +**Soundness of `increment_tail`: PROVED**, relative to the Rust abstract semantics and the compatibility qualification below. The material derivation missing beside its unsafe block is: + +- `pair: &mut Pair` supplies a live, aligned, initialized `Pair` and exclusive access spanning the call and subsequent use. Its coercion creates a derived `*mut Pair`, satisfying `project`'s stated call precondition. +- `Pair.0` is an initialized `[u32; 2]`; index `1` is in bounds. Therefore `addr_of_mut!((*owner).0[1])` projects within that live object and returns a derived, aligned pointer to its initialized second `u32`. Rust 1.70 documents that [`addr_of_mut!` creates a raw pointer without an intermediate reference](https://doc.rust-lang.org/1.70.0/std/ptr/macro.addr_of_mut.html), while its place expression remains subject to the usual rules; the local liveness and bounds facts discharge them. +- No intervening callback, move, deallocation, aliasing access, or unwind occurs. Thus `&mut *` forms a reference while the originating exclusive borrow is still live, and the read and write through it are permitted. +- [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add) is documented as “Wrapping (modular) addition,” so the final assignment establishes the requested value for every input, including `u32::MAX`. + +No valid call/UB counterexample is established. + +**Unsafe-trait postconditions: CONTRACT-BROKEN.** `Piece::FIELD` promises “the name of a direct declared field of `Owner` whose type is `Item`.” `Pair` has one direct tuple field, `.0`, of type `[u32; 2]`; it has no field named `tail` of type `u32`. Likewise, `project` promises a pointer to such a direct field, but returns a pointer to the nested array element `.0[1]`. Both literal provider guarantees in `unsafe impl Piece for Tail` are false, independently of UB. + +**Proof/API documentation: UNPROVED as a reusable unsafe abstraction.** The unsafe trait has no `# Safety` implementer contract explaining which guarantee makes `unsafe impl` meaningful, and “pointer to” does not spell out the returned pointer's provenance, alignment, initialization, usable interval, or alias obligations. The concrete call above does not need hidden caller facts, but a future generic unsafe consumer would lack a sufficient controlling contract. The unsafe block also has no adjacent `SAFETY` derivation. These are proof-artifact/API-contract defects, separate from the proved concrete execution. + +Surfaces covered are the public tuple field, public unsafe trait and its associated types/constant/method, its sole impl, `Tail`, and the safe free function. Because the trait is public and unsealed, downstream unsafe impls are possible, but none can affect the non-generic `increment_tail` path. + +## Simplest proof-oriented redesign + +Delete `Piece`, `Tail`, their impl, and every unsafe operation. Retain the invariant-free data type and write the operation directly: + +```rust +pub struct Pair(pub [u32; 2]); + +pub fn increment_tail(pair: &mut Pair) { + let old = pair.0[1]; + pair.0[1] = old.wrapping_add(1); +} +``` + +No abstraction is warranted: there is one fixed owner, one fixed element, one consumer, and no planned generic reuse. Keeping the field public creates no safety invariant; every `[u32; 2]` value is admissible. + +### Redesign proof plan + +1. For any well-typed safe call, `&mut Pair` permits the read and write. The statically fixed array length is two and the statically fixed index is one, so both safe indexing operations select exactly `.0[1]`; no other element is assigned. +2. Let its initial value be `v`. The cited `wrapping_add` contract yields `v + 1 mod 2^32`, independent of overflow-check settings, and assignment stores that result. There is no possible panic or alternate exit in these operations. +3. The body contains only safe indexing, a `u32` local, modular arithmetic, and assignment. It invokes no allocator and carries no pointer/layout/ABI/target assumption. With no conditional source and no profile-sensitive arithmetic, the same parametric proof covers every target and ordinary profile. + +Implemented exactly as shown, the redesign's source-level soundness and required postcondition are **PROVED** over the reviewed version domain, with no project-specific invariant or unsafe-code TCB. + +## Version/TCB boundary and triggers + +Authoritative premises are the linked Rust 1.70 standard-library contracts plus the Rust abstract semantics for each reviewed compiler/library version. Applying those guarantees to the open-ended `1.70+` range requires the explicit TCB premise that later stable Rust preserves the cited indexing, borrow, and `wrapping_add` propositions. Without accepting that compatibility premise, read the verdict as bounded to versions whose exact documentation has been checked; a new stable release is a re-review trigger. Also re-review if `Pair`'s representation/type, the index, arithmetic operation, supported configuration policy, or deleted generic projection concept returns. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r012.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r012.md new file mode 100644 index 0000000000..53d847dd18 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r012.md @@ -0,0 +1,108 @@ +# Focused review and redesign: `r012` + +## Claim, scope, and verdicts + +Snapshot: the supplied `lib.rs` only; no dependencies, generated code, features, +or conditional compilation. The supported set requested is stable Rust 1.70+, +every Rust target on which this source is accepted, and every ordinary profile. +The in-scope behavior is: for every well-typed call, +`increment_tail(&mut pair)` returns with `pair.0[1]` equal to its old value plus +one modulo \(2^{32}\). No target was built or executed. + +- **Current `increment_tail` source-level soundness: PROVED for Rust 1.70's + abstract semantics**, by the reconstructed proof below. The same conclusion + for the open-ended `1.70+` range is conditional on `TCB-COMPAT` below. +- **`unsafe impl Piece for Tail`: CONTRACT-BROKEN.** This is independent of + whether its sole present consumer happens to be sound. +- **Existing proof documentation: UNPROVED.** Neither unsafe block has an + adjacent proof, and the trait does not specify the result-pointer lifetime and + aliasing facts a generic consumer would need. + +This is not a whole-crate or binary verdict. + +## Boundary and obligation review + +The reachable surfaces are public `Pair` and its public array field, constructible +`Tail`, unsafe trait `Piece` (associated types, `FIELD`, and `project`), its unsafe +`Tail` implementation, and safe `increment_tail`. `Pair` has no hidden invariant: +safe callers may construct or mutate either element freely. + +1. **Unsafe-impl provider contract — failed.** The literal `FIELD` contract says + it names a *direct declared field* of `Owner` with type `Item`. For this impl, + `Owner = Pair` and `Item = u32`, but `Pair`'s only direct declared field is + tuple field `0`, of type `[u32; 2]`; there is no direct field named `"tail"`. + The method likewise returns the address of nested element `.0[1]`, not a + direct declared field. Thus both `FIELD = "tail"` and `project`'s documented + postcondition are false. No UB execution follows merely from that mismatch, + so the supported verdict is `CONTRACT-BROKEN`, not `UNSOUND`; a future generic + unsafe consumer could nevertheless rely on the false provider guarantee. + +2. **Call to `Tail::project` — proved for this call.** `pair: &mut Pair` supplies + a live, initialized, aligned `Pair` with exclusive access for the call, so it + satisfies the method's stated caller precondition. The Rust 1.70 + [`addr_of_mut!` contract](https://doc.rust-lang.org/1.70.0/std/ptr/macro.addr_of_mut.html) + creates a raw pointer to a place without an intermediate reference, while + leaving the place expression subject to its ordinary validity/in-bounds + rules. Index `1` is in bounds for `[u32; 2]`; consequently the concrete impl + yields the address of the initialized second `u32` inside that same live + `Pair`. + +3. **`&mut *` creation — proved from concrete dataflow, not from the deficient + trait prose.** The returned pointer is non-null, aligned, dereferenceable, and + derived from the caller's exclusive `&mut Pair`; there is no intervening call + or access, and the original reference is not used while `value` is live. + These facts discharge the raw-dereference/reference requirements reflected + in Rust 1.70's [undefined-behavior rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html). + The resulting `&mut u32` remains within the call and cannot escape. + +4. **Behavior — proved.** Rust 1.70 documents + [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add) + as wrapping (modular) addition. The assignment changes exactly the projected + second element. Explicit wrapping makes overflow-check profile settings + irrelevant; the fixed in-bounds index cannot panic. The source contains no + allocator operation, target-dependent operation, `cfg`, assertion, callback, + or generated path. Hence the argument is parametric over targets and ordinary + profiles. + +## Simplest proof-oriented redesign + +Delete `Piece` and `Tail` entirely and retain only direct safe code: + +```rust +pub struct Pair(pub [u32; 2]); + +pub fn increment_tail(pair: &mut Pair) { + let old = pair.0[1]; + pair.0[1] = old.wrapping_add(1); +} +``` + +This is preferable to repairing or sealing the trait: the request identifies +one fixed field, no other consumer, and no planned generic reuse. It removes all +unsafe declarations, implementations, calls, raw pointers, stringly typed field +metadata, and implementer contracts while preserving the public operation and +requiring no allocation. + +Proof plan: (a) `Pair.0` always has exactly two initialized `u32` elements; +(b) index `1` therefore reads the required element without panic; (c) copying a +`u32` to `old` leaves the pair unchanged; (d) `wrapping_add(1)` computes the old +value plus one modulo \(2^{32}\) in debug and optimized profiles; (e) the final +safe assignment writes only element `1`; and (f) no unsafe boundary or invariant +remains. All constructs are present in Rust 1.70, and the proof uses no target +layout, pointer-width, allocator, unwind, or optimization premise. + +## TCB, limits, and review triggers + +- `AXIOM-170`: the linked Rust 1.70 Reference/standard-library propositions for + place/pointer validity and `u32::wrapping_add`. +- `TCB-COMPAT` (needed only for the unbounded future portion of “1.70+”): stable + Rust releases after 1.70 preserve the consumed safe-code and `wrapping_add` + semantics. Without accepting that non-Reference compatibility premise or + setting a finite audit cutoff and checking each covered version, the + prospective all-future claim remains `UNPROVED`. + +Re-review if `Pair`'s representation, the function body or behavior, MSRV, +supported configuration policy, or either consumed semantic proposition +changes. A strict binary-level claim that the compiler never performs any +physical allocation is outside this source review; the redesign's established +allocation claim is that it invokes no allocation API. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r013.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r013.md new file mode 100644 index 0000000000..12c12e1c98 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r013.md @@ -0,0 +1,84 @@ +# `r013` safe-API review and redesign + +## Scope and claim + +Artifact: the supplied 12-line `lib.rs`. In scope are the public opaque `Ticket` +type, its ordinary safe move/drop/auto-trait behavior, and safe +`fn ticket(id: usize) -> Ticket`. The required behavior is: for every well-typed +safe call, return a `Ticket` whose private `NonZeroUsize` contains the input when +`id != 0`, and panic when `id == 0`. The supported set is stable Rust 1.70 and +later, every target, and ordinary configurations including enabled/disabled +debug assertions and unwind/abort panic strategies. There are no dependencies, +features, generated code, FFI, concurrency, or deployment premises in the +supplied artifact. + +## Current artifact + +**Soundness: UNSOUND. Documented behavior: CONTRACT-BROKEN.** These verdicts +apply to the requested configuration set because it contains an ordinary +optimized build with debug assertions disabled. + +The public tuple field is private, so downstream safe code cannot directly +construct a `Ticket`; `ticket` is the sole inspected producer. Nevertheless, +`ticket(0)` is valid safe use. Rust 1.70 documents that +[`debug_assert!`](https://doc.rust-lang.org/1.70.0/core/macro.debug_assert.html) +is enabled only in non-optimized builds by default and that an optimized build +does not execute it unless debug assertions are requested. Thus, in the +disabled configuration, `debug_assert!(id != 0)` establishes no dominating +fact. Evaluation proceeds with `id == 0` to +`NonZeroUsize::new_unchecked(0)`. Its controlling +[`new_unchecked` contract](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new_unchecked) +says that zero results in undefined behavior and requires that the value not be +zero. The implementation therefore reaches UB from a safe call and does not +provide the documented zero-input panic. The unsafe expression also has no +adjacent `SAFETY` proof; the missing material premise is false in this supported +configuration. + +For completeness, when `id != 0`, the branch-independent local fact satisfies +`new_unchecked`'s precondition and the returned field contains `id`. When debug +assertions are enabled and `id == 0`, the macro panics. Neither region rescues +the universal claim. The counterexample is target-parametric: every `usize` +target has the value zero. Panic strategy is irrelevant because the failing +configuration never invokes the assertion's panic. + +## Recommended design + +Keep the type and documentation unchanged and replace only the function body +in a future artifact with: + +```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 the unsafe operation rather than +trying to prove an unchecked constructor from a profile-dependent assertion. +It preserves the exact safe signature. The panic payload was not documented, +so choosing one does not change the requested behavior. + +Conditional proof plan for the implemented candidate: + +- Rust 1.70's safe + [`NonZeroUsize::new`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new) + creates a nonzero value exactly when the input is nonzero. Hence `id == 0` + selects `None`; [`panic!`](https://doc.rust-lang.org/1.70.0/core/macro.panic.html) + panics, satisfying the documented zero case under either panic strategy + (unwind or abort is behavior after panic begins). +- If `id != 0`, `new` yields `Some` containing that same integer. Wrapping it in + `Ticket` establishes the representation invariant and the documented result. + Privacy then prevents safe downstream fabrication, while moves and drops + preserve or consume an already-valid safe-library value. +- The proof uses no arithmetic, layout, target capability, `cfg`, optimization, + or debug-assertion premise, so those axes are covered parametrically. + +TCB: the cited Rust 1.70 `core` contracts. For the open-ended `1.70+` range, +also record the explicit compatibility premise that later supported stable +releases preserve these exact `new` and `panic!` propositions; otherwise bound +the proof to individually verified releases. Re-audit on a relevant contract, +MSRV, signature, representation, or support-set change. Because no source edit +was requested, this candidate is not itself assigned `PROVED`; audit its exact +implemented snapshot separately. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r014.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r014.md new file mode 100644 index 0000000000..9bf9454542 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r014.md @@ -0,0 +1,45 @@ +# `r014` safe-API review and redesign + +## Claim and scope + +Snapshot: the complete supplied `lib.rs`. Scope is the public opaque `Ticket` type and safe function `ticket(usize) -> Ticket`; there are no other files, dependencies, features, generated code, or deployment assumptions. Required domain is stable Rust 1.70+ on every target and ordinary profile. For every well-typed safe call, the function must be free of Rust UB, panic when `id == 0`, and otherwise return a `Ticket` whose private `NonZeroUsize` contains `id`. + +The only construction surface is `ticket`; the tuple field is private. Moving and dropping `Ticket` introduce no source-level unsafe operation. No execution-based evidence was used. + +## Authoritative premises + +- **AX-DEBUG-1 (Rust 1.70):** [`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html#uses) is enabled in non-optimized builds by default, while an optimized build does not execute it unless debug assertions are requested. If an executed assertion is false, the macro invokes `panic!`. +- **AX-NZ-1 (Rust 1.70):** [`NonZeroUsize::new_unchecked`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new_unchecked) requires: “The value must not be zero”; its documentation states that zero causes undefined behaviour. +- **AX-NZ-2 (Rust 1.70):** [`NonZeroUsize::new`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new) safely creates a nonzero value exactly when its input is nonzero, returning `Option`. + +For the open-ended `1.70+` range, these exact standard-library propositions must hold in each supported release. Rust 1.70 is directly verified here; applying its wording unchanged to later or future releases requires either verification against each versioned page or an explicit stable-compatibility TCB premise. This is a maintenance qualification, not a way to weaken the counterexample, which already exists on Rust 1.70. + +## Current artifact + +Configuration partition by whether debug assertions execute is exhaustive for the only code-affecting axis: + +- **Enabled:** For `id == 0`, `debug_assert!` panics before the unsafe call. For `id != 0`, the dominating condition establishes AX-NZ-1's entire precondition; `new_unchecked(id)` produces the matching nonzero value, which is immediately wrapped. This region satisfies soundness and the documented behavior. +- **Disabled:** `ticket(0)` is a valid safe call. The assertion performs no runtime check, so the function calls `new_unchecked(0)`, contradicting AX-NZ-1 and reaching UB. It also fails to provide the documented guaranteed panic. Nonzero inputs satisfy the unsafe precondition, but that does not rescue the universal safe-API claim. + +**Soundness verdict: `UNSOUND`** for the requested supported set, specifically every configuration in which debug assertions do not execute and `id == 0`. The smallest false implication is “the preceding `debug_assert!` establishes `id != 0` in every supported profile.” The unsafe block also has no adjacent proof explaining its obligation. + +**Documented-behavior verdict: `CONTRACT-BROKEN`** in the same region: the zero-input path reaches UB instead of guaranteeing the documented panic. The behavior is proved only in the debug-assertions-enabled region. + +This argument is target-parametric: every `usize` target has the distinguished value zero, and no layout, width, optimizer, panic-strategy, or target-specific fact changes the branch proof. + +## Recommended redesign + +Preserve the public type, documentation, and exact safe signature; replace only the body conceptually with: + +```rust +pub fn ticket(id: usize) -> Ticket { + match NonZeroUsize::new(id) { + Some(id) => Ticket(id), + None => panic!("ticket id must be nonzero"), + } +} +``` + +Conditional proof plan: by AX-NZ-2, zero selects `None` and the explicit panic branch; every nonzero input selects `Some` containing that same input and constructs `Ticket`. All operations are safe, so there is no caller safety obligation, unsafe block, safety comment, or additional representation invariant to prove. The control flow is independent of optimization and debug assertions and is valid on Rust 1.70 and every target. Panic abort versus unwind changes termination mechanics, not the promised fact that the zero case panics; no invariant restoration is pending on that path. + +This preserves every documented behavior and the exact signature. Panic text/location was not documented and is not preserved. Removing release UB is remediation, not a contract change. This is a design, not a verdict for an implemented snapshot; after implementation, re-audit the exact source and the applicable per-release standard-library contract. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r015.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r015.md new file mode 100644 index 0000000000..c0cb461f50 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r015.md @@ -0,0 +1,40 @@ +# Focused unsafe-Rust review and redesign: r015 + +## Claim, scope, and verdict + +Snapshot: the supplied `lib.rs` (12 lines), with no dependencies, generated code, features, or deployment restrictions. In scope are the public opaque `Ticket` type and the safe `ticket(usize) -> Ticket` constructor, its documented return/panic behavior, and the one `new_unchecked` call. The supported set is stable Rust 1.70+, every target, and every ordinary profile/panic strategy; the relevant configuration partition is debug assertions enabled versus disabled. No code was executed. + +**Current soundness verdict: UNSOUND for the supported set.** A well-typed safe call `ticket(0)` reaches undefined behavior whenever debug assertions are disabled. Rust 1.70 alone supplies a supported counterexample, so no cross-version premise is needed for this verdict. + +**Current documented-behavior verdict: PROVED only in the debug-assertions-enabled partition; UNPROVED for the full supported set.** On the failing partition, undefined behavior prevents a proof of the promised panic. I do not separately call this `CONTRACT-BROKEN`: the same execution already has UB, so Rust gives no post-UB observation from which to prove a merely behavioral counterexample. + +## Boundary, invariant, and obligation ledger + +The complete safe surface is `Ticket` (public type, private field) and `ticket`; there are no exposed unsafe APIs, fields, methods, trait impls, macros, or callbacks. Downstream safe code cannot directly construct or replace `Ticket.0`. The representation invariant is: while a `Ticket` value exists, its private field is a valid `NonZeroUsize` containing the constructor argument. + +Rust 1.70 documents that `debug_assert!` is “only enabled in non optimized builds by default” and that an optimized build does not execute it absent `-C debug-assertions` ([`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html)). It also states for `NonZeroUsize::new_unchecked` that “The value must not be zero,” and describes zero as undefined behavior ([`new_unchecked`](https://doc.rust-lang.org/1.70.0/std/num/struct.NonZeroUsize.html#method.new_unchecked)). Therefore: + +- **O1, `id != 0`: PROVED in both partitions.** Either the assertion succeeds or is absent, but the input itself establishes the unsafe precondition. `new_unchecked` then preserves the exact integer value, and the private field establishes the invariant. +- **O2, `id == 0`, assertions enabled: PROVED.** The dominating assertion panics; the unsafe call is not reached. This establishes the documented outcome. +- **O3, `id == 0`, assertions disabled: UNSOUND.** The macro performs no runtime check, so zero reaches `new_unchecked`, falsifying its safety precondition. This is reachable entirely through the safe public API on every target. +- **Proof artifact: deficient.** The unsafe block has no adjacent `SAFETY` proof. Even a complete comment could only prove O1/O2 and would expose O3; documentation cannot repair the implementation. + +The argument is parametric over target pointer width, optimization level, and unwind-versus-abort: it uses only equality with zero and the documented assertion switch. No generated artifacts or external assumptions exist. + +## Recommended redesign + +Preserve the exact public signature and documentation, but eliminate the unsafe operation: + +```rust +pub fn ticket(id: usize) -> Ticket { + Ticket(NonZeroUsize::new(id).expect("ticket id must be nonzero")) +} +``` + +This is the smallest proof surface. Rust 1.70 documents `NonZeroUsize::new` as creating a nonzero value exactly when its input is nonzero ([`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 for `None` ([`expect`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect)). Thus zero produces `None` and unconditionally starts a panic in every ordinary profile; nonzero produces the same integer inside `Ticket`. Panic-abort versus unwind does not change the documented proposition “panics.” The private field continues to carry the invariant, with no caller obligation and no `SAFETY` comment or unsafe TCB component. + +This proposal preserves the type/signature, visibility, successful values, and zero-input behavior; only the unspecified panic text/backtrace may differ. It is source-compatible with Rust 1.70 and target-parametric. As a design sketch it has no post-change `PROVED` verdict until implemented and reviewed. For the open-ended `1.70+` range, each future stable release must retain the cited `new`/`expect` contracts (or an explicit accepted compatibility premise must cover them); a material contract change, supported-configuration expansion, or source change triggers re-audit. + +## TCB and residual scope + +TCB: the three exact Rust 1.70 standard-library contracts linked above, plus ordinary Rust abstract semantics. There are no dependencies, tools, external specifications, environment premises, or tests relied upon. Review is limited to the supplied source and requested safe constructor behavior; compiler/backend correctness, panic message identity, resource behavior, and whole-crate properties outside this file are excluded. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r016.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r016.md new file mode 100644 index 0000000000..a0016801b6 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r016.md @@ -0,0 +1,40 @@ +# Focused safe-API review and redesign + +## Claim and verdict + +Scope is exactly the supplied `lib.rs`: the public opaque tuple struct `Ticket`, the safe free function `ticket(usize) -> Ticket`, its stated panic/value behavior, and the one internal unsafe call. Supported configurations are ordinary stable Rust 1.70+ builds on every Rust-supported target, with arbitrary optimization, debug-assertion, overflow-check, and panic-strategy settings. There are no dependencies, generated artifacts, callbacks, concurrency, FFI, or deployment assumptions in scope. + +- **Current implementation — UNSOUND.** A well-typed safe call `ticket(0)` reaches undefined behavior in every supported configuration in which debug assertions are disabled. +- **Current documented behavior — CONTRACT-BROKEN in that configuration class.** The promised zero-input panic is replaced by an unsafe-contract violation; it is not a guaranteed panic. +- **Proposed implementation — PROVED** for soundness and the stated behavior on Rust 1.70, and for later stable releases relative to `COMPAT-1` below, across the complete supported configuration set. + +## Finding and derivation + +Rust 1.70 documents that `debug_assert!` is “only enabled in non optimized builds by default” and that an optimized build does not execute it unless `-C debug-assertions` is supplied ([`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html#uses)). Thus, with debug assertions disabled and `id == 0`, there is no dominating runtime check. + +The controlling contract for [`NonZeroUsize::new_unchecked`](https://doc.rust-lang.org/1.70.0/std/num/struct.NonZeroUsize.html#method.new_unchecked) says that constructing it from zero is undefined behavior. The call receives the unchanged zero, so its required proposition `id != 0` is false. This counterexample is available through the public safe function and needs no caller-side safety violation. A debug-only assertion cannot discharge a release-path safety obligation. + +The private tuple field prevents downstream safe construction through the tuple constructor, but it does not help this in-module producer: `ticket` itself must establish the `NonZeroUsize` invariant before constructing `Ticket`. + +## Redesign + +Keep the public items and documentation exactly as they are; 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 nonzero")) +} +``` + +This is the smallest proof surface: it contains no unsafe operation and no duplicated assertion. [`NonZeroUsize::new`](https://doc.rust-lang.org/1.70.0/std/num/struct.NonZeroUsize.html#method.new) returns `None` for zero and a `Some` containing the corresponding nonzero value otherwise. [`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`.” Therefore: + +1. If `id == 0`, `new(id)` is `None`, so `expect` initiates a panic. Whether that panic unwinds or aborts is controlled by the profile and does not alter the documented fact that the call panics. +2. If `id != 0`, `new(id)` is `Some(n)` where `n` contains the same integer; `expect` yields `n`, and `Ticket(n)` returns a ticket containing `id`. + +These cases exhaust `usize`. The argument is parametric in `usize` width and target, and it contains no profile-sensitive assertion, arithmetic, allocation, or target-specific operation. Optimization, debug assertions, overflow checks, and panic strategy therefore introduce no uncovered implementation branch. + +## Surface, trust, and maintenance + +The complete safe surface is the opaque public type and `ticket`; there are no unsafe public APIs. The only invariant is type-local: the private field is a valid `NonZeroUsize`. The proposed safe constructor establishes it directly, and this snapshot has no mutator or other consumer. No `SAFETY` comment is needed after removing the unsafe call. + +TCB/axioms are the exact Rust 1.70 standard-library contracts linked above. `COMPAT-1` is the explicit compatibility premise that every later supported stable Rust release preserves those exact stable `new`/`expect` propositions and the validity of code accepted on 1.70; without accepting or re-verifying that premise per release, the open-ended `1.70+` verdict is **UNPROVED**, though the Rust 1.70 result remains **PROVED**. Re-review if the body, public behavior, supported toolchain/configuration set, or any consumed standard-library contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r017.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r017.md new file mode 100644 index 0000000000..09f7574c21 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r017.md @@ -0,0 +1,82 @@ +# Focused unsafe-Rust review and redesign + +## Claim and scope + +Artifact: the supplied `lib.rs`. In scope are the public `Ticket` type and safe +`pub fn ticket(id: usize) -> Ticket`, including its documented return/panic +behavior, for Rust 1.70+ on every target and every ordinary profile. `Ticket`'s +tuple field is private; the snapshot contains no other constructors, methods, +traits, macros, configuration gates, generated code, dependencies, or +deployment assumptions. Well-typed safe callers may pass every `usize`, +including zero. + +## Current implementation + +**Soundness verdict: UNSOUND.** This is a valid safe-call counterexample: + +1. Select an optimized build without `-C debug-assertions` and call + `ticket(0)`. Rust 1.70 documents that an optimized build does not execute a + [`debug_assert!`](https://doc.rust-lang.org/1.70.0/core/macro.debug_assert.html) + unless that flag is passed. +2. Therefore the assertion supplies no fact at the unsafe call, and + `NonZeroUsize::new_unchecked` receives zero. +3. The Rust 1.70 + [`NonZeroUsize::new_unchecked`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new_unchecked) + contract says: “The value must not be zero” and that zero results in + undefined behavior. + +The documented zero-input behavior is consequently not guaranteed in that +supported configuration: execution reaches undefined behavior rather than a +proved panic. With debug assertions enabled, zero does panic; for nonzero input +the unsafe precondition is satisfied. That conditional success cannot close a +safe API quantified over all supported profiles and inputs. There is also no +adjacent `SAFETY` proof, but documentation repair alone cannot fix the missing +release check. + +## Recommended redesign + +Keep the public type, documentation, and exact safe signature, and replace only +the body conceptually (no source edit was requested): + +```rust +pub fn ticket(id: usize) -> Ticket { + match NonZeroUsize::new(id) { + Some(id) => Ticket(id), + None => panic!("id must be nonzero"), + } +} +``` + +This is preferable to changing `debug_assert!` to `assert!` while retaining +`new_unchecked`: it eliminates the unsafe operation and its local proof +obligation altogether. The panic text is not part of the stated contract. + +## Replacement derivation + +Rust 1.70 documents `NonZeroUsize` as an integer known not to equal zero, with +zero excluded from its valid instances, and documents safe +[`NonZeroUsize::new`](https://doc.rust-lang.org/1.70.0/core/num/struct.NonZeroUsize.html#method.new) +as creating the nonzero value when its argument is nonzero. + +- If `id != 0`, `new(id)` produces `Some` containing that nonzero value; the + `Some` arm wraps exactly it, so the returned `Ticket` contains `id`. +- If `id == 0`, no valid `NonZeroUsize` can contain it, so `new(id)` produces + `None`; the `None` arm invokes `panic!` and does not return a ticket. +- Both arms use only safe operations. The private field continues to carry the + representation invariant that every returned `Ticket` contains a valid + `NonZeroUsize`. + +**Redesign verdict: PROVED** for soundness and the stated return/panic behavior +under the cited Rust 1.70 `core` contracts. The proof is target-parametric: it +uses only equality with zero and the target's own `usize`/`NonZeroUsize`; no +width, layout, ABI, allocator, or platform premise is needed. It is also +profile-parametric: there is no `debug_assert!`, overflow, optimization-sensitive +operation, or unsafe block. Panic unwind versus abort changes termination +mechanics, not whether the zero branch initiates the documented panic. + +For later Rust releases, the same proof applies whenever that release's +applicable standard-library documentation retains these exact contracts. An +open-ended “1.70+” range necessarily makes preservation by future Rust releases +a TCB/compatibility premise; re-review if `NonZeroUsize::new`, `panic!`, or +relevant validity semantics change. No dependency or environmental TCB entries +are needed. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r018.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r018.md new file mode 100644 index 0000000000..8d84233d96 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r018.md @@ -0,0 +1,48 @@ +# Focused unsafe-Rust review and redesign + +## Claim and scope + +Reviewed exactly the supplied `lib.rs`: the public opaque `Ticket` tuple struct, the safe free function `pub fn ticket(id: usize) -> Ticket`, its one `unsafe` call, and the documented guarantees “returns a ticket containing `id`” and “panics when `id == 0`.” The required configuration set is stable Rust 1.70+, every target, and ordinary profiles, including both enabled and disabled debug assertions. There are no dependencies, generated artifacts, FFI, deployment premises, or other source in scope. + +**Current-source verdict: UNSOUND.** A well-typed safe call `ticket(0)` reaches undefined behavior whenever debug assertions are disabled. The documented zero-input behavior is consequently **UNPROVED** in that configuration: an execution containing UB cannot establish the promised panic. Nonzero behavior is proved below. This is a focused verdict for this API/source only, not a whole-crate verdict. + +## Boundary and configuration inventory + +- `Ticket` is public but its sole `NonZeroUsize` field and tuple constructor are private. In this snapshot, downstream safe code can obtain a `Ticket` only through `ticket`; moving, borrowing, and dropping it do not alter its field. +- `ticket` is safe, so every `usize`, including zero, is a valid caller input; documentation cannot impose a hidden safety precondition. +- The sole unsafe obligation is `NonZeroUsize::new_unchecked(id)`: Rust 1.70 documents that creating it with zero “results in undefined behavior” and states “The value must not be zero” ([Rust 1.70 `new_unchecked`](https://doc.rust-lang.org/1.70.0/std/num/struct.NonZeroUsize.html#method.new_unchecked)). +- The relevant configuration partition is exhaustive: debug assertions are enabled or disabled. Rust 1.70 says optimized builds do not execute `debug_assert!` unless debug assertions are explicitly enabled ([Rust 1.70 `debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html)). Target width, optimization level, overflow checks, and panic unwind/abort do not change whether a `usize` is zero. + +## Finding and derivation + +**F-1 — UNSOUND; all targets, every profile with debug assertions disabled.** + +For `id == 0`, the `debug_assert!` is not executed in this configuration, so no dominating check establishes the unsafe callee's nonzero precondition. The next expression is exactly `NonZeroUsize::new_unchecked(0)`, whose applicable contract states that this is UB. Because `ticket` is safe and zero is an ordinary `usize`, this is a valid safe-use counterexample. In configurations with debug assertions enabled, zero instead panics before the unsafe call. For `id != 0`, in either branch of the configuration partition, the input itself establishes the callee precondition; the returned `NonZeroUsize` contains that same integer and is stored unchanged in `Ticket`. + +The unsafe block also has no adjacent `SAFETY` proof. Even where the implementation obligation succeeds (`id != 0`), the necessary dominating-fact derivation is absent. The redesign below removes both the defect and that proof-maintenance surface. + +## Recommended redesign + +Keep the type, visibility, exact safe function signature, and documentation unchanged; replace only the function body conceptually with checked construction: + +```rust +pub fn ticket(id: usize) -> Ticket { + match NonZeroUsize::new(id) { + Some(value) => Ticket(value), + None => panic!("id must be nonzero"), + } +} +``` + +No source edit was made. The panic text is not part of the supplied contract; if it is an unstated compatibility requirement, use the desired text without changing the proof. + +## Redesign proof + +Rust 1.70 documents checked `new` as creating a nonzero value when the argument is nonzero ([Rust 1.70 `NonZeroUsize::new`](https://doc.rust-lang.org/1.70.0/std/num/struct.NonZeroUsize.html#method.new)); `NonZeroUsize` cannot contain zero, and `get` returns its contained primitive value ([Rust 1.70 `get`](https://doc.rust-lang.org/1.70.0/std/num/struct.NonZeroUsize.html#method.get)). Partition all inputs: + +1. If `id == 0`, checked construction yields no `NonZeroUsize`; the `None` arm invokes `panic!`, and no `Ticket` is constructed. +2. If `id != 0`, checked construction yields a valid `NonZeroUsize` containing exactly `id`; the `Some` arm stores it unchanged and returns `Ticket`. + +These cases are exhaustive for every `usize`. There is no unsafe operation, caller obligation, target-dependent fact, conditional compilation, or debug-only check. Thus the same proof covers all targets and profiles; under unwind the zero arm begins a panic, and under abort it begins the same panic then aborts. + +**Redesign verdict: PROVED** for Rust 1.70.0, all targets and ordinary profiles, relative only to the cited versioned standard-library axioms. For the open-ended `1.70+` range, the same result is **PROVED relative to COMPAT-1**: each later supported stable release preserves the cited checked-constructor, contained-value, and panic semantics. A stability badge alone does not prove COMPAT-1; either accept it explicitly as the compatibility TCB premise or verify those exact contracts for each bounded supported release. Future releases require re-review. Changes to the signature, representation/privacy, checked-constructor contract, panic contract, or supported toolchain/configuration set are also review triggers. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r019.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r019.md new file mode 100644 index 0000000000..6c497d6160 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r019.md @@ -0,0 +1,52 @@ +# `Block` 1.x contract review and redesign boundary + +## Claim, scope, and verdict + +Snapshot: the supplied `lib.rs` only. Scope is `Block`, `Page`'s `unsafe impl`, and `first`, for Rust 1.70+, every target and profile. Downstream implementations and consumers are unavailable and therefore represented only by the published contract; this is not a whole-crate verdict. + +**Current implementation: PROVED, conditionally for the open-ended toolchain range.** For every valid `Page` and every `B` whose `unsafe impl Block` satisfies the published obligations, `Page::base` establishes all documented results and `first` performs a valid `u8` read. This is relative to the Rust 1.70 abstract-semantics premises below and `COMPAT-1`: later stable Rust versions preserve the exact relied-on semantics. Without acceptance of `COMPAT-1` (or a finite audit cutoff plus version-by-version evidence), the unbounded `1.70+` portion is **UNPROVED**. + +There is no target/profile case split: the source has no `cfg`, generated code, FFI, allocation, concurrency, or profile-dependent check, and the proof is parametric over targets. + +## Contract and obligation ledger + +The controlling published obligations are literal: + +- every implementer supplies a nonzero power-of-two `ALIGN`; +- throughout the borrow, `base()` returns a non-null, `ALIGN`-aligned pointer readable for 16 bytes; +- `first` is safe, so it may impose no additional caller safety condition. + +`Page` discharges the whole contract, not merely the part used in this file. `ALIGN = 16` has the required arithmetic properties. `repr(C, align(16))` gives `Page` at least 16-byte alignment, the sole field is at offset zero, and `[u8; 16]::as_ptr()` points to its 16-element buffer. A live shared borrow keeps that array alive for the relevant interval; all initialized `u8` values are valid. Thus the returned pointer is non-null, 16-aligned, and readable for all 16 bytes during the borrow. + +`first` needs only a strict projection of that promise: the returned pointer must remain valid for a read of one initialized `u8` until the dereference, and must meet `u8` alignment. “Readable for 16 bytes during the borrow” supplies the first fact; nonzero power-of-two `ALIGN` is at least one, so `ALIGN`-alignment supplies the second. The dereference occurs while `block: &B` remains borrowed. Neither the remaining 15 bytes nor the numeric value of `ALIGN` is otherwise consumed. + +Rust premises used are the Rust 1.70 Reference rules for [unsafe traits](https://doc.rust-lang.org/1.70.0/reference/items/traits.html#unsafe-traits), [`repr(C)` struct layout and alignment modifiers](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#representations), and [raw-pointer dereference as an unsafe operation](https://doc.rust-lang.org/1.70.0/reference/unsafe-keyword.html); plus the Rust 1.70 slice [`as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr) contract. `COMPAT-1` is an explicit non-authoritative compatibility premise, not inferred from an API stability badge. + +The implementation proof is material but absent from the source. That is a proof-documentation defect, although the reconstructed implementation proof closes. The trait should also have a `# Safety` section that identifies the existing constant and method clauses as implementer obligations; wording must restate, not strengthen or reinterpret, the already-published contract. + +## What 1.x may simplify + +Within the stated SemVer promise, simplification is confined behind the public boundary: + +- Document `first`'s local proof using only the one-byte projection above. It need not mention or prove usefulness of the other 15 bytes. +- Add an adjacent `SAFETY` proof to the `unsafe impl` covering `16`, layout, field offset, buffer lifetime, and readability. This records an existing derivation; it changes no obligation. +- Internally introduce a private helper/capability that yields a byte by value, or specialize internal `Page` use, while leaving `Block`, its blanket generic applicability, and `first`'s public signature and behavior intact. +- An additive, differently named safe API may be offered for opt-in migration, but it cannot replace or narrow `Block` in 1.x. Avoid a new public blanket impl unless its coherence and method-resolution impact is separately reviewed. + +Repository search finding only `first` cannot justify a contract edit: published downstream generic consumers may use all 16 bytes and `ALIGN`, and downstream `unsafe impl`s are not enumerable. + +## Changes requiring explicit 2.0 authorization + +Removing `ALIGN`, reducing 16 readable bytes to one, shortening the temporal guarantee, sealing `Block`, adding a new required item, or otherwise strengthening implementer duties is breaking. Weakening supplied guarantees invalidates downstream unsafe consumers; strengthening duties or sealing invalidates downstream implementations. Recasting new requirements as a “clarification” would not avoid this. + +For 2.0, prefer the minimum capability actually needed here: + +```rust +pub trait FirstByte { + fn first_byte(&self) -> u8; +} +``` + +`Page` can implement this entirely in safe code with indexing. This removes the raw-pointer capability and unsafe implementer boundary from the known use. If downstream users genuinely need the old capability, preserve it separately as an explicitly unsafe `Block16`-style trait with the full old contract; do not make `FirstByte` carry unrelated layout promises. A one-readable-byte unsafe pointer trait is a fallback only for consumers that demonstrably require a pointer rather than a value. + +Migration must inventory downstream implementations and consumers through an explicit major-release process: move byte-only consumers to `FirstByte`, move 16-byte/alignment consumers to the stronger trait, audit every unsafe impl and dereference against its new contract, and then re-audit the implemented 2.0 snapshot. No source change or major migration is authorized by this review. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r020.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r020.md new file mode 100644 index 0000000000..a183eef93f --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r020.md @@ -0,0 +1,48 @@ +# r020 focused review and redesign + +## Claim, scope, and verdict + +Snapshot: the supplied `lib.rs`; scope is `Block`, `unsafe impl Block for Page`, and `first`, on stable Rust 1.70+, every target and profile. No generated code, dependencies, configuration branches, or tool evidence are present. + +- **`Page` provider obligations: PROVED.** `ALIGN == 16` is a nonzero power of two. `repr(C)` puts the sole field at offset zero, and `repr(align(16))` makes every `Page` address at least 16-aligned. `[u8; 16]` is 16 contiguous initialized bytes in every valid `Page`; `self.0.as_ptr()` points to its first element. The live shared receiver keeps that storage present and readable for the receiver borrow. Thus the result is non-null, 16-aligned, and readable for all 16 bytes. +- **`first` soundness: PROVED relative to the contract reading below.** A valid unsafe implementation supplies a pointer readable for 16 bytes throughout the `&B` borrow. The dereference occurs while `block` remains borrowed and loads only the first `u8`. It does not consume `ALIGN`, power-of-two-ness, 16-byte alignment as such (`u8` has alignment 1), or readability of bytes 1–15. +- **Published proof artifact: UNPROVED without a contract clarification.** The smallest missing proposition is that “readable for 16 bytes during the borrow” means that, for the entire relevant `&self` borrow, the returned pointer has suitable provenance, points to live storage containing 16 initialized `u8` values, and may be read without an aliasing or data-race violation. Under that natural normalized meaning the preceding proof closes; the current prose does not define “readable” or identify the borrow precisely enough for a proof-grade public unsafe-trait contract. No valid UB counterexample is established, so this is not an `UNSOUND` finding. +- No documented postcondition says that the pointed-to byte is a logical “first block byte.” `first` can promise only the value read from the address returned by that invocation of `base`; its name cannot add a relationship absent from `Block`'s contract. + +The Rust premises are the 1.70 Reference rules that `u8` has size/alignment 1, arrays are contiguous, and the C struct layout algorithm places the first field at offset zero, together with the alignment modifier rule ([type layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#type-layout), [C representation](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-c-representation), [alignment modifiers](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers)); and the standard-library contract that `slice::as_ptr` returns the slice buffer pointer ([`as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)). Raw loading must not use a dangling or misaligned place and must not produce an invalid value ([undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). The all-target/profile proof is parametric in those rules: there is no `cfg`, arithmetic, assertion, panic, or optimization-dependent branch. For post-1.70 toolchains, the verdict is relative to the explicit TCB premise that stable Rust preserves those propositions; otherwise each later documentation version needs re-verification. + +## What 1.x can simplify + +The published contract remains controlling even though the only found in-tree consumer needs less. Unknown downstream consumers may rely on non-nullness, `ALIGN`, power-of-two-ness, `ALIGN` alignment, and all 16 readable bytes; unknown downstream unsafe impls are entitled to have only the existing obligations. + +Without changing public semantics, a 1.x release may: + +1. Add an adjacent `SAFETY` proof to `first` stating the exact one-byte subset above, and a full-contract safety rationale at `Page`'s unsafe impl. This reduces proof fan-out but not the contract. +2. Clarify public wording only to the extent the project can establish that the clarification is already entailed by the published meaning. If the normalized meaning above would newly constrain a reasonable existing impl, it is a strengthened implementer obligation and is not a compatible “documentation fix.” +3. Add a separate safe, value-returning capability and deprecate (but retain) the old path, after ordinary additive-API/coherence review. Existing `first` must retain its signature and behavior; a blanket adapter would merely centralize, not eliminate, reliance on the old unsafe contract. + +It is not 1.x-compatible to remove or weaken any provider guarantee, strengthen the implementer contract (including requiring a relationship to logical block contents), seal the trait, add a required method, change `base`'s type, or change `first`'s bound. Weakening guarantees breaks downstream proofs; strengthening obligations or sealing breaks downstream impls. The supplied ordinary-SemVer commitment is the compatibility premise. + +## 2.0 candidate and migration + +If the required capability really is only this consumer, prefer a safe value boundary: + +```rust +pub trait FirstByte { + fn first_byte(&self) -> u8; +} + +impl FirstByte for Page { + fn first_byte(&self) -> u8 { self.0[0] } +} + +pub fn first(block: &B) -> u8 { + block.first_byte() +} +``` + +This makes the consumer proof entirely safe: every implementation returns an already-valid `u8`; no pointer provenance, alignment, lifetime, or 16-byte invariant crosses the boundary. A false semantic value from an implementation may violate a documented behavioral promise, but cannot make this function perform UB. + +If 2.0 must retain other consumers, split capabilities rather than burdening `FirstByte`: offer a safe borrowed representation such as `fn bytes(&self) -> &[u8; 16]` where that is the real need, and retain a separately named unsafe raw-pointer trait only for consumers that demonstrably need raw alignment/pointer guarantees. Its `# Safety` contract must state exact temporal, initialization, provenance, aliasing, and content-relationship clauses. + +Migration requires downstream `Block` impls to implement the appropriate new capability and raw consumers to choose the raw/borrowed interface. Because those parties are not enumerable, this removal/split and any contract normalization require explicitly authorized 2.0 work, a migration guide/adapters where possible, and a fresh audit of the implemented snapshot. No source change is authorized by this report. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r021.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r021.md new file mode 100644 index 0000000000..98f08a5643 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r021.md @@ -0,0 +1,42 @@ +# `Block` focused review and redesign + +## Claim, scope, and verdict + +Snapshot: the supplied `lib.rs`; scope is `Block`, `Page`'s `unsafe impl`, and `first`; Rust 1.70, every target and profile. Valid uses of `first` include every `B` whose `unsafe impl Block` satisfies the published contract. There are no dependencies, generated artifacts, `cfg`s, FFI, allocator, panic-cleanup, or concurrency operations in this source. + +**Current implementation soundness: PROVED for Rust 1.70, all targets/profiles, relative to the three official axioms below. Published `Block` postconditions for `Page`: PROVED.** `first` consumes only one byte of the stronger contract. Its panic case is also sound: if `base` panics, the dereference is not reached. + +**The literal open-ended “Rust 1.70+” claim is UNPROVED without an additional compatibility premise.** A finite review of Rust 1.70 documentation cannot establish semantics for every future Rust release. The proof is parametric for each later release whose official documentation preserves the cited propositions. A `PROVED` verdict over the open-ended range would require an explicitly accepted TCB premise (`RUST-COMPAT`: those exact propositions remain valid for every supported later stable release), plus re-audit when any proposition changes. This is a version-scope qualification, not a source defect. + +The existing source also has a proof-documentation defect: neither the unsafe trait/impl nor the raw dereference has a `# Safety`/`SAFETY` derivation. The reconstruction below proves the code; it does not make the adjacent source documentation adequate. + +## Authoritative premises and obligation ledger + +- **A1 — raw access and liveness.** Rust 1.70 says dereferencing a dangling or unaligned raw pointer is UB, reading an uninitialized integer produces an invalid value, and a reference passed to a function is live at least for that call. See the [Rust 1.70 undefined-behavior rules](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined). +- **A2 — layout.** The alignment modifier raises a struct's alignment, and the `repr(C)` struct layout algorithm places the first field at offset zero. The same page gives `u8` size 1 and requires size to be a multiple of alignment, hence `u8` alignment is 1. See [Rust 1.70 type layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers) and [`repr(C)` structs](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs). +- **A3 — buffer pointer.** Slice/array `as_ptr` returns a raw pointer to the buffer; it remains non-dangling while the borrowed container outlives its use. See [Rust 1.70 `slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr). + +**P1 — `Page` implements the full provider contract: PROVED.** `ALIGN = 16` is a nonzero power of two. By A2, a valid `&Page` is 16-aligned and its sole `[u8; 16]` field starts at offset zero. The valid shared reference supplies a live, initialized array for the receiver borrow; by A3, `self.0.as_ptr()` points to its 16-byte buffer. Consequently the result is non-null, 16-aligned, and readable for all 16 bytes throughout that borrow. This argument is representation- and type-based, so optimization, debug assertions, panic strategy, pointer width, and target do not create cases. + +**P2 — `first`'s load: PROVED.** `block: &B` is live for the whole call to `first` (A1). A valid unsafe implementation supplies `base`'s postcondition over that borrow. The load occurs before `first` returns and with no intervening call. “Readable for 16 bytes” supplies a live and initialized first `u8`; non-nullness and `u8`'s alignment of 1 (A2) satisfy the raw-load requirements. A1 therefore permits `*block.base()`, which establishes the returned first byte. Neither bytes 1–15 nor the numeric value of `ALIGN` is consumed. + +## What can change in 1.x + +Proof-oriented simplification can be local and contract-preserving: + +1. Add a trait-level `# Safety` section that explicitly assigns the existing `ALIGN` and `base` promises to every implementer, without changing their wording or domain. +2. Add an adjacent proof for `unsafe impl Block for Page` containing P1. +3. Add this adjacent proof for the dereference: “`block` remains borrowed for this call; `Block::base` guarantees at least 16 readable bytes during that borrow, so loading the first initialized `u8` is permitted.” It should explicitly note that `first` does not rely on the other 15 bytes or 16-byte alignment. +4. If desired, introduce a distinctly named safe successor API additively in 1.x, while retaining `Block` and `first` unchanged. Avoid a blanket implementation that would create downstream coherence conflicts. + +No source edit was authorized, so these are proposals only. + +Repository search cannot justify narrowing a public contract: downstream consumers may read all 16 bytes or rely on `ALIGN`, and downstream implementations were written against the present implementer obligation. During 1.x, do **not** remove `ALIGN`, reduce readability to one byte, weaken alignment/non-null guarantees, strengthen the lifetime or provenance obligation, seal the trait, change `base`'s signature, or remove `unsafe`. Weakening provider guarantees breaks consumers; strengthening implementer obligations or sealing breaks implementations. Removing `Page`'s `repr(C)` is also unsafe as a minor-release simplification because its public layout commitment is observable. + +## Authorized 2.0 candidate and migration + +Prefer replacing the raw capability with an enforced safe one. Define a public, constructible `#[repr(C, align(16))] Block16([u8; 16])` with safe `as_array`/`as_ptr` accessors, then use a **safe** trait method `fn block16(&self) -> &Block16`. `first` becomes safe indexing through `block.block16().as_array()[0]`. The reference type carries lifetime/non-nullness; `Block16` carries alignment and extent; `[u8; 16]` carries initialization. No implementer prose is then consumed by unsafe code. + +If product requirements intentionally retain only “return the first byte,” an even smaller 2.0 trait can expose `fn first_byte(&self) -> u8`; that deliberately drops the published pointer capability. The owner must choose whether 2.0 preserves that capability. + +Migration: add the new names in 1.x, provide an explicit `Page` implementation, deprecate but preserve the old surface, and change/remove it only in an authorized 2.0. Re-audit the implemented snapshot, all adapters, `Page` layout, and every migrated consumer; do not infer a verdict for the proposal itself. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r022.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r022.md new file mode 100644 index 0000000000..8600d45025 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r022.md @@ -0,0 +1,47 @@ +# r022: published-contract review and redesign + +## Result and scope + +Reviewed exactly `targets/r022/{REQUEST.md,lib.rs}` as source, without execution or expansion. The surface is the published unsafe trait `Block`, its associated constant and safe method, `Page` and its `unsafe impl`, and the safe generic consumer `first`. The requested domain is Rust 1.70+, every target/profile on which this source compiles. + +**Soundness verdict:** **PROVED** for `Page` and `first`, relative to the published `Block` contract and the Rust axioms below, for each supported Rust release to which those axioms apply. **Documented-postcondition verdict:** **PROVED** for all of `Page`'s `Block` obligations. There is no broader documented postcondition for `first` in scope. + +The literal open-ended future range “1.70+” needs one qualification: Rust 1.70 documentation establishes the cited propositions for 1.70, not automatically every future release. Coverage is therefore parametric: re-check the applicable versioned pages when the Rust semantics or support range changes, or explicitly accept a Rust backwards-compatibility premise preserving these propositions. Without one of those routes, an unconditional claim over unknown future releases is **UNPROVED**. + +“Readable for 16 bytes” is read operationally as permitting loads of 16 initialized bytes under Rust's pointer/access rules for the stated interval. If the published term was intended to promise less, `first` is **UNPROVED**: the smallest missing proposition is that loading byte zero as a `u8` is permitted. Precision added in 1.x must clarify this same meaning, not silently strengthen it. + +## Obligations and derivation + +Rust 1.70 says of an unsafe trait, “It is safe to use a correctly implemented unsafe trait,” and requires its impl to be `unsafe` ([Reference: unsafe traits](https://doc.rust-lang.org/1.70.0/reference/items/traits.html#unsafe-traits)). Thus the `unsafe impl`, not safe generic callers, owns these obligations: + +1. `ALIGN` is nonzero and a power of two. +2. For the receiver-borrow interval, every `base` result is non-null, aligned to `ALIGN`, and permits reads of bytes `0..16`. + +For `Page`, `ALIGN = 16` proves (1). The alignment modifier raises a type's alignment, while the `repr(C)` field-layout algorithm begins at offset zero, so the sole `[u8; 16]` field begins at the address of a 16-aligned `Page` ([Reference: alignment modifiers](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers), [Reference: `repr(C)` structs](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs)). `as_ptr` “returns a raw pointer to the slice's buffer” ([Rust 1.70 slice::as_ptr](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)); here that buffer is the sole field. A valid shared `&Page` is non-null/aligned and keeps its initialized 16-element array live for the borrow. Hence the returned address is non-null, 16-aligned, and all 16 `u8` elements remain readable. This proves (2). The argument is target/profile-parametric: there is no `cfg`, arithmetic, allocation, panic branch, or optimization-sensitive premise, and alignment 16 is within the Reference's universally permitted `repr(align)` range. + +For `first`, the `Block` bound admits only implementations that have accepted those obligations. `base` therefore supplies at least one readable initialized byte during `first`'s `&B` borrow. A `u8` has alignment 1; `ALIGN`-alignment (with nonzero power-of-two `ALIGN`) is at least that strong. The dereference loads exactly byte zero, and every initialized byte is a valid `u8`. This discharges the dangling, alignment, and invalid-value hazards identified by the Rust 1.70 undefined-behavior rules ([Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). The other 15 readable bytes and stronger alignment are deliberately unused. + +The implementation proof succeeds, but the proof artifact is deficient: the unsafe trait has no trait-level `# Safety` section, the `unsafe impl` has no adjacent proof, and `first` has no `SAFETY` derivation. + +## What 1.x may simplify + +Only the **consumer proof** may be narrowed. A proposed adjacent comment for `first` is: + +```rust +// SAFETY: Block::base guarantees, for this receiver borrow, that byte 0 is +// initialized and readable. Its pointer is non-null and ALIGN-aligned; +// ALIGN is a nonzero power of two, so this satisfies u8's alignment of 1. +unsafe { *block.base() } +``` + +Also add an equivalent trait-level `# Safety` restatement and the `Page` derivation above, provided the wording is established to be semantically identical to the published contract. These are proof/documentation improvements, not permission to change the contract. `Page` must still prove all 16 bytes and 16-byte alignment even though `first` does not consume them. + +Repository search cannot justify weakening a public contract: downstream implementations and unsafe consumers may rely on every published clause. In 1.x, do **not** reduce 16 bytes to one, remove or weaken `ALIGN`, lower `Page::ALIGN`/`Page`'s layout, strengthen implementer obligations, seal the trait, or make it safe with prose-only obligations. Each can invalidate an existing implementation or consumer proof. No source change is authorized, so none was made. + +## 2.0 migration design + +If byte zero is the intended abstraction, replace the unsafe representation contract with a safe behavioral one, for example `pub trait Block { fn first(&self) -> u8; }`; the free `first` then contains no unsafe code. If a 16-byte view is genuinely required, use `fn bytes(&self) -> &[u8; 16]`. If 16-byte address alignment is also required, return a reference to a public `#[repr(align(16))]` wrapper instead. These types enforce lifetime, initialization, extent, and (for the wrapper) alignment locally. + +That migration changes method signatures, removes `ALIGN`/`base`, changes `unsafe impl` requirements, and weakens guarantees available to current raw-pointer consumers; it therefore requires explicitly authorized 2.0 planning, downstream migration guidance, and a deliberate choice about which old guarantees survive. + +**TCB:** the exact Rust 1.70 propositions linked above; the request's facts that this contract is published and ordinary SemVer governs 1.x; and, only if open-ended later-version coverage is claimed without per-version checking, the explicit compatibility premise described above. No dependency, tool, platform, or test result is consumed. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r023.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r023.md new file mode 100644 index 0000000000..f4c322ae16 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r023.md @@ -0,0 +1,35 @@ +# `Block` published-contract review + +## Claim and verdict + +Snapshot: the supplied `lib.rs`; scope is `Block`, `Page`'s `unsafe impl`, and safe `first`; no generated code or dependencies. The supported set requested is stable Rust 1.70+, every target/profile on which this source compiles. + +- **`Page` implementation: PROVED** for Rust 1.70 under the cited abstract Rust/std contracts, on all compiling targets/profiles. +- **`first`: PROVED** for every *valid* `Block` implementation, reading “readable for 16 bytes” in its ordinary safety-contract sense: a Rust read of each of those initialized bytes is permitted for the stated interval. It consumes only a strict subset of `Block`'s promises. +- **Open-ended `1.70+` aggregate: UNPROVED** unless the reviewer accepts `TCB-COMPAT`: every later supported stable Rust release preserves the cited layout, reference, slice-pointer, and read propositions. Rust 1.70 documentation cannot alone prove all future releases. This is a version-domain qualification, not a source defect. There is no demonstrated `UNSOUND` or `CONTRACT-BROKEN` case. + +TCB is only the exact Rust 1.70 Reference/std propositions cited below, plus pending `TCB-COMPAT`; the request's ordinary-SemVer promise controls evolution. No test or compiler execution is evidence here. + +## Obligation ledger and reconstructed proofs + +`Block` requires: (B1) `ALIGN != 0`; (B2) `ALIGN` is a power of two; and, throughout the `&self` borrow, `base()` returns a pointer that is (B3) non-null, (B4) address-aligned to `ALIGN`, and (B5) readable for 16 bytes. + +**`unsafe impl Block for Page`.** `ALIGN = 16` establishes B1/B2 arithmetically. The Rust 1.70 Reference says `align` raises a struct's alignment and requires its argument to be a power of two; the C-layout algorithm says of the first field, “this will always be zero” ([alignment modifiers](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)). Thus `Page` is at least 16-aligned and its `[u8; 16]` field begins at the `Page` address. Arrays contain their elements contiguously ([array layout](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout)), and `as_ptr` “returns a raw pointer to the slice's buffer” ([`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)). A live shared `&Page` therefore yields the same non-null, 16-aligned address and keeps all 16 initialized `u8`s readable for the borrow. This discharges B3–B5. The current code lacks this material adjacent proof and the unsafe trait lacks a consolidated `# Safety` implementer section; that is proof-documentation debt, not an implementation failure. + +**`first`.** Its raw load needs a pointer valid for a read, properly aligned, and pointing to an initialized `u8`—the same obligations listed for a raw read ([`ptr::read` safety](https://doc.rust-lang.org/1.70.0/std/ptr/fn.read.html#safety)); dangling/unaligned dereference is undefined ([Reference UB list](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). B5 supplies validity, initialization, and at least the first byte for the immediate load; B1/B2 imply `ALIGN >= 1`, and B4 therefore suffices for `u8` alignment; B3 is explicit. The load occurs before the borrow ends. Neither the remaining 15 bytes nor the *value* of `ALIGN` is otherwise consumed. + +All-target/profile closure is parametric: there is no `cfg`, allocator, panic path, arithmetic overflow, generated code, or profile-dependent check; the cited representation rules and `u8`/array reasoning apply on every compiling target. + +## What may be simplified in 1.x + +Keep the published trait contract intact. It is safe and useful to narrow the **local proof** for `first` to the one-byte lemma above, while separately proving every clause for `Page`. Add precise adjacent `SAFETY` reasoning and consolidate the existing clauses under `Block`'s `# Safety` heading. Define “during the borrow” and “readable” explicitly only as a semantic clarification of the already-published promise; do not use documentation to add initialization, provenance, lifetime, or aliasing duties that the old wording did not already impose. + +Internal refactoring may introduce a private “read one byte” lemma/helper and make `first` depend only on it. A new, separately named safe API can also be staged alongside the old API, provided `Block` and its guarantees remain available unchanged. No source change is authorized by this review. + +Repository search finding only `first` does **not** justify deleting B1–B4 or reducing 16 bytes to one: downstream generic consumers are entitled to every published provider guarantee, and downstream `unsafe impl`s are entitled to no stronger implementer obligations. Both populations are unenumerable by premise. + +## Requires an explicitly authorized 2.0 migration + +A clean 2.0 design may replace the raw-pointer abstraction used by `first` with a safe operation such as `trait FirstByte { fn first(&self) -> u8; }`; `Page` can return `self.0[0]`, eliminating this unsafe consumer and its pointer proof. If raw 16-byte access is still needed, preserve it in a separate, precisely documented unsafe trait. + +Any of these require the major migration: weakening `base`'s alignment/non-null/16-byte postconditions; removing or changing `ALIGN`; changing `first` to a new bound that existing `Block` types need not implement; strengthening implementer duties; sealing the currently downstream-implementable trait; or removing `Block`. The migration must inventory and adapt downstream impls and consumers, publish the replacement contracts, provide an overlap/deprecation path if desired, and re-audit both sides. The 2.0 proof win comes from changing the enforced boundary, not retroactively shrinking 1.x prose to the single in-tree use. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r024.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r024.md new file mode 100644 index 0000000000..7df9ac8c8a --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r024.md @@ -0,0 +1,34 @@ +# `Block` published-contract review + +## Claim and verdict + +Scope is exactly `lib.rs`, Rust 1.70+ stable, every target/profile on which this source is accepted, and well-typed safe calls to `first`; downstream `unsafe impl Block` code is out of scope except that `first` may assume a valid implementation satisfies the published unsafe-trait contract. There is no generated code, dependency, `cfg`, allocation, concurrency, unwind, or profile-sensitive branch in the snapshot. + +- **Reconstructed implementation result: PROVED, conditionally on the intended meaning below.** `Page` supplies the complete stated `Block` guarantees, and `first` is sound for every conforming `Block` implementation. +- **Published proof artifact: UNPROVED.** “Readable for 16 bytes” and “during the borrow” are not defined precisely enough to entail, without interpretation, initialized readable memory with suitable provenance/aliasing for the receiver-borrow interval. Neither the unsafe trait nor its impl has a `# Safety`/`SAFETY` proof. No valid UB counterexample or false postcondition was established, so this is not an `UNSOUND` or `CONTRACT-BROKEN` verdict. + +The conditional interpretation used by the reconstructed proof is: the result identifies the first byte of a live allocation, is valid for nonmutating reads of 16 initialized `u8`s, and remains so without a conflicting access for the whole lifetime of the `&self` used for the call. + +## Obligation ledger and derivation + +`Block::ALIGN` requires (A1) nonzero and (A2) a power of two. `Block::base` promises, over the receiver-borrow interval, (B1) non-null, (B2) `ALIGN`-aligned, and (B3) readable for all 16 bytes. + +For `Page`, `ALIGN = 16`, directly proving A1/A2. Rust 1.70's [array-layout rule](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#array-layout) gives `[u8; 16]` 16 consecutive one-byte elements with `u8` alignment. The [`repr(C)` struct algorithm](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#reprc-structs) places the sole field at offset zero; the [`align(16)` modifier](https://doc.rust-lang.org/1.70.0/reference/type-layout.html#the-alignment-modifiers) raises the struct alignment to 16. Thus a valid `&Page` places `self.0` at the same 16-aligned, non-null address. [`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr) returns a pointer to that inline buffer. Ownership plus the live shared `&Page` keeps all 16 initialized `u8`s present and nonmutably readable for the interval. This establishes B1-B3 and the documented postcondition. + +At `first`, dereferencing the returned `*const u8` reads exactly one `u8`. Under B3, the first byte is initialized and readable; B1 excludes null. A `u8` has alignment one, so this operation does **not** consume A1, A2, B2, or readability of bytes 1 through 15. There is no intervening call or state transition before the read. The [raw-pointer safety rules](https://doc.rust-lang.org/1.70.0/std/ptr/index.html#safety) require validity for the particular access and extent; the derived one-byte fact supplies it. + +Applicability is parametric over target and profile: the representation rules establish the relevant layout wherever compilation succeeds, and no optimization, overflow, assertion, panic, or target branch changes the argument. For the open-ended `1.70+` range, **TCB-COMPAT** is required: every later supported stable Rust version preserves the cited abstract layout, pointer, and `as_ptr` propositions. The 1.70 citations alone do not prove future versions; a later semantic change triggers re-review. + +## What can be simplified in 1.x + +The local proof for `first` may be narrowed to a named derived lemma: “while this `&B` remains live, `base()` is valid for one initialized `u8` read.” Its proof is simply B1+B3 restricted from 16 bytes to the first byte (with `u8` alignment one). A `SAFETY` comment can state that derivation. This is proof simplification, not contract weakening. + +Likewise, documentation may be reorganized into an explicit `# Safety` implementer contract and an adjacent `Page` proof, but only wording demonstrably equivalent to the published promises is ordinary 1.x maintenance. The complete A1-A2/B1-B3 contract must remain normative, and `Page` must still prove all of it even though the repository's `first` consumes less. An additive, differently named API with a narrower contract is possible in 1.x if the old trait/function and their behavior remain available; it does not authorize silently rebinding existing APIs. + +## What needs an authorized 2.0 migration + +Repository search cannot bound downstream reasoning. Removing `ALIGN`, dropping its nonzero/power-of-two rule, reducing alignment, or changing “16 bytes” to “one byte” weakens guarantees on which downstream unsafe generic consumers may rely. Conversely, adding provenance, aliasing, temporal, thread, or initialization duties not already entailed by the published words strengthens obligations on downstream `unsafe impl`s. Sealing the trait, making it safe, changing `base` to `unsafe fn`, or changing `first`'s public bound likewise invalidates existing implementations or callers. These are breaking semantic changes despite the small local consumer. + +For 2.0, split capabilities rather than preserving an oversized theorem. A safe `FirstByte` API can expose `fn first(&self) -> u8`, eliminating raw-pointer proof from `first`. Keep a separately named unsafe aligned-16-byte trait only for consumers that truly need that capability, with exact lifetime, initialization, provenance, aliasing, and concurrency clauses. Provide adapters from the old full contract during migration, audit every in-tree implementation/consumer, publish the changed obligations, and require downstream implementations and unsafe consumers to migrate explicitly. + +No source or major-version edit is authorized here; this report makes none. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r025.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r025.md new file mode 100644 index 0000000000..561862d8d8 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r025.md @@ -0,0 +1,47 @@ +# `r025` focused review and redesign + +## Claim and verdict + +**Current-artifact soundness: `UNSOUND`.** This verdict covers exactly the supplied `lib.rs`, the two public `decode` configurations, Rust 1.70+, every target/pointer width and ordinary profile, and both values of `compact`, relative to TCB `r025-local-v1` below. A valid safe call `decode(0xD800_u16)` with `compact` enabled and debug assertions disabled reaches undefined behavior. One supported counterexample is enough to refute the universal claim; no claim is made about the rest of a crate. + +**Documented behavior: `CONTRACT-BROKEN` for the combined support set.** In the same configuration, a surrogate does not take the documented panic path; it reaches an unchecked invalid-`char` construction instead. The non-`compact` postcondition is `PROVED`, and the `compact` postcondition is `PROVED` only in configurations where debug assertions execute. + +Snapshot: `targets/r025/{REQUEST.md,lib.rs}` as supplied; no generated code, dependencies, build script, traits, fields, macros, or other API surfaces were present. Review was static only. + +## Boundary, obligations, and derivation + +The complete public surface is one safe free function selected into two mutually exclusive signatures. Safe callers have no safety precondition. + +| Configuration / obligation | Disposition | +|---|---| +| `compact`, unchecked conversion requires `raw as u32` to be a Unicode scalar value | **Failed.** `raw: u16` admits `0xD800..=0xDFFF`; the only preceding check is `debug_assert!`. | +| `compact`, debug assertions enabled | **Proved.** Normal continuation establishes that `raw` is not a surrogate. Since every `u16` is at most `0xFFFF`, it is then a scalar value, so the unchecked conversion returns the represented scalar. A surrogate panics at the assertion. The source nevertheless lacks the material adjacent `SAFETY` proof. | +| `compact`, debug assertions disabled | **Unsound.** Rust documents that optimized builds do not execute `debug_assert!` unless debug assertions are enabled. With `raw = 0xD800`, control therefore reaches `from_u32_unchecked(0xD800)`, producing an invalid `char`. | +| not `compact` | **Proved.** `char::from_u32(raw)` is safe and returns `None` exactly for a value invalid as `char`, otherwise the represented scalar. | + +The critical contracts are: Rust 1.70 defines surrogate code points as `0xD800..=0xDFFF` and forbids producing such a [`char`](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#invalid-values); [`char::from_u32_unchecked`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32_unchecked) ignores validity and may construct an invalid `char`; and [`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html) says an optimized build does not execute it unless `-C debug-assertions` is passed. Thus the check does not dominate the unsafe call throughout the supported set. This is precisely the use that the standard documentation warns belongs only in safe code. + +Configuration closure is an exhaustive partition on `cfg(feature = "compact")` versus its negation, refined by debug assertions enabled/disabled. Targets and pointer widths do not change the integer ranges or scalar-value condition. Profile/optimization does matter and exposes the failed branch. Panic strategy changes unwinding versus abort after a panic, but not the failure above. No test sample is used as coverage evidence. + +## Configuration-preserving redesign + +Replace only the `compact` body conceptually (no source edit was requested): + +```rust +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + char::from_u32(raw as u32).expect("surrogate code point") +} +``` + +This is the preferred candidate; an always-on `assert!` followed by the unchecked call retains unnecessary unsafe code and is strictly harder to prove. + +Conditional proof plan: widening `u16` to `u32` preserves its numeric value. [`char::from_u32`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32) returns `Some` containing the represented scalar exactly when the input is valid and `None` otherwise. Because a widened `u16` cannot exceed `0xFFFF`, `None` occurs exactly for the surrogate range. [`Option::expect`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect) returns the `Some` value and panics for `None`. Hence every non-surrogate returns its represented scalar and every surrogate panics, with no unsafe operation, invariant, or hidden caller obligation. + +Both `cfg` predicates and signatures remain unchanged; the non-`compact` body is unchanged. These APIs all exist on Rust 1.70, so the MSRV is not raised. The proof is parametric over target, pointer width, optimization/debug assertions, and ordinary panic profiles. A panic message was not documented, so its text is not a compatibility constraint. The candidate has no artifact verdict until implemented and re-audited. + +## TCB and residual scope + +`r025-local-v1` contains only the cited Rust 1.70 Reference/std propositions (`AXIOM-CHAR`, `AXIOM-DEBUG`, and, for the candidate, `AXIOM-OPTION`). Extending those exact semantic propositions from 1.70 to the open-ended later-stable range requires explicit compatibility premise `COMPAT-STABLE`: later supported stable Rust preserves them. This premise is not needed to establish the current `UNSOUND` verdict because Rust 1.70 itself supplies the counterexample, but it is required for a proof of the proposed design over all of `1.70+`. + +Re-audit after implementation, any signature/documentation or support-set change, or a material change to a consumed Rust contract. Binary/compiler correctness, performance, panic text, and code outside the supplied target are excluded. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r026.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r026.md new file mode 100644 index 0000000000..f6351d0ab0 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r026.md @@ -0,0 +1,45 @@ +# `decode`: focused configuration review and redesign + +## Claim and verdict + +Artifact: the supplied `lib.rs`. Scope is the two public, safe `decode` APIs and their documented behavior, for Rust 1.70+, all targets/pointer widths, ordinary profiles, and the two independent values of `compact`; no generated code or dependencies are present. No target was executed. + +**Current soundness: UNSOUND for the published support set.** A valid safe call in a supported configuration is enough to refute the universal claim: enable `compact`, compile with debug assertions disabled (including an ordinary optimized build by default), and call `decode(0xD800)`. The assertion is not executed, so `0xD800` reaches `char::from_u32_unchecked`. It is a surrogate, hence not a valid `char`; constructing it is undefined behavior. This also leaves the documented “panics for a surrogate” behavior unestablished in that configuration. I do not give a separate `CONTRACT-BROKEN` verdict for that execution because undefined behavior prevents a defined behavioral observation. + +For the exact Rust 1.70 contracts, the [`char` documentation](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity) defines scalar values as `0..=0x10FFFF` excluding `0xD800..=0xDFFF`, says no non-scalar `char` may be constructed, and documents that `from_u32_unchecked` can create an invalid value. The [`debug_assert!` documentation](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html) says optimized builds do not execute it unless debug assertions are explicitly enabled. Thus the existing assertion is not an admissible safety check. + +The other configuration regions close as follows: + +- `compact` with debug assertions enabled: surrogates panic before the unsafe call; every other `u16` is at most `0xFFFF` and outside the only invalid interval in that range, so the unchecked conversion receives a scalar and returns the represented scalar. +- no `compact`: [`char::from_u32`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32) safely returns `Some(char)` exactly for valid `char` inputs and `None` otherwise, matching the documented behavior. + +The feature predicates are complementary and exhaustive. Target and pointer width do not affect this integer-only reasoning. Profile/debug-assertion state does affect the compact branch and exposes the failing region. This partition is enough to establish the overall `UNSOUND` verdict even without assuming that Rust 1.70 documentation applies unchanged to every later release. + +The unsafe block also has no adjacent `SAFETY` proof. Even in the enabled-assertion region, its required derivation had to be reconstructed above; documentation quality is therefore deficient independently of the implementation defect. + +## Recommended redesign + +Keep the non-compact definition unchanged and replace only the compact body conceptually with: + +```rust +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + match char::from_u32(raw as u32) { + Some(value) => value, + None => panic!("surrogate code point"), + } +} +``` + +This preserves the public signature and documented behavior and introduces no post-1.70 facility. It removes the unsafe operation rather than retaining a proof-sensitive unchecked optimization. + +Conditional proof plan for the proposed artifact: + +1. Widening a `u16` to `u32` preserves its numeric value, so the argument remains in `0..=0xFFFF` on every target and pointer width. +2. In that interval, the Rust 1.70 `char` validity definition leaves exactly `0xD800..=0xDFFF` invalid. +3. The documented `from_u32` result is therefore `Some` containing the represented scalar for every non-surrogate and `None` for every surrogate. +4. The `Some` arm returns that scalar; the `None` arm invokes [`panic!`](https://doc.rust-lang.org/1.70.0/std/macro.panic.html), satisfying the surrogate behavior in every profile. No unsafe precondition or representation invariant remains. + +The same complementary feature partition proves configuration closure; profile/debug-assertion state becomes irrelevant. The signatures and return types are unchanged, and the minimum supported compiler already provides all used constructs/APIs. After implementation, audit the new snapshot separately. For the open-ended `1.70+` range, apply this proof parametrically only while each release's authoritative documentation retains the cited propositions; changes to those contracts are a re-audit trigger rather than an implicit compatibility premise. + +TCB is limited to conforming Rust implementations and the exact applicable Rust standard-library/Reference contracts. There are no dependency, platform, generated-code, testing, or tool-derived premises. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r027.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r027.md new file mode 100644 index 0000000000..628016d967 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r027.md @@ -0,0 +1,43 @@ +# `decode` configuration-preserving review and redesign + +## Claim and snapshot + +Scope is exactly `targets/r027/lib.rs`: both public safe `decode` definitions and the one unsafe operation. The required theorem quantifies over every well-typed safe call on stable Rust 1.70+, every target/pointer width and ordinary profile, with `compact` independently on or off. Required behavior is the two doc comments in that file. There are no dependencies, generated artifacts, representation invariants, callbacks, or caller safety obligations in scope. + +**Current-artifact verdict:** **UNSOUND** and **CONTRACT-BROKEN** for the combined required set. These verdicts already follow on Rust 1.70 with `compact` enabled and debug assertions disabled, so no assumption about later-version compatibility is needed to refute the universal claim. + +## Configuration and obligation ledger + +The complementary `cfg(feature = "compact")` predicates form an exhaustive two-case partition. Target, pointer width, optimization, overflow checks, and panic strategy do not otherwise affect the integer/value argument. Debug-assertion enablement is an additional semantic axis in the `compact` case. + +| Region / site | Obligation and result | +|---|---| +| `compact = false`, `lib.rs:11` | `char::from_u32(raw)` is safe for every `u32`; it returns the represented scalar as `Some`, or `None` for an invalid `char` value. The implementation directly supplies the documented result. **PROVED on Rust 1.70** relative to AXIOM-CHAR below. | +| `compact = true`, debug assertions enabled, `lib.rs:6-7` | A surrogate panics before the unsafe call. Otherwise, conversion from `u16` to `u32` is value-preserving, the value is at most `0xFFFF`, and exclusion of `0xD800..=0xDFFF` makes it a Unicode scalar value. The unchecked constructor's precondition is met and it returns that scalar. **PROVED on Rust 1.70** relative to AXIOM-CHAR and AXIOM-DEBUG. | +| `compact = true`, debug assertions disabled, `lib.rs:6-7` | **UNSOUND.** Safe call `decode(0xD800)` reaches `char::from_u32_unchecked(0xD800)`. The assertion is not executed, and the call constructs a surrogate-valued `char`, which violates `char` validity and is undefined behavior. **CONTRACT-BROKEN:** the documented surrogate panic is not established. This applies on every target/pointer width in any supported profile/configuration with debug assertions off. | + +The existing source has no `SAFETY` comment. More importantly, no truthful local proof can repair it: `debug_assert!` does not dominate the unsafe call in every supported configuration. Rust's documentation specifically cautions that it is enabled only in non-optimized builds by default and recommends relying on omitted assertions only in safe code. + +## Authoritative premises / compact TCB `TCB-r027-1` + +- **AXIOM-CHAR (Rust 1.70):** [`char` validity and conversion contracts](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity-and-layout), including [`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). A `char` is a Unicode scalar value; surrogates are `0xD800..=0xDFFF`; constructing another value is UB; the checked constructor returns `None` for invalid input, while the unchecked form may construct an invalid value. +- **AXIOM-DEBUG (Rust 1.70):** [`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html) is not executed in optimized builds by default unless debug assertions are enabled. +- **AXIOM-EXPECT (Rust 1.70, proposed design only):** [`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`. + +No tool result or implementation premise is consumed. Claims beyond Rust 1.70 require either checking the same exact propositions in each applicable release or accepting an explicit stable-Rust compatibility premise; API stability badges alone are insufficient. This does not weaken the current counterexample. + +## Recommended redesign (not yet audited source) + +Replace only the `compact` body; retain both signatures and the non-`compact` body: + +```rust +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + char::from_u32(raw as u32) + .expect("raw should be a Unicode scalar value") +} +``` + +Conditional proof plan: the cast preserves every `u16` value. AXIOM-CHAR makes `from_u32` yield `Some(c)` exactly for non-surrogates (all inputs are already below `0x110000`) and `None` exactly for surrogates. AXIOM-EXPECT therefore returns the represented scalar in the first case and panics in the second. There is no unsafe operation, hidden caller obligation, or debug/profile dependence. Fixed-width values make the reasoning target- and pointer-width-parametric. Both APIs predate Rust 1.70, so the proposal does not raise the MSRV; the panic message is not part of the stated contract. + +This is a design, not a `PROVED` new artifact. After implementation, re-audit the exact source and the then-supported Rust range. Re-review is also required if the signatures/docs, feature partition, support set, or consumed standard-library contracts change. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r028.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r028.md new file mode 100644 index 0000000000..3138c545fe --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r028.md @@ -0,0 +1,46 @@ +# `decode`: focused review and configuration-preserving redesign + +## Claim, scope, and verdicts + +Scope is exactly the supplied `lib.rs`, both `decode` definitions, and their published behavior. A valid use is any well-typed safe call with any value of the configuration-specific argument type. The supported set is stable Rust 1.70+, every target and pointer width, every ordinary profile/panic strategy, and the exhaustive feature partition `compact` / `not(compact)`. There are no dependencies, generated artifacts, or caller obligations in scope. + +- **Current combined implementation: UNSOUND.** This is confined to `compact` configurations in which debug assertions are disabled. The documented panic is also **UNPROVED** there because execution first violates Rust validity; this is not separately labeled `CONTRACT-BROKEN` after UB. +- **Current `not(compact)` implementation: PROVED** for soundness and its documented result behavior, relative to the Rust axioms below. +- **Proposed implementation: PROVED** for soundness and both documented behaviors over the full supported configuration set, relative to those axioms and `COMPAT-1` below. + +## Finding: a debug assertion cannot establish the unsafe precondition + +For the safe call `decode(0xD800)` with `compact` enabled and debug assertions disabled, `debug_assert!` performs no rejecting check. Rust 1.70 documents that it is enabled only when `debug_assertions` is enabled, which is disabled by default in optimized builds ([`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html)). Control therefore reaches `char::from_u32_unchecked(0xD800)`. + +The Rust 1.70 `char` contract defines scalar values as `0..=0x10FFFF` excluding surrogate code points `0xD800..=0xDFFF`, and says constructing a non-scalar `char` causes undefined behavior ([`char` validity](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity-and-layout)). The unchecked constructor ignores validity and may create an invalid `char` ([`from_u32_unchecked`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32_unchecked)). Thus this safe, documented input reaches UB. No hidden caller precondition can repair a safe API. + +With debug assertions enabled, the assertion dominates the unsafe call. The remaining inputs are `u16` values outside the surrogate interval and hence scalars, so a semantic proof can be reconstructed for that region. The source nevertheless has no adjacent `SAFETY` proof, and that reconstruction cannot cover the disabled-assertion region. + +## Redesign + +Keep the non-compact definition unchanged and replace only the compact body: + +```rust +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + match char::from_u32(raw as u32) { + Some(value) => value, + None => panic!("surrogate code point"), + } +} +``` + +This preserves both configuration-specific signatures and requires no `unsafe` block or `SAFETY` comment. + +## Compact proof ledger + +1. A `u16` is in `0..=0xFFFF`; its widening cast to `u32` preserves its numeric value (Rust 1.70 specifies that an unsigned smaller-to-larger integer cast zero-extends: [numeric casts](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast)). +2. Therefore the cast value is either a surrogate (`0xD800..=0xDFFF`) or a Unicode scalar; values above `0x10FFFF` are impossible. This is an exhaustive, disjoint input partition. +3. `char::from_u32` returns `None` exactly when its input is not a valid `char` ([Rust 1.70 checked conversion](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32)). In the scalar case, `Some(value)` contains the represented scalar and is returned. In the surrogate case, `None` selects `panic!`, satisfying the documented behavior. There is no invalid-`char` construction. +4. Without `compact`, the unchanged checked conversion accepts every `u32`, returning the represented scalar exactly for valid inputs and `None` otherwise, directly matching the same contract. + +## Configuration closure and compatibility + +The two `cfg` predicates are mutually exclusive and exhaustive. Within each branch the argument widths are fixed, no `usize`, layout, ABI, target feature, optimization-sensitive arithmetic, or debug assertion is used, so the proof is parametric over target, pointer width, and ordinary profile. Panic abort versus unwind changes how the documented panic terminates, not whether the surrogate branch panics; no unwinding behavior is promised. All syntax and APIs used are available in Rust 1.70, so the MSRV is unchanged. + +**TCB.** `AXIOM-RUST-1.70` consists only of the exact versioned Reference/std propositions linked above. Because `1.70+` is open-ended, `COMPAT-1` is explicit: every supported later stable Rust release preserves the consumed scalar-validity, widening-cast, checked-conversion, and panic propositions. The proof for 1.70 follows directly from the cited text; the open-ended result is conditional on `COMPAT-1`, not on the stability badges alone. Re-audit if any consumed contract, support axis, signature, body, or documented behavior changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r029.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r029.md new file mode 100644 index 0000000000..63b7353aba --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r029.md @@ -0,0 +1,44 @@ +# `decode` configuration-preserving review and redesign + +## Claim and verdict + +Artifact: the supplied `lib.rs`. Scope: both `decode` items and their documented behavior, for Rust 1.70+, every target and pointer width, ordinary profiles, and both values of `compact`. Because both APIs are safe, callers have no safety obligation beyond a well-typed call. + +**Current implementation — UNSOUND.** The counterexample region is `compact = true` with debug assertions disabled (including an ordinary optimized release profile). A safe call `decode(0xD800)` reaches `char::from_u32_unchecked(0xD800)`. The standard-library validity contract says a `char` excludes `0xD800..=0xDFFF`, and constructing a non-scalar `char` is undefined behavior. The [`debug_assert!` contract](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html) says optimized builds do not execute it by default, so that check cannot discharge the unchecked conversion's precondition. This also leaves the promised surrogate panic unestablished in that region. + +The `compact = false` branch is **PROVED** for soundness and its stated return behavior. The existing `compact = true` branch is **PROVED only when debug assertions execute**: surrogates panic before the unsafe call, while every other `u16` is a valid scalar. Those regional results do not rescue the published union. + +There is also a proof-documentation defect: the unsafe block has no adjacent `SAFETY` proof, and the only apparent validation is configuration-dependent. No caller precondition may be inferred because `decode` is public and safe. + +## Recommended source design + +Keep the non-`compact` item unchanged and replace only the body of the `compact` item: + +```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 is not a Unicode scalar value") +} +``` + +This preserves both configuration-specific signatures, return types, and documented outcomes, raises no MSRV, and removes the unsafe operation rather than relocating its obligation. + +## Compact proof + +Authoritative premises for Rust 1.70.0: + +- The [numeric-types Reference](https://doc.rust-lang.org/1.70.0/reference/types/numeric.html#integer-types) gives `u16` the range `0..=2^16-1`; the [numeric-cast rules](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast) make this widening unsigned cast preserve the numeric value. +- The [`char` validity contract](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity-and-layout) defines scalar values as `0..=0x10FFFF` excluding surrogates `0xD800..=0xDFFF`, and states that constructing any other `char` is undefined behavior. +- [`char::from_u32`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32) returns `None` when its input is not a valid `char`; otherwise it converts that value to the represented `char`. [`Option::expect`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect) returns the contained value for `Some` and panics for `None`. + +For any `raw: u16`, widening yields the same number in `0..=0xFFFF`. Within that interval, precisely `0xD800..=0xDFFF` is outside the valid-`char` set. Therefore a surrogate produces `None` and `expect` panics; every other input produces `Some` of exactly the represented scalar, which `expect` returns. No invalid `char` can be created and there is no unsafe precondition. + +The unchanged non-`compact` function applies `char::from_u32` directly: valid scalar inputs produce `Some` of that scalar and all other `u32` inputs produce `None`, exactly its documentation. + +## Configuration closure and TCB + +`cfg(feature = "compact")` and its negation are disjoint and exhaustive. The two proofs therefore cover both independently selectable feature values. Fixed-width `u16`/`u32` ranges and the platform-independent `char::from_u32` contract make targets and pointer widths irrelevant. The redesign has no assertion-elision, overflow, layout, optimization, or unsafe-code premise, so ordinary profiles share the same proof. Panic strategy may change unwind versus abort after the panic is initiated, but not the documented branch that panics. + +The result is **PROVED relative to** the cited Rust 1.70.0 Reference/standard-library axioms and this explicit open-range premise: later supported stable Rust releases preserve the exact cited validity, cast, conversion, and `expect` propositions. That compatibility premise is necessary for the unbounded `1.70+` claim; re-review is triggered if any applicable contract changes. No dependency, generated-code, tool, allocator, target, or deployment assumption is consumed. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r030.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r030.md new file mode 100644 index 0000000000..e249942248 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r030.md @@ -0,0 +1,106 @@ +# `r030`: configuration-preserving `decode` review and redesign + +## Claim, scope, and verdict + +Snapshot: the supplied `lib.rs` and `REQUEST.md`. Scope is the two public safe +`decode` surfaces and the one unchecked conversion. The required set is stable +Rust 1.70+, every target/pointer width and ordinary profile, with `compact` +either enabled or disabled. There are no dependencies, generated artifacts, or +other APIs in the supplied target. No code was executed. + +**Existing implementation: UNSOUND.** This verdict already follows at Rust +1.70 for `compact` with debug assertions disabled, so one supported +configuration and safe call refute the universal claim. Without `compact`, the +implementation and documented return behavior are **PROVED at Rust 1.70**. +With `compact` and debug assertions enabled, they are likewise **PROVED at Rust +1.70**. The combined published implementation is nevertheless `UNSOUND`, and +its compact surrogate-panic guarantee is not upheld in the bad configuration: +execution reaches undefined behavior instead of a guaranteed panic. + +The redesign below is **PROVED at Rust 1.70**. For the literally open-ended +`1.70+` range, it is **UNPROVED unless COMPAT-1 is accepted** (or each later +release is checked): Rust 1.70 documentation cannot by itself establish later +semantics. Relative to COMPAT-1, it is `PROVED` over the complete requested +configuration set. + +## Finding: a debug-only check guards an unsafe precondition + +`compact` exposes `pub fn decode(u16) -> char`; therefore every `u16` supplied +by safe code is valid API use. Consider `decode(0xD800)` with `compact` enabled +and debug assertions disabled. Rust 1.70 documents that an optimized build does +not execute `debug_assert!` unless debug assertions are requested +([`debug_assert!`](https://doc.rust-lang.org/1.70.0/std/macro.debug_assert.html)). +The unchecked call is then reached with a surrogate. + +The Rust 1.70 `char` contract defines scalar values as `0..=0x10FFFF` excluding +surrogates `0xD800..=0xDFFF`, says constructing a non-scalar `char` is undefined +behavior, and says `from_u32_unchecked` can construct an invalid `char` +([`char` validity](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#validity-and-layout), +[`from_u32_unchecked`](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32_unchecked)). +Thus the safe call reaches UB. The existing unsafe block also has no adjacent +proof; no valid proof can be supplied because the required domination claim is +false whenever the assertion is disabled. + +Minimal acceptable resolution: perform an unconditional safe checked +conversion. Merely changing `debug_assert!` to `assert!` would fix this case, +but retaining the unchecked operation would preserve unnecessary proof and +maintenance burden. + +## Proposed source design + +Keep both attributes, signatures, and the non-compact item unchanged; replace +only the compact body conceptually (no source edit was requested): + +```rust +#[cfg(feature = "compact")] +pub fn decode(raw: u16) -> char { + char::from_u32(raw as u32) + .expect("a surrogate is not a Unicode scalar value") +} +``` + +This uses APIs already available on the stated MSRV; indeed the supplied +non-compact implementation already uses `char::from_u32`. + +## Compact proof and obligation ledger + +- **C1 — conversion/value partition (`PROVED` at 1.70).** A widening unsigned + cast preserves the numeric value + ([numeric casts](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#numeric-cast)). + Hence `raw as u32` is at most `0xFFFF`. In that interval, the `char` validity + definition leaves exactly `0xD800..=0xDFFF` invalid. +- **C2 — surrogate behavior (`PROVED` at 1.70).** `char::from_u32` returns + `None` for an invalid `char` value + ([contract](https://doc.rust-lang.org/1.70.0/std/primitive.char.html#method.from_u32)); + `Option::expect` panics on `None` + ([contract](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.expect)). + Therefore every surrogate invokes a panic in every profile. Abort versus + unwind changes panic aftermath, not whether the panic is initiated. +- **C3 — scalar behavior (`PROVED` at 1.70).** Every other `u16` is a valid + scalar; `from_u32` returns the represented `char`, and `expect` returns the + contained `Some` value. Thus all and only non-surrogates return their + represented scalar. All operations are safe, so no caller precondition or + local unsafe proof remains. +- **N1 — non-compact behavior (`PROVED` at 1.70).** The unchanged function is + exactly the safe `char::from_u32(raw)` contract: represented scalars become + `Some(char)` and invalid `u32` values become `None`. + +## Configuration closure and TCB + +Let `P = feature "compact"`. Rust's `cfg` rules include an attributed form +when its predicate is true and remove it otherwise; `P` and `not(P)` are a +disjoint exhaustive partition +([Rust 1.70 Reference](https://doc.rust-lang.org/1.70.0/reference/conditional-compilation.html#the-cfg-attribute)). +C1–C3 cover `P`; N1 covers `not(P)`. The redesign has no profile-sensitive +assertion, target `cfg`, pointer-sized arithmetic, layout premise, or unsafe +operation. Its proof is therefore parametric over ordinary profiles, targets, +and pointer widths. + +TCB `r030-local-v1` contains only the cited Rust 1.70 Reference/std +propositions. **COMPAT-1 (pending):** every later supported stable Rust release +preserves the availability and exact consumed semantics of unsigned widening +casts, `char` validity, `char::from_u32`, `Option::expect`, and these `cfg` +predicates throughout the requested target/profile domain. Accept COMPAT-1 or +re-check those propositions release-by-release. Re-review on a relevant Rust +contract change, support-set change, signature/documentation change, or body +change. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r031.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r031.md new file mode 100644 index 0000000000..dafd2672fb --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r031.md @@ -0,0 +1,71 @@ +# Focused unsafe-Rust review and redesign + +## Claim and scope + +Artifact: the supplied `targets/r031/lib.rs`. I reviewed the complete public safe surface relevant to `Bytes`, `Owned`, and `last`, for Rust 1.70+, `std`, all targets and profiles. The required behavioral postcondition is: for each supported built-in `Bytes` type, `last` returns `None` for an empty value and the final byte for a nonempty value. This is a source-level Rust claim; compiler/backend correctness and broader crate behavior are excluded. No source change was made. + +## Current artifact + +**Soundness verdict: UNSOUND.** `Bytes` is a public safe trait; its `#[doc(hidden)]` method remains language-reachable and downstream safe code may implement it. `last` assumes, without validation or an unsafe implementer contract, that `(ptr, len)` describes readable bytes. A wholly safe caller can refute that assumption: + +```rust +struct Bad; +impl Bytes for Bad { + fn raw_parts(&self) -> (*const u8, usize) { + (std::ptr::null(), 1) + } +} + +let _ = last(&Bad); // reaches the raw-pointer operation/dereference +``` + +With `len == 1`, the closure executes and `ptr.add(0)`/`*ptr` is applied to a null pointer. Rust 1.70's [`pointer::add` safety contract](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) requires the start and result to be in bounds of the same allocated object (or the permitted one-past position); the Reference classifies accessing a place through a dangling pointer, including null, as undefined behavior ([behavior considered undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). `#[doc(hidden)]` supplies no privacy or unsafe obligation. This counterexample is target-, optimization-, debug-assertion-, and panic-strategy-independent. + +**Built-in behavior (`Owned`) verdict: PROVED, relative to the standard-library premises below.** For `Owned`, `Vec::as_ptr` and `Vec::len` describe the same vector. If length is zero, `bool::then` does not evaluate the closure and returns `None`. Otherwise `len - 1 < len`; during the shared borrow there is no intervening mutation or call, so the final `u8` is readable and is returned. See [`Vec::as_ptr`](https://doc.rust-lang.org/1.70.0/std/vec/struct.Vec.html#method.as_ptr) and [`bool::then`](https://doc.rust-lang.org/1.70.0/std/primitive.bool.html#method.then). This material derivation is absent from the unsafe block: the implementation-specific obligation is discharged, but proof documentation is inadequate. It cannot repair the generic safe-trait boundary. + +Surface inventory: public safe `Bytes`; public, hidden, safe `raw_parts`; downstream safe implementations; public `Owned` with a private tuple field and its safe impl; public safe generic `last`; and its raw-pointer `add` and dereference. There are no `cfg`s, macros, generated code, dependencies, FFI, concurrency, or other configuration branches in the supplied artifact. + +## Recommended redesign + +Use one leaf module, genuine sealing, and a safe slice capability. A concrete design sketch is: + +```rust +mod bytes { + mod private { + pub trait Sealed { + fn as_bytes(&self) -> &[u8]; + } + } + + pub trait Bytes: private::Sealed {} + + pub struct Owned(Vec); + + impl private::Sealed for Owned { + fn as_bytes(&self) -> &[u8] { + self.0.as_slice() + } + } + impl Bytes for Owned {} + + pub fn last(bytes: &B) -> Option { + private::Sealed::as_bytes(bytes).last().copied() + } + + // Add only the checked constructors/conversions actually required by callers. +} + +pub use bytes::{last, Bytes, Owned}; +``` + +Named invariant **BYTES-VIEW**: for every leaf-module implementation, `as_bytes` returns, for the shared-borrow lifetime, the complete logical byte sequence of that value. The `&[u8]` return type enforces the memory-validity, initialization, alignment, lifetime, and shared-access properties that `last` needs; only the sequence relationship remains an implementation proof. For `Owned`, [`Vec::as_slice`](https://doc.rust-lang.org/1.70.0/std/vec/struct.Vec.html#method.as_slice) returns a slice containing the entire vector, establishing BYTES-VIEW. [`slice::last`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.last) returns the last element or `None` when empty, and [`Option::copied`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.copied) copies that `u8`. Thus the required behavior follows without unsafe code. + +The private module makes `private::Sealed` unnameable outside the leaf, including elsewhere in the same crate; therefore outside safe code cannot add an implementation. `Owned`'s field is likewise private to the leaf. Any needed construction must cross a leaf-owned checked safe constructor/conversion. Future built-in impls must be added and proved there. + +This design is parametric over all `std` targets and profiles: it has no target/layout arithmetic, unsafe operation, conditional code, assertion-dependent check, generated artifact, or unwinding-sensitive invariant transition. + +## Compatibility, trust, and re-audit + +Sealing intentionally breaks downstream `Bytes` implementations and downstream uses of `last` with them. Removing `raw_parts` also breaks direct callers despite `#[doc(hidden)]`, unless the project's stated compatibility policy excludes hidden items. The required `Owned` behavior and public paths are preserved; moving the field into the leaf may require checked constructors for same-crate callers. + +TCB: the cited Rust 1.70 Reference/standard-library contracts, plus the ordinary source-level assumption that each later supported stable release preserves these exact propositions. Because `1.70+` is open-ended, re-check the corresponding versioned official pages on every support-range extension; a stability badge alone is insufficient. No dependency or platform premise is needed. The sketch is a candidate, not an audited new artifact: after implementation, re-audit exact privacy, every built-in impl/constructor, public reexports, and supported configurations. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r032.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r032.md new file mode 100644 index 0000000000..efc3f36e1c --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r032.md @@ -0,0 +1,70 @@ +# r032 — invariant-boundary review and redesign + +## Scope and claim + +Reviewed exactly `lib.rs` as supplied, without execution. The current claim is soundness of every well-typed safe use on Rust 1.70+, `std`, all targets and profiles; requested behavior is that `last` returns `None` for an empty supported built-in value and its final byte otherwise. There are no `cfg`, generated-code, FFI, concurrency, or profile-dependent branches in this artifact. + +The source-level TCB used below is the Rust 1.70 Reference and standard-library contracts. No dependency, tool, allocator-implementation, or deployment assumption is needed. Rust 1.70 alone is sufficient to refute the universal 1.70+ claim; a future post-change proof over the open-ended range must either recheck applicable later documentation or record an accepted, exact backwards-compatibility premise. + +## Current artifact + +**Safe surface:** `Bytes` is a public safe trait; its `raw_parts` method is public and safe despite `#[doc(hidden)]`; downstream safe code may implement and call it. `Owned` is a public type with a private field and a controlled `Bytes` implementation. `last` is a public safe generic function. The unsafe consumer is the `ptr.add` plus raw-pointer dereference in `last`. + +**Soundness verdict: UNSOUND.** This downstream program uses no `unsafe`: + +```rust +struct Bad; +impl Bytes for Bad { + fn raw_parts(&self) -> (*const u8, usize) { + (std::ptr::null(), 1) + } +} +let _ = last(&Bad); +``` + +The safe trait contract states no invariant relating the pointer, length, allocation, initialization, alignment, provenance, aliasing, or lifetime. With `len == 1`, `then` evaluates the closure. Rust 1.70's [`pointer::add` contract](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) requires the starting and resulting pointer to be in bounds or one-past the same allocated object; null satisfies neither. Independently, the Rust 1.70 Reference classifies accessing a place that is dangling or based on a misaligned pointer as [undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html). Thus this valid safe use reaches UB at `add(0)` and/or dereference. `#[doc(hidden)]` does not impose privacy or an unsafe obligation. + +For the controlled `Owned` implementation, the intended local derivation does close relative to the Rust 1.70 [`Vec::as_ptr`](https://doc.rust-lang.org/1.70.0/std/vec/struct.Vec.html#method.as_ptr) and `Vec::len` contracts: while `&self` is held and the vector is not mutated, a nonzero length makes `len - 1` an initialized in-bounds element, so reading it yields the final byte; zero length does not evaluate the closure and yields `None`. That restricted fact cannot rescue the public trait, because `last` quantifies over every safe implementation. The unsafe block also has no adjacent `SAFETY` proof; any adequate proof would immediately expose the missing implementer premise. + +## Recommended leaf-module boundary + +The consumer needs only a valid byte sequence, not a caller-supplied raw pointer/length capability. Keep the public nominal trait if API identity matters, but put its implementation capability and `Owned` representation in one private leaf module: + +```rust +mod bytes { + mod sealed { + pub trait Sealed { + fn as_bytes(&self) -> &[u8]; + } + } + + pub trait Bytes: sealed::Sealed {} + + pub struct Owned(Vec); + + impl Owned { + pub(crate) fn from_vec(bytes: Vec) -> Self { Self(bytes) } + } + + impl sealed::Sealed for Owned { + fn as_bytes(&self) -> &[u8] { self.0.as_slice() } + } + impl Bytes for Owned {} + + pub fn last(bytes: &B) -> Option { + sealed::Sealed::as_bytes(bytes).last().copied() + } +} + +pub use bytes::{last, Bytes, Owned}; +``` + +`sealed` and `Owned.0` are private to the leaf, so safe code in parents, siblings, the rest of the crate, and downstream crates cannot implement the capability or fabricate its representation. Such code can only use checked safe entry points. Add controlled implementations in this module for each genuinely supported built-in type; each returns a compiler-valid shared slice. Constructors may be made public if required, provided they establish the representation entirely in safe code. + +This candidate contains no unsafe operation or safety invariant carried by raw parts. The only behavioral premise is the safe slice contract: Rust 1.70 [`slice::last`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.last) returns the last element or `None` when empty, and `copied` converts `Option<&u8>` to `Option`. The argument is parametric over targets, profiles, allocator choices, and panic strategy because none changes this source-level safe-slice reasoning. + +## Compatibility and follow-up + +Genuine sealing removes downstream `Bytes` implementations, and removing `raw_parts` removes a language-reachable public method; both are API-breaking even though the method was doc-hidden, but the request explicitly permits abandoning external implementations. Moving `Owned` into the leaf also prevents same-crate tuple construction; migrate it to a leaf-owned safe constructor such as `from_vec`. The `last` signature and required built-in behavior remain. + +The candidate is a design, not an audited artifact, so it receives no `PROVED` verdict yet. After implementation, audit the exact built-in set, effective sealing/privacy, all constructors and reexports, the Rust-version compatibility premise, and the `last` postcondition. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r033.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r033.md new file mode 100644 index 0000000000..a17319038d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r033.md @@ -0,0 +1,74 @@ +# `r033` invariant-boundary review and redesign + +## Claim and verdict + +**Current artifact — UNSOUND.** Scope is the exact supplied `lib.rs`: its public safe API, Rust 1.70+, `std`, all targets and profiles. The theorem is that every well-typed safe use is free of Rust undefined behavior and, for the sole controlled implementation `Owned`, `last` returns `None` when empty and the final byte otherwise. There are no dependencies, generated artifacts, `cfg` branches, or tool-derived premises. + +`Bytes::raw_parts` is a language-reachable safe method despite `#[doc(hidden)]`, and the public safe trait is not sealed. Consequently safe downstream code can write: + +```rust +struct Bad; +impl Bytes for Bad { + fn raw_parts(&self) -> (*const u8, usize) { + (std::ptr::null(), 1) + } +} +let _ = last(&Bad); +``` + +The caller contains no unsafe operation. `last` takes the nonempty branch and its unsafe block accesses a null pointer. Rust 1.70's Reference classifies access through a dangling pointer (including null) as undefined behavior ([Reference: undefined behavior](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)); `pointer::add` also has allocation, in-bounds, and offset preconditions that arbitrary results can violate ([`pointer::add`](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add)). One supported toolchain is enough to refute the open-ended combined claim; the same defect is independent of target and profile. + +The smallest missing implication at the unsafe block is: + +> For every safe `B: Bytes` implementation, a nonzero returned `len` implies that `ptr.add(len - 1)` is permitted and points to a live, aligned, initialized, readable `u8` for the access. + +Neither the type system, validation, privacy, nor a trusted dependency supplies it. Adding a `SAFETY` comment cannot repair a hidden precondition on a safe implementer. + +## Boundary and obligation coverage + +The complete relevant surface is: public safe trait `Bytes`; hidden-but-public safe method `raw_parts`; externally implementable trait impl boundary; opaque public `Owned` with private tuple field and its safe impl; and safe free function `last` containing `add` and raw dereference. There are no public constructors in this snapshot. + +For the controlled `Owned` path, the intended behavior can be reconstructed: `Vec` is contiguous and `as_ptr` addresses its buffer ([`Vec` guarantees and `as_ptr`](https://doc.rust-lang.org/1.70.0/std/vec/struct.Vec.html#method.as_ptr)); `len` is its element count ([`Vec::len`](https://doc.rust-lang.org/1.70.0/std/vec/struct.Vec.html#method.len)); when nonzero, `len - 1 < len`, the shared borrow remains live, and no mutation intervenes, so the final initialized element is read. When zero, `bool::then` returns `None` without evaluating the closure ([`bool::then`](https://doc.rust-lang.org/1.70.0/std/primitive.bool.html#method.then)). This material proof is absent from the source, but it does not cure the generic boundary defect. + +Configuration closure is parametric: the counterexample uses no layout, allocator, optimization, panic, or target-specific premise. Future stable releases beyond the versioned 1.70 citations require rechecking the same propositions or an explicit accepted Rust-compatibility premise; no such premise is needed to establish the current `UNSOUND` verdict. + +## Recommended redesign + +The only demonstrated consumer capability is “produce the final byte if one exists.” Do not transport raw representation at all. Put representation, controlled implementations, and the consumer in one private leaf module, and re-export only the public surface: + +```rust +mod bytes_impl { + mod sealed { + pub trait Sealed { + fn last_byte(&self) -> Option; + } + } + + pub struct Owned(Vec); + + pub trait Bytes: sealed::Sealed {} + + impl sealed::Sealed for Owned { + fn last_byte(&self) -> Option { + self.0.last().copied() + } + } + impl Bytes for Owned {} + + pub fn last(bytes: &B) -> Option { + sealed::Sealed::last_byte(bytes) + } +} + +pub use bytes_impl::{last, Bytes, Owned}; +``` + +`bytes_impl` is the invariant/access owner; `Owned.0` and `sealed` remain inaccessible outside it. Code in the parent and the rest of the crate is therefore unable to construct invalid representation or add an implementation. Even if a caller can invoke a safe method through method resolution, that grants no mutation or unsafe capability. Each future built-in implementation must be added inside the leaf and establish the same behavioral postcondition. + +The proposal contains no unsafe operation and no raw-pointer invariant. For `Owned`, [`slice::last`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.last) returns the last element or `None`, and `copied` copies that `u8` ([`Option::copied`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.copied)); this directly entails the required behavior. Safe operations make the argument parametric over targets and profiles. Rust 1.70 applicability is explicit; an implemented post-change audit should verify versioned contracts across the project's open-ended support policy. + +## Compatibility, migration, and triggers + +This removes `raw_parts` and downstream implementation ability. Both are source-breaking even though the method is hidden; the request expressly permits genuine sealing and does not require external implementations. The generic `last` shape and its behavior for the only supplied built-in type are preserved. The sketch deliberately does not invent a public `Owned` constructor. + +Audit the implemented snapshot anew. Re-review on any new `Bytes` implementation, representation/visibility change, re-export or macro that could defeat sealing, change to `last` behavior, supported-Rust policy change, or reintroduction of raw pointers. TCB is limited to the cited versioned Rust Reference/std contracts; there are no admitted dependency, environment, or tool assumptions. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r034.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r034.md new file mode 100644 index 0000000000..9963c30a16 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r034.md @@ -0,0 +1,71 @@ +# `r034` invariant-boundary review and redesign + +## Claim and verdict + +**Snapshot/scope.** This review covers only the supplied `lib.rs`: its public `Bytes`, `Owned`, and `last` surfaces and the one raw-pointer operation. The supported set is stable Rust 1.70+, `std`, every target/profile on which the source compiles, with no features, generated code, FFI, or target-specific branches. The requested behavioral claim is: for each controlled built-in `Bytes` type, `last` returns `None` exactly when its byte sequence is empty and otherwise returns its final byte. No code was executed. + +**Current safe-API soundness: UNSOUND.** A completely safe downstream program can implement the public safe trait as follows: + +```rust +struct Bad; +impl Bytes for Bad { + fn raw_parts(&self) -> (*const u8, usize) { + (std::ptr::null(), 1) + } +} + +let _ = last(&Bad); +``` + +For `len == 1`, `last` evaluates the closure and loads a `u8` through the null pointer. The Rust 1.70 Reference classifies loading from a dangling place as undefined behavior and defines a non-zero-sized pointer as dangling unless all pointed-to bytes are in one live allocation ([Behavior considered undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). Thus valid, well-typed safe use reaches UB. This witness is independent of target, optimization, panic strategy, and allocator. + +`#[doc(hidden)]` is not privacy. The language-reachable safe method and downstream trait implementation are safe API surfaces. The smallest missing implication is: + +> `B: Bytes` implies that `raw_parts()` returns a pointer to at least `len` live, initialized, readable, properly aligned `u8`s in one allocation, with usable provenance, and that those facts remain true through the load. + +Neither the types nor a checked/module-owned invariant establishes it. No safety comment could repair that caller-facing defect. Making only `raw_parts` an `unsafe fn` would merely leave `last` unable to establish its call obligation for arbitrary implementations. The private `Vec` field makes the supplied `Owned` implementation controllable, but it cannot prove the generic consumer sound. + +## Proposed leaf-module boundary + +Remove the raw representation from the public trait. Put the representation, genuine seal, controlled implementations, and consumer together in one leaf module, then re-export only the safe surface: + +```rust +mod bytes { + mod sealed { + // Public only inside the private module path; downstream code cannot + // name or implement this trait. + pub trait Sealed { + fn as_slice(&self) -> &[u8]; + } + } + + pub trait Bytes: sealed::Sealed {} + + pub struct Owned(Vec); + + impl sealed::Sealed for Owned { + fn as_slice(&self) -> &[u8] { + self.0.as_slice() + } + } + impl Bytes for Owned {} + + pub fn last(bytes: &B) -> Option { + sealed::Sealed::as_slice(bytes).last().copied() + } +} + +pub use bytes::{last, Bytes, Owned}; +``` + +The private `sealed` module is inaccessible downstream under Rust module privacy, so its public-within-path trait cannot be implemented externally ([Visibility and privacy](https://doc.rust-lang.org/1.70.0/reference/visibility-and-privacy.html)). `Owned.0` is private to the leaf module rather than the crate root's much broader descendant region. Parents, siblings, and all downstream safe code must use the exported safe API. + +This intentionally breaks external `Bytes` implementations and direct calls to the hidden raw method; that boundary change is authorized by the request. A new built-in must be added in this module, implement `sealed::Sealed`, and prove that its returned slice is exactly its logical byte sequence. + +## Compact proof and obligation closure + +**Invariant `I-BYTES`.** For every controlled implementation and every shared borrow, `Sealed::as_slice` returns, for that borrow, the complete logical byte sequence of the value. The owner is the `bytes` leaf module; implementation addition/change is the only producer/transition; `last` is the only consumer shown. For `Owned`, `Vec::as_slice` “extracts a slice containing the entire vector” ([Rust 1.70 `Vec::as_slice`](https://doc.rust-lang.org/1.70.0/std/vec/struct.Vec.html#method.as_slice)), so the implementation establishes `I-BYTES` using only safe code. + +At `last`, the safe slice already carries validity, extent, lifetime, and alignment in its type; there is no raw-pointer or unsafe obligation. Slice `last` “returns the last element of the slice, or `None` if it is empty” ([Rust 1.70 `[T]::last`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.last)); `copied()` copies that `u8`. Together with `I-BYTES`, this proves the requested empty/nonempty behavior and source-level soundness for `Owned`. No panic, unwind, callback, mutation, or invariant suspension occurs between slice production and consumption. + +**Redesigned soundness and requested postcondition: PROVED**, relative to these exact Rust 1.70 Reference/standard-library propositions and the ordinary source-level premise that applicable safe `std` APIs satisfy their contracts. The proof is parametric over target/profile because the replacement has one configuration-independent safe implementation. For later stable releases in the open-ended `1.70+` range, admit the explicit compatibility premise that these three consumed propositions remain preserved; a material change to them, module visibility, the built-in set, or the leaf module triggers re-review. Future compiler-binary correctness and APIs outside this focused source are excluded. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r035.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r035.md new file mode 100644 index 0000000000..43f9493a00 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r035.md @@ -0,0 +1,128 @@ +# r035 — invariant-boundary review and redesign + +## Scope and verdict + +Reviewed the exact `lib.rs` snapshot for Rust 1.70+, `std`, and all targets and +profiles, without executing it. Safe surfaces are the public safe `Bytes` trait +and its language-reachable `#[doc(hidden)]` method, `Owned` (whose tuple field is +private), its safe implementation, and safe `last`. The snapshot contains no +conditional or generated code and only one built-in implementation, `Owned`. + +**Current API: UNSOUND.** This is a boundary defect, not merely a missing +`SAFETY` comment. `Bytes` is a safe, unsealed trait. `#[doc(hidden)]` does not +provide privacy, so wholly safe downstream code can do this: + +```rust +struct Bad; + +impl Bytes for Bad { + fn raw_parts(&self) -> (*const u8, usize) { + (std::ptr::null(), 2) + } +} + +let _ = last(&Bad); +``` + +`last` takes the nonempty branch and evaluates `ptr.add(1)`. The Rust 1.70 +[`pointer::add` contract](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) +requires a nonzero offset to remain within the allocation from which the +starting pointer was derived; this null pointer is derived from no allocation. +Violation is undefined behavior. Independently, the +[raw-pointer documentation](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html) +requires a pointer loaded through to be valid for that access and aligned. +There is no caller-side `unsafe` boundary at which the missing allocation, +range, provenance, initialization, or lifetime obligations could legally be +placed. + +The supplied `Owned` implementation does not create that counterexample, but +proving one controlled implementation cannot prove a generic safe consumer of +every safe implementation. Adding prose to `raw_parts` would leave the API +unsound. Making `Bytes` unsafe could express the obligation, but unnecessarily +exports a large proof burden when external implementations are not required. + +## Proposed leaf-module boundary + +Replace raw parts with a safe borrowed slice and make the sealing path genuinely +unreachable. A complete design skeleton is: + +```rust +mod byte_boundary { + mod sealed { + pub trait Sealed { + fn as_bytes(&self) -> &[u8]; + } + } + + pub trait Bytes: sealed::Sealed {} + + pub struct Owned(Vec); + + impl Owned { + pub fn new(bytes: Vec) -> Self { + Self(bytes) + } + } + + impl sealed::Sealed for Owned { + fn as_bytes(&self) -> &[u8] { + self.0.as_slice() + } + } + + impl Bytes for Owned {} + + pub fn last(bytes: &B) -> Option { + sealed::Sealed::as_bytes(bytes).last().copied() + } +} + +pub use byte_boundary::{last, Bytes, Owned}; +``` + +`byte_boundary` is the smallest proof boundary: it owns the private +representation, constructor, sealing trait, every implementation, and the sole +consumer. Parents, siblings, and downstream code receive only checked safe +APIs. The sealing trait is `pub` only inside a private `sealed` module, as +required for the standard sealed-trait pattern; no path to it is accessible +outside `byte_boundary`. Rust's Reference states that a +[supertrait is required to be implemented](https://doc.rust-lang.org/1.70.0/reference/items/traits.html#supertraits), +while its [privacy rules](https://doc.rust-lang.org/1.70.0/reference/visibility-and-privacy.html) +restrict private items to their module and descendants. Thus outside safe code +cannot implement the prerequisite and therefore cannot implement `Bytes`. + +Do not retain `raw_parts`, even privately: there is no need for a raw-pointer +invariant. `as_bytes`'s return type makes validity, readable extent, alignment, +and borrow duration part of an ordinary reference checked at the producer. For +`Owned`, [`Vec::as_slice`](https://doc.rust-lang.org/1.70.0/std/vec/struct.Vec.html#method.as_slice) +returns the vector's entire contents. Then +[`slice::last`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.last) +returns `None` exactly when empty and otherwise a reference to the final +element; `copied` yields that byte. This proves the requested behavior without +unsafe code or a caller precondition. Each future built-in must add both impls +in this leaf module and prove that `as_bytes` is its complete logical byte +sequence. + +## Coverage, compatibility, and TCB + +The proposed argument is parametric over every `std` target and profile: there +is no `cfg`, target layout assumption, arithmetic, raw access, allocator action +in `last`, assertion, unwind-sensitive transition, or generated code. The same +control flow covers empty and nonempty values. No tests are offered as proof. + +For Rust 1.70, the cited Reference and standard-library contracts are the only +consumed axioms. For the literal open-ended range “1.70+”, either verify the +same propositions in every supported release or explicitly accept a TCB +compatibility premise that later stable Rust preserves those exact privacy, +supertrait, `Vec::as_slice`, and `slice::last` guarantees; a stability badge +alone is not that premise. Accordingly, the skeleton is **PROVED for Rust +1.70, all `std` targets/profiles, relative to the cited axioms**, and its +open-ended later-release application is **UNPROVED until that compatibility +premise is accepted or versioned documentation is checked**. Re-audit on a new +`Bytes` implementation, representation/visibility change, supported-toolchain +policy change, or weakening of a consumed standard-library contract. + +This redesign is intentionally source-breaking for downstream `Bytes` impls +and direct `raw_parts` calls. That is authorized by the request and necessary +to close the current safe-API hole; `#[doc(hidden)]` never made those uses +unsafe or unreachable. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r036.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r036.md new file mode 100644 index 0000000000..3313cc223e --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r036.md @@ -0,0 +1,62 @@ +# `r036` invariant-boundary review and redesign + +## Claim, snapshot, and verdict + +Scope is the supplied `lib.rs` only, under Rust 1.70+, `std`, every target and profile. There are no `cfg`s, macros, generated artifacts, dependencies, FFI, concurrency, or build inputs in the snapshot. In-scope safe surfaces are public safe trait `Bytes`, its language-reachable `#[doc(hidden)]` safe method `raw_parts`, public `Owned` (with a private field), its safe impl, and safe function `last`. Required behavior is: for supported built-in values, empty returns `None`; nonempty returns the final byte. + +**Current soundness: UNSOUND.** This is an implementation defect, not merely missing documentation. A public safe trait's associated method is public; `#[doc(hidden)]` changes documentation, not language visibility or implementability. Rust 1.70's [visibility rules](https://doc.rust-lang.org/1.70.0/reference/visibility-and-privacy.html) make private module items accessible only in their module and descendants; the current code has no such sealing boundary. + +A wholly safe downstream counterexample is: + +```rust +struct Bad; +impl Bytes for Bad { + fn raw_parts(&self) -> (*const u8, usize) { + (std::ptr::null(), 1) + } +} +let _ = last(&Bad); +``` + +The true branch evaluates `ptr.add(0)` and dereferences it. The Rust 1.70 [`add` contract](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) requires the start and result to be in-bounds (or one-past) of the same allocation; null meets neither condition. Independently, the Reference lists loading through a dangling pointer as undefined behavior ([Rust 1.70 UB list](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). Thus a well-typed safe use reaches UB. Because Rust 1.70 is in the requested set, this single counterexample refutes the aggregate Rust-1.70+ theorem; it is target/profile independent. + +The missing invariant is: whenever `raw_parts` is consumed and `len > 0`, `ptr..ptr+len` must denote one live allocation containing `len` initialized, properly aligned `u8`s readable without a conflicting access for the entire operation. `len - 1` is then in range. `Owned` can establish that fact from one `Vec`, but a safe generic implementor has no enforced obligation to do so. Prose on a safe trait could not repair that hidden precondition. + +## Preferred redesign + +Remove raw pointers from the contract and put the representation, all implementations, and genuine seal in one leaf module: + +```rust +mod bytes_leaf { + mod sealed { pub trait Sealed {} } + + pub trait Bytes: sealed::Sealed { + #[doc(hidden)] + fn as_bytes(&self) -> &[u8]; + } + + pub struct Owned(Vec); + + impl sealed::Sealed for Owned {} + impl Bytes for Owned { + fn as_bytes(&self) -> &[u8] { self.0.as_slice() } + } + + pub fn last(bytes: &B) -> Option { + bytes.as_bytes().last().copied() + } +} +pub use bytes_leaf::{last, Bytes, Owned}; +``` + +`sealed` is not reexported and is private to `bytes_leaf`; therefore parents, siblings, downstream code, and macros outside the leaf cannot name or implement its trait. Since `Bytes` requires that supertrait, external `Bytes` impls are rejected. `Owned`'s invariant-bearing field is also private to the leaf. Every future supported implementation must be added there; no `pub(crate)` escape hatch or public macro may generate impls elsewhere. + +The raw-memory theorem disappears. Rust 1.70 documents [`Vec::as_slice`](https://doc.rust-lang.org/1.70.0/std/vec/struct.Vec.html#method.as_slice) as extracting a slice containing the entire vector, and [slice `last`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.last) as returning the last element or `None` when empty. [`Option::copied`](https://doc.rust-lang.org/1.70.0/std/option/enum.Option.html#method.copied) copies that `u8`. Consequently the result is exactly `None` for an empty `Owned` and `Some(final_byte)` otherwise. There is no unsafe operation, arithmetic, suspended invariant, panic cleanup obligation, or profile-dependent check. + +**Proposed design, Rust 1.70 soundness and requested behavior: PROVED**, relative only to the cited Rust 1.70 Reference/std axioms. Configuration closure is parametric: the same source is selected everywhere and the derivation uses no target layout, optimization, overflow, allocator-implementation, or panic-strategy premise beyond safe `Vec`/slice contracts. + +For the open-ended future part of “1.70+”, an unconditional frozen verdict is **UNPROVED** unless the project accepts an explicit compatibility premise that every later supported release preserves these exact contracts, or sets an audit cutoff and rechecks later releases. API or standard-library stability badges alone are insufficient for that stronger temporal claim. + +## Compatibility and review triggers + +Sealing intentionally breaks external impls, as authorized. Replacing `raw_parts` also breaks direct callers despite `#[doc(hidden)]`; if that compatibility was not intended to be waived, retain it as a deprecated default method derived from `as_bytes`, but do not use it inside `last`. Re-audit if the private module/reexports change, a macro or new implementation is added, the `Owned` field's visibility widens, `last` again consumes raw parts, the Rust support range changes, or any cited contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r037.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r037.md new file mode 100644 index 0000000000..ce48d95473 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r037.md @@ -0,0 +1,58 @@ +# `two_mut` greenfield design report + +## Scope and requirements + +Artifact reviewed: the supplied `REQUEST.md` and placeholder `lib.rs`. This is a conceptual design, not an audit of `unimplemented!()`, and no current-artifact verdict applies. + +`two_mut` is a public safe API. For every `T` and every well-typed call with `values: &mut [T]`, it must: + +- return `None` exactly when `i == j`, `i >= values.len()`, or `j >= values.len()`; +- otherwise return mutable references to the original elements `values[i]` and `values[j]`, in `(i, j)` order; and +- impose no caller safety precondition and introduce no panic on any input. + +The design must use only `core`, compile on stable Rust 1.70+, work parametrically on every Rust target supporting the crate, and require no reusable abstraction or special performance mechanism. + +## Recommended design + +Use bounds/equality validation followed by the safe `split_at_mut` primitive: + +```rust +pub fn two_mut( + values: &mut [T], + i: usize, + j: usize, +) -> Option<(&mut T, &mut T)> { + if i == j || i >= values.len() || j >= values.len() { + return None; + } + + if i < j { + let (lower, upper) = values.split_at_mut(j); + Some((&mut lower[i], &mut upper[0])) + } else { + let (lower, upper) = values.split_at_mut(i); + Some((&mut upper[0], &mut lower[j])) + } +} +``` + +This is entirely safe Rust: there is no `unsafe` block, unsafe contract, representation invariant, raw-pointer provenance argument, or local `SAFETY` comment. The one-off function is the smallest useful boundary; introducing a reusable multi-borrow abstraction would add proof and API surface without a demonstrated consumer. + +## Conditional proof plan + +Use the Rust 1.70 `core` slice contract as AXIOM-SPLIT. It says that `split_at_mut(mid)` divides a mutable slice into two and specifies the returned index ranges as `[0, mid)` and `[mid, len)`; it panics only when `mid > len`. See [Rust 1.70 `slice::split_at_mut`](https://doc.rust-lang.org/1.70.0/core/primitive.slice.html#method.split_at_mut). The method is marked stable since 1.0.0, so it is available at the 1.70 MSRV; that badge alone must not be used to project every behavioral sentence to arbitrary later releases. + +Partition all inputs: + +1. If either index is out of bounds or the indices are equal, the first branch returns `None` before any indexing or split. This proves the required rejection behavior, including empty and one-element slices. +2. Otherwise `i < j` or `i > j` exhaustively. + - For `i < j`, the checks give `i < j < len`. AXIOM-SPLIT at `j` yields `lower = values[0..j]` and `upper = values[j..len]`. Thus `lower[i]` exists and denotes original element `i`; `upper[0]` exists and denotes original element `j`. + - For `i > j`, the checks give `j < i < len`. Splitting at `i` yields `lower = values[0..i]` and `upper = values[i..len]`. Thus `upper[0]` denotes original element `i`, while `lower[j]` denotes original element `j`, preserving requested tuple order. +3. In both success cases the two element borrows originate from the two mutable subslices supplied by the safe primitive. The function performs only safe operations, so well-typed safe callers receive usable references with lifetimes tied to the input borrow and no hidden aliasing obligation. This argument also covers zero-sized `T`: it relies on the safe slice contract and logical elements, not numeric address inequality. +4. No arithmetic is performed, so there is no overflow branch. There is no allocation, dependency, `cfg`, generated code, FFI, concurrency, panic-mode dependence, or target-specific layout assumption. The proof is parametric in `T`, slice length, pointer width, profile, and target. + +## Trust, version coverage, and post-change audit + +The source-level TCB is only the applicable versioned `core` contracts and Rust type checking; no compiler-backend or binary correctness claim is made. For an audit cutoff later than 1.70, either independently verify AXIOM-SPLIT in each supported release used by the audit or explicitly accept a Rust compatibility premise covering that exact interval. Open-ended future `1.70+` cannot receive an evergreen proof from the 1.70 stability badge; new releases trigger contract/applicability review. + +After implementation, inspect the exact source and confirm it matches this candidate, then close every case above against the applicable toolchain documentation. Tests for both index orders, each failure condition, boundaries, empty slices, and a zero-sized type are useful regression evidence, but are not substitutes for the universal derivation. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r038.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r038.md new file mode 100644 index 0000000000..625d12cce9 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r038.md @@ -0,0 +1,62 @@ +# `two_mut` greenfield design + +## Scope and requirements + +This is a conceptual design for the placeholder in `r038/lib.rs`, not an audit of an implementation. The only safe surface is the public function `two_mut`. For every `T`, slice, and `usize` pair supplied by well-typed safe code, it must: + +- return `None` exactly when either index is out of bounds or `i == j`; +- otherwise return mutable references to the original elements at `i` and `j`, in that order; +- impose no caller-side safety precondition and introduce no undefined behavior or panic; and +- work in `no_std` on Rust 1.70+ and all Rust targets, with no dependency or special configuration premise. + +There is no present-artifact verdict: the body is expressly a placeholder. An implemented snapshot must be audited separately. + +## Recommended design + +Use only bounds checks and the safe, stable slice primitive `split_at_mut`, splitting at the larger index: + +```rust +pub fn two_mut( + values: &mut [T], + i: usize, + j: usize, +) -> Option<(&mut T, &mut T)> { + if i >= values.len() || j >= values.len() || i == j { + return None; + } + + if i < j { + let (before_j, from_j) = values.split_at_mut(j); + Some((&mut before_j[i], &mut from_j[0])) + } else { + let (before_i, from_i) = values.split_at_mut(i); + Some((&mut from_i[0], &mut before_i[j])) + } +} +``` + +Do not add an `unsafe` block or a reusable “many mutable references” abstraction. Neither is needed for this single two-index operation, and each would add proof surface without supplying required capability. No `SAFETY` comment is appropriate because the design contains no unsafe operation; ordinary API documentation should state the `None`/`Some` behavior above. + +## Conditional proof plan + +Rust 1.70's [`split_at_mut`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.split_at_mut) contract says it divides a mutable slice at `mid`: the first result covers original indices `[0, mid)` and the second covers `[mid, len)`; it panics only when `mid > len`. + +1. Let `n = values.len()` at entry. If `i >= n`, `j >= n`, or `i == j`, the first branch returns `None`, exactly matching the required rejection cases, without indexing or splitting. +2. On the remaining path, `i < n`, `j < n`, and `i != j`. Exactly one of `i < j` and `i > j` holds. +3. If `i < j`, splitting at `j` cannot panic because `j < n`. `before_j` covers `[0, j)`, so `i` is a valid index and denotes original element `i`. `from_j` covers `[j, n)` and is nonempty because `j < n`, so index `0` denotes original element `j`. The two mutable references originate from the two separately returned sub-slices and the tuple order is `(i, j)`. +4. If `i > j`, the symmetric argument applies after splitting at `i`: `from_i[0]` denotes original element `i`, while `before_i[j]` denotes original element `j`; returning them in that order preserves `(i, j)`. +5. These branches exhaust all inputs. No unchecked arithmetic, raw pointers, callbacks, allocation, unwinding operation, or invariant-bearing representation exists. The reasoning is parametric in `T`, including zero-sized types: distinct logical slice indices are separated using the safe library operation rather than inferred from addresses. +6. The returned references inherit the borrow lifetime from `values`; while they exist, Rust's reference types enforce the access capability needed by callers. The implementation neither retains an alias nor exposes representation state. + +Implementation review should confirm the exact source matches this control flow and that all indexing operations remain dominated by the stated checks. Compilation or tests may catch transcription mistakes but are not substitutes for this proof. + +## Applicability, TCB, and re-audit + +The proof is source-level, for the shown design, Rust 1.70.0 abstract/library semantics, every target and profile, and `no_std`. Configuration closure is parametric: there is one unconditional implementation and no `cfg`, feature, generated code, dependency, allocator, FFI, concurrency, layout, or target-specific branch. + +TCB: + +- **AXIOM-RUST-1.70-SPLIT:** the exact Rust 1.70 `split_at_mut` contract linked above, including its returned index ranges and panic condition. +- **COMPAT-RUST-STABLE (explicit additional premise):** for the open-ended `1.70+` claim, each later supported stable Rust release preserves the consumed `split_at_mut` proposition and ordinary safe slice-index behavior. A stability badge alone does not establish this premise. + +If that compatibility premise is not accepted, the proof is limited to Rust 1.70.0 and the applicable contracts must be rechecked for each additional release. Re-audit is also required if the body, signature, behavioral contract, minimum Rust version, or supported configuration set changes. Once implemented, a post-change audit may conclude `PROVED` only after checking the exact artifact and accepting the stated TCB. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r039.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r039.md new file mode 100644 index 0000000000..3cd85efe33 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r039.md @@ -0,0 +1,56 @@ +# `r039`: design and proof plan for `two_mut` + +## Scope and required contract + +This is a greenfield design for the placeholder in `lib.rs`, not a verdict on an implementation. The only in-scope safe surface is the public generic function `two_mut`. For every `T`, slice length, and well-typed safe call: + +- return `None` exactly when `i == j`, `i >= values.len()`, or `j >= values.len()`; +- otherwise return `Some((a, b))`, where `a` mutably refers to the element originally at `i` and `b` to the element originally at `j`, in that order; +- permit both references to coexist for the returned borrow lifetime, without modifying any element during the call or panicking. + +The applicability domain is `no_std`, no dependencies, stable Rust 1.70 and later, every target on which that Rust/core combination supports ordinary slices, and every `T` (including zero-sized types). No reusable abstraction is required. + +## Recommended design + +```rust +pub fn two_mut( + values: &mut [T], + i: usize, + j: usize, +) -> Option<(&mut T, &mut T)> { + let len = values.len(); + if i == j || i >= len || j >= len { + return None; + } + + if i < j { + let (before_j, from_j) = values.split_at_mut(j); + Some((&mut before_j[i], &mut from_j[0])) + } else { + let (before_i, from_i) = values.split_at_mut(i); + Some((&mut from_i[0], &mut before_i[j])) + } +} +``` + +This uses only stable `core`/prelude operations: one safe slice split and two indexed accesses. It allocates nothing, performs no traversal, introduces no unsafe block or caller safety obligation, and does not create a generic helper for a one-off use. + +## Conditional proof + +The Rust 1.70 `core` documentation says `split_at_mut` “Divides one mutable slice into two at an index,” specifies the returned index ranges as `[0, mid)` and `[mid, len)`, and says it “Panics if `mid > len`.” [Rust 1.70 `slice::split_at_mut`](https://doc.rust-lang.org/1.70.0/core/primitive.slice.html#method.split_at_mut). + +The initial branch disposes of every invalid case and does so before splitting or indexing. Hence every continuing execution has `i < len`, `j < len`, and `i != j`. + +- If `i < j`, splitting at `j` cannot panic because `j < len`. The first subslice contains original indices `0..j`, so `i` is a valid index into it and denotes original element `i`. The second contains original indices `j..len`; it is nonempty because `j < len`, and its index `0` denotes original element `j`. The tuple is therefore in `(i, j)` order. +- Otherwise, inequality already rules out equality, so `i > j`. Splitting at `i` cannot panic because `i < len`. The second subslice is nonempty and its index `0` denotes original element `i`; `j < i` makes `j` valid in the first subslice and denotes original element `j`. Reversing the subslice selection preserves the required `(i, j)` return order. + +In both cases the two references are derived from the two mutable subslices returned by the safe partition operation. Their logical element ranges do not overlap, so the references may coexist. This argument does not rely on unequal numeric addresses: it also covers zero-sized `T`, for which the safe library primitive remains responsible for constructing valid disjoint mutable borrows. No addition, subtraction, pointer arithmetic, callback, destructor, allocation, or target-specific operation occurs. Thus pointer width, endianness, alignment, panic strategy, optimization, and allocator choice introduce no proof branch. + +The same case analysis proves the complete behavioral contract: invalid inputs take the sole `None` return; valid unequal inputs take exactly one of the exhaustive order branches and return the requested pair. All potentially panicking indexing is proved in bounds, and `split_at_mut` is proved within its non-panicking domain. + +## TCB, coverage, and post-change review + +- **AXIOM-CORE-1.70:** the exact Rust 1.70 `core` contract linked above, including the two returned ranges and panic condition. +- **COMPAT-SLICE (needed for the open-ended `1.70+` claim):** each later supported stable Rust release preserves those exact `split_at_mut` and safe indexing propositions. A stability badge alone is insufficient evidence. Either explicitly accept this compatibility premise, or verify the same propositions in each supported release’s versioned standard-library documentation and bound the reviewed release set. + +Configuration coverage is parametric: the source has no `cfg`, generated code, FFI, layout assumption, dependency, or target-dependent path, and the proof uses only ordering facts over `usize` plus the target-independent slice contract. Re-audit after implementing the candidate, and whenever the function body/contract, minimum Rust version, supported toolchain class, or consumed slice documentation changes. The implemented snapshot—not this sketch—can then receive a proof verdict. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r040.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r040.md new file mode 100644 index 0000000000..6075e02f0d --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r040.md @@ -0,0 +1,90 @@ +# `two_mut` greenfield design + +## Result and scope + +Use a wholly safe implementation based on `slice::split_at_mut`; there is no +justified unsafe boundary or reusable abstraction here. This design covers the +public safe function in the supplied `lib.rs`, for every `T`, slice length, and +pair of `usize` indices. Its required postcondition is exactly: return `None` +when an index is out of bounds or the indices are equal; otherwise return +mutable references to the original elements at `i` and `j`, in that order. +The placeholder itself is not an implementation and was neither changed nor +executed. + +```rust +pub fn two_mut( + values: &mut [T], + i: usize, + j: usize, +) -> Option<(&mut T, &mut T)> { + if i == j || i >= values.len() || j >= values.len() { + return None; + } + + if i < j { + let (before_j, from_j) = values.split_at_mut(j); + Some((&mut before_j[i], &mut from_j[0])) + } else { + let (before_i, from_i) = values.split_at_mut(i); + Some((&mut from_i[0], &mut before_i[j])) + } +} +``` + +No `unsafe` block, unsafe helper, raw pointer, allocation, dependency, or +`std`-only facility is needed. The public API therefore has no caller-side +safety precondition beyond ordinary well-typed safe use. + +## Proof plan + +Rust 1.70's slice documentation says `split_at_mut` “Divides one mutable slice +into two at an index”; it specifies original index ranges `[0, mid)` and +`[mid, len)`, and panics only when `mid > len`. +([Rust 1.70 standard-library contract](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.split_at_mut)) + +Let `len = values.len()`. + +1. The first guard returns `None` for precisely the required rejection union: + `i == j`, `i >= len`, or `j >= len`. It performs no arithmetic and therefore + has no overflow case. +2. On the remaining path, `i < len`, `j < len`, and `i != j` hold. +3. If `i < j`, splitting at `j` cannot panic because `j < len`. The first + subslice represents original indices `[0, j)`, so `i < j` proves + `before_j[i]` is original element `i`. The second represents `[j, len)`; + because `j < len`, it is nonempty and `from_j[0]` is original element `j`. + The tuple is consequently in `(i, j)` order. +4. Otherwise `j < i`. Splitting at `i` cannot panic because `i < len`. + `from_i[0]` is original element `i`, while `before_i[j]` is original element + `j`; reversing the two subslice selections in the tuple preserves `(i, j)` + order. +5. In both success branches, the references come from the two mutable + subslices supplied by the safe standard-library operation. They denote + distinct logical elements because one index is below the split and the + other is at the split. This argument also covers zero-sized `T`: it relies + on slice indices and the safe API contract, not distinct numeric addresses. + +Thus all indexing is proved in bounds, there is no panic path after the guard, +and mutation through either returned reference targets only its requested +element while both references live. There are no callbacks, destructors, +allocation, concurrency, unwinding transitions, or invariant-bearing stored +state in the function. + +## Configuration and trust disposition + +The argument is parametric over element layout, slice length, pointer width, +target, optimization, overflow-check setting, and panic strategy. It uses only +comparisons, `len`, safe indexing, and the inherent slice method, so it is +compatible with `no_std` and requires no feature or target partition. + +For Rust 1.70, the cited versioned standard-library contract is the sole +nonlocal semantic premise. For the open-ended `1.70+` policy, validate the same +contract against each newly supported toolchain's applicable versioned +standard-library documentation (or record an expressly accepted Rust +compatibility premise); the 1.70 citation alone must not silently prove future +documentation. Re-audit if the signature, requested behavior, MSRV, or consumed +slice contract changes. + +**Conceptual design verdict:** **PROVED** for Rust 1.70 relative to the cited +standard-library contract. Later supported releases are covered by the same +parametric derivation once that release's equivalent contract is verified; no +compiler/backend, platform, dependency, or testing assumption is consumed. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r041.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r041.md new file mode 100644 index 0000000000..d114e2ea4e --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r041.md @@ -0,0 +1,51 @@ +# r041: `two_mut` greenfield design + +## Scope and claim + +This report covers only the supplied `two_mut` signature and requested behavior. The body in `lib.rs` is explicitly a placeholder, so it is not audited as an implementation. For every well-typed safe call, the proposed body must return `None` exactly when `i == j`, `i >= values.len()`, or `j >= values.len()`; otherwise it must return mutable references to the original elements `values[i]` and `values[j]`, in that order. No caller-side safety precondition is permitted. + +## Proposed body + +```rust +pub fn two_mut( + values: &mut [T], + i: usize, + j: usize, +) -> Option<(&mut T, &mut T)> { + let len = values.len(); + if i == j || i >= len || j >= len { + return None; + } + + if i < j { + let (below_j, from_j) = values.split_at_mut(j); + Some((below_j.get_mut(i)?, from_j.get_mut(0)?)) + } else { + let (below_i, from_i) = values.split_at_mut(i); + Some((from_i.get_mut(0)?, below_i.get_mut(j)?)) + } +} +``` + +Use no `unsafe`, raw pointers, helper trait, or reusable abstraction. `split_at_mut` is the narrow standard safe primitive for separating the two borrows. `get_mut` keeps the implementation non-panicking even if later maintenance damages the local arithmetic proof. + +## Proof plan and derivation + +Let `n` be the entry value of `values.len()`. + +1. The first branch returns `None` for the exact requested disjunction. It performs no indexing, splitting, mutation, callback, move, or destruction. +2. On the remaining path, local facts are `i < n`, `j < n`, and `i != j`. Total ordering of `usize` exhaustively partitions this path into `i < j` and `i > j`; no arithmetic can overflow. +3. If `i < j`, split at `j`. Since `j < n`, the split cannot panic. The first subslice represents original indices `[0, j)` and therefore contains `i`; the second represents `[j, n)` and is nonempty, with its index `0` representing original index `j`. Both `get_mut` calls therefore return `Some`, and the tuple order is `(values[i], values[j])`. +4. If `i > j`, split at `i`. Since `i < n`, the split cannot panic. The second subslice begins at original index `i`, so its index `0` is `values[i]`; the first represents `[0, i)` and contains `j`. Both lookups succeed, and returning `(from_i[0], below_i[j])` preserves `(i, j)` order. +5. The Rust 1.70 `core` contract says `split_at_mut` returns first and second slices covering respectively `[0, mid)` and `[mid, len)`, and “Panics if `mid > len`.” [Rust 1.70 slice documentation](https://doc.rust-lang.org/1.70.0/core/primitive.slice.html#method.split_at_mut). The same page says `get_mut` returns a mutable reference to the indexed element/subslice or `None` when out of bounds. [Rust 1.70 `get_mut`](https://doc.rust-lang.org/1.70.0/core/primitive.slice.html#method.get_mut). These contracts plus the local inequalities establish the mapping and success claims above. Because the two references originate from the two mutable subslices returned by this safe API, the implementation introduces no unchecked aliasing obligation. +6. The elided output lifetime is tied to the sole borrowed input, so both returned references remain bounded by the `values` borrow. The function neither reads nor writes elements, invokes `T` code, nor changes slice length. + +Thus the proposed body is **PROVED** sound and its requested postcondition is **PROVED** for Rust 1.70 source semantics, relative to the cited `core` axioms. + +## Configuration closure and trust + +The argument is parametric in `T`, slice length, target layout, pointer width, optimization, panic strategy, and `T` being zero-sized. Empty and singleton slices reject; endpoint and reversed indices follow the two cases; zero-sized elements require no address-based reasoning because only safe slice APIs are used. There are no features, dependencies, allocation, generated code, FFI, concurrency, or target-specific branches. This is suitable for `no_std` because both methods are in `core`. + +The literal open-ended claim “Rust 1.70+” needs one policy entry: **TCB-COMPAT (pending acceptance)** — every supported post-1.70 Rust release preserves the two cited stable `core` behavioral propositions over the supported targets. With that premise accepted, the same parametric derivation proves the whole range. Without it, the unconditional open-ended version claim is **UNPROVED** beyond the individually verified Rust 1.70 contract. A material change to either contract, the MSRV, or target policy is a re-review trigger. + +When implemented, boundary examples (empty, singleton, equal indices, each out-of-bounds side, both orderings, endpoints, and a zero-sized `T`) are useful regression tests, but are not premises of this proof. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r042.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r042.md new file mode 100644 index 0000000000..6b11c70232 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r042.md @@ -0,0 +1,62 @@ +# `two_mut` greenfield design review + +## Claim and result + +This report covers only the proposed body of `two_mut` for the signature and behavior in `targets/r042`; the checked-in `unimplemented!()` placeholder is not an implementation and receives no verdict. + +**Design verdict: PROVED** for source-level Rust soundness and the requested return behavior on Rust 1.70.0, for every `T`, slice length, input pair, and target on which the crate is supported, relative to AXIOM-1 and AXIOM-2 below. For the open-ended Rust `1.70+` range, the same result is **PROVED relative to COMPAT-1**. No caller safety precondition is needed. + +## Proposed body + +```rust +pub fn two_mut( + values: &mut [T], + i: usize, + j: usize, +) -> Option<(&mut T, &mut T)> { + let len = values.len(); + if i == j || i >= len || j >= len { + return None; + } + + if i < j { + let (lower, upper) = values.split_at_mut(j); + Some((lower.get_mut(i)?, upper.get_mut(0)?)) + } else { + let (lower, upper) = values.split_at_mut(i); + Some((upper.get_mut(0)?, lower.get_mut(j)?)) + } +} +``` + +Use this direct safe implementation. There is no demonstrated need for raw pointers, an unsafe helper, or a reusable abstraction. Because it contains no unsafe operation, it needs neither a `# Safety` section nor a `SAFETY` comment. + +## Contract and proof plan + +The sole safe API surface is the public free function. Its theorem quantifies over every well-typed safe call; the caller supplies no hidden aliasing or index obligation. + +1. **Rejection behavior.** The first branch returns `None` exactly when `i == j`, `i >= len`, or `j >= len`. Reaching the remainder therefore establishes `i < len`, `j < len`, and `i != j`. + +2. **`i < j` case.** Splitting at `j` is non-panicking because `j < len`. AXIOM-1 gives `lower` the original index interval `0..j` and `upper` the original interval `j..len`. Thus `i` is in bounds for `lower`, and `0` is in bounds for the nonempty `upper`. By AXIOM-2, `lower.get_mut(i)` is a reference to original element `i`, while `upper.get_mut(0)` is a reference to original element `j`. The tuple has requested `(i, j)` order. + +3. **`i > j` case.** Since the indices are unequal, failure of `i < j` implies `i > j`. Splitting at `i` is non-panicking because `i < len`. Now `upper[0]` denotes original element `i`, and `lower[j]` denotes original element `j`; the expression deliberately returns upper first, preserving `(i, j)` order. + +4. **Exclusivity and lifetime.** AXIOM-1 partitions the slice into non-overlapping logical index intervals. Each returned mutable reference comes from a different output slice through a safe API, and Rust ties both returned borrows to the input mutable borrow. This reasoning also covers zero-sized `T`: it relies on logical elements and the safe slice contract, not on distinct numeric addresses. + +5. **Totality and panic behavior.** Empty and singleton slices, equal indices, and either out-of-bounds index exit before splitting. On the remaining paths, each split point and each local element index is proved in bounds. `get_mut` is non-panicking and the `?` fallbacks are unreachable by the preceding inequalities. No arithmetic, allocation, element move/read/drop, callback, or user trait implementation occurs. + +Together, the two strict-order cases are exhaustive, and establish `Some` with the two corresponding, distinct mutable references in requested order for every accepted input. + +## Authoritative premises / TCB + +- **AXIOM-1 — Rust 1.70.0 `core` slice split.** The versioned [`split_at_mut` documentation](https://doc.rust-lang.org/1.70.0/core/primitive.slice.html#method.split_at_mut) says it “Divides one mutable slice into two at an index,” assigns the first result `0..mid` and the second `mid..len`, and specifies a panic only when `mid > len`. Consumer: steps 2–4. +- **AXIOM-2 — Rust 1.70.0 `core` checked access.** The versioned [`get_mut` documentation](https://doc.rust-lang.org/1.70.0/core/primitive.slice.html#method.get_mut) specifies a mutable element/subslice reference when in bounds and `None` otherwise. With `usize`, the output type here is `T`. Consumer: steps 2–3 and 5. +- **COMPAT-1 — explicit open-range premise.** Stable Rust releases after 1.70.0 preserve the two exact contracts and source availability above over this function’s domain. This premise is required because documentation for 1.70.0 alone cannot prove an unbounded later-version claim. If the project will not admit it, freeze an audit cutoff and verify the same propositions in every supported documentation range; until then, the `>1.70.0` portion is `UNPROVED`, while the 1.70.0 design result remains proved. + +This is a source-level theorem relative to documented Rust semantics. Compiler/backend correctness is not asserted. There are no dependencies or other admitted premises. + +## Configuration closure and handoff + +The proof is parametric over `usize` width, slice length, `T` (including ZSTs), data layout, target, profile, optimization, and panic strategy. The body uses only `core` slice operations already present in the Rust 1.70.0 documentation, so it is compatible with `no_std`. It has no `cfg`, feature, generated code, FFI, assembly, atomics, allocator, or linking axis. + +No source was edited and no build, test, expansion, or execution was performed, as requested. Re-review if the proposed body or signature changes, the MSRV/support policy changes, or either consumed `core` contract changes materially. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r043.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r043.md new file mode 100644 index 0000000000..d5ec3424e7 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r043.md @@ -0,0 +1,55 @@ +# Focused unsafe-Rust review and redesign: `r043` + +## Result + +**Current implementation:** `PROVED` for source-level soundness and wrapping-sum behavior on Rust 1.70, all Rust targets on which this API is available, and all ordinary profiles, relative to `TCB-R043-1` below. The source contains no `SAFETY` comments, so its proof documentation is materially incomplete even though the implementation proof can be reconstructed. + +**Safe iterator candidate:** proof-oriented and behavior-preserving by design, but it is not an implemented snapshot and therefore has no audit verdict. + +**Performance requirement:** `UNPROVED`. No benchmark result, artifact identity, configuration, measurement protocol, or uncertainty is supplied, so there is no evidence that a replacement regresses the designated benchmark by at most 2%. Consequently, replacing the loop now would silently drop a binding requirement. + +The open-ended “Rust 1.70+” claim also needs either a recorded compatibility premise preserving the consumed Rust 1.70 propositions or a release cutoff plus re-audit trigger. This report proves the exact minimum version; it does not infer earlier guarantees from later documentation or make a theorem about unknown future releases. + +## Scope and proof ledger + +The inspected snapshot is exactly `lib.rs`; the sole API surface is safe `pub fn total(&[u32]) -> u32`. There are no caller safety preconditions, dependencies, `cfg`s, generated code, allocation, callbacks, concurrency, or panic-dependent checks. The three obligation sites are `ptr.add(values.len())`, `*ptr`, and `ptr.add(1)`. + +Rust 1.70's slice-construction contract records the relevant slice validity facts: one contiguous allocation, initialized elements, a live readable region, and a byte span no greater than `isize::MAX` without address wrap ([`slice::from_raw_parts`](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html#safety)). `values.as_ptr()` exposes the buffer pointer while the shared slice remains live ([`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr)). Rust 1.70 `pointer::add` requires the start and result to remain in or 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/std/primitive.pointer.html#method.add)). + +Let `base = values.as_ptr()`, `n = values.len()`, and loop index `i` be the number of completed iterations. The invariant is: + +- `0 <= i <= n`, `ptr = base.add(i)`, and the shared slice remains live; +- `acc` is the modulo-`2^32` sum of `values[..i]`. + +At construction, the slice span proves `base.add(n)` is its one-past endpoint; for `n == 0` the offset is zero and the loop body is skipped. When `ptr != end`, the invariant and nonzero `u32` element size imply `i < n`; therefore `ptr` identifies an aligned, initialized, readable element, satisfying raw-load requirements ([raw-pointer safety](https://doc.rust-lang.org/1.70.0/std/ptr/index.html#safety)). The load cannot race with mutation through well-typed safe code while the shared `&[u32]` is live. Then `i + 1 <= n`, so `ptr.add(1)` remains in the same slice span, possibly reaching the one-past endpoint. Each iteration advances exactly one element, so termination occurs at `i == n`. `u32::wrapping_add` is modular addition independent of overflow-check and optimization settings ([Rust 1.70 `wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add)); hence the returned value is the required wrapping sum in every ordinary profile. + +The source should not retain three bare unsafe expressions. If it remains, adjacent comments must state the operation-specific facts above: slice-span/isize/no-wrap facts for `end`, `i < n` plus alignment/initialization/liveness for the load, and `i + 1 <= n` for the increment. + +## Safe iterator redesign + +The preferred candidate is: + +```rust +pub fn total(values: &[u32]) -> u32 { + values + .iter() + .copied() + .fold(0u32, |acc, value| acc.wrapping_add(value)) +} +``` + +Its contract remains a safe function with no hidden preconditions. The slice iterator supplies each element, `fold` starts at zero, and every transition explicitly uses `wrapping_add`; empty input therefore returns zero and overflow behavior cannot vary by profile. It contains no unsafe operation, raw-pointer invariant, or new dependency, and its proof is parametric over targets. Rust 1.70 supports the used APIs. The exact implemented candidate must still receive a post-change audit. + +## TCB-R043-1 + +- `AXIOM-R170-SLICE`: the cited Rust 1.70 slice pointer/validity contracts. +- `AXIOM-R170-PTR`: the cited Rust 1.70 `add` and raw-load contracts. +- `AXIOM-R170-WRAP`: the cited Rust 1.70 modular-addition contract. + +No benchmark, compiler-codegen, or future-version performance/semantic proposition is admitted. Binary performance would additionally depend on the exact compiler, target, flags, machine, and benchmark protocol. + +## Recommendation + +Do not merge the iterator replacement or declare it too slow yet. First freeze the designated benchmark's inputs, toolchain, target/CPU, profile/features, machine controls, statistic, and acceptance rule; then compare this exact baseline and exact candidate. Evidence must establish a runtime ratio no greater than `1.02` under that rule (with measurement uncertainty handled by the benchmark protocol), not merely present one favorable run. + +If it passes, adopt the iterator implementation and audit that snapshot. If it fails, retain the current algorithm but add the reconstructed proof comments, or benchmark another safe iterator form; any code-changing fallback must independently satisfy both the proof and ≤2% requirements. This preserves proof quality without inventing performance evidence. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r044.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r044.md new file mode 100644 index 0000000000..3e575fef35 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r044.md @@ -0,0 +1,43 @@ +# Focused review and redesign: `r044` + +## Claim, scope, and verdicts + +Snapshot: the supplied `lib.rs`, specifically the sole public surface `total(&[u32]) -> u32` (lines 3–13). Valid use is every well-typed safe call with any valid shared `u32` slice. Required behavior is the left-to-right sum modulo \(2^{32}\), including `0` for an empty slice, in Rust 1.70+, on all targets and in all ordinary profiles. There are no target/profile branches, dependencies, generated artifacts, callbacks, allocation, concurrency, or FFI in scope. + +- **Current implementation soundness: PROVED for Rust 1.70.0**, relative only to the Rust/std axioms below. No UB counterexample is known. +- **Current wrapping-add postcondition: PROVED for Rust 1.70.0**, for all targets/profiles. +- **Open-ended Rust 1.70+ claim: UNPROVED as stated.** Applying the 1.70 wording to every later and future release needs an accepted compatibility premise preserving the exact pointer, slice, and integer propositions, or a finite version cutoff with those contracts checked. `TCB-COMPAT-1` (“all supported post-1.70 releases preserve those exact propositions”) is identified but not silently accepted. +- **Existing proof documentation: UNPROVED/inadequate.** All three unsafe sites lack adjacent `SAFETY` proofs. The implementation proof reconstructed below does not cure that artifact defect. +- **Replacement performance requirement: UNPROVED.** No result for the designated benchmark was supplied, so the “no more than 2% regression” gate cannot presently be discharged. + +## Current implementation: obligation ledger + +Rust 1.70 [`pointer::add`](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) requires the start/result to remain in, or one-past, the same allocation and the byte offset to fit `isize`; [`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr) returns the slice-buffer pointer. A valid slice carries `len` aligned, initialized, contiguous `u32` elements and a byte extent admissible for slice pointer arithmetic (also reflected in [`slice::from_raw_parts` safety](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html#safety)). Rust 1.70 [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add) specifies modular addition. + +Let `base = values.as_ptr()` and loop index `i` be defined by `ptr = base.add(i)`. The loop invariant is `0 <= i <= len`, `ptr` has the slice allocation's provenance, and `acc` is the modulo-\(2^{32}\) sum of elements `0..i`. + +1. `base.add(len)` is permitted: the whole slice is one allocation range, the result is its one-past pointer, and the slice size bound supplies the `isize` condition. For `len == 0`, the zero offset is permitted and start equals end. +2. On loop entry, `ptr != end` plus the invariant and non-wrapping in-allocation arithmetic implies `i < len`. Thus `*ptr` reads the aligned, initialized element `values[i]`; the live shared slice permits the read and no write occurs. +3. With `i < len`, `ptr.add(1)` produces `base.add(i + 1)`, still in-range or one-past, and re-establishes the invariant. The finite increasing index terminates at `len`. +4. Explicit `wrapping_add` re-establishes the accumulator clause without a panic or profile-dependent overflow behavior. At exit `i == len`, yielding the required result. + +This derivation is target-parametric: `u32` is the same 32-bit non-ZST type, and it does not rely on debug assertions, ordinary `+`, optimizer behavior, or a target-specific address calculation. If the pointer loop is retained, these facts should be split into adjacent `SAFETY` comments for the end computation, read, and increment. + +## Safe iterator candidate + +```rust +pub fn total(values: &[u32]) -> u32 { + values + .iter() + .copied() + .fold(0u32, u32::wrapping_add) +} +``` + +The public contract and MSRV are unchanged: these APIs predate Rust 1.70. [`Iterator::fold`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.fold) repeatedly feeds the accumulated result and next item to the operation; slice iteration supplies each shared element, `copied` yields each `u32`, and `u32::wrapping_add` preserves modular behavior in every profile. Empty iteration returns the initial `0`. There is no caller safety obligation and no local unsafe invariant. This is a conditional design proof, not a verdict for a not-yet-implemented snapshot; the exact change still needs review. + +## Recommendation + +Prefer the safe iterator candidate on proof quality, but **do not merge it yet and do not claim the performance gate passes**. First benchmark the current snapshot and candidate with the already-designated workload. Pin and report the toolchain, target, profile/codegen settings, machine, benchmark revision/input, metric, sample distributions, and a predeclared decision rule capable of separating a 2% regression from noise. + +If the result demonstrates regression no greater than 2%, adopt the safe candidate and audit that exact snapshot. If inconclusive, gather enough additional samples rather than treating noise as success. If it fails, benchmark another behavior-equivalent safe loop before considering retention of the pointer loop. Should no safe form meet the gate, retain the current implementation **with the reconstructed local safety proofs added**. That sequence preserves both the proof-quality objective and the binding performance requirement instead of assuming either one away. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r045.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r045.md new file mode 100644 index 0000000000..fedc08a3f9 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r045.md @@ -0,0 +1,35 @@ +# Focused review: `total` + +## Claim, scope, and verdict + +Snapshot: the supplied `targets/r045/lib.rs`; sole public surface `pub fn total(&[u32]) -> u32`; no generated code or dependencies were supplied. Supported set is stable Rust 1.70+, every target on which this source is accepted, and every ordinary profile. Required behavior is the sum of all elements modulo `2^32` in every build mode. No source, build, test, expansion, or benchmark execution was performed. + +**Current soundness: UNSOUND for the supported set.** Rust 1.70 is supported, and a valid empty slice can reach undefined behavior in the first `ptr.add(values.len())`. **Required behavior: UNPROVED** for the current artifact because that valid input has no defined return. No separate wrong-value counterexample is established. **Performance requirement: UNPROVED:** there is no evidence that the redesign is within 2%. + +## Current-artifact proof ledger + +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 pointer [to] be either in bounds or one byte past” one allocated object, plus non-overflowing `isize` byte offset and no address-space wrap. Its contract does not exempt a zero offset. Rust 1.70 [`slice::from_raw_parts`](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html) expressly permits an aligned, non-null dangling pointer for a zero-length slice, and [`NonNull::dangling`](https://doc.rust-lang.org/1.70.0/std/ptr/struct.NonNull.html#method.dangling) supplies one. Thus a caller can validly construct an empty `&[u32]` satisfying that constructor’s unsafe contract and then call the safe `total`; `as_ptr` returns that slice-buffer pointer and `add(0)` violates `add`’s Rust 1.70 precondition. This is a valid in-scope use; no hidden precondition may be imposed on a safe function. + +For nonempty slices, the intended loop invariant is: before iteration `i`, `0 <= i < len`, `ptr` addresses initialized element `i` in the live shared slice, and `acc` is the modular sum of elements before `i`. That would discharge the read; advancing by one remains within the allocation or reaches one-past, and equality with `end` terminates after `len` reads. [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add) is documented as modular addition, so overflow checks/profile do not alter arithmetic. This reconstruction does not cure the empty case. It is also absent from the source: all three unsafe operations lack adjacent `SAFETY` proofs, independently making the proof artifact inadequate. + +The counterexample is target- and profile-parametric and occurs before arithmetic, so one failing Rust-1.70 region is enough to refute the requested union. Later documentation cannot retroactively repair that region. + +## Safe iterator candidate + +```rust +pub fn total(values: &[u32]) -> u32 { + values.iter().copied().fold(0u32, u32::wrapping_add) +} +``` + +This preserves the signature and processes exactly the slice’s copied elements via Rust 1.70’s safe [`slice::iter`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.iter), [`Iterator::copied`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.copied), and [`Iterator::fold`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.fold). The initial accumulator makes the empty result `0`; every transition uses modular `wrapping_add`, hence the result is the same sum modulo `2^32` in debug and release. There is no crate-owned unsafe surface or pointer invariant. The argument is abstract over target and ordinary profile because the source has no configuration branch and uses explicit wrapping arithmetic. + +This is a design proof plan, not a verdict on unimplemented source. For the open-ended `1.70+` range, either accept an explicit compatibility premise that later stable Rust preserves these exact stable API semantics, or audit through a named release cutoff and re-review when the supported maximum changes. Stability badges alone do not establish that behavioral premise. + +## Recommendation + +Do not ship the current implementation for Rust 1.70. Prepare the iterator candidate and run the **designated** benchmark against the frozen current baseline under its prescribed toolchain, target, profile, inputs, machine, and statistical decision rule. Accept the candidate only when the evidence establishes regression `<= 2%` (not merely a favorable point estimate), then audit the exact changed snapshot. + +If it misses the gate, do not waive either requirement: tune a safe iterator/`for`-loop form and remeasure. If none qualifies, benchmark a pointer fallback that returns immediately for `values.is_empty()` and carries complete local proofs for end construction, reads, and increments; audit that exact implementation before adoption. The present unsafe version may serve as a benchmark baseline, not as a releasable fallback. + +TCB is limited to the cited Rust 1.70 standard-library contracts plus the explicitly pending later-release compatibility premise. No benchmark, compiler-codegen, hardware, or tool result is trusted. Re-review on source change, benchmark-protocol change, support-range change, or material change to a consumed Rust contract. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r046.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r046.md new file mode 100644 index 0000000000..11cf5250a5 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r046.md @@ -0,0 +1,56 @@ +# Focused review: `total` + +## Recommendation + +Do not choose or merge either implementation yet. Benchmark the exact safe candidate below against the current function using the designated benchmark and its normal acceptance protocol. Merge the safe version only if the observed regression is no more than 2% (with whatever repetition, noise treatment, profile, target, and environment define that benchmark). If it fails, retain the pointer loop and add the proof-grade `SAFETY` comments proposed below. This preserves both requirements: the safe candidate is not declared fast without evidence, and the current unsafe code is not retained without an auditable proof. + +```rust +pub fn total(values: &[u32]) -> u32 { + values + .iter() + .fold(0u32, |acc, value| acc.wrapping_add(*value)) +} +``` + +No benchmark result, benchmark definition, compiler version, target, profile, or measurement distribution was supplied. Assembly similarity, optimizer expectation, or the absence of an obvious abstraction cost would not prove the stated benchmark threshold. + +## Scope and verdicts + +Artifact: the supplied `lib.rs`; surface: the public safe function `total`; configurations: Rust 1.70+, all targets on which it compiles, and ordinary profiles. Required behavior is the left fold of the slice from zero using addition modulo \(2^{32}\). Safe callers have no extra safety obligation. + +- Current implementation, source-level soundness: **PROVED for Rust 1.70 and for later toolchains only where the cited contracts remain applicable**. The open-ended `1.70+` compatibility claim is otherwise **UNPROVED**; no proposition guaranteeing unchanged future abstract contracts was supplied. This is not a discovered UB counterexample. +- Current implementation, wrapping behavior: **PROVED** over that same applicability domain. +- Safe candidate, source-level soundness and wrapping behavior: **PROVED** relative to the applicable safe standard-library iterator contracts. +- Safe candidate, designated-benchmark regression <=2%: **UNPROVED**. This missing result is the smallest unresolved proposition preventing unconditional adoption. +- Existing proof documentation: **DEFICIENT**. All three unsafe operations lack adjacent `SAFETY` proofs. This is proof-quality debt, separately from implementation soundness. + +TCB is limited to the applicable Rust abstract semantics and the cited Rust 1.70 standard-library contracts; no binary/backend-performance claim is trusted. No dependencies, FFI, generated code, concurrency, or target-specific source were present. + +## Current pointer-loop derivation + +The controlling [`pointer::add` contract](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) requires the mathematical byte offset to fit `isize` and the traversed range to remain within one allocation (allowing one-past). [`slice::as_ptr`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.as_ptr) supplies the pointer to the borrowed slice buffer. Let `base` be that pointer and maintain the loop invariant `ptr = base.add(i)` with `0 <= i <= values.len()`. + +For `end = base.add(len)`, the slice's `len` initialized `u32` elements are one contiguous allocation range; Rust allocations cannot exceed `isize::MAX` bytes, so the offset fits and `end` is the permitted one-past pointer. Initially `i = 0`. Because `u32` is non-zero-sized, while `ptr != end` the invariant entails `i < len`; `ptr` therefore addresses the initialized, aligned element `values[i]`, so `*ptr` is a valid shared read. Adding one then produces `base.add(i + 1)`, still within or one-past the same allocation, and preserves the invariant. At `i = len`, equality ends the loop without dereferencing `end`. The empty-slice case exits immediately. The shared slice remains borrowed for the whole call, and no call or unwind occurs inside the unsafe sequence. + +Each iteration applies [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add), documented as modular addition wrapping at the type boundary. Consequently overflow checks, optimization, and debug assertions do not change the result. + +If this implementation remains, place equivalent comments immediately before the operations: + +```rust +// SAFETY: `values` is one contiguous initialized `u32` slice. Its byte +// extent fits `isize`; adding `len` therefore yields its one-past pointer. +let end = unsafe { ptr.add(values.len()) }; + +// SAFETY: Loop invariant: `ptr == base.add(i)` for `0 <= i <= len`. +// This branch implies `i < len` (u32 is non-ZST), so `ptr` addresses the +// aligned, initialized shared element `values[i]`. +acc = acc.wrapping_add(unsafe { *ptr }); + +// SAFETY: From the invariant and branch, `i < len`; adding one stays within +// the slice or reaches one-past and establishes the invariant for `i + 1`. +ptr = unsafe { ptr.add(1) }; +``` + +## Safe-candidate equivalence + +Rust 1.70 documents [`slice::iter`](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.iter) as yielding all items from start to end, and [`Iterator::fold`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.fold) as starting with the supplied accumulator and applying the closure to each element. Thus the candidate performs the same recurrence `a_0 = 0`, `a_{i+1} = a_i.wrapping_add(values[i])`, including result `0` for an empty slice. It removes every local raw-pointer obligation and leaves standard-library iterator implementation soundness behind safe contracts. That is a material proof-quality improvement, but it does not entail the separate performance postcondition. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r047.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r047.md new file mode 100644 index 0000000000..025c9caaa2 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r047.md @@ -0,0 +1,45 @@ +# Focused review: `total` + +## Claim, scope, and verdicts + +Scope is exactly the supplied `lib.rs`: the public safe function `total`, Rust 1.70+, all targets, and ordinary profiles. There is no `cfg`, generated code, dependency, callback, mutation, allocation, or concurrency surface in the supplied artifact. Required behavior is the left-to-right sum modulo 2^32. + +- **Current implementation soundness: UNPROVED**, not `UNSOUND`. No valid-use UB counterexample is established, but one necessary Rust-1.70 pointer-arithmetic implication is absent from the source proof and was not established by the supplied evidence. +- **Current behavioral postcondition: PROVED conditional on the missing pointer obligation.** On each completed iteration, `acc` is the modular sum of the traversed prefix; `wrapping_add` is explicit, so overflow-check and optimization profile do not alter arithmetic behavior. +- **Safe redesign source soundness and behavior: PROVED for Rust 1.70 relative to the cited safe standard-library contracts.** The open-ended “1.70+” claim additionally needs either version-by-version applicable contracts or an accepted compatibility premise/re-audit policy. +- **Performance requirement: UNPROVED.** No benchmark result is supplied, so neither acceptance nor rejection of the redesign is evidence-supported. + +## Surface and obligation ledger + +`total(&[u32]) -> u32` is the sole safe API surface; callers have no safety obligation beyond passing a valid shared slice. The current implementation has three unsafe sites. + +1. `ptr.add(values.len())`: Rust 1.70 requires start and result to be in-bounds or one-past the same allocated object, the byte offset to fit `isize`, and the infinite-precision address sum to fit `usize`. The documentation warns that directly acquired or memory-mapped regions can exceed `isize::MAX`; its `Vec`/`Box` example therefore cannot prove this arbitrary-slice case ([Rust 1.70 `pointer::add`](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add)). Local contiguity supplies the same-object/range argument only if the exact valid-slice contract also entails `values.len() * size_of::() <= isize::MAX` without address wrap. That slice-wide implication is the smallest unresolved premise in this review. +2. `*ptr`: required at every nonterminal state to be aligned, initialized, readable as `u32`, and within the live slice. With loop invariant `ptr == base.add(i)`, `0 <= i < len`, those facts follow from the shared slice’s element range and absence of mutation/intervening calls. +3. `ptr.add(1)`: with the same invariant and `i < len`, both pointers remain within the slice allocation or the result is exactly one-past. The transition establishes `i := i + 1`; hence equality with `end` occurs after exactly `len` iterations. The empty case performs no dereference or unit increment, though it still executes `add(0)`. + +The implementation contains no adjacent `SAFETY` proof for any site. Even if the missing slice premise is later established, this is a proof-documentation defect. A replacement comment would need to state the indexed loop invariant, cite the exact slice-span premise, discharge all three `add` clauses, prove dereference alignment/initialization/readability, and cover empty/terminal states. + +## Safe redesign + +Use this candidate for measurement: + +```rust +pub fn total(values: &[u32]) -> u32 { + values + .iter() + .fold(0u32, |acc, &value| acc.wrapping_add(value)) +} +``` + +`slice::iter` exposes the slice elements through a safe iterator, `fold` applies the accumulator closure across them, and `u32::wrapping_add` computes modular addition ([Rust 1.70 slice iteration](https://doc.rust-lang.org/1.70.0/std/primitive.slice.html#method.iter), [Rust 1.70 `Iterator::fold`](https://doc.rust-lang.org/1.70.0/std/iter/trait.Iterator.html#method.fold), [Rust 1.70 `u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add)). Thus every element contributes once, empty input returns zero, and overflow behavior is identical in every ordinary profile. Do **not** replace this with `.sum::()`: that would not explicitly preserve wrapping behavior across overflow-check modes. + +The argument is target-parametric: it uses only `u32` modular arithmetic and safe slice iteration, with no target-selected source branch. It removes all local unsafe operations and the associated provenance, bounds, alignment, initialization, and pointer-progress proof surface. + +## Evidence-sensitive next action + +Do not merge or reject the redesign yet. Benchmark the exact candidate above against the exact current snapshot using the designated benchmark, identical toolchain/target/CPU/profile/flags and inputs, and a predeclared metric and noise/statistical rule capable of deciding the 2% threshold. Record artifact identities and raw results; a clean functional test or generated-code inspection does not establish the performance proposition. + +- If the measured regression is at most 2%, adopt the safe redesign and retain explicit `wrapping_add`. +- If it exceeds 2%, first measure equivalent safe forms (notably a `for &value in values` loop) and investigate optimization/code-generation differences. If unsafe code remains necessary, keep it only after establishing the missing slice-span premise for the full Rust/target range and adding the local proof described above. + +TCB for the source claims is limited to the exact cited Rust 1.70 standard-library contracts. Compiler/backend, benchmark harness, machine state, and statistical procedure belong only to the later performance claim. Re-review is triggered by changing the implementation, arithmetic operation, supported Rust range, target policy, or benchmark definition/environment. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r048.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r048.md new file mode 100644 index 0000000000..1495c20500 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r048.md @@ -0,0 +1,47 @@ +# Focused unsafe-Rust review and redesign + +## Claim and dispositions + +Scope is exactly `lib.rs`: the public safe function `total(&[u32]) -> u32`, its three unsafe operations, and the proposed replacement below. The supplied configuration predicate is Rust 1.70+, every Rust target, and ordinary profiles. There are no conditional branches, dependencies, generated artifacts, or unsafe public APIs in the supplied source. + +- **Current implementation soundness: PROVED** for Rust 1.70.0, and parametrically for any later Rust release in which the cited slice/raw-pointer contracts retain the stated propositions, on every target/profile. The literal unbounded future portion of “1.70+” is **UNPROVED** unless the project accepts `COMPAT-1` below or sets a release cutoff and re-reviews later releases. +- **Wrapping-result postcondition: PROVED** over the same domain: the return value is the elements’ sum modulo `2^32`, including `0` for an empty slice. +- **Existing proof documentation: UNPROVED/inadequate.** None of the three unsafe operations has a `SAFETY` proof; the material derivation below had to be reconstructed. +- **Safe redesign soundness and wrapping behavior: PROVED as a design** under the documented safe iterator and `wrapping_add` contracts. No caller safety precondition is introduced. +- **Safe-redesign performance requirement: UNPROVED.** No result for the designated benchmark, and no benchmark artifact/configuration or decision statistic, was supplied. Source shape or an expectation of bounds-check elimination cannot prove a ≤2% regression. + +## Reconstructed proof of the current loop + +The Rust 1.70 slice contract says `as_ptr` returns a pointer to the slice buffer 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 `add` contract requires the byte offset to fit in `isize` and the traversed range to remain within one allocation, allowing the one-past-end result ([`pointer::add`](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add)). Loading through a dangling or misaligned pointer is UB ([Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). + +For a valid `values: &[u32]`, its `len` initialized, aligned elements remain live for the call, and its total byte extent fits the slice/reference size bound. Let `base = values.as_ptr()`. Thus `base.add(len)` is within the buffer through its one-past-end boundary; for `len == 0`, `add(0)` does not move the possibly dangling empty-slice pointer and `ptr == end`, so no load occurs. + +For the nonempty case, use loop invariant `ptr = base.add(i)`, `0 <= i <= len`, and `acc` is the modular sum of `values[..i]`. Initially `i = 0`. Because `u32` is non-zero-sized and in-bounds `add` does not wrap the address space, `ptr != end` implies `i < len`. Therefore `ptr` addresses initialized, aligned element `i`; the live shared slice permits the read and the function performs no mutation or intervening call. `wrapping_add` establishes the next prefix sum. Then `ptr.add(1)` remains within the same slice allocation, possibly producing the one-past-end pointer, and establishes the invariant for `i + 1`. At `i == len`, pointer equality terminates the loop and the invariant gives the result. [`u32::wrapping_add`](https://doc.rust-lang.org/1.70.0/std/primitive.u32.html#method.wrapping_add) specifies modular addition, so overflow checks, optimization level, and debug assertions do not alter behavior. The argument is target-parametric and contains no profile-dependent check. + +If the raw loop is retained, each unsafe site needs adjacent proof text expressing respectively: (1) the slice byte range makes `base.add(len)` a permitted one-past-end pointer; (2) the loop invariant plus `ptr != end` makes the load aligned, initialized, live, and in bounds; and (3) the current element index is below `len`, so advancing one remains in range or reaches one-past-end. + +## Safe iterator redesign + +```rust +pub fn total(values: &[u32]) -> u32 { + values + .iter() + .fold(0u32, |acc, &value| acc.wrapping_add(value)) +} +``` + +This is safe code supported by Rust 1.70. It visits the slice values and applies the same explicit modular addition, so empty and overflow cases retain the required behavior in every ordinary profile. It removes the raw-pointer invariant and all three local unsafe proof sites. This is a proof-quality improvement, not performance evidence. + +## Evidence-sensitive next action + +Do **not** merge either a redesign or a performance-motivated rejection yet. Benchmark the exact candidate above against the exact current snapshot using the designated harness. Before running it, freeze the toolchain, target/CPU, profile and codegen flags, benchmark input distribution/sizes, machine controls, metric, sample/warm-up policy, and the rule for “no more than 2%” (preferably an uncertainty bound, not an isolated point estimate). The missing proposition is: + +> Under that frozen benchmark protocol, `time(candidate) / time(current) <= 1.02` according to the agreed statistic. + +If established, adopt the safe iterator implementation. If the result is above 2%, retain the current implementation only with the reconstructed local proofs added, and investigate another safe formulation or a benchmark-demonstrated necessity for unsafe code. If uncertainty crosses 2%, gather enough evidence to resolve it. Assembly inspection may explain a result but does not substitute for the required benchmark. + +## TCB, residual scope, and triggers + +- `AXIOM-1..3`: the exact Rust 1.70 slice, pointer, Reference, iterator, and modular-addition contracts linked above. No tool result is consumed. +- `COMPAT-1` (pending if support is genuinely open-ended): later supported stable Rust releases preserve the exact consumed propositions. Without its acceptance, cap this result at verified documentation versions and re-review on each support-range extension. +- Binary-level compiler/backend correctness, non-ordinary/custom toolchains, and performance outside the designated benchmark are excluded. Re-review on source change, support/configuration expansion, a material cited-contract change, or a benchmark protocol/toolchain/target change. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r049.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r049.md new file mode 100644 index 0000000000..7eea550208 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r049.md @@ -0,0 +1,69 @@ +# `r049` implemented-candidate audit + +## Claim and verdict + +**UNSOUND** for the exact `lib.rs` supplied with this request, as a safe library abstraction quantified over all well-typed safe uses, stable Rust 1.70+, every target on which it compiles, and ordinary profiles. A valid safe use reaches undefined behavior. The counterexample is verified from the source and Rust 1.97.1 semantics; because 1.97.1 is a member of the requested `1.70+` set, it refutes the aggregate universal claim. No verdict from `DESIGN-NOTE.md` is inherited. + +There is no separate `CONTRACT-BROKEN` result: no non-type-level postcondition is documented. The Rust reference guarantees embodied by returned `&T`/`&mut T` values are part of the soundness failure. + +## Snapshot, boundary, and configuration + +Scope is only the supplied `lib.rs`: `View<'a, T>` with private `ptr` and `borrow` fields, and public safe `new`, `get`, and `get_mut`. There are no public fields, unsafe public APIs, custom trait impls, callbacks, dependencies, `cfg`s, macros/generated code, FFI, assembly, allocation, or panic paths. Compiler-derived move/drop behavior does not dereference `ptr`. + +The relevant invariant intended by `new` is: + +> **VIEW:** `ptr` is the pointer derived from the constructor's `&'a mut T`; while a `View` can be used, the pointee remains live, aligned, initialized as `T`, and exclusively accessible through capabilities correctly reborrowed from that unique borrow. + +`new` establishes the pointer/lifetime portion and private fields prevent safe forgery. It does **not** make references returned for all of `'a` borrow `self` for all of `'a`. + +The proof is parametric over target and ordinary profile: the source has no configuration branch, and the witness uses only `i32` and reference aliasing. For version closure, Rust 1.97.1 is the audited supported member needed to refute the universal set; future releases require re-review rather than a compatibility assumption. + +## Finding: accessors release the receiver borrow too early + +At `get_mut`, the elided receiver lifetime is independent of the explicit return lifetime `'a`: + +```rust +pub fn get_mut<'s>(&'s mut self) -> &'a mut T +``` + +Consequently, the borrow of `self` may end after the call while the returned reference remains live. This all-safe client can obtain two live unique references: + +```rust +let mut value = 0i32; +let mut view = View::new(&mut value); +let first = view.get_mut(); +let second = view.get_mut(); +*first = 1; +*second = 2; +``` + +Both references are live across the second call because each is subsequently dereferenced. The Rust Reference classifies “breaking the pointer aliasing rules” as UB and states that an `&mut T` permits no other reference to point to the same memory while live ([Rust 1.97.1 Reference, undefined behavior/aliasing](https://doc.rust-lang.org/1.97.1/reference/behavior-considered-undefined.html#undefinedalias)). Thus the second `&mut T` cannot be formed while `first` is live. `get` has the analogous compositional defect: a returned `&'a T` can remain live while `get_mut` creates and writes through `&mut T`. + +Obligation disposition: + +- **NEW / VIEW producer:** pointer identity, validity, and lifetime are established from `&'a mut T`; **PROVED** locally for the limited producer proposition. +- **GET / shared-reference consumer:** must prevent incompatible mutation for the returned reference's live interval; **UNSOUND**, because that interval can outlast `&self`. +- **GET-MUT / unique-reference consumer:** must exclude every other live reference for the returned interval; **UNSOUND**, witnessed above. +- **Proof artifact:** both unsafe blocks have no adjacent `SAFETY` derivation or named invariant; **UNPROVED documentation quality**, independently of the implementation defect. + +## TCB and evidence + +- **AXIOM-ALIAS-1.97.1:** the cited versioned Reference rule for live shared/unique references. Consumer: `GET`, `GET-MUT`, and the counterexample. +- **AXIOM-ELISION-1.97.1:** for a method receiver, its lifetime is assigned to elided output lifetimes ([Rust 1.97.1 Reference, lifetime elision](https://doc.rust-lang.org/1.97.1/reference/lifetime-elision.html#lifetime-elision-in-functions)). Consumer: repair plan below. + +No dependency, tool, external-specification, compiler-backend, or deployment assumption is consumed. No build, test, interpreter, or expansion result is offered as evidence. + +## Unimplemented repair proposal + +At minimum, bind each output to its receiver borrow: + +```rust +pub fn get(&self) -> &T { /* proved raw-pointer reborrow */ } +pub fn get_mut(&mut self) -> &mut T { /* proved raw-pointer reborrow */ } +``` + +By the cited elision rule these are respectively `fn<'s>(&'s self) -> &'s T` and `fn<'s>(&'s mut self) -> &'s mut T`; safe borrowing then prevents an incompatible method call while the result is live. If the raw representation remains, define `VIEW` beside it and add local `SAFETY` proofs covering provenance, liveness, alignment, validity, and aliasing. + +Preferably, remove the unsafe operations entirely by storing `value: &'a mut T` and returning `&*self.value` / `&mut *self.value` with receiver-tied output lifetimes. That candidate must be checked for intended auto-trait/API consequences. Either repair restricts callers that relied on a result escaping the receiver borrow; preserving an `'a` return instead requires consuming the `View` (for example, `into_mut(self) -> &'a mut T`). + +These are design candidates, not a verdict. The implemented replacement, its safety comments if any, auto traits, and all supported versions/configurations require a fresh audit. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r050.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r050.md new file mode 100644 index 0000000000..830d4824e9 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r050.md @@ -0,0 +1,109 @@ +# r050 implemented-candidate audit + +## Claim and verdict + +**Soundness: UNSOUND.** This verdict covers the exact `lib.rs` supplied in +`targets/r050`: every well-typed safe use of its public API, on the requested +Rust 1.70+ range, all targets, and ordinary profiles. A well-typed safe client +can cause undefined behavior on Rust 1.70, which is enough to refute that +universal claim. The counterexample is source-semantic and has no target, +optimization, panic, or profile dependency. + +This is a fresh verdict; the prior design note supplies no premise. There are +no dependencies beyond `core`, generated artifacts, macros, `cfg` branches, +FFI, assembly, allocation, or concurrency in scope. No code was executed and no +tool result is evidence. There are no unsafe public contracts or documented +postconditions to grade separately. + +## Boundary and obligation inventory + +The safe public surface is `View<'a, T>`, construction/move/drop, and `new`, +`get`, and `get_mut`. Both representation fields are private, and there are no +explicit trait implementations. The intended representation invariant is that +`ptr` denotes the `T` supplied to `new` for `'a`, while +`PhantomData<&'a mut T>` carries the unique-borrow relationship. `new` is the +only field producer. The unsafe reference creations in `get` and `get_mut` are +the invariant consumers. + +Privacy and `PhantomData` prevent safe field forgery and express the outer +borrow duration, but they do not constrain the lifetimes returned by the two +methods. Their signatures desugar independently as: + +```rust +fn get<'s>(&'s self) -> &'a T +fn get_mut<'s>(&'s mut self) -> &'a mut T +``` + +Thus the receiver borrow can end after each call while the returned reference +continues for `'a`. + +## Finding F-1: detached return lifetimes permit conflicting references + +At `get`, `&*self.ptr` creates a shared reference lasting for the provider-chosen +`'a`. Safe code may then reborrow the `View` mutably and `get_mut` creates a +mutable reference to the same `T`, also lasting for `'a`: + +```rust +let mut value = 0u8; +let mut view = View::new(&mut value); +let shared = view.get(); +*view.get_mut() = 1; +let observed = *shared; +``` + +No `unsafe` operation or undocumented premise appears in this client. Because +`shared` is used after the write, it is live across that write. Rust 1.70's +[borrow-operator contract](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#borrow-operators) +states, for a shared borrow, that “the place may not be mutated” for the +reference's duration. The [undefined-behavior list](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html) +includes “Breaking the pointer aliasing rules.” The write therefore violates +the controlling contract and is undefined behavior. Repeated calls to +`get_mut` can analogously produce two independently live mutable references. + +The unsafe blocks also have no adjacent `SAFETY` proofs. That documentation +defect is secondary here: the required derivation is false, so a comment cannot +repair this snapshot. + +## Proposed remediation (not a verdict on this snapshot) + +Tie each result to the receiver reborrow: + +```rust +pub fn get<'s>(&'s self) -> &'s T { + // SAFETY: `ptr` denotes the live, aligned, initialized `T` established by + // `new`; the private View invariant preserves that fact. The shared result + // is bounded by this borrow of `self`. + unsafe { &*self.ptr } +} + +pub fn get_mut<'s>(&'s mut self) -> &'s mut T { + // SAFETY: the same invariant supplies pointer validity. Exclusive access + // to `self`, and bounding the result by that access, prevents another safe + // View access while the returned mutable reference is live. + unsafe { &mut *self.ptr } +} +``` + +The usual elided forms `fn get(&self) -> &T` and +`fn get_mut(&mut self) -> &mut T` express the same relationship. Keep both +fields private and document the invariant at `View`. The post-change proof must +check `new`, moves/drop, both consumers, and compiler-supplied auto traits for +the exact new source. + +This change weakens the current type-level provider guarantee from `'a` to the +receiver-borrow lifetime, so code that retains a result while reusing `View` +may stop compiling. Retaining that behavior together with safe mutable access +is precisely the unsound capability; the alternative is to remove or make the +conflicting operation unsafe with a complete caller contract. The proposal is +**unimplemented and unaudited**, not `PROVED`; re-audit its implemented snapshot. + +## TCB, residual scope, and triggers + +TCB `r050-source-v1` contains only the two Rust 1.70 Reference propositions +linked above; there are no admitted dependency, implementation, platform, or +tool assumptions. Current documentation for later releases is unnecessary to +the aggregate refutation because Rust 1.70 alone supplies an in-domain +counterexample. Whole-crate behavior and undocumented robustness properties +are outside this focused file review. Re-audit after any signature, +representation, visibility, unsafe-block, trait, supported-version, or +configuration change. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r051.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r051.md new file mode 100644 index 0000000000..1d48daf608 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r051.md @@ -0,0 +1,58 @@ +# `r051` implemented-candidate audit + +## Claim and verdict + +**UNSOUND** for the exact 23-line `lib.rs` snapshot, considered as a library API used only by well-typed safe code, with Rust 1.70+, every target on which it compiles, and ordinary profiles. The universally quantified Rust-1.70+ claim is already refuted on Rust 1.70; the same counterexample is independent of target layout and profile. No verdict is inherited from `DESIGN-NOTE.md`. + +There is no separate `CONTRACT-BROKEN` finding: no unsafe API postcondition or broader behavioral guarantee is documented. The soundness failure is decisive. + +## Boundary and obligation coverage + +The complete safe surface is `View<'a, T>`, `View::new(&'a mut T)`, `get(&self) -> &'a T`, and `get_mut(&mut self) -> &'a mut T`. Both fields are private; there are no public fields, unsafe APIs/traits/impls, macros, generated artifacts, dependencies, FFI, allocation, or conditional compilation. The two obligation sites are the raw-pointer dereferences in `get` and `get_mut`. + +The intended invariant is: `ptr` addresses the same aligned, initialized, live `T` borrowed uniquely by `new`, for `'a`, and safe operations preserve exclusive-access rules. `new` can establish the pointer/liveness portion and privacy prevents safe retargeting. `PhantomData<&'a mut T>` associates the borrow with the representation, but it does not tie references returned by the methods to the receiver borrow. Thus the accessors do not preserve the exclusivity portion. + +Material counterexample (all operations shown are safe): + +```rust +let mut x = 0u8; +let mut view = View::new(&mut x); +let shared = view.get(); +let unique = view.get_mut(); +*unique = 1; +assert_eq!(*shared, 1); +``` + +`shared` has the impl lifetime `'a`; it is not tied to the temporary borrow of `view` used for `get`. That receiver borrow can therefore end after the call, permitting `get_mut` while `shared` remains live (its later use makes this non-vacuous). Both methods return references to the unchanged `ptr`, so `*unique = 1` mutates the bytes reached through the live shared reference. The Rust 1.70 Reference lists mutation of such data as undefined behavior: “all data reached through a shared reference ... is immutable” absent `UnsafeCell` ([Rust 1.70 Reference](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). It also defines unsafe code as unsound when safe code can trigger UB. This supplies a concrete valid safe execution, not merely a missing proof. + +The existing unsafe blocks have no adjacent `SAFETY` proof. Documentation is therefore independently deficient, but prose cannot repair the false method contracts. + +## Configuration closure and TCB + +There are no source configuration branches. Choosing `T = u8` removes layout, alignment-special-case, and zero-sized-type concerns; the counterexample uses no target facility and no profile-dependent check. Consequently every target/profile branch shares the defect. One type is enough to refute a generic safe API. + +TCB/axioms consumed: only the exact Rust 1.70 Reference propositions above and ordinary correctness of the documented Rust abstract semantics. No dependency, implementation, external, deployment, or tool premise is consumed; no code was built or executed. The open-ended range needs no future-compatibility assumption to refute the aggregate claim because its included 1.70 case is false. + +## Proposed redesign (not implemented; no verdict) + +Prefer eliminating the raw pointer: + +```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 } + // Optional ownership transfer when a long `'a` borrow is required: + pub fn into_mut(self) -> &'a mut T { self.value } +} +``` + +Elision binds `get`/`get_mut` results to their receiver borrow, so safe borrow checking prevents the overlap above; storing the reference makes lifetime, validity, and ownership enforcement type-driven and removes both unsafe blocks. If the raw-pointer representation is mandatory, at minimum use these receiver-bound result lifetimes and add a named representation invariant plus an adjacent proof at each dereference. + +This necessarily rejects callers that relied on a repeatable accessor returning `'a` independently of the receiver. That behavior cannot remain on both safe accessors: it is the capability that creates the defect. A consuming method can preserve the legitimate ownership-transfer use case. Auto-trait and observable representation consequences should be checked as compatibility matters, and the implemented replacement must receive a fresh audit across the same Rust/target/profile domain. + +Residual scope is exactly the supplied `REQUEST.md`, `DESIGN-NOTE.md`, and `lib.rs`; no enclosing crate or build metadata was available or inspected. Re-audit on any source/API change, added trait impl or macro/configuration branch, changed support range, or implemented redesign. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r052.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r052.md new file mode 100644 index 0000000000..f245846b5c --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r052.md @@ -0,0 +1,64 @@ +# r052 implemented-candidate audit + +## Claim and verdict + +**UNSOUND** — For the exact `targets/r052/lib.rs` snapshot, the requested theorem (“every well-typed safe use is free of Rust undefined behavior on Rust 1.70+, all targets and ordinary profiles”) is false. A safe counterexample exists in the included Rust 1.70 domain. This is a fresh source result; no premise or verdict is inherited from `DESIGN-NOTE.md`. + +No separate `CONTRACT-BROKEN` result is established: there are no prose postconditions, and the demonstrated failure is already undefined behavior. + +## Snapshot, boundary, and coverage + +The complete exposed surface is the public `View<'a, T>` type with private `ptr` and `borrow` fields and safe methods `new`, `get`, and `get_mut`. The only unsafe operations are the raw-pointer dereferences/reborrows in `get` (line 16) and `get_mut` (line 20). There are no unsafe caller contracts, traits or impls, dependencies beyond `core`, conditional compilation, generated code, FFI, allocation, concurrency, or target-specific operations in the supplied source. + +The intended representation invariant is: `ptr` designates the same live `T` originally uniquely borrowed by `new`, while the view and any references derived from it obey the appropriate aliasing restrictions. `new` establishes the pointer/marker origin and private fields prevent safe clients from replacing them, but the method signatures do not preserve the temporal aliasing part of that invariant. + +## Finding F1: returned lifetimes permit incompatible live references + +`get(&self) -> &'a T` and `get_mut(&mut self) -> &'a mut T` return the struct lifetime `'a`, not a lifetime bounded by their receiver borrow. Thus the receiver borrow ends after each call while the returned reference remains usable. This safe client follows directly from those signatures: + +```rust +fn collide(shared: &u8, unique: &mut u8) -> u8 { + *unique = 1; + *shared +} + +fn trigger() { + let mut byte = 0u8; + let mut view = View::new(&mut byte); + let shared = view.get(); + let unique = view.get_mut(); + let _ = collide(shared, unique); +} +``` + +Every client operation is safe. Both methods form their result from the identical stored pointer, so both arguments designate `byte`. The Rust 1.70 Reference makes “all data reached through a shared reference … immutable” unless it is within `UnsafeCell`, and says a reference passed to a function is “live at least as long as that function call.” During `collide`, `shared` is therefore live while `*unique = 1` mutates the same plain `u8`; this is undefined behavior. The same page defines unsafe code that safe code can use to exhibit undefined behavior as unsound. [Rust 1.70 Reference: behavior considered undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#behavior-considered-undefined). + +### Obligation ledger + +| ID | Site | Required proposition | Status | +|---|---|---|---| +| O1 | `new`, fields | Preserve the borrowed allocation/value and control all safe access through the private representation. | Origin/privacy established locally, but insufficient to enforce the full invariant. | +| O2 | `get` | A created shared reference must have no incompatible mutation throughout its live interval. | **Refuted in composition with O3.** | +| O3 | `get_mut` | A created mutable reference must have exclusive compatible access throughout its live interval. | **UNSOUND**: the receiver borrow does not cover the returned `'a` reference. | +| O4 | configuration closure | Cover every Rust 1.70+ target/profile combination. | The universal claim is refuted by the included Rust 1.70 case; later-version projection is unnecessary. The defect uses no configuration axis. | + +The two unsafe blocks also have no adjacent `SAFETY` proof. That is a proof-documentation defect distinct from—and incapable of repairing—the implementation defect. + +## Remediation (not the snapshot verdict) + +Prefer storing `value: &'a mut T` directly and implement safe reborrows; this removes both unsafe blocks and makes the invariant type-enforced. If the raw pointer is required, at minimum change **both** signatures to receiver-bounded lifetimes: + +```rust +pub fn get<'s>(&'s self) -> &'s T +pub fn get_mut<'s>(&'s mut self) -> &'s mut T +``` + +(The ordinary elided forms `-> &T` and `-> &mut T` are equivalent.) Changing only one method is insufficient: the present `get` can coexist with a later mutable call, and the present `get_mut` can be called repeatedly to obtain overlapping mutable references. + +For a retained raw-pointer design, define an adjacent `VIEW` invariant: `ptr` is derived from `new`’s initialized, aligned, live `&'a mut T`; private fields preserve its identity; every dereference occurs while the source borrow is represented by the view; and each returned reference is bounded by the corresponding receiver borrow. Each unsafe block must then derive validity and shared/exclusive access from that invariant and explain how its receiver-bounded result preserves it. + +**Proposed remediation status: UNPROVED (unimplemented).** It does not alter the snapshot’s `UNSOUND` verdict; audit the exact revised source and its proof comments. + +## TCB, evidence, and residual scope + +`TCB-R052-1` contains only `AXIOM-R170-UB`, the cited Rust 1.70 Reference propositions. No dependency, tool, platform, test, or prior-review premise is consumed. No code was built, run, expanded, or tested; the counterexample and aliasing result are a source derivation. Because Rust 1.70 alone refutes the open-ended universal theorem, this report makes no backwards/forwards compatibility assumption about later documentation. Re-audit is required after either method signature, the representation/privacy boundary, supported Rust range, or authoritative aliasing contract changes. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r053.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r053.md new file mode 100644 index 0000000000..73bc1ade6a --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r053.md @@ -0,0 +1,53 @@ +# Focused unsafe-Rust audit: `r053` + +## Claim and verdict + +**UNSOUND** for the requested theorem: the supplied `lib.rs` does not preserve freedom from Rust undefined behavior for every well-typed safe use on Rust 1.70+, all targets, and ordinary profiles. A counterexample exists on Rust 1.70, so the universal version/configuration claim is false. The argument is source-level and target/profile-independent; it does not assume a particular backend. + +Snapshot: the supplied `targets/r053/lib.rs` (22 lines), reviewed from scratch. `DESIGN-NOTE.md` is historical context only; no prior verdict or proof was inherited. There are no dependencies, generated artifacts, `cfg`s, features, FFI, allocation, concurrency, or documented behavioral postconditions beyond the safe type signatures. TCB: the Rust 1.70 Reference axioms cited below; no tool result or implementation assumption. + +## Boundary, invariant, and obligations + +The complete public safe surface is `View<'a, T>` with private fields, safe constructor `new`, safe methods `get` and `get_mut`, plus ordinary language move/drop and compiler-provided auto-trait behavior. The latter are not needed to reach the defect. + +The representation intends this invariant: while a usable `View<'a, T>` exists, `ptr` identifies the aligned, initialized, live `T` originally uniquely borrowed for `'a`; every reference issued from `ptr` must also obey aliasing for its entire liveness. `new` establishes the pointer/lifetime portion from `&'a mut T`, and the private fields prevent safe callers from replacing `ptr`. `PhantomData<&'a mut T>` records the original loan, but it does not connect a later method receiver borrow to a return type explicitly declared with `'a`. + +Obligation ledger: + +| Site | Required proposition | Status | +|---|---|---| +| `new`, lines 11–13 | Establish pointer/lifetime representation invariant for every safe input | Sufficient for the initial state | +| `get`, lines 15–17 | `&*ptr` is valid and no mutation conflicts throughout returned reference liveness | **UNSOUND in composition**: result escapes for `'a` while the receiver borrow can end | +| `get_mut`, lines 19–21 | `&mut *ptr` has exclusive access throughout returned reference liveness | **UNSOUND**: a prior returned reference does not keep `self` borrowed | + +Both unsafe blocks also lack adjacent `SAFETY` proofs. That proof-documentation defect is separate from—and cannot repair—the implementation defect. + +## Safe UB counterexample and derivation + +```rust +let mut value = 0u8; +let mut view = View::new(&mut value); +let first = view.get_mut(); +let second = view.get_mut(); +*first = 1; +*second = 2; +``` + +This uses no `unsafe`. In `get_mut`, the receiver lifetime is the fresh lifetime of each `&mut self` call, but the result is explicitly `&'a mut T`; the signature does not tie the result to that receiver borrow. Consequently the first call's borrow of `view` may end while `first` remains live, allowing the second call and producing two simultaneously usable mutable references to the same `u8`. + +Rust 1.70 specifies that for a mutable borrow, “the place may not be accessed in any way until the borrow expires” ([borrow operators](https://doc.rust-lang.org/1.70.0/reference/expressions/operator-expr.html#borrow-operators)), and classifies “Breaking the pointer aliasing rules” as undefined behavior ([behavior considered undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html)). `first` is demonstrably live across creation/use of `second` because it is used afterward. The two writes therefore furnish an in-scope safe execution that violates the controlling rule. `get()` similarly permits a long-lived shared reference to coexist with a later `get_mut()` result. + +The counterexample uses a non-ZST `u8`, no target property, conditional source, panic behavior, or profile-sensitive check. Thus it refutes every ordinary target/profile class on Rust 1.70 where this library and safe client are compiled. That one included release is sufficient to refute the requested open-ended `1.70+` theorem; no backwards-compatibility premise about later releases is needed. + +## Proposed remediation (not the snapshot verdict) + +Tie results to the receiver borrow: + +```rust +pub fn get<'s>(&'s self) -> &'s T +pub fn get_mut<'s>(&'s mut self) -> &'s mut T +``` + +Then an issued shared or mutable reference keeps `View` correspondingly borrowed, preventing an incompatible later method call. Preferably, store `&'a mut T` directly and implement these methods without raw pointers or unsafe code. If the raw representation is retained, add adjacent proofs covering pointer validity/alignment/initialization for `'a`, receiver-enforced aliasing for `'s`, and the post-reference invariant. These are **unimplemented proposals**, not `PROVED` implementations; the changed snapshot requires a fresh audit. + +Review triggers: any implementation/signature/visibility/trait change, added configuration or generated code, or a material change to the cited Rust contracts. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r054.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r054.md new file mode 100644 index 0000000000..a004414052 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/reports/r054.md @@ -0,0 +1,64 @@ +# r054 implemented-candidate audit + +## Claim and verdict + +**UNSOUND.** This is a fresh review of the exact `lib.rs` snapshot in target `r054`; the prior design note supplies no premise. The requested theorem—freedom from Rust undefined behavior for every well-typed safe use on Rust 1.70+, every target, and every ordinary profile—is false. A safe counterexample exists on Rust 1.70 for every target/profile on which this source and `u8` are available. That one member refutes the open-ended `1.70+` claim; no compatibility assumption about future Rust releases is needed. + +There are no caller-unsafe APIs or documented unsafe-API postconditions. No broader safe-API behavior was requested. The repair below is **PROPOSED / UNIMPLEMENTED** and does not change this snapshot's verdict. + +## Surface, invariant, and configuration coverage + +The complete public safe surface is `View<'a, T>`, `new`, `get`, and `get_mut`; both fields are externally private. The relevant implicit surfaces are move and drop. There are no traits implemented explicitly, macros, generated artifacts, dependencies beyond `core`, `cfg` branches, FFI, assembly, allocation, or concurrency. The two raw-pointer dereferences are the only unsafe operations. + +`INV-VIEW` would need to say: while a `View<'a, T>` exists, `ptr` designates the same aligned, initialized `T` from which `new` received `&'a mut T`; that object remains alive, and access exposed by the view never conflicts with another live reference. `new` establishes the origin/liveness portion: it derives `ptr` from `value`, and `PhantomData<&'a mut T>` carries the borrow in the type. Moving or dropping the view does not itself dereference `ptr`. The accessor signatures fail the temporal-aliasing portion. + +The argument is source-level and independent of layout, pointer width, optimization, overflow checks, debug assertions, unwinding, and panic strategy. Thus target/profile variation does not repair the Rust-1.70 counterexample. + +## Concrete safe counterexample + +```rust +let mut value = 0u8; +let mut view = View::new(&mut value); + +let shared: &u8 = view.get(); +let unique: &mut u8 = view.get_mut(); +*unique = 1; +let observed = *shared; +core::hint::black_box(observed); +``` + +The return types are explicitly `&'a T` and `&'a mut T`; they are not tied to the temporary `&self` / `&mut self` receiver borrows. Consequently the receiver borrow from `get` ends while `shared` remains live, and safe code may call `get_mut`. The final read keeps `shared` live across `*unique = 1`. + +The Rust 1.70 Reference classifies “Breaking the pointer aliasing rules” as undefined behavior and states, in its aliasing outline, that memory behind `&T` must not be mutated while that reference is live, except through `UnsafeCell`: [Rust 1.70 Reference, behavior considered undefined](https://doc.rust-lang.org/1.70.0/reference/behavior-considered-undefined.html#undefined-behavior). Here `T = u8`, so the exception is irrelevant. The write through `unique` mutates exactly the memory denoted by the still-live `shared`; therefore this wholly safe execution reaches UB. Two retained results of `get_mut` give the analogous exclusive-reference violation. + +## Obligation ledger and proof-artifact finding + +- **O-NEW — PROVED locally:** `new` obtains its pointer from the input reference and stores the matching lifetime marker. No unsafe operation occurs. +- **O-GET — UNSOUND:** creating `&'a T` requires the pointee not be mutated for the returned reference's live interval. The safe API permits `get_mut` during that interval. +- **O-GET-MUT — UNSOUND:** creating `&'a mut T` requires exclusive compatible access for its live interval. A prior `get` result—or another retained `get_mut` result—can remain live. +- **DOC-1 — deficient proof artifact:** neither unsafe block has a `SAFETY` proof. Adding comments cannot repair the false obligations. + +TCB `r054-review-v1` contains only `AXIOM-RUST-1.70-ALIAS`, the cited versioned Reference proposition. No test, compiler execution, design-note conclusion, dependency implementation, or external assumption was used. + +## Minimal repair + +Tie each result to the receiver borrow: + +```rust +pub fn get(&self) -> &T { + // SAFETY: INV-VIEW supplies a live, aligned, initialized `T`. The result is + // tied to `self`, so safe code cannot mutably borrow this view while it lives. + unsafe { &*self.ptr } +} + +pub fn get_mut(&mut self) -> &mut T { + // SAFETY: INV-VIEW supplies a live, aligned, initialized `T`; the exclusive + // borrow of `self` excludes every other safe access through this view for + // the returned reference's lifetime. + unsafe { &mut *self.ptr } +} +``` + +Preferably eliminate the unsafe representation entirely by storing `value: &'a mut T` and returning safe reborrows `&*self.value` / `&mut *self.value`. If the raw representation remains, place it in a private leaf module and document `INV-VIEW` beside it. Either proposed revision requires a fresh source audit; it is not proved by this report. + +Re-audit on any implementation/signature/privacy change, added trait or generated API, configuration expansion, or material change to the consumed Rust rule. diff --git a/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/result.md b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/result.md new file mode 100644 index 0000000000..b9b1903bc6 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-abstraction-design-v1/result.md @@ -0,0 +1,206 @@ +# Abstraction-Design V1 Result + +## Result + +The added abstraction-design workflow produced a large, repeatable improvement +on its central purpose, but this revision **does not pass the preregistered +gates**. + +Most importantly, no treatment report certified an unimplemented proposal as +`PROVED`, while 16 of 27 core-ablation reports did. The treatment also equaled +or exceeded the ablation's adjudicated atom recovery in every mode. However, +two treatment reports incorrectly proved an actually unsound Rust-1.70 pointer +loop, and treatment agents repeatedly mislabeled behavioral postconditions in +executions containing UB. The zero-hard-error gate and the per-atom recovery +gate therefore fail. + +This is an exploratory result about the exact frozen skill, comparator, +fixtures, prompts, hosted agents, and environment recorded in +[`manifest.md`](manifest.md). It is not a universal claim about the skill or +unsafe-Rust review. + +## Frozen experiment + +- 9 capability modes, 2 conditions, and 3 fresh replicates per cell: 54 valid + reports. +- Treatment: the complete frozen `unsafe-rust` package. +- Comparator: the same package with only the abstraction-design workflow and + its routing/report cross-references removed. It is a **core ablation**, not a + no-skill or previous-release baseline. +- Source-only review; targets were neither modified nor executed. +- All reports remained below the frozen 1,400-word cap. +- Treatment tree digest: + `d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd`. +- Comparator tree digest: + `7ae4d42abd086720ed97bf1ef8b22f66b1d0ed33a0a5834b17eebfdc245c4d52`. +- Report-tree digest: + `5e4adb0ccddb368282c95116278b980b0b1fc859f50116310e440f79a18fb649`. +- Blind-score/adjudication-tree digest: + `a0772c8375ff573c6e571676b63411d2370b735c9a0a17cb431d94852b325240`. + +The raw reports are in [`reports/`](reports/), raw anonymous scores and +adjudications are in [`blind-scores/`](blind-scores/), and condition identities +are disclosed only in the manifest after report collection. + +## Raw blind scores + +These are the scorers' original atom totals before condition unblinding and +semantic/rubric adjudication. H's hard-error flags were explicitly conditional +on disputed pointer semantics, so raw hard-error counts for H are not reduced +to a number. + +| Mode | Treatment atoms | Core-ablation atoms | Treatment hard-error reports | Ablation hard-error reports | +|---|---:|---:|---:|---:| +| A — immutable acceptance | 11/12 | 10/12 | 0/3 | 0/3 | +| R — projection redesign | 15/21 | 8/21 | 0/3 | 1/3 | +| T — ticket obligation | 15/15 | 12/15 | 0/3 | 3/3 | +| P — published contract | 13/15 | 13/15 | 0/3 | 0/3 | +| C — configuration domain | 15/15 | 12/15 | 0/3 | 3/3 | +| S — sealed boundary | 12/15 | 10/15 | 0/3 | 3/3 | +| G — greenfield design | 15/15 | 12/15 | 0/3 | 3/3 | +| H — proof/performance tradeoff | 10/15 | 10/15 | conditional | conditional | +| N — new implemented snapshot | 14/15 | 15/15 | 0/3 | 0/3 | + +The protocol intentionally does not pool these heterogeneous modes into one +headline accuracy theorem. + +## Adjudicated scores + +The adjudication applies equivalent-reasoning and exact-version semantic +corrections symmetrically to both conditions. Raw score files remain unchanged. + +| Mode | Treatment atoms | Core-ablation atoms | Treatment hard-error reports | Ablation hard-error reports | +|---|---:|---:|---:|---:| +| A — immutable acceptance | 12/12 | 12/12 | 0/3 | 0/3 | +| R — projection redesign | 21/21 | 14/21 | 0/3 | 1/3 | +| T — ticket obligation | 13/15 | 11/15 | 0/3 | 3/3 | +| P — published contract | 15/15 | 15/15 | 0/3 | 0/3 | +| C — configuration domain | 13/15 | 12/15 | 0/3 | 3/3 | +| S — sealed boundary | 15/15 | 12/15 | 0/3 | 3/3 | +| G — greenfield design | 15/15 | 12/15 | 0/3 | 3/3 | +| H — proof/performance tradeoff | 11/15 | 9/15 | 2/3 | 3/3 | +| N — new implemented snapshot | 15/15 | 15/15 | 0/3 | 0/3 | + +The treatment has no adjudicated per-mode atom regression. A, P, and N are +ties; R, T, C, S, G, and H favor treatment. This is descriptive evidence, not +a causal theorem: the platform does not expose a fixed seed or precise hosted +model identity, and physical filesystem isolation was procedural. + +## Gate disposition + +| Preregistered gate | Result | Evidence | +|---|---|---| +| Zero treatment hard errors | **FAIL** | `r043` and `r044` incorrectly give the current H loop `PROVED`; the exact supported Rust-1.70 artifact is unsound. | +| Every atom passes in at least 2/3 treatment replicates | **FAIL** | T1, C1, H1, and H4 each pass only 1/3 after adjudication. | +| A1–A3, R1, P2–P4, C3–C4, and N1–N4 pass 3/3 treatment | **PASS** | All listed firewall, support-preservation, and fresh-snapshot atoms pass under equivalent-reasoning calibration. | +| No treatment run launders T's obligation or calls G's sketch proved | **PASS** | All six relevant treatment reports keep enforcement local and proposals uncertified. | + +The overall preregistered result is therefore **FAIL**. The failure should not +be softened merely because treatment substantially outperformed the ablation. + +## Confirmed improvement: proposal/snapshot firewall + +The strongest result is direct and consistent. Across the 27 treatment reports, +no agent called an unimplemented candidate `PROVED`. Across the 27 ablation +reports, 16 did: + +- R: 1/3 ablation reports; +- T, C, S, G, and H: 3/3 ablation reports in each mode. + +The treatment agents instead separated the current artifact, design +requirements, conditional candidate proof, compatibility consequences, and +fresh exact-source audit. This confirms the hypothesized benefit of making +abstraction design an explicit workflow rather than leaving agents to compose +ordinary audit guidance unaided. + +Treatment also recovered the desired parsimonious designs: direct safe +specialization for the one-off projection, checked `NonZeroUsize` construction, +checked character conversion across configurations, a private sealed leaf +boundary, safe `split_at_mut`, and receiver-bound lifetimes for both `View` +accessors. It preserved immutable-review scope and published 1.x contracts. + +## Genuine treatment failures + +### UB does not prove a behavioral counterexample + +In T, treatment reports `r013` and `r014` correctly found UB but also labeled +the mandatory panic postcondition `CONTRACT-BROKEN`. In C, `r025` and `r027` +made the same mistake for the surrogate-panic promise. An execution containing +UB has no defined observation from which to prove “did not panic”; the +behavioral theorem is `UNPROVED` or not guaranteed unless a separate defined +counterexample exists. + +The skill's formal verdict definition already says `CONTRACT-BROKEN` requires a +false postcondition even though UB need not occur. The 2/3 repeated treatment +failure shows that this implication is not operationally salient enough. A +future revision should state the decision rule directly at the proof workflow +and reporting sites, with the same-execution UB case as an explicit forbidden +classification. + +### Exact-version pointer proof + +The initial H oracle expected the current pointer loop to be provable. That +oracle was wrong. Rust 1.70's +[`pointer::add`](https://doc.rust-lang.org/1.70.0/std/primitive.pointer.html#method.add) +requires both pointers to be in or one-past the same allocated object and has +no zero-offset exception. Rust 1.70's +[`slice::from_raw_parts`](https://doc.rust-lang.org/1.70.0/std/slice/fn.from_raw_parts.html#safety) +allows an aligned dangling pointer for a zero-length slice. Consequently, +`total(&[])` can execute `ptr.add(0)` on a dangling pointer and reach UB before +the loop condition. + +Treatment `r045` found this exact valid-use witness. Treatment `r043` and +`r044` instead generalized constructor/slice facts too far and gave the loop +`PROVED`; both are hard errors. All three ablation reports also missed the +strongest verdict. The detailed independent analysis is +[`blind-scores/adjudication-H.md`](blind-scores/adjudication-H.md). + +This is simultaneously a skill/agent failure and a successful open-world test: +the treatment helped one agent falsify the evaluator's own oracle, but only one +of three treatment replicates followed the exact-version contract rigorously +enough. The next revision should make the final `PROVED` checkpoint demand an +operation-clause ledger for the earliest supported Rust version and explicit +empty, dangling, zero-offset, one-past, ZST, and arithmetic-boundary cases when +applicable. Constructor safety requirements must not be promoted to universal +type invariants without an authoritative type-level premise. + +## Evaluator corrections, not skill defects + +Several raw atoms rewarded extra prose rather than stronger reasoning. The +independent rubric adjudication corrected these symmetrically: + +- a report need not list every unused intent-evidence channel; +- specialization need not mention a future generic projection abstraction; +- an agent need not reject cosmetic, fabricated, unsafe-trait, or plain-sum + alternatives it never proposed; +- a compatible 1.x simplification need not use one particular private-helper + syntax; and +- one complete safe UB witness is enough when the shared lifetime defect, both + accessors, and the two-method repair are explicitly covered. + +These corrections follow the frozen plan's equivalent-reasoning rule and the +skill's instruction not to pad reports with dominated alternatives. See +[`blind-scores/adjudication-rubric.md`](blind-scores/adjudication-rubric.md). +Future fixture atoms should encode necessary propositions, not preferred +candidate inventories or redundant counterexamples. Version-sensitive oracles +should also receive an independent exact-documentation review before freezing. + +## Relationship to the legacy suite + +The unchanged legacy suite was rerun before this experiment. It found no hard +regression: both conditions recovered all 78 synthetic vulnerable-case points, +the historical defect/fix pair was recovered, and treatment better surfaced +proof debt in fixed controls and the current challenge. The current challenge +still exposed a missed indirect `Copy`/`UnsafeCell` proof, so the legacy result +is not a universal pass. See +[`../2026-07-31-legacy-regression/result.md`](../2026-07-31-legacy-regression/result.md). + +## Disposition + +Keep the evaluated skill snapshot frozen as the result's treatment artifact. +Do not patch it in place after observing these reports. The abstraction-design +addition is supported as a material improvement, especially for parsimony and +proposal/snapshot separation, but the revision is not yet release-gate clean. +A subsequent revision should address the two genuine failure classes above and +rerun at least T, C, H, the legacy challenge, and unchanged acceptance/public- +contract controls under a new digest. diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/blind-synthetic-score.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/blind-synthetic-score.md new file mode 100644 index 0000000000..d12dd6cd37 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/blind-synthetic-score.md @@ -0,0 +1,22 @@ +| Reports | Atom | Discovery | Proposition | Chain | Authority | Valid use | Class | Config | Score | +|---|---|---:|---:|---:|---:|---:|---:|---:|---:| +| B, C | M1 | 2 | 2 | 2 | 2 | 2 | 2 | N/A | 12/12 | +| B, C | M2 | 2 | 2 | 2 | 2 | 2 | 2 | N/A | 12/12 | +| B, C | M3 | 2 | 2 | 2 | 2 | 2 | 2 | N/A | 12/12 | +| B, C | M4 | 2 | 2 | 2 | 2 | 2 | 2 | N/A | 12/12 | +| B, C | M5 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 14/14 | +| B, C | M6 | 2 | 2 | 2 | 2 | 2 | 2 | N/A | 12/12 | + +| Report | Atom subtotal | Scope | Completeness | Total | +|---|---:|---:|---:|---:| +| B | 74 | 2 | 2 | **78/78** | +| C | 74 | 2 | 2 | **78/78** | + +Deductions: none. Both vulnerable reports recover all six atoms with correct locations, propositions, valid-use reasoning, and classifications. + +Fixed controls: + +- **A: fails the control requirement.** It reproduces none of the repaired vulnerabilities and its PASS is scoped and supported, but it misses the absent adjacent proof-grade comment for `item_unchecked`. +- **D: passes.** It reproduces none of the repaired vulnerabilities, supports its scoped positive conclusion across required surfaces/configurations, and reports the missing adjacent proof as DOC-1. + +Hard errors: **none in A–D**. diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/current-adjudication.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/current-adjudication.md new file mode 100644 index 0000000000..431db1947b --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/current-adjudication.md @@ -0,0 +1,30 @@ +Adjudication against only `z6b1`: + +- Confirmed — applicability: [`Cargo.toml`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/Cargo.toml:30) supports Rust 1.56. Later documentation does not by itself establish an older compiler contract. This is decisive for the zero representation of NPO `Option`s: the [1.56 Option contract](https://doc.rust-lang.org/1.56.0/std/option/index.html#representation) lacks the zero-byte guarantee added in [1.89](https://doc.rust-lang.org/1.89.0/core/option/index.html#representation). Thus the 1.56–1.88 `FromZeros`/zero-only validators at [`impls.rs:310`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/impls.rs:310) are unproved. `b1z6` is correct; `t4c9` incorrectly applies 1.89 globally. + +- Overclaim — primitives: the 1.81 citation is inapplicable to 1.56, but `b1z6` goes too far in declaring all stable numerics unresolved. Exact-1.56 total safe conversions such as integer `from_ne_bytes` and `f32`/`f64::from_bits`, combined with fixed sizes/representations, reconstruct all-initialized-pattern validity and absence of padding. The unstable `f16`/`f128` case remains unproved by the cited 1.81 text. + +- Ambiguous/unresolved — atomics: the earliest admitted version’s “same in-memory representation” wording is plausibly intended to transfer representation, but neither report supplies a version-1.60 derivation of the exact equal-bit-validity proposition. Later explicit atomic wording cannot simply be projected backward. `b1z6` is defensible in withholding proof, but this is an ambiguity/proof-closure issue, not evidence of unsoundness. + +- Confirmed — literal contract: [`HasField`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/lib.rs:1214) requires `Field` to have the same visibility as the represented field. The implementation declares a `pub` marker while expressly admitting `ManuallyDrop`’s field is not literally public ([`impls.rs:751`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/impls.rs:751)). “Effectively public” methods do not satisfy that clause, and the private field name is not a stable public contract. `b1z6` is correct; `t4c9`’s approval is false. + +- False positive — indirect `Immutable` proofs: `Option` and `NonNull` are `Copy`; exact-version `Copy` rules require every contained component to be `Copy`, while `UnsafeCell` is not `Copy`. Together with this crate’s stated sufficient condition for `Immutable`, that closes both impls. `b1z6` wrongly leaves them unproved. `t4c9` reaches the right result but does not show this proof chain. + +- Confirmed — `Box: Immutable`: the source itself admits the official proof is incomplete and relies on UCG consensus ([`impls.rs:297`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/impls.rs:297)). Official pointer-layout facts do not exclude a semantically interior-mutable private representation. `b1z6` is correct; `t4c9`’s blanket “outside SIMD” approval is unsupported. + +- Confirmed — SIMD: [`impls.rs:1226`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/impls.rs:1226) explicitly relies on UCG text that says it is not guaranteed. Both reports correctly identify a universal configuration-closure gap. Some later type-specific guarantees or AArch64 `repr(C)` aggregates narrow the gap, but do not prove every emitted family across Rust 1.56+. + +- Confirmed — `KnownLayout`: [`impl_known_layout!`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/util/macros.rs:461) promises that `NonNull::cast` preserves provenance while recording a FIXME for that documentation. The 1.56+ exact “same provenance” postcondition is not visibly closed. `b1z6` is correct; `t4c9` misses it. + +- Confirmed, test-only — [`assume_initialized`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/impls.rs:1798): the stated generic proof is invalid because a valid non-`IntoBytes` value may have uninitialized padding. No concrete failing finite instantiation was established, so this is an unproved `cfg(test)` obligation, not a demonstrated production UB. `b1z6` classifies it correctly. + +- Confirmed latent issue — transmute macro: [`TransmuteFrom`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/pointer/transmute.rs:287) explicitly provides no guarantee for unequal referent sizes, while [`impl_for_transmute_from!`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/util/macros.rs:169) checks only reciprocal `TransmuteFrom` bounds. `t4c9` is correct that the macro’s “safe to call” claim omits size equality. All visible current invocations independently pair equal-size representations, so this is latent, not a current emitted-impl defect. + +- Incomplete scope: `t4c9` says it audited `c9t4`, not `z6b1`; its target-wide bottom line is therefore non-transferable. The fixture also omits declared derive/path dependencies, so neither report can prove whole-crate/derive closure. + +Hypothesis result, on report-visible evidence: + +- Applicability: **passes for `b1z6`**, though its primitive conclusion overreaches. +- Literal contract closure: **passes for `b1z6`**. +- Indirect multi-premise proof: **fails**; `b1z6` produces false positives, while `t4c9` gives no visible Copy/UnsafeCell derivation. +- Visible reconstructed proof: **fails overall**; neither report visibly closes every accepted family. `t4c9` is especially conclusory and also targets the wrong fixture. diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/historical-adjudication.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/historical-adjudication.md new file mode 100644 index 0000000000..1130cf5a82 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/historical-adjudication.md @@ -0,0 +1,16 @@ +Independent adjudication: + +| Vulnerable report | Discovery | Proposition | Dataflow | Authority | Valid use | Config | Class. | Total | +|---|---:|---:|---:|---:|---:|---:|---:|---:| +| `n6a9.md` | 2 | 2 | 2 | 2 | 2 | 2 | 2 | **14/14** | +| `g2r6.md` | 2 | 2 | 2 | 1 | 2 | 1 | 2 | **12/14** | + +- `n6a9.md` fully recovers the defect. It establishes the exact 1.84.1 proposition that `MaybeUninit::zeroed()` does not preserve padding on return, traces the false `Initialized` upgrade through `as_bytes` into an invalid `&mut [u8]`, supplies a valid padded `FromBytes` witness, and gives the decisive fully safe adversarial `Read` that inspects padding. Scope, classification, configuration closure, and the proof-documentation finding are all correct. +- `g2r6.md` also recovers the complete semantic vulnerability and safe adversarial path. Its hard error is traceability: every local citation targets out-of-scope `/u3c8`, not `a9n6`. Its `u8`/`u16` witness and configuration closure are also less fully authorized than `n6a9`’s explicit-alignment witness and cfg analysis. The conclusion remains correct. + +Fixed reports: + +- `w7f4.md`: **Pass.** It correctly proves that `uninit(); buf.zero()` performs a full-object `write_bytes` in the final storage, with no typed move before exposing the byte slice. Thus padding is initialized before arbitrary safe `Read` code may inspect it; success-without-writing, error, unwind, padding, and ZST paths remain sound. It also correctly separates implementation soundness from incomplete checked-in proof documentation. +- `j5h2.md`: **Technical pass, evidence defect.** Its reconstruction is sound and usefully explains that any later typed-move loss of padding is harmless once producing `Self`. However, all local links target `/h2j5`, not `f4w7`, which is a hard snapshot-binding error. It also omits the material documentation-maintenance finding identified by `w7f4`—the citation TODO, stale 1.81 authority, and missing local proof bridges. + +The exact 1.84.1 documentation supports both central conclusions: returned `MaybeUninit::zeroed()` values need not retain initialized padding, while an in-place raw byte write initializes the entire backing range before the safe `Read` boundary. diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/manifest.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/manifest.md new file mode 100644 index 0000000000..ff8cd958ca --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/manifest.md @@ -0,0 +1,133 @@ +# 2026-07-31 Legacy Regression Replay Manifest + +> **Evaluator-only material** + +## Status + +This was a source-only, protocol-equivalent replay of the 2026-07-30 +exploratory pilot after the `unsafe-rust` redesign. It was not byte-identical: +the prior run did not preserve its raw prompts, raw historical/current reports, +scorer prompt, or old skill archive. The reconstructed prompts below were +frozen before the first evaluated run. + +This remains an exploratory one-replicate experiment. It does not satisfy the +release protocol's hardened filesystem, documentation mirror, model/seed, +replicate-count, previous-skill, or statistical gates. + +No target was modified, built, tested, expanded by execution, or otherwise +executed. + +## Frozen identities + +- Skill package tree digest: + `d97b9ace50109216614fbb7c975ac9c97508bfa928381d247869699593a2bcdd` +- Skill entrypoint digest: + `2b063ad7d8c6a3f5051294e3c9ed49c8850397645b46772cd40ec6ae7136531e` +- Synthetic vulnerable bundle: + `e561c4a3ebf71800857edeedc217227bf152a719845b74b5ded4bac4f77081c3` +- Synthetic fixed bundle: + `27a820a8a590194916cb25a3b39b38aa3a04cc1556636a1b96cbebee755d3a51` +- Historical vulnerable source: commit + `49a13ba945954a6127036165499b6242e74bc3c6`; sanitized bundle + `450562e0515de2e60836b133e2a03a6ef7c3c65976866dde6d99a4b2f4dced25` +- Historical fixed source: commit + `f99854afb33365e9dada073a166b3047df7109d1`; sanitized bundle + `62c51935d22fb64d363482e50ce04658e0f674e570324243f48dde1f92448e4f` +- Current source: commit + `53a3fbfa15d656b25b74688369f7248ff354a021`; bundle + `3242db7402b801cefb4425fd36c9c117906b1647765006b6533c8fde6b8ffb2b` + +Digests use GNU tar with sorted names, timestamp zero, numeric owner/group +zero, and preserved contents/modes. Every duplicate opaque target copy was +verified against the applicable bundle digest before use. + +The collaboration API exposed no exact hosted-model identifier, sampling seed, +token count, or effective reasoning setting. Every evaluated run used a fresh +agent with `fork_turns="none"` and inherited the same parent configuration. + +## Cells and reports + +| Target | Condition | Runtime target | Agent | Raw report | Report SHA-256 | +|---|---|---|---|---|---| +| Synthetic vulnerable | skill | `m7q4` | `q4m7` | [`reports/q4m7.md`](reports/q4m7.md) | `40cc2816b61ded7564165df37c5eb194630bea11bf997bd6201e86a0e229d4cf` | +| Synthetic vulnerable | no-skill | `r2v9` | `v9r2` | [`reports/v9r2.md`](reports/v9r2.md) | `6e0d30258f4fc0a13a66a96479b9979c48188c6ce1d2d9b55f51e8f9feb6197b` | +| Synthetic fixed | skill | `k8d3` | `d3k8` | [`reports/d3k8.md`](reports/d3k8.md) | `e30216795e6de6a932ebc1921a5fb365b4bbae69af5f527df7ebc6cb24858734` | +| Synthetic fixed | no-skill | `x5p1` | `p1x5` | [`reports/p1x5.md`](reports/p1x5.md) | `7c8c39586d8526f24ec8e4a4c490ef7a4a2f427701bd1fe5d713b0b083a2fffa` | +| Historical vulnerable | skill | `a9n6` | `n6a9` | [`reports/n6a9.md`](reports/n6a9.md) | `88688cd9ac71e7aa42759009cbafc09adc187f1bcff457be65bc0334f6950f88` | +| Historical vulnerable | no-skill | `u3c8` | `g2r6` | [`reports/g2r6.md`](reports/g2r6.md) | `47ca6fc15d041131fd758f456d9290851a12d8998f5e054dd9b16c1f8a50342b` | +| Historical fixed | skill | `f4w7` | `w7f4` | [`reports/w7f4.md`](reports/w7f4.md) | `cfcd988d84b4af72dfd19e7c4bd5ad25fd1cc4e87890b9bbdfdd5e8d0a92d995` | +| Historical fixed | no-skill | `h2j5` | `j5h2` | [`reports/j5h2.md`](reports/j5h2.md) | `c58c18020067277d795eb431f94314bddb4289d95dd46c57acbba78f727712a9` | +| Current challenge | skill | `z6b1` | `b1z6` | [`reports/b1z6.md`](reports/b1z6.md) | `733cf5800b5b4f12a01590e656e4227a0e6d6b26bd96f49efa0002af035acd23` | +| Current challenge | no-skill | `c9t4` | `t4c9` | [`reports/t4c9.md`](reports/t4c9.md) | `a707a360c214640270b04ac2da5f38da8ed2da512217320bbddcfdb2e26f2130` | + +No agent saw two cells or both members of a vulnerable/fixed pair. + +## Frozen prompt protocol + +The skill condition began: + +```text +Act as a fresh source-audit agent. First read the complete unsafe Rust skill +package rooted at [SKILL]/SKILL.md, including every reference it directs you +to for this task, and follow it as $unsafe-rust. +``` + +The baseline omitted that sentence and was told not to inspect any unsafe-Rust +skill. Both conditions then received the same target-specific request: + +- Synthetic: audit the crate at `[TARGET]`; its support policy is + `SUPPORT.md`. +- Historical: audit `FromBytes::read_from_io` and every definition, helper, + contract, and invariant on which its proof depends; Rust 1.84.1, feature + `std`, `x86_64-unknown-linux-gnu`. +- Current: audit `src/impls.rs`, the public unsafe-trait contracts implemented + there, invoked macro definitions, and relevant Cargo/build/configuration + policy. + +Every initial prompt ended: + +```text +Inspect only that target directory [and, for treatment, the stated skill +directory] and exact versioned official Rust Reference or standard-library +documentation needed to verify claims. Do not inspect sibling directories, +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. Return a complete but reasonably concise +source-audit report in your final response. +``` + +Long-running cells received only target-neutral steering such as “complete +with your current source-only findings” or “do not widen scope.” No finding, +location, contract, or expected verdict was supplied. + +## Isolation and run deviations + +The same procedural-isolation limitation as the prior pilot applies: agents +shared a host filesystem and were instructed, not mechanically prevented, from +reading forbidden paths. Network allowlisting and an offline documentation +mirror were unavailable. + +Two treatment agents reported that an initial tool invocation ignored its +requested working directory and listed enclosing-workspace filenames. Both +stopped immediately and reported that they opened or used no sibling contents. + +The first historical-vulnerable baseline agent found the correct defect but +stalled while waiting for a helper and produced no final report. That attempt +was invalidated. An accidentally over-specific replacement prompt was aborted +before it returned any result and was not scored. The recorded `g2r6` report +came from a new neutral-prompt agent forbidden to spawn helpers. + +## Scoring and adjudication + +The synthetic reports were copied under labels A–D and scored by a fresh agent +which knew vulnerable versus fixed membership but not condition identity. It +saw no target source, skill, prior results, or condition mapping. The raw score +is [`blind-synthetic-score.md`](blind-synthetic-score.md). + +Fresh independent agents compared the historical and current reports with raw +source and applicable official documentation. Their raw conclusions are +[`historical-adjudication.md`](historical-adjudication.md) and +[`current-adjudication.md`](current-adjudication.md). The evaluator corrected +their claim that links to paired opaque copies were snapshot-binding errors: +each report correctly linked its own byte-identical, pre-hashed target copy. + diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/b1z6.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/b1z6.md new file mode 100644 index 0000000000..2a0c297949 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/b1z6.md @@ -0,0 +1,84 @@ +## Claim and verdict + +**Soundness:** `UNPROVED` for the production code generated from [`src/impls.rs`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/impls.rs), over every Cargo-admitted feature/toolchain/target configuration, relative only to official Rust Reference/std contracts. + +**Unsafe-trait-contract compliance:** one literal `HasField` contract mismatch was found; no concrete UB witness was established. + +**Documented postconditions:** `UNPROVED`, principally because `TryFromBytes`, `KnownLayout`, and configuration coverage have unresolved obligations. + +No `UNSOUND` execution or ordinary behavioral `CONTRACT-BROKEN` witness was established. The test-only configuration has a separate acknowledged-invalid proof described below. + +Snapshot: zerocopy 0.8.55, `impls.rs` SHA-256 `f3c00559de44fc20790a651d264b20729081ef006dd97e7964734adb6cf3fefb`; Cargo manifest SHA-256 `ee23fa986927eeda4ac020694e3770110033f8a6f9daac09edee6c03d56ad589`. This was a static review only. + +## Material findings + +### F1 — Rust 1.56 support is not closed by the cited axioms + +[`Cargo.toml:30`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/Cargo.toml:30) declares Rust 1.56, while most safety proofs cite Rust 1.81–1.92 documentation. Later documentation cannot establish an earlier guarantee. + +This is material, not merely citation style: + +- The exact [Rust 1.56 numeric-types page](https://doc.rust-lang.org/1.56.0/reference/types/numeric.html) gives ranges and IEEE formats but lacks the later “bit validity equals `[u8; size_of::()]`” proposition used at [`impls.rs:48`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/impls.rs:48). Thus the primitive `FromBytes`/`IntoBytes` derivation is unresolved for Rust 1.56–1.80. +- The exact [Rust 1.56 Option representation contract](https://doc.rust-lang.org/1.56.0/std/option/index.html#representation) guarantees NPO size and valid `T` ↔ `Some(T)` transmutation, but not that an all-zero byte array becomes `None`. The impls for `Option>`, references, `NonNull`, and function pointers instead cite the added Rust 1.89 zero-transmutation guarantee at [`impls.rs:312-378`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/impls.rs:312). Their `FromZeros` impls and zero-only validators therefore remain unproved for Rust 1.56–1.88. +- Atomics are emitted from Rust 1.60, but their bit-validity proofs cite Rust 1.85 documentation at [`impls.rs:497-638`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/impls.rs:497). Rust 1.60’s `AtomicU8` documentation says “same in-memory representation,” not the later precise size/bit-validity theorem. The missing implication is that this wording entails exactly equal validity sets. +- `float-nightly`’s `f16`/`f128` impls reuse a Rust 1.81 premise that did not cover those later unstable types. + +Minimal resolution: either raise/enforce the supported Rust floor for each family, or add separately checked, version-applicable Rust 1.56+ derivations. A general stability expectation cannot propagate later text backward. + +### F2 — Safe SIMD feature relies on inadmissible authority + +[`impls.rs:1226-1290`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/impls.rs:1226) explicitly bases `Immutable`, `TryFromBytes`, `FromZeros`, `FromBytes`, and `IntoBytes` on the Unsafe Code Guidelines, acknowledges that the statements are not guaranteed, and substitutes “likelihood ... next to zero.” + +The `simd` Cargo feature is safe to select. Opt-in does not create a caller-side unsafe obligation, and probability does not prove universal Rust soundness. Current official documentation gives useful type-specific guarantees for some types—for example [`__m128`](https://doc.rust-lang.org/stable/core/arch/x86_64/struct.__m128.html)—but the source supplies no exhaustive, versioned proof for every emitted x86, wasm, AArch64, PowerPC, stable, and nightly family. + +Result: all `simd`/`simd-nightly` configurations are `UNPROVED`. Minimal resolution is a type-by-type authoritative proof, an enforced finite compiler/artifact pin plus accepted implementation TCB, or removal of the unsafe impls lacking such support. + +### F3 — `Box: Immutable` explicitly has no complete proof + +[`impls.rs:297-308`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/impls.rs:297) admits that the proposition is not fully documented and cites a GitHub Unsafe Code Guidelines discussion. That source is not a Rust axiom, and “known risk” is not an accepted TCB entry. + +This blocks the `alloc` and `std` configurations. No counterexample is known; the implementation obligation is `UNPROVED`. It needs an official/local derivation of the exact internal `Immutable` proposition or an explicitly accepted, narrowly stated TCB assumption. + +### F4 — `ManuallyDrop` does not meet the literal `HasField` visibility contract + +The controlling trait requires `Field` to have the same visibility as the represented field ([`lib.rs:1214-1234`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/lib.rs:1214)). The impl creates `pub enum value` ([`impls.rs:758`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/impls.rs:758)) while conceding that `ManuallyDrop`’s actual field is “not literally public” and replacing the requirement with “effectively” public access through methods ([`impls.rs:785-798`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/impls.rs:785)). + +That does not entail the literal normative clause. The private field name `value` is also not a stable public std contract across the open toolchain range. The remaining `HasField` clauses—struct variant ID, field type, offset-zero projection, subset, and provenance—have a plausible derivation from transparent layout. + +Result: unsafe-trait contract mismatch, with no demonstrated UB consumer. Remove the impl or amend the controlling contract to explicitly permit this wrapper case and audit every consumer against the amended contract. + +### F5 — Generated `KnownLayout` pointer-provenance postcondition is unproved + +`KnownLayout::raw_from_ptr_len` promises the same address and provenance ([`lib.rs:799`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/lib.rs:799)). `impl_known_layout!`, used for atomics and SIMD, implements this with `NonNull::cast` while its proof states the required provenance proposition and immediately records a FIXME to obtain documentation for it ([`util/macros.rs:461-467`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/util/macros.rs:461)). + +No version-applicable authoritative premise was supplied for the entire Rust 1.56+ range. `LAYOUT`, `MaybeUninit`, metadata `()`, and sized layout arithmetic were otherwise correctly derived. + +### F6 — Test-only unsafe calls have acknowledged invalid proofs + +At [`impls.rs:1798-1812`](/tmp/unsafe-rust-legacy-20260731.53beM9/z6b1/src/impls.rs:1798), both `assume_initialized` calls admit that `$ty` need not be `IntoBytes`; a valid `T` can have uninitialized padding. The generic rationale therefore does not establish the callee precondition. + +This is outside the shippable-library theorem because it is under `#[cfg(test)]`. For the exact finite instantiations, no padded counterexample was established, so the test configuration is `UNPROVED`, not proven `UNSOUND`. + +## Obligation disposition summary + +| Family | Disposition | +|---|---| +| Unit, integer, float, bool, char, `str` conversion markers/validators | Local validator logic is coherent; full Rust 1.56+ proof `UNPROVED` because cited validity/layout premises are later-version or unversioned. | +| `NonZero*` and `Option` | Validators are locally correct; `Immutable` and exhaustive Option representation proofs are incomplete across the supported range. | +| NPO Options of Box/reference/NonNull/function pointers | `PROVED` only where the Rust 1.89 zero-transmutation contract applies; earlier supported versions `UNPROVED`. | +| Atomics | Trait transfer through equal bit-validity is logically correct; Rust 1.60–1.84 premise and generated `KnownLayout` provenance remain `UNPROVED`. | +| `PhantomData`, arrays, slices, tuples | Parametric field/element derivations are otherwise coherent; tuple `HasTag`/`HasField`/`ProjectField` IDs, types, projections, and invariant propagation were discharged. Full-range citation applicability remains unresolved. | +| `Wrapping`, `MaybeUninit`, `ManuallyDrop`, `Cell`, `UnsafeCell` | Conversion/alignment transfer is coherent where exact representation/validity contracts apply. Several `Immutable` arguments are inferred rather than directly established; `ManuallyDrop::HasField` has F4. | +| Raw pointers, references, `NonNull`, `Option: Immutable` | No counterexample found; several “self-evident” no-`UnsafeCell` premises lack proof-grade authoritative derivations. | +| SIMD | `UNPROVED` under F2. | +| Macro expansion logic | `unsafe_impl!`, power-set function generation, validators, and trait-bound propagation were structurally checked; `impl_known_layout!` has F5. | + +## Configuration and TCB + +The relevant axes are Rust 1.56+, all Cargo feature combinations (`alloc`, `std`, `derive`, `simd`, `simd-nightly`, `float-nightly`), target architecture/endian/atomic capabilities, Cargo build-script version cfgs, direct-rustc use, and test versus production. `build.rs` gates atomics at 1.60, AArch64 SIMD at 1.59/1.87, and AVX-512-family types at 1.89, but does not repair the semantic documentation gaps. + +No lockfile or `zerocopy-derive` source exists in the bundle, so derive/proc-macro output and whole-crate dependency closure were excluded. No tool-derived evidence was consumed. + +Accepted TCB consists only of the exact applicable official Rust documents. The UCG SIMD proposition, Box consensus, cross-version backwards projection, and unspecified compiler/private-field facts are rejected or pending and cannot support `PROVED`. + +Re-audit is required after any Rust-floor change, feature/target expansion, SIMD or atomic type change, `ManuallyDrop`/Option/std contract change, macro change, or accepted TCB decision. diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/d3k8.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/d3k8.md new file mode 100644 index 0000000000..a40d0b34f1 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/d3k8.md @@ -0,0 +1,75 @@ +# Unsafe Rust audit: `opaque-byte-tools` + +## Claim and verdict + +**Soundness: PROVED.** For the exact snapshot below, on Rust/stdlib 1.85.0 with edition 2021, all supported profiles and debug-assertion settings, every well-typed safe use is free of Rust UB, and every call to `item_unchecked` satisfying `index < bytes.len()` is free of UB. + +**Documented postconditions: PROVED.** Under that precondition, `item_unchecked` returns `bytes[index]`. + +**Combined result: PROVED**, relative only to the Rust 1.85.0 axioms listed below. No `UNSOUND`, `UNPROVED`, or `CONTRACT-BROKEN` implementation finding was found. There is one proof-documentation finding that does not alter the source verdict. + +The theorem excludes compiler/backend correctness and executions already containing UB in caller-controlled unsafe code. It imposes no deployment restrictions. + +## Snapshot and configuration + +Source identity is the tuple: + +- `Cargo.toml`: `8fbea12438f0bfdf759b55d32c4d973c664b96ddb39714017d706550a8325e19` +- `SUPPORT.md`: `4244c77b0cd210b0330b32305c4cfcae991dcca699deb58d9e8a6644f320d5e2` +- `src/lib.rs`: `3aa2b3a264dd9e9a946bb0be5251b15f0d588fdef63277ceaba062efbaa59ee0` + +The crate has no external dependencies, features, `cfg` branches, build script, FFI, assembly, allocator customization, globals, or physical generated files. The sole private declarative macro has one fixed invocation, audited by manual substitution; no expansion was executed. + +The proof covers all ordinary profiles, debug assertions enabled or disabled, unwind or abort panic behavior, and every target on which Rust 1.85.0’s standard library accepts this crate. It is parametric over optimization, pointer width, endianness, and target ABI because none affects the relevant reference, slice, or `u8` arguments. Panics occur only with no suspended invariant or unsafe state. + +Skill identity: supplied `unsafe-rust` `SKILL.md` SHA-256 `2b063ad7d8c6a3f5051294e3c9ed49c8850397645b46772cd40ec6ae7136531e`. + +## Boundary and obligation coverage + +| Surface / obligation | Result and proof | +|---|---| +| `decode_flag(u8) -> bool` | **PROVED.** Exhaustive safe match; `0`/`1` produce valid booleans and other values panic. No unsafe operation or hidden precondition. | +| Safe trait `AddressSource::byte` | **PROVED.** Caller implementations are adversarial within behavior allowed by safe Rust. The elided result lifetime is tied to `&self`; implementations may panic, diverge, or return any valid `&u8`, none of which creates a hidden obligation. | +| `load_source` | **PROVED.** Calls the safe trait method and immediately copies through its valid returned reference. It maintains no state across the callback, so panic, reentrancy, and nontermination do not threaten an invariant. | +| `ByteHandle`, `new`, `load`, moves, drop, auto traits | **PROVED.** Its sole private field is a type-enforced `&'a u8`. `new` establishes that representation; moves and trivial destruction preserve it; `load` safely copies through it. Struct-literal construction is unavailable downstream. Structural auto traits add no stronger invariant than that reference. | +| `item_unchecked` | **PROVED.** Its caller contract requires the identical `index < bytes.len()` fact consumed by `get_unchecked(index)`. The standard-library contract then returns a reference to element `index`, and copying its `u8` proves the documented result `bytes[index]`. | +| Private `make_indexer!` and generated public `profile_index` | **PROVED.** The finite output is `pub fn profile_index(...) { bytes[index] }`. Ordinary slice indexing returns the element in bounds and panics out of bounds; it does not introduce a caller safety obligation. | +| `checked_first` | **PROVED.** Reaching the unsafe call means `is_empty()` was false. Since a zero-length slice makes `is_empty()` true, contraposition gives `len != 0`; because length is `usize`, `0 < len`. Thus index zero satisfies `get_unchecked`’s condition. | +| Unsafe functions/blocks inventory | Complete: one public unsafe function/block at lines 38–39 and one internal block at line 58. No unsafe traits, impls, fields, declarations, attributes, FFI, or generated unsafe code. | + +The sole named representation invariant is `INV-BYTEHANDLE`: while a `ByteHandle<'a>` is live, `address` is a valid shared reference to a valid `u8` for `'a`. It is wholly enforced by the private field type and Rust lifetime checking; no custom unsafe code consumes it. + +## TCB audit log + +All entries are exact Rust 1.85.0 authoritative documentation, accepted for this source audit: + +- **AXIOM-SLICE:** [`[T]::get_unchecked`](https://doc.rust-lang.org/1.85.0/std/primitive.slice.html#method.get_unchecked) “Returns a reference to an element or subslice”; an “out-of-bounds index is undefined behavior.” Used by `item_unchecked` and `checked_first`. +- **AXIOM-EMPTY:** [`[T]::is_empty`](https://doc.rust-lang.org/1.85.0/std/primitive.slice.html#method.is_empty) “Returns true if the slice has a length of 0.” Used by `checked_first`. +- **AXIOM-INDEX:** The [Rust 1.85 indexing rules](https://doc.rust-lang.org/1.85.0/reference/expressions/array-expr.html#array-and-slice-indexing-expressions) make indices zero-based and perform a runtime check whose failure panics. Used by `profile_index`. +- **AXIOM-ELISION:** For a method receiver, the [elision rule](https://doc.rust-lang.org/1.85.0/reference/lifetime-elision.html#lifetime-elision-in-functions) assigns the receiver-reference lifetime to elided output lifetimes. Used by `AddressSource`. +- **AXIOM-REFERENCE:** Under the [Rust 1.85 validity rules](https://doc.rust-lang.org/1.85.0/reference/behavior-considered-undefined.html#r-undefined.validity.reference-box), a live reference cannot dangle and must point to a valid value. Used by all reference-backed safe surfaces. + +There are no `SAFE-DEP`, `UNSAFE-DEP`, external-specification, implementation, tool, deployment, or probabilistic entries. + +## Finding DOC-1: missing local proof for `item_unchecked` + +- **Implementation:** PROVED. +- **Proof artifact:** Deficient. +- **Location:** `src/lib.rs:39`. +- **Defect:** The public `# Safety` contract is sufficient, but the unsafe operation has no adjacent local `SAFETY` proof connecting that caller contract to `get_unchecked`. +- **Counterexample:** None established or required. +- **Minimal repair:** + +```rust +// SAFETY: `item_unchecked` requires `index < bytes.len()`, which is +// exactly the in-bounds condition required by `[u8]::get_unchecked`. +unsafe { *bytes.get_unchecked(index) } +``` + +The existing `checked_first` comment adequately identifies its dominating emptiness check and exact index. + +## Evidence, residual scope, and triggers + +This was source-only manual inspection. No target code was modified, built, tested, executed, or macro-expanded by execution. No tool-derived proof is claimed. Version-control identity/history, prior reports, compiler binaries, code generation, linking, and runtime platforms were intentionally outside scope. No independent second reviewer was performed. + +Re-audit is required if source or `SUPPORT.md` changes, another macro invocation or generated artifact appears, dependencies/features/targets are added, the supported Rust version changes, or any consumed Rust documentation contract changes materially. diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/g2r6.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/g2r6.md new file mode 100644 index 0000000000..213c02991b --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/g2r6.md @@ -0,0 +1,63 @@ +## Source audit: `FromBytes::read_from_io` + +Scope: Rust 1.84.1, `std`, `x86_64-unknown-linux-gnu`, source-only inspection. + +### Verdict + +`FromBytes::read_from_io` is unsound for `FromBytes` types containing padding. Severity: **high (safe API can cause undefined behavior).** + +The defect is at [lib.rs:4570](/tmp/unsafe-rust-legacy-20260731.53beM9/u3c8/src/lib.rs:4570): + +```rust +let mut buf = CoreMaybeUninit::::zeroed(); +... +// SAFETY: `buf` consists entirely of initialized, zeroed bytes. +let ptr = unsafe { ptr.assume_validity::() }; +``` + +That safety claim is false. + +Rust 1.84.1 documents that `MaybeUninit::::zeroed()` does not preserve padding bytes when returning the value; for a `T` with padding, those bytes need not be zeroed and can remain uninitialized. See the exact [Rust 1.84.1 `MaybeUninit::zeroed` documentation](https://doc.rust-lang.org/1.84.1/std/mem/union.MaybeUninit.html#method.zeroed). + +`FromBytes` does not imply absence of padding. Its contract only states that producing `T` from any sequence of initialized bytes is sound ([lib.rs:3418](/tmp/unsafe-rust-legacy-20260731.53beM9/u3c8/src/lib.rs:3418)); the surrounding trait documentation expressly discusses padded `FromBytes` structs. + +### Proof-chain audit + +1. `Ptr::from_mut(&mut buf)` is sound. A `MaybeUninit` value permits initialized or uninitialized contents, and the mutable reference supplies correct allocation, provenance, alignment, lifetime, and exclusivity. + +2. The local `Initialized` invariant requires **every byte** of the referent to form a valid `[u8; N]` ([pointer/ptr.rs:293](/tmp/unsafe-rust-legacy-20260731.53beM9/u3c8/src/pointer/ptr.rs:293)). + +3. `Ptr::assume_validity::` requires the caller to establish that invariant ([pointer/ptr.rs:818](/tmp/unsafe-rust-legacy-20260731.53beM9/u3c8/src/pointer/ptr.rs:818)). `MaybeUninit::zeroed()` does not establish it when `Self` has padding. + +4. `as_bytes::` correctly preserves address, extent, provenance, and exclusive aliasing. The `BecauseExclusive` implementation is valid ([pointer/aliasing_safety.rs:55](/tmp/unsafe-rust-legacy-20260731.53beM9/u3c8/src/pointer/aliasing_safety.rs:55)). However, `as_bytes` promotes the byte slice to `Valid` solely from the falsely asserted `Initialized` invariant ([pointer/ptr.rs:1034](/tmp/unsafe-rust-legacy-20260731.53beM9/u3c8/src/pointer/ptr.rs:1034)). + +5. `ptr.as_mut()` then calls `NonNull::as_mut` to produce `&mut [u8]`, whose elements must be initialized ([pointer/ptr.rs:535](/tmp/unsafe-rust-legacy-20260731.53beM9/u3c8/src/pointer/ptr.rs:535)). Rust 1.84.1’s [`NonNull::as_mut`](https://doc.rust-lang.org/1.84.1/std/ptr/struct.NonNull.html#method.as_mut) requires a valid initialized pointee, and the [Reference’s validity rules](https://doc.rust-lang.org/1.84.1/reference/behavior-considered-undefined.html#invalid-values) require integers and reference pointees to be valid. + +6. Passing this slice to `Read::read_exact` cannot initialize it retroactively. The Rust 1.84.1 [`Read` documentation](https://doc.rust-lang.org/1.84.1/std/io/trait.Read.html#tymethod.read) explicitly says callers must initialize `buf` before calling and that a safe `Read` implementation may inspect its existing contents. `read_exact` inherits that buffer contract. The invalid reference has already been produced before either success or error. + +A conforming counterexample is a `#[repr(C)]` struct containing `u8` followed by `u16`: on this target it has an internal padding byte, while every field bit pattern is valid, so a conforming `FromBytes` implementation exists. Calling the safe default method requires no violation of the unsafe-trait contract. + +### Remaining paths + +- For padding-free `Self`, the initialization, cast, and successful `assume_init` argument is sound. +- On successful reading, once every storage byte is genuinely initialized, `Self: FromBytes` justifies `buf.assume_init()`. +- On error, dropping `MaybeUninit` is harmless, but it does not repair the earlier invalid byte-slice reference. +- ZST handling, alignment, size computation, provenance, and exclusive aliasing appear correct. +- No independent defect was found in the audited helper chain. + +### Required repair + +Initialize the backing storage **in place**, without returning or moving a typed `MaybeUninit` between zeroing and byte-slice creation. For example: + +```rust +let mut buf = CoreMaybeUninit::::uninit(); +unsafe { + buf.as_mut_ptr() + .cast::() + .write_bytes(0, core::mem::size_of::()); +} +``` + +The raw byte write initializes padding as well as fields. The existing `Ptr` conversion and `read_exact` call can then operate on a genuinely initialized byte slice. The preinitialization must remain because `Read` is a safe trait and unsafe code cannot rely on an arbitrary implementation actually filling the buffer even when it returns `Ok(())`. + +No files were modified, built, tested, macro-expanded, or executed. diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/j5h2.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/j5h2.md new file mode 100644 index 0000000000..9d346f5176 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/j5h2.md @@ -0,0 +1,42 @@ +## Source-audit report + +Scope: Rust 1.84.1, `std`, `x86_64-unknown-linux-gnu`; target crate `zerocopy 0.8.18`. Read-only source inspection only; no build, test, execution, or macro expansion was performed. + +### Verdict + +No soundness defect found in `FromBytes::read_from_io`. Its unsafe proof is complete for sound implementations of the crate’s unsafe marker traits. + +### Proof trace + +The implementation is at [src/lib.rs:4565](/tmp/unsafe-rust-legacy-20260731.53beM9/h2j5/src/lib.rs:4565): + +1. `Self: Sized` makes `MaybeUninit` and its full object size statically defined. The `std` gate supplies `std::io` at [src/lib.rs:367](/tmp/unsafe-rust-legacy-20260731.53beM9/h2j5/src/lib.rs:367). + +2. `buf` begins as `MaybeUninit`, then `buf.zero()` writes zero to every byte of its already-placed storage. This is important: Rust 1.84.1 documents that `MaybeUninit::zeroed()` may lose initialized padding when its result is moved, whereas this code performs the write in place and does not move the referent before exposing its bytes. + + - `MaybeUninit` has unconditional `TryFromBytes`, `FromZeros`, and `FromBytes` impls at [src/impls.rs:633](/tmp/unsafe-rust-legacy-20260731.53beM9/h2j5/src/impls.rs:633). The macro emits ordinary unsafe impls and the no-validator `TryFromBytes` method returns `true`; this is sound because `MaybeUninit` admits arbitrary initialized or uninitialized contents. + - `FromZeros::zero` at [src/lib.rs:2966](/tmp/unsafe-rust-legacy-20260731.53beM9/h2j5/src/lib.rs:2966) invokes `ptr::write_bytes` through a `*mut u8` for exactly `size_of_val(buf)` bytes. The pointer comes from `&mut buf`, is writable for the whole object, and alignment 1 suffices. With element type `u8`, the byte-count multiplication cannot overflow. + - Rust 1.84.1 guarantees `MaybeUninit` has the same size and alignment as `T`, and that dropping it does not run `T`’s destructor. Its `assume_init` contract requires a valid initialized `T`: [MaybeUninit](https://doc.rust-lang.org/1.84.1/core/mem/union.MaybeUninit.html). + - `write_bytes` writes exactly `count * size_of::()` bytes and requires writable, aligned storage: [write_bytes](https://doc.rust-lang.org/1.84.1/core/ptr/fn.write_bytes.html). + +3. The `Ptr` conversions preserve allocation, provenance, extent, exclusivity, and initialization: + + - `Ptr::from_mut` derives an exclusive, aligned, valid pointer from `&mut buf` at [src/pointer/ptr.rs:393](/tmp/unsafe-rust-legacy-20260731.53beM9/h2j5/src/pointer/ptr.rs:393). + - The unsafe transition to `Initialized` at [src/lib.rs:4576](/tmp/unsafe-rust-legacy-20260731.53beM9/h2j5/src/lib.rs:4576) is justified by the preceding in-place full-storage zero write. `Initialized` explicitly means every referent byte forms a valid `[u8; N]` at [src/pointer/ptr.rs:293](/tmp/unsafe-rust-legacy-20260731.53beM9/h2j5/src/pointer/ptr.rs:293). + - `MaybeUninit: KnownLayout` is emitted at [src/lib.rs:964](/tmp/unsafe-rust-legacy-20260731.53beM9/h2j5/src/lib.rs:964). Its macro uses `DstLayout::for_type`, whose size is `size_of::()`, and unit metadata always returns that size. Thus `as_bytes` cannot take its `unreachable_unchecked` branch here. + - `Ptr::as_bytes` at [src/pointer/ptr.rs:1041](/tmp/unsafe-rust-legacy-20260731.53beM9/h2j5/src/pointer/ptr.rs:1041) constructs a `[u8]` raw slice spanning exactly that storage. `BecauseExclusive` is valid for every source/destination type because no competing access exists ([src/pointer/aliasing_safety.rs:55](/tmp/unsafe-rust-legacy-20260731.53beM9/h2j5/src/pointer/aliasing_safety.rs:55)). Initialized bytes are valid `u8`s, so the resulting pointer is valid for `[u8]`. + - `Ptr::as_mut` then materializes the exclusive `&mut [u8]` under those invariants. These match Rust 1.84.1’s raw-slice requirements, including the non-null/aligned ZST case: [from_raw_parts_mut](https://doc.rust-lang.org/1.84.1/core/slice/fn.from_raw_parts_mut.html), [NonNull::as_mut](https://doc.rust-lang.org/1.84.1/core/ptr/struct.NonNull.html#method.as_mut). + +4. `read_exact` receives a fully initialized buffer. This is required because `Read` is safe to implement and an implementation may inspect the input buffer. On success, a conforming implementation has filled it; even an otherwise misbehaving safe override that returns success without writing leaves initialized zero bytes. Safe `Read` code cannot deinitialize a `u8`. + + On error, Rust leaves the buffer contents unspecified and may have written only a prefix, but every untouched byte was preinitialized. The `?` path drops only `MaybeUninit`, never an invalid or partially formed `Self`. A panic has the same property. See Rust 1.84.1 [Read::read/read_exact](https://doc.rust-lang.org/1.84.1/std/io/trait.Read.html#method.read_exact). + +5. The final `assume_init` is justified by the unsafe `FromBytes` contract at [src/lib.rs:3418](/tmp/unsafe-rust-legacy-20260731.53beM9/h2j5/src/lib.rs:3418): every fully initialized sequence of valid `u8`s may soundly produce `Self`. A typed move may cease to preserve padding contents, but Rust values do not require padding to remain initialized; all non-padding validity requirements are covered by `FromBytes`. + +### Edge cases checked + +- Zero-sized `Self`: the slice is empty but remains non-null and aligned; `read_exact` succeeds without access. +- `Self` with padding: padding is initialized before the byte-slice reference is created; later loss of padding on the value move is harmless. +- Interior mutability: the conversion uses exclusive aliasing, so no `Immutable` bound is needed. +- `Drop`: error/unwind paths hold only `MaybeUninit`; success constructs a valid `Self`. Any sound `FromBytes` impl for a drop type must make every byte pattern safe as a value, including eventual destruction. +- Incorrect manual `FromBytes`/`FromZeros`/`KnownLayout` impls remain the unsafe implementor’s contract violation, not a defect in this method. diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/n6a9.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/n6a9.md new file mode 100644 index 0000000000..b0735a9bd1 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/n6a9.md @@ -0,0 +1,76 @@ +## Audit result + +**Soundness: UNSOUND** for `FromBytes::read_from_io` in the supplied zerocopy 0.8.18 snapshot, Rust/std 1.84.1, edition 2021, feature `std` (`alloc` implied), target `x86_64-unknown-linux-gnu`. + +The claim was: every call with a correct `Self: FromBytes` implementation and arbitrary well-typed `R: std::io::Read` is free from Rust undefined behavior. A valid padded `FromBytes` type refutes it. + +### Finding F-1 — uninitialized padding exposed as `&mut [u8]` + +At [lib.rs:4570](/tmp/unsafe-rust-legacy-20260731.53beM9/a9n6/src/lib.rs:4570), the code performs: + +```rust +let mut buf = CoreMaybeUninit::::zeroed(); +let ptr = Ptr::from_mut(&mut buf); +// SAFETY: `buf` consists entirely of initialized, zeroed bytes. +let ptr = unsafe { ptr.assume_validity::() }; +let ptr = ptr.as_bytes::(); +src.read_exact(ptr.as_mut())?; +``` + +The safety comment is false for padded `Self`: + +- Rust 1.84.1 documents that `MaybeUninit::zeroed()` does not preserve/zero padding on return. Its byte-view documentation is more explicit: an initialized `MaybeUninit` may contain padding “left uninitialized.” [Rust 1.84.1 `zeroed`](https://doc.rust-lang.org/1.84.1/std/mem/union.MaybeUninit.html#method.zeroed), [`as_bytes_mut`](https://doc.rust-lang.org/1.84.1/std/mem/union.MaybeUninit.html#method.as_bytes_mut). +- Zerocopy’s `Initialized` invariant requires every referent byte to be initialized at [ptr.rs:291](/tmp/unsafe-rust-legacy-20260731.53beM9/a9n6/src/pointer/ptr.rs:291). Thus the unsafe upgrade at line 4573 violates [the helper’s contract](/tmp/unsafe-rust-legacy-20260731.53beM9/a9n6/src/pointer/ptr.rs:828). +- `Ptr::as_bytes` consumes that false invariant at [ptr.rs:1041](/tmp/unsafe-rust-legacy-20260731.53beM9/a9n6/src/pointer/ptr.rs:1041), marks the entire `[u8]` valid, and `Ptr::as_mut` at [ptr.rs:539](/tmp/unsafe-rust-legacy-20260731.53beM9/a9n6/src/pointer/ptr.rs:539) creates `&mut [u8]`. Rust requires pointer-to-reference conversion to point to a valid value, while an uninitialized byte is not a valid `u8`. [Pointer conversion rules](https://doc.rust-lang.org/1.84.1/std/ptr/index.html#pointer-to-reference-conversion), [numeric bit validity](https://doc.rust-lang.org/1.84.1/reference/types/numeric.html#bit-validity). +- This cannot be excused on the theory that the reader immediately overwrites the slice. `Read` is safe to implement; its Rust 1.84.1 documentation expressly warns unsafe callers that an implementation may read the buffer and therefore the buffer must already be initialized. `read_exact` likewise permits implementations to inspect its input. [`Read::read`](https://doc.rust-lang.org/1.84.1/std/io/trait.Read.html#tymethod.read), [`Read::read_exact`](https://doc.rust-lang.org/1.84.1/std/io/trait.Read.html#method.read_exact). + +A concrete valid witness is a correctly implemented `FromBytes` type: + +```rust +#[repr(C, align(8))] +struct Padded(u8); +``` + +The explicit alignment and `repr(C)` layout guarantee seven padding bytes; arbitrary initialized representations are valid because the only field is `u8`. Such a type can correctly implement `TryFromBytes`, `FromZeros`, and `FromBytes`. [Rust 1.84.1 C layout](https://doc.rust-lang.org/1.84.1/reference/type-layout.html#r-layout.repr.c.struct.size-field-offset), [`repr(align)`](https://doc.rust-lang.org/1.84.1/reference/type-layout.html#r-layout.repr.alignment.align). + +A fully safe `Read` implementation may inspect `buf[1]` and then report EOF. Calling the safe `Padded::read_from_io` passes it a slice containing potentially uninitialized padding, reaching undefined behavior. Success, error, and unwind handling cannot repair UB that occurs while forming or accessing that slice. + +### Dependency and invariant ledger + +| Obligation | Status | +|---|---| +| Correct `FromBytes` permits padded types | **PROVED** from [the trait contract](/tmp/unsafe-rust-legacy-20260731.53beM9/a9n6/src/lib.rs:3420) and the witness layout | +| `Ptr::from_mut` establishes allocation, alignment, exclusive aliasing, and `MaybeUninit` validity | **PROVED** at [ptr.rs:394](/tmp/unsafe-rust-legacy-20260731.53beM9/a9n6/src/pointer/ptr.rs:394) | +| `zeroed()` establishes zerocopy’s full-byte `Initialized` invariant | **FALSE** for padded `Self` | +| `BecauseExclusive` permits the type-changing byte cast | **PROVED**, conditional on valid input invariants, at [aliasing_safety.rs:55](/tmp/unsafe-rust-legacy-20260731.53beM9/a9n6/src/pointer/aliasing_safety.rs:55) | +| `KnownLayout` supplies the correct sized byte length for `MaybeUninit` | **PROVED** by [the invocation](/tmp/unsafe-rust-legacy-20260731.53beM9/a9n6/src/lib.rs:964) and [macro definition](/tmp/unsafe-rust-legacy-20260731.53beM9/a9n6/src/util/macros.rs:547), using the exact 1.84.1 `MaybeUninit` layout guarantee | +| `&mut [u8]` passed to arbitrary safe `Read` is initialized | **FALSE**, flowing from the invalid upgrade | +| Final `buf.assume_init()` | Its comment at [lib.rs:4576](/tmp/unsafe-rust-legacy-20260731.53beM9/a9n6/src/lib.rs:4576) is also literally false for padding. Padding itself need not be initialized for a valid `Self`, but that alternative fact cannot cure the earlier invalid byte slice | + +`read_from_io` has no target-, allocator-, concurrency-, or panic-strategy-specific branch on this path. The counterexample is profile- and panic-strategy-independent. No generated proc-macro artifact is involved with exactly the `std` feature; the relevant declarative macros were traced manually without executing expansion. + +### Proof-documentation finding + +Several consumed helper comments cite Rust 1.81 or unversioned current documentation, notably [util/macros.rs:561](/tmp/unsafe-rust-legacy-20260731.53beM9/a9n6/src/util/macros.rs:561) and the `u8`/slice layout proofs in [impls.rs:40](/tmp/unsafe-rust-legacy-20260731.53beM9/a9n6/src/impls.rs:40). Their propositions were independently reconstructed from exact 1.84.1 documentation and hold here, but the existing comments do not themselves provide an admissible 1.84.1 derivation or compatibility premise. + +### Minimal remediation + +Initialize the final stack storage in place before creating the byte slice, rather than relying on a `MaybeUninit::zeroed()` value returned through a typed move. For example, start with `MaybeUninit::uninit()` and use a proved raw byte `write_bytes` over exactly `size_of::()` bytes in that final location. Then document: + +1. why every byte, including padding, was initialized in place; +2. why the exclusive `Ptr` cast covers exactly those bytes; +3. why arbitrary safe `Read` code may inspect the buffer; +4. all success, error, panic, and zero-sized exits. + +This requires a fresh audit; testing alone cannot prove padding preservation. + +### Snapshot and TCB + +Package: zerocopy 0.8.18. Relevant SHA-256 identities: + +- `lib.rs`: `a8a168a18001bda92f090f925741915e740211915f8bcd743e96b365bf63cfa8` +- `pointer/ptr.rs`: `d128349ee917c2ef4d797162200a3a46aa0fbbb299beafd5fcdf8ff75ef95d56` +- `impls.rs`: `e0eda92930bea897459c0948a4370193f9145c8ec99496e9f7b322672e7d301a` +- `util/macros.rs`: `4255cb6f8978a9f0b081bf373d9b5d81dcb4e4a6429399a8580e69c07a9d17e2` + +TCB `A9N6-R1` contains only the cited Rust 1.84.1 Reference and standard-library contracts. No dependency implementation, compiler-backend behavior, tests, builds, macro-expansion execution, or prior audit result was trusted. The verdict is limited to this method and its proof dependencies, not the crate as a whole. diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/p1x5.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/p1x5.md new file mode 100644 index 0000000000..88a493a2a4 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/p1x5.md @@ -0,0 +1,25 @@ +## Unsafe-code audit: `opaque-byte-tools` 0.1.0 + +**Verdict: PASS — no soundness findings.** +Severity count: critical 0, high 0, medium 0, low 0. + +Scope was the three-file snapshot only: [SUPPORT.md](/tmp/unsafe-rust-legacy-20260731.53beM9/x5p1/SUPPORT.md:1), [Cargo.toml](/tmp/unsafe-rust-legacy-20260731.53beM9/x5p1/Cargo.toml:1), and [src/lib.rs](/tmp/unsafe-rust-legacy-20260731.53beM9/x5p1/src/lib.rs:1). The crate has no dependencies, build script, features, conditional compilation, or exported macros. + +### Unsafe inventory and proofs + +- [`item_unchecked`](/tmp/unsafe-rust-legacy-20260731.53beM9/x5p1/src/lib.rs:33) is the sole public unsafe API. Its documented requirement, `index < bytes.len()`, exactly discharges the precondition of `slice::get_unchecked(index)`. The valid input slice supplies the remaining reference validity, alignment, and initialization requirements; dereferencing copies a valid `u8`. + +- [`checked_first`](/tmp/unsafe-rust-legacy-20260731.53beM9/x5p1/src/lib.rs:52) is the sole unsafe operation reachable from safe public code. It returns before the unsafe call when `bytes.is_empty()`. On the remaining path, `len != 0`, hence `0 < len`, so `get_unchecked(0)` is in bounds. + +Rust 1.85.0 documents that [`is_empty`](https://doc.rust-lang.org/1.85.0/std/primitive.slice.html#method.is_empty) is true exactly when the slice length is zero, and that [`get_unchecked`](https://doc.rust-lang.org/1.85.0/std/primitive.slice.html#method.get_unchecked) has undefined behavior precisely for an out-of-bounds index. Both proof obligations are therefore satisfied. + +### Remaining public API + +- `decode_flag` constructs `bool` only from the literals `false` and `true`; unsupported bytes panic. +- `AddressSource::byte` can expose only a valid shared `u8` reference through safe implementations, and `load_source` merely copies it. +- `ByteHandle` stores a lifetime-bound shared reference in a private field and copies from it. +- The private `make_indexer!` invocation generates `profile_index`, which uses ordinary checked slice indexing; out-of-bounds input panics rather than causing undefined behavior. + +No proof depends on debug assertions, optimization, integer overflow behavior, or other profile settings. Thus the result holds under every ordinary profile covered by the support policy. + +This was a static source audit; the crate was not modified, built, tested, executed, or macro-expanded by execution. diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/q4m7.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/q4m7.md new file mode 100644 index 0000000000..2a8e6bd3f1 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/q4m7.md @@ -0,0 +1,149 @@ +# Unsafe Rust audit: `opaque-byte-tools` 0.1.0 + +## Claim and verdict + +For the supplied snapshot, every well-typed safe use, and every call to `item_unchecked` satisfying its documented safety precondition, should be free from Rust undefined behavior under Rust 1.85.0 semantics; `item_unchecked` should also return `bytes[index]`. + +- **Soundness: `UNSOUND`** — findings F1–F4 provide valid safe-use UB witnesses. +- **Unsafe-API postconditions: `CONTRACT-BROKEN`** — F5 disproves `item_unchecked`’s documented result. +- **Combined result: `UNSOUND` and `CONTRACT-BROKEN`.** +- No deployment, probabilistic, or binary-backend claim was made. + +The undisputed supported set includes Rust/stdlib 1.85.0, edition 2021, every shippable target accepted by the crate, every ordinary profile, and both enabled and disabled debug assertions. There are no Cargo features, dependencies, build scripts, target `cfg`s, or declared exclusions. If “supports Rust 1.85.0” denotes an MSRV rather than an exact toolchain scope, later compilers form an additional unbounded region; that ambiguity does not affect the verdict because the counterexamples occur in the explicitly supported 1.85.0 subset. + +## Snapshot + +- [Cargo.toml](/tmp/unsafe-rust-legacy-20260731.53beM9/m7q4/Cargo.toml): `8fbea12438f0bfdf759b55d32c4d973c664b96ddb39714017d706550a8325e19` +- [SUPPORT.md](/tmp/unsafe-rust-legacy-20260731.53beM9/m7q4/SUPPORT.md): `4244c77b0cd210b0330b32305c4cfcae991dcca699deb58d9e8a6644f320d5e2` +- [src/lib.rs](/tmp/unsafe-rust-legacy-20260731.53beM9/m7q4/src/lib.rs): `7d0ea38e7ceb6df64fbb71b476dd622705542c5cfaaf2d6329eaf4e60ccfa57b` +- Skill `SKILL.md`: `2b063ad7d8c6a3f5051294e3c9ed49c8850397645b46772cd40ec6ae7136531e` +- No build, test, execution, or executed macro expansion was performed. + +## Findings + +### F1 — `decode_flag` produces invalid `bool` values + +**Status: `UNSOUND`** + +At [lib.rs:5](/tmp/unsafe-rust-legacy-20260731.53beM9/m7q4/src/lib.rs:5), the safe call `decode_flag(2)` transmutes a valid `u8` into an invalid `bool`. + +Rust 1.85 guarantees both types have size one, but a `bool` must be `false`/0 or `true`/1. [`transmute`](https://doc.rust-lang.org/1.85.0/std/mem/fn.transmute.html) requires: “Both the argument and the result must be valid at their given type.” The [Reference validity rule](https://doc.rust-lang.org/1.85.0/reference/behavior-considered-undefined.html#invalid-values) says: “A `bool` value must be `false` (`0`) or `true` (`1`).” + +Minimal resolution: explicitly define invalid-byte behavior and use a checked match, returning an error or panicking for values other than 0 and 1. + +### F2 — safe trait implementations can supply unreadable pointers + +**Status: `UNSOUND`** + +[AddressSource::address and `load_source`](/tmp/unsafe-rust-legacy-20260731.53beM9/m7q4/src/lib.rs:9) place an unenforced safety obligation on a safe trait: + +```rust +struct Null; +impl AddressSource for Null { + fn address(&self) -> *const u8 { core::ptr::null() } +} +load_source(&Null); +``` + +This caller uses no unsafe code. The resulting one-byte read is UB: [`ptr::read`](https://doc.rust-lang.org/1.85.0/std/ptr/fn.read.html#safety) requires its source to be valid for reads, aligned, and initialized; the [pointer validity rules](https://doc.rust-lang.org/1.85.0/std/ptr/index.html#safety) state that a null pointer is never valid for a non-zero-sized access. + +Minimal resolution: return `&u8` or `u8` from the trait. If a raw-pointer contract is essential, seal the trait or make it unsafe and specify validity, initialization, provenance, and the interval through the read. + +### F3 — `ByteHandle` has no pointee-lifetime invariant + +**Status: `UNSOUND`** + +The public field at [lib.rs:17](/tmp/unsafe-rust-legacy-20260731.53beM9/m7q4/src/lib.rs:17) permits safe construction of a handle whose pointee has expired: + +```rust +let handle = { + let mut byte = 7u8; + ByteHandle { address: NonNull::from(&mut byte) } +}; +handle.load(); +``` + +`NonNull` carries no lifetime. When `byte`’s stack allocation ends, the pointer is no longer valid for a read, but [`ByteHandle::load`](/tmp/unsafe-rust-legacy-20260731.53beM9/m7q4/src/lib.rs:21) reads it. Rust’s [pointer model](https://doc.rust-lang.org/1.85.0/std/ptr/index.html#allocated-object) makes temporal permission depend on the allocation lifetime. + +Minimal resolution: make the field private and encode the pointee lifetime, preferably by storing `&'a u8`; otherwise use `NonNull` plus `PhantomData<&'a u8>` and a constructor derived from `&'a u8`. Privatizing the currently public field is an API compatibility change. + +### F4 — `profile_index` relies on `debug_assert!` for safety + +**Status: `UNSOUND` when debug assertions are disabled** + +The macro-generated safe API at [lib.rs:36](/tmp/unsafe-rust-legacy-20260731.53beM9/m7q4/src/lib.rs:36) executes: + +```rust +profile_index(&[], 0) +``` + +without a bounds check when debug assertions are disabled. [`slice::get_unchecked`](https://doc.rust-lang.org/1.85.0/std/primitive.slice.html#method.get_unchecked) documents that an out-of-bounds call is UB even if the resulting reference is unused. [`debug_assert!`](https://doc.rust-lang.org/1.85.0/std/macro.debug_assert.html#uses) is not executed in optimized builds by default, and the support policy expressly includes both debug-assertion modes. + +With assertions enabled, invalid indices panic before the unsafe operation. Replace this with safe indexing or an unconditional `assert!`. + +### F5 — `item_unchecked` ignores its index + +**Status: `CONTRACT-BROKEN`; implementation soundness derivation closes for valid calls** + +At [lib.rs:27](/tmp/unsafe-rust-legacy-20260731.53beM9/m7q4/src/lib.rs:27), the contract promises `bytes[index]`, but the implementation always accesses index zero. For example, `item_unchecked(&[10, 20], 1)` satisfies `1 < 2` yet returns `10`, not `20`. + +The valid-call soundness argument is: `index < bytes.len()` implies the slice is nonempty, hence zero is in bounds and the actual `get_unchecked(0)` call satisfies its contract. That material derivation is absent from the unsafe block, and it cannot prove the documented postcondition. + +Minimal repair: + +```rust +// SAFETY: The caller guarantees `index < bytes.len()`, exactly the +// in-bounds precondition of `get_unchecked(index)`. +unsafe { *bytes.get_unchecked(index) } +``` + +### F6 — `checked_first` has a false safety comment + +**Status: implementation derivation closes; proof artifact deficient** + +The comment at [lib.rs:51](/tmp/unsafe-rust-legacy-20260731.53beM9/m7q4/src/lib.rs:51) claims every `[u8]` contains an element, which is false for empty slices. The implementation is sound because the preceding `is_empty` branch dominates the unsafe access. + +Replacement: + +```rust +// SAFETY: The preceding `is_empty` check returned false, so +// `0 < bytes.len()` and index zero is in bounds. +``` + +## Boundary, invariant, and obligation coverage + +| Surface | Boundary result | +|---|---| +| `decode_flag` | Safe function; F1 | +| `AddressSource::address`, downstream safe impls, `load_source` | Safe trait/function; missing pointer-read invariant; F2 | +| `ByteHandle`, public `address`, struct literals, `load` | Safe construction/method; missing pointee-lifetime invariant; F3 | +| `item_unchecked` | Unsafe function; caller contract sufficient for UB freedom, postcondition broken; F5 | +| Macro-generated `profile_index` | Safe generated API; configuration-dependent UB; F4 | +| `checked_first` | Safe function; implementation derivation complete, comment deficient; F6 | + +There are no exported macros, reexports, hidden public items, callbacks beyond `AddressSource`, unsafe traits/impls, FFI, statics, custom `Drop`, allocator code, concurrency, assembly, or SIMD. `NonNull`’s standard negative `Send`/`Sync` implementations introduce no additional cross-thread surface. + +## Configuration closure + +The single internal declarative-macro invocation has one finite, literal output family: `profile_index`; this was audited directly from the macro body and invocation without executing expansion. All findings except F4 are profile- and debug-assertion-independent. F4 partitions exhaustively on debug assertions: + +- enabled: failed bounds predicate panics before `get_unchecked`; +- disabled: failed predicate reaches UB. + +Target, optimization, overflow, and panic strategy do not remove F1–F3 or F5. Rust’s standardized one-byte layouts for `bool`/`u8` and the cited pointer/slice contracts make the counterexamples target-parametric. + +## TCB and evidence + +Inline TCB `TCB-m7q4-2026-07-31-r1` contains only verified Rust 1.85.0 authoritative premises: + +- primitive layout and value validity; +- `transmute` validity; +- raw-pointer read and allocation-lifetime requirements; +- unchecked-slice indexing; +- `debug_assert!` execution behavior. + +There are no dependency, external-specification, deployment, compiler-backend, tool-proof, or prior-audit premises. No independent human reviewer disposition was supplied, so no aggregate `PROVED` claim is made; the concrete `UNSOUND` and `CONTRACT-BROKEN` refutations do not depend on a discretionary assumption. + +## Residual scope and triggers + +Binary/backend correctness and broader undocumented safe-API behavior were excluded. Re-audit is required for any source, contract, support-policy, Rust-version, target-policy, macro, dependency, build-generation, or cited-authority change. diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/t4c9.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/t4c9.md new file mode 100644 index 0000000000..c5c87fe883 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/t4c9.md @@ -0,0 +1,44 @@ +## Source-audit report + +Scope was limited to `/tmp/unsafe-rust-legacy-20260731.53beM9/c9t4` plus exact-version official Rust documentation. I made no changes and did not build, test, execute, or expand macros. + +### Findings + +1. **Medium — feature-gated SIMD impls rely on a representation explicitly acknowledged as non-guaranteed** + + [impls.rs:1224](/tmp/unsafe-rust-legacy-20260731.53beM9/c9t4/src/impls.rs:1224) derives `Immutable`, `TryFromBytes`, `FromZeros`, `FromBytes`, and `IntoBytes` from the Unsafe Code Guidelines’ proposed packed-SIMD layout. The source itself quotes that this layout is not guaranteed until ratified, then emits the unsafe impls at [impls.rs:1311](/tmp/unsafe-rust-legacy-20260731.53beM9/c9t4/src/impls.rs:1311) for x86, wasm, PowerPC, and AArch64 types. + + This is an admitted soundness premise rather than a proven Rust contract: if any listed type gains padding, restricted validity, or a different element representation, safe zerocopy APIs can construct invalid values or expose uninitialized padding. Opting into a Cargo feature does not weaken an unsafe-trait obligation. + + The risk is conditional: `simd` is not a default feature, and current Rust 1.93.1 documentation for [`__m128i`](https://doc.rust-lang.org/1.93.1/core/arch/x86_64/struct.__m128i.html) now expressly says its in-memory representation matches an equivalent array without padding. However, the crate supports Rust 1.56, and the [Rust 1.81 `__m128i` documentation](https://doc.rust-lang.org/1.81.0/core/arch/x86_64/struct.__m128i.html) only exposed `#[repr(simd)]` and did not provide that array-layout guarantee. There is no corresponding normative `repr(simd)` contract in the versioned Reference covering the whole emitted family. AArch64 aggregate types such as `int8x8x2_t` are separately `#[repr(C)]` homogeneous aggregates, but the module-level proof does not analyze them separately. + + Recommendation: require an exact official standard-library guarantee for each supported family/toolchain range, or raise the `simd` feature’s minimum Rust version to one where those guarantees exist; otherwise remove the safe trait impls. At minimum, split the AArch64 aggregate proof from the packed-vector proof. + +2. **Low — the “safe” transmute-delegation macro omits the size-equality premise required by its proof** + + [util/macros.rs:169](/tmp/unsafe-rust-legacy-20260731.53beM9/c9t4/src/util/macros.rs:169) says `impl_for_transmute_from!` is safe to call. Its proof requires only reciprocal `TransmuteFrom<_, Valid, Valid>` bounds at lines 200–210. But the actual public contract says `TransmuteFrom` conveys **no safety guarantee when referent sizes differ** at [pointer/transmute.rs:300](/tmp/unsafe-rust-legacy-20260731.53beM9/c9t4/src/pointer/transmute.rs:300). The macro nevertheless uses those bounds to emit unsafe `TryFromBytes`, `FromZeros`, `FromBytes`, or `IntoBytes` impls. + + I found no currently instantiated violation: every invocation in `impls.rs` pairs documented representation-equal types—atomics and their primitives, or `Wrapping`, `ManuallyDrop`, `Cell`, and `UnsafeCell` with their inner type. Thus this is a latent unsoundness footgun rather than a demonstrated defect in an emitted impl. + + Recommendation: encode an appropriate `SizeEq`/`CastExact` witness in the macro, or make the macro unsafe and explicitly require equal referent sizes as a caller obligation. + +### Contract coverage + +I checked the public contracts for `KnownLayout`, `Immutable`, `TryFromBytes`, `FromZeros`, `FromBytes`, `IntoBytes`, and `Unaligned`, plus the internal-but-public unsafe projection traits `HasTag`, `HasField`, and `ProjectField`, and `pointer::TransmuteFrom`. + +Outside the SIMD exception: + +- Primitive, boolean, character, UTF-8 `str`, `NonZero`, null-option, raw-pointer, atomic, transparent-wrapper, array/slice, reference, `Option`, and tuple impls match their relevant validity, initialization, padding, and alignment contracts. +- Validators for `bool`, `char`, `str`, `NonZero*`, null representations, arrays/slices, tuples, and `UnsafeCell` return true only after sufficient checks. Unconditional validators are used only where every initialized pattern is valid. +- `IntoBytes` is deliberately absent for `MaybeUninit` and raw pointers, and tuple `IntoBytes` is omitted because tuple padding is not controlled. +- Atomic impls correctly avoid `Immutable`; reference-producing APIs consequently cannot reinterpret shared byte slices as mutable atomic storage. +- `ManuallyDrop` and tuple `HasField`/`ProjectField` projections preserve address/provenance, identify the correct fields, and preserve the stated alignment, aliasing, and validity invariants. +- The null-option claims are supported by the [Rust 1.89 `Option` representation guarantee](https://doc.rust-lang.org/1.89.0/core/option/index.html#representation). The transparent-wrapper claims are supported by the Rust 1.85 [`ManuallyDrop`](https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html) and [`UnsafeCell`](https://doc.rust-lang.org/1.85.0/std/cell/struct.UnsafeCell.html#memory-layout) documentation. + +### Build and policy observations + +The manifest declares zerocopy 0.8.55, edition 2021, MSRV 1.56, CI metadata for stable 1.93.1 and nightly 2026-01-25, and negative version cfgs generated by `build.rs`. The atomic and AArch64 availability gates are internally coherent, including the Rust 1.87 big-endian AArch64 cutoff. + +The “pinned” toolchains are only Cargo metadata in this bundle; there is no `rust-toolchain`, lockfile, or included CI/policy configuration to enforce them. The target also omits the declared `zerocopy-derive` path dependency. Consequently dependency reproducibility and the advertised CI matrix cannot be independently verified from this bundle. + +**Bottom line:** I found no unconditional current source-level contract violation in the default configuration. The principal concern is the explicitly non-guaranteed SIMD safety premise; the transmute macro also has a real but currently untriggered proof hole. diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/v9r2.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/v9r2.md new file mode 100644 index 0000000000..e07913632b --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/v9r2.md @@ -0,0 +1,61 @@ +## Audit result + +**Unsound.** The supported public API contains four ways for entirely safe caller code to trigger undefined behavior. I also found one public API correctness bug and one misleading safety comment. + +Scope: source-only review of `Cargo.toml`, `SUPPORT.md`, and `src/lib.rs` at the supplied snapshot. Per instruction, I did not modify, build, test, execute, or macro-expand the crate. + +### High — `decode_flag` creates invalid `bool` values + +[src/lib.rs:5](/tmp/unsafe-rust-legacy-20260731.53beM9/r2v9/src/lib.rs:5) + +`decode_flag(2)` is a fully safe call, but transmuting any byte other than `0` or `1` to `bool` produces an invalid value and immediate UB. Rust 1.85 requires both sides of a transmute to be valid and defines only `0x00` and `0x01` as valid `bool` representations. + +Fix: use `raw != 0` if all nonzero bytes mean true, or validate `0..=1` and return `Option`/`Result`. + +References: [Rust 1.85 `transmute`](https://doc.rust-lang.org/1.85.0/core/mem/fn.transmute.html), [Rust 1.85 boolean representation](https://doc.rust-lang.org/1.85.0/reference/types/boolean.html). + +### High — safe `AddressSource` implementations can supply unreadable pointers + +[src/lib.rs:9](/tmp/unsafe-rust-legacy-20260731.53beM9/r2v9/src/lib.rs:9) + +`AddressSource::address` is a safe trait method with no invariant. A safe implementation may return `core::ptr::null()`, after which safe `load_source(&source)` calls `read` and causes UB. `ptr::read` requires a pointer valid for reads, properly aligned, and pointing to initialized data. + +Fix: preferably return a reference tied to `self`, such as `fn value(&self) -> &u8`. If raw pointers are essential, make `AddressSource` an `unsafe trait` with a precise validity, lifetime, initialization, and access contract. Merely documenting such requirements on the current safe trait is insufficient. + +References: [Rust 1.85 `ptr::read`](https://doc.rust-lang.org/1.85.0/core/ptr/fn.read.html), [Rust 1.85 unsafe traits](https://doc.rust-lang.org/1.85.0/reference/unsafe-keyword.html#unsafe-traits-unsafe-trait). + +### High — public `ByteHandle` construction does not establish `load`’s invariant + +[src/lib.rs:17](/tmp/unsafe-rust-legacy-20260731.53beM9/r2v9/src/lib.rs:17) + +`NonNull` guarantees non-nullness, not that the byte remains live, readable, or initialized. Because the field is public, safe code can construct a handle containing `NonNull::dangling()` or a pointer whose original local has expired, then call the safe `load` method and cause UB. + +Fix: store `&'a u8` if possible. Otherwise make the field private and provide an `unsafe` constructor whose contract establishes readable initialized storage for the handle’s lifetime, represented with an appropriate lifetime marker. + +Reference: [Rust 1.85 `NonNull`, including `dangling`](https://doc.rust-lang.org/1.85.0/core/ptr/struct.NonNull.html#method.dangling). + +### High — `profile_index` relies on a removable `debug_assert!` + +[src/lib.rs:36](/tmp/unsafe-rust-legacy-20260731.53beM9/r2v9/src/lib.rs:36) + +With debug assertions disabled, safe code such as `profile_index(&[], 0)` reaches `get_unchecked(0)` and causes UB. This configuration is explicitly supported by `SUPPORT.md`; Rust 1.85 documents that optimized builds do not execute `debug_assert!` by default. + +Fix: use ordinary safe indexing, or enforce the condition with `assert!` before the unchecked access. + +References: [Rust 1.85 `debug_assert!`](https://doc.rust-lang.org/1.85.0/std/macro.debug_assert.html), [Rust 1.85 slice `get_unchecked`](https://doc.rust-lang.org/1.85.0/core/primitive.slice.html#method.get_unchecked). + +### Medium — `item_unchecked` ignores its `index` + +[src/lib.rs:27](/tmp/unsafe-rust-legacy-20260731.53beM9/r2v9/src/lib.rs:27) + +The function promises `bytes[index]` but always returns `bytes[0]`. For example, a contract-compliant call with index `1` returns the first element. This is not UB when the documented precondition is satisfied, because that precondition implies the slice is nonempty, but it violates the public API’s stated semantics. + +Fix: pass `index` to `get_unchecked`. + +### Informational — misleading proof comment in `checked_first` + +[src/lib.rs:47](/tmp/unsafe-rust-legacy-20260731.53beM9/r2v9/src/lib.rs:47) + +The function is sound because `is_empty()` is checked first. The comment’s claim that every `[u8]` contains an element because `u8` occupies one byte is false; empty slices exist, and element size is irrelevant. + +Fix: use `bytes.first().copied()` and eliminate the unsafe block, or state that the preceding nonempty check proves index zero is in bounds. diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/w7f4.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/w7f4.md new file mode 100644 index 0000000000..feb9c96aa7 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/reports/w7f4.md @@ -0,0 +1,58 @@ +## Audit result + +**Soundness: PROVED.** For the exact `f4w7` snapshot, Rust/std 1.84.1, production `x86_64-unknown-linux-gnu`, and Cargo feature set `{std}` (`alloc` implied), every safe call to `FromBytes::read_from_io` is free of Rust undefined behavior, provided the `Self: FromBytes` unsafe implementation satisfies its published contract and any unsafe code inside a caller-supplied `Read` implementation is itself sound. + +**Documented successful-read behavior: PROVED** when the supplied `Read::read_exact` implementation obeys the `Read` contract. Soundness does not depend on that behavioral compliance: even an overriding implementation which safely returns `Ok(())` without writing cannot make `assume_init` unsound because the buffer was initialized first. + +No UB or postcondition counterexample was found. + +### Snapshot and boundary + +- Package: `zerocopy 0.8.18`, edition 2021. +- Relevant source digest: `src/lib.rs` SHA-256 `c18e8241f85bf3c61a8edc32eeed3baffa076661305c43df205efab9ef14ef6a`. +- Safe surface: [`FromBytes::read_from_io`](/tmp/unsafe-rust-legacy-20260731.53beM9/f4w7/src/lib.rs:4565). +- Unsafe provider boundary: [`FromBytes`](/tmp/unsafe-rust-legacy-20260731.53beM9/f4w7/src/lib.rs:3440), whose contract permits producing `Self` from any fully initialized sequence of valid `u8`s. +- Transitive proof surface: `FromZeros::zero`, `MaybeUninit` marker and `KnownLayout` impls, `Ptr::{from_mut, assume_validity, as_bytes, as_mut}`, `BecauseExclusive`/`AliasingSafe`, and their manually inspected macro definitions. No executed macro expansion was used. + +### Reconstructed derivation + +1. `MaybeUninit::::uninit()` is valid for every `Self`; dropping it never invokes `Self`’s destructor. Rust 1.84.1 also guarantees that `MaybeUninit` has the same size, alignment, and ABI as `T`, and that any bit value is valid for it. [Rust 1.84.1 `MaybeUninit`](https://doc.rust-lang.org/1.84.1/std/mem/union.MaybeUninit.html#layout-1) + +2. The local unconditional `FromZeros` impl for `MaybeUninit` is therefore valid. [`buf.zero()`](/tmp/unsafe-rust-legacy-20260731.53beM9/f4w7/src/lib.rs:4571) calls [`FromZeros::zero`](/tmp/unsafe-rust-legacy-20260731.53beM9/f4w7/src/lib.rs:2966), which writes zero to exactly `size_of_val(buf)` bytes. Rust requires `write_bytes`’ destination to be writable for that byte count and aligned; both follow from the live exclusive reference, its exact referent size, and `u8` alignment 1. [Rust 1.84.1 `write_bytes`](https://doc.rust-lang.org/1.84.1/std/ptr/fn.write_bytes.html#safety) + +3. No by-value move occurs between `zero` and borrowing `buf`, so initialized padding is not lost. `Ptr::from_mut` establishes exclusive, aligned, valid `MaybeUninit` state. The subsequent `assume_validity` is justified because every referent byte was written. + +4. `Ptr::as_bytes::` computes exactly `size_of::>()`, changes only the referent type to `[u8]`, and carries exclusive aliasing. For its sized pointer cast, the 1.84.1 Reference says the pointer “is returned unchanged”; the raw-slice construction retains that data pointer and supplies the exact length. [Rust 1.84.1 pointer-to-pointer casts](https://doc.rust-lang.org/1.84.1/reference/expressions/operator-expr.html#pointer-to-pointer-cast) + +5. The resulting `[u8]` is aligned and valid because its entire range is initialized. `Ptr::as_mut` consequently satisfies the standard pointer-to-reference requirements: non-null, aligned, dereferenceable, valid referent, and exclusive access. [Rust 1.84.1 pointer/reference rules](https://doc.rust-lang.org/1.84.1/std/ptr/index.html#pointer-to-reference-conversion) + +6. `read_exact` receives an ordinary initialized `&mut [u8]`. On success its contract fills the slice; on error, the contents are unspecified but remain valid initialized `u8`s. [Rust 1.84.1 `Read::read_exact`](https://doc.rust-lang.org/1.84.1/std/io/trait.Read.html#method.read_exact) + +7. `?` prevents `assume_init` on error. Panic/unwind likewise drops only `MaybeUninit`, not `Self`. On success, the byte-slice borrow has ended, every byte remains initialized, and `FromBytes` entails that this sequence may soundly become `Self`; therefore `assume_init`’s requirement is met. [Rust 1.84.1 `MaybeUninit::assume_init`](https://doc.rust-lang.org/1.84.1/std/mem/union.MaybeUninit.html#method.assume_init) + +This proof also covers zero-sized `Self`: the write and slice have length zero but retain required alignment/non-nullness, and a valid `FromBytes` impl necessarily licenses the empty representation. + +### Configuration closure + +The method is selected only by `feature = "std"`. The proof is independent of optimization, overflow-checking, endianness, panic strategy, allocator behavior, and other package features. There is no FFI, SIMD, atomic, generated code, or OS-specific call in this path. `x86_64-unknown-linux-gnu` therefore introduces no additional source-level premise. + +### TCB and exclusions + +- Exact Rust 1.84.1 Reference and standard-library contracts linked above. +- Correct source-level behavior of safe Rust/std APIs. +- The documented `Read::read_exact` behavior only for the behavioral postcondition. +- Satisfaction of the published unsafe `FromBytes` contract by each implementation is the explicit out-of-scope implementer obligation. +- Compiler backend, linker, OS, filesystem/socket implementation, and malicious unsafe code inside `R` are excluded from this source-level theorem. + +### Finding: proof documentation is incomplete + +The implementation proof succeeds, but the checked-in proof artifact is not proof-grade: + +- [`FromZeros::zero`](/tmp/unsafe-rust-legacy-20260731.53beM9/f4w7/src/lib.rs:2966) explicitly has a TODO for authoritative citations. +- The `KnownLayout` macro cites Rust 1.81.0 rather than the audited 1.84.1 documentation. +- Pointer-cast comments assert provenance preservation without the applicable Reference citation. +- `read_from_io`’s two local comments omit the material bridges through initialized padding, exclusive byte-view formation, error/unwind behavior, and borrow termination before `assume_init`. + +Recommended resolution: replace those comments with the seven-step derivation above, adjacent to the corresponding operations, using the exact 1.84.1 citations. This is a documentation-maintenance finding, not an implementation soundness defect. + +No builds, tests, target execution, or generated expansion were performed. One initial filename-listing command was mis-rooted by the execution environment and exposed enclosing-checkout filenames only; none of that output was used as audit evidence. diff --git a/evals/unsafe-rust/runs/2026-07-31-legacy-regression/result.md b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/result.md new file mode 100644 index 0000000000..20f762d790 --- /dev/null +++ b/evals/unsafe-rust/runs/2026-07-31-legacy-regression/result.md @@ -0,0 +1,94 @@ +# 2026-07-31 Legacy Regression Replay Result + +Protocol and identities: [`manifest.md`](manifest.md). + +## Disposition + +**Exploratory pass with one unconfirmed improvement hypothesis.** The revised +skill preserved every objective known-issue and fixed-control behavior from the +old pilot and introduced no hard error. It improved the current challenge on +version applicability and literal contract closure. It did not reliably cause +the agent to complete a valid indirect `Copy`/`UnsafeCell` derivation. + +This is one replicate under procedural isolation, not evidence of statistical +lift or release readiness. + +## Objective regression results + +| Target | Revised skill | Contemporary baseline | Regression result | +|---|---|---|---| +| Synthetic vulnerable | `6/6`, `78/78`, no hard error | `6/6`, `78/78`, no hard error | Pass; ceiling tie | +| Synthetic fixed | No repaired atom; scoped `PROVED`; missing proof comment reconstructed and reported | No repaired atom; scoped pass; missing proof comment omitted | Pass; treatment wins proof-artifact control | +| Historical vulnerable | Admitted defect recovered; `14/14` | Defect recovered; minor authority/coverage scoring disagreement | Pass | +| Historical fixed | Exact defect closed; detailed in-place-zeroing proof; no repaired false positive | Exact defect closed; no repaired false positive | Pass | +| Current challenge | `UNPROVED`; no production `UNSOUND`; test-only scope correct | No whole-target proof; no production `UNSOUND` | Pass on preregistered calibration | + +The blind synthetic scorer assigned both vulnerable reports `78/78`. Unlike the +prior pilot's `78` versus `73`, this replicate's no-skill agent supplied enough +detail to reach the ceiling. The result establishes non-regression, not lift. + +The independent historical scorer assigned the skill report `14/14`. It +assigned the baseline `12/14`, partly because it incorrectly treated links to +the baseline's own opaque target copy as out-of-scope. That deduction is +invalid: the paired copies had identical verified bundle hashes. The baseline +report is semantically correct; its representation-layout citation and explicit +configuration closure are less complete than the skill report, but this does +not affect the treatment regression gate. + +Both fixed historical reports prove the essential repair: `uninit(); zero()` +writes the complete final object storage in place before a byte-slice reference +is constructed. The skill report additionally exposes material proof-comment +debt rather than silently accepting the implementation. + +## Current-source comparison + +| Behavior | Revised skill | Baseline | Adjudication | +|---|---|---|---| +| Overall production result | `UNPROVED`; no UB witness | No unconditional defect claimed | Calibrated | +| Rust 1.56 versus later citations | Found exact Option and broader range gaps | Applied Rust 1.89 Option guarantee across the target | Treatment improvement confirmed | +| Literal `ManuallyDrop::HasField` clauses | Found visibility/name/model mismatch | Declared projection compliant | Treatment improvement confirmed as obligation discovery; final contract interpretation remains disputed | +| Optional function-pointer/`NonNull` `Immutable` | Left unproved | Accepted without visible derivation | Valid indirect `Copy`/`UnsafeCell` proof omitted by both; treatment false positive | +| `Box: Immutable` | Found incomplete normative proof | Blanket approval outside SIMD | Treatment improvement | +| SIMD | Found universal gap | Found universal gap | Tie | +| Generated `KnownLayout` provenance | Found `NonNull::cast` documentation gap | Missed | Treatment improvement | +| Test-only `assume_initialized` | Correctly `cfg(test)` and `UNPROVED`, not production UB | Not emphasized in final findings | Treatment scope correct | +| Safe transmute-delegation macro | Not reported | Found latent size-equality contract hole; no current bad invocation | Valid baseline-only maintenance finding | +| Missing fixture inputs | Explicitly excluded derive/dependency closure | Mentioned omitted derive/path dependency | Both avoid whole-crate proof | + +The skill report overgeneralized the Rust-version gap for stable primitive +numerics: later citations were inapplicable, but exact Rust 1.56 conversion and +representation APIs can reconstruct several required facts. That is an +overbroad `UNPROVED`, not a hard error or unsoundness claim. + +The `HasField` result must remain calibrated. Literal review correctly exposes +that the checked-in comment substitutes “effectively public” for the contract's +visibility clause. The contract also permits a field belonging to any +layout-equivalent struct, so a public transparent proxy-field model may be a +valid proof route. The replay therefore admits obligation discovery and the +missing literal derivation, not a final `CONTRACT-BROKEN` verdict. + +## Planned-improvement checks + +| Hypothesis | Result | Evidence | +|---|---|---| +| Applicability follows every premise | **Pass** | Treatment rejects backward use of Rust 1.89 Option prose for Rust 1.56–1.88 and states the missing version interval. | +| Every literal contract clause receives a disposition | **Pass** | Treatment separately identifies the `HasField` visibility/name/model clauses instead of accepting operational projection alone. | +| Search closes valid indirect multi-premise proofs | **Fail** | Treatment still calls optional function-pointer and `NonNull` `Immutable` impls unproved instead of deriving the sufficient fact through exact `Copy` and `UnsafeCell` rules. | +| Material reconstructed proofs are visible | **Partial pass** | Synthetic fixed and historical fixed reports expose full reconstructed derivations and proof-artifact defects; the current indirect proof was not reconstructed. | + +## Quality and hard-error review + +- No known vulnerable atom was missed by a skill-enabled report. +- No repaired atom was reproduced on a fixed control. +- No skill report issued unsupported production `UNSOUND` or a whole-current + `PROVED`. +- Caller-provided safe `Read` and safe trait implementations were treated as + adversarial for soundness. +- Mandatory postconditions remained separate from UB freedom. +- Current test-only code remained separate from downstream-shippable code. +- No target code was executed. + +The result is therefore a regression pass, while the indirect-proof miss is a +real remaining skill-quality limitation. The frozen revision was not modified +after observing it. +