From b638cade34555becae1144eb89c404efcc724769 Mon Sep 17 00:00:00 2001 From: pitboss Date: Sun, 17 May 2026 08:10:32 -0500 Subject: [PATCH] [pitboss/grind] deferred session-0023 (20260517T044708Z-e058) --- .gitignore | 1 + fuzz/dynamic_corpus/Cargo.lock | 14 ++ fuzz/dynamic_corpus/src/main.rs | 43 +++--- scripts/check_no_unseeded_rand.sh | 18 ++- src/dynamic/sandbox/process_macos.rs | 12 ++ tests/sandbox_hardening_macos.rs | 96 +++++++++++- tools/sb-trace.sh | 223 +++++++++++++++++++++++++++ tools/sb-trace/README.md | 77 +++++++++ 8 files changed, 458 insertions(+), 26 deletions(-) create mode 100755 tools/sb-trace.sh create mode 100644 tools/sb-trace/README.md diff --git a/.gitignore b/.gitignore index d84f7105..0a4b9b6b 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ node_modules __pycache__/ *.pyc +tools/sb-trace/*.trace.raw diff --git a/fuzz/dynamic_corpus/Cargo.lock b/fuzz/dynamic_corpus/Cargo.lock index 289b5c50..1f5b8991 100644 --- a/fuzz/dynamic_corpus/Cargo.lock +++ b/fuzz/dynamic_corpus/Cargo.lock @@ -1011,6 +1011,7 @@ dependencies = [ "serde", "serde_json", "smallvec", + "tempfile", "terminal_size", "thiserror", "tokio", @@ -1586,6 +1587,19 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "terminal_size" version = "0.4.4" diff --git a/fuzz/dynamic_corpus/src/main.rs b/fuzz/dynamic_corpus/src/main.rs index 27eee9ef..a50228ff 100644 --- a/fuzz/dynamic_corpus/src/main.rs +++ b/fuzz/dynamic_corpus/src/main.rs @@ -23,10 +23,10 @@ use nyx_scanner::dynamic::corpus::{ audit_marker_collisions, materialise_bytes, payloads_for, CuratedPayload, Oracle, PayloadProvenance, CORPUS_VERSION, }; +use nyx_scanner::dynamic::rand::SpecRng; use nyx_scanner::labels::Cap; use std::collections::HashSet; use std::path::{Path, PathBuf}; -use std::time::SystemTime; fn main() { let args: Vec = std::env::args().collect(); @@ -138,14 +138,16 @@ fn cmd_run(args: &[String]) { } let mut corpus: Vec> = seed_bytes.clone(); - let mut rng_state: u64 = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .map(|d| d.as_nanos() as u64) - .unwrap_or(12345); + // Deterministic RNG keyed on the spec hash so two runs against the + // same fixture produce identical candidate streams. The Phase 27 + // events.jsonl replay invariant + Phase 28 repro bundle hermeticity + // contract both require the verifier (and any fuzzer feeding it) to + // be reproducible from inputs alone — no host entropy mixed in. + let mut rng = SpecRng::seeded(&spec_hash); for iter in 0..iterations { - let seed = &corpus[lcg_next(&mut rng_state) as usize % corpus.len()]; - let candidate = mutate_bytes(seed, &mut rng_state); + let seed = &corpus[rng.gen_range(corpus.len())]; + let candidate = mutate_bytes(seed, &mut rng); if seen.contains(&candidate) { continue; @@ -162,7 +164,7 @@ fn cmd_run(args: &[String]) { if interesting { discovered += 1; - let filename = format!("candidate-{:016x}", lcg_next(&mut rng_state)); + let filename = format!("candidate-{:016x}", rng.next_u64()); let candidate_path = out_path.join(&filename); std::fs::write(&candidate_path, &candidate).unwrap_or_else(|e| { eprintln!("Failed to write candidate: {e}"); @@ -206,31 +208,26 @@ fn parse_cap(name: &str) -> Option { } } -fn lcg_next(state: &mut u64) -> u64 { - *state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407); - *state -} - -fn mutate_bytes(input: &[u8], rng: &mut u64) -> Vec { +fn mutate_bytes(input: &[u8], rng: &mut SpecRng) -> Vec { let mut out = input.to_vec(); if out.is_empty() { return out; } - match lcg_next(rng) % 5 { + match rng.next_u64() % 5 { 0 => { // Flip a random byte. - let idx = (lcg_next(rng) as usize) % out.len(); - out[idx] ^= (lcg_next(rng) as u8) | 1; + let idx = rng.gen_range(out.len()); + out[idx] ^= (rng.next_u64() as u8) | 1; } 1 => { // Insert a byte. - let idx = (lcg_next(rng) as usize) % (out.len() + 1); - out.insert(idx, lcg_next(rng) as u8); + let idx = rng.gen_range(out.len() + 1); + out.insert(idx, rng.next_u64() as u8); } 2 => { // Delete a byte. if out.len() > 1 { - let idx = (lcg_next(rng) as usize) % out.len(); + let idx = rng.gen_range(out.len()); out.remove(idx); } } @@ -240,15 +237,15 @@ fn mutate_bytes(input: &[u8], rng: &mut u64) -> Vec { b"'", b"\"", b";", b"--", b" OR 1=1", b"