#![cfg(target_os = "linux")] include!("data_plane_test_support.inc"); use std::alloc::{GlobalAlloc, Layout, System}; use std::cell::Cell; use proptest::prelude::*; struct ThreadCountingAllocator; thread_local! { static COUNT_ALLOCATIONS: Cell = const { Cell::new(false) }; static ALLOCATION_COUNT: Cell = const { Cell::new(0) }; } #[global_allocator] static COUNTING_ALLOCATOR: ThreadCountingAllocator = ThreadCountingAllocator; unsafe impl GlobalAlloc for ThreadCountingAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { COUNT_ALLOCATIONS.with(|enabled| { if enabled.get() { ALLOCATION_COUNT.with(|count| count.set(count.get() + 1)); } }); // SAFETY: this allocator delegates the unchanged layout to `System`. unsafe { System.alloc(layout) } } unsafe fn dealloc(&self, pointer: *mut u8, layout: Layout) { // SAFETY: `pointer` came from `System` with this layout. unsafe { System.dealloc(pointer, layout) }; } unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { COUNT_ALLOCATIONS.with(|enabled| { if enabled.get() { ALLOCATION_COUNT.with(|count| count.set(count.get() + 1)); } }); // SAFETY: this allocator delegates the unchanged layout to `System`. unsafe { System.alloc_zeroed(layout) } } unsafe fn realloc(&self, pointer: *mut u8, layout: Layout, size: usize) -> *mut u8 { COUNT_ALLOCATIONS.with(|enabled| { if enabled.get() { ALLOCATION_COUNT.with(|count| count.set(count.get() + 1)); } }); // SAFETY: `pointer` and `layout` came from `System`. unsafe { System.realloc(pointer, layout, size) } } } fn start_allocation_count() { ALLOCATION_COUNT.with(|count| count.set(0)); COUNT_ALLOCATIONS.with(|enabled| enabled.set(true)); } fn finish_allocation_count() -> usize { COUNT_ALLOCATIONS.with(|enabled| enabled.set(false)); ALLOCATION_COUNT.with(Cell::get) } const REQUIRED_MUTATION_TARGETS: &[(&str, &str)] = &[ ( "omit_access_check", "forbidden_actions_report_stable_errno_and_do_not_mutate_live_state", ), ( "advance_requested_count", "generated_blob_sequences_preserve_exact_tagged_prefixes", ), ( "early_stream_eof", "raw_stream_descriptor_hides_record_boundaries_and_preserves_eof", ), ( "release_partial_record", "partial_record_cursor_keeps_capacity_pinned_until_full_release", ), ( "duplicate_stream_prefix", "raw_stream_descriptor_hides_record_boundaries_and_preserves_eof", ), ( "cross_wire_open_grant", "concurrent_descriptor_opens_keep_paths_and_payloads_correlated", ), ( "publish_aborted_blob", "legal_blob_sequences_cover_boundaries_offsets_mapping_close_and_abort", ), ( "reclaim_live_mapping", "raw_blob_mapping_is_bounded_and_can_outlive_descriptor_close", ), ( "writable_read_only_export", "test_raw_descriptor_blob_io_mapping_and_errno", ), ( "retain_cancelled_waiter", "cancellation_and_transport_faults_reclaim_waiters_and_preserve_unrelated_progress", ), ( "double_publication", "raw_blob_descriptor_enforces_offsets_rights_and_terminal_state", ), ( "authorize_unresolved_alias", "missing_and_unauthorized_paths_are_rejected", ), ( "silently_stage_direct_map", "raw_blob_mapping_is_bounded_and_can_outlive_descriptor_close", ), ( "touch_outside_region", "generated_blob_sequences_preserve_exact_tagged_prefixes", ), ( "leak_failed_open_lease", "cancelled_write_open_releases_queued_grant", ), ]; #[test] fn mutation_adequacy_targets_cover_every_required_bad_behavior() { let mut identifiers: Vec<&str> = REQUIRED_MUTATION_TARGETS .iter() .map(|(identifier, _)| *identifier) .collect(); assert_eq!(identifiers.len(), 15); identifiers.sort_unstable(); identifiers.dedup(); assert_eq!(identifiers.len(), 15, "mutation identifiers must be unique"); assert!( REQUIRED_MUTATION_TARGETS .iter() .all(|(_, oracle)| !oracle.is_empty()) ); } #[test] fn legal_blob_sequences_cover_boundaries_offsets_mapping_close_and_abort() { let harness = harness(2 << 20); let data_plane = harness.bootstrap.data_plane.clone(); future::block_on(async { for (case, length) in [0_usize, 1, 7, 64].into_iter().enumerate() { let logical = path(&format!("/runs/self/results/legal-{case}")); let payload: Vec = (0..length).map(|index| index as u8).collect(); let mut writer = data_plane .open(&logical, OpenOptions::staged_blob(length as u64)) .await .expect("legal staged open"); for chunk in payload.chunks(3) { writer.write_all(chunk).await.expect("legal partial write"); } writer.close().await.expect("legal publication"); let mut reader = data_plane .open(&logical, OpenOptions::read_only()) .await .expect("legal read open"); let mapping = reader .map(MapRequest { protection: Protection::Read, sharing: Sharing::Shared, target: MapTarget::Host, offset: 0, length: length as u64, }) .expect("legal read mapping"); assert_eq!(mapping.as_ref(), payload); assert_eq!(mapping.route(), TransferRoute::Direct); let mut observed = Vec::new(); let mut destination = [0xa5_u8; 11]; loop { let count = reader .read(&mut destination[..(case + 1).min(11)]) .await .expect("legal sequential read"); if count == 0 { break; } observed.extend_from_slice(&destination[..count]); } assert_eq!(observed, payload); assert_eq!(reader.read(&mut destination).await.expect("sticky eof"), 0); reader.close().await.expect("legal reader close"); } let aborted = path("/runs/self/results/legal-abort"); let mut writer = data_plane .open(&aborted, OpenOptions::staged_blob(4)) .await .expect("abort candidate"); writer.write_all(b"nope").await.expect("staged bytes"); writer.abort().await.expect("explicit abort"); assert_eq!( data_plane .open(&aborted, OpenOptions::read_only()) .await .expect_err("aborted blob is absent") .errno(), Errno::Enoent ); }); } proptest! { #![proptest_config(ProptestConfig::with_cases(8))] #[test] fn generated_blob_sequences_preserve_exact_tagged_prefixes( payload in prop::collection::vec(any::(), 0..96), write_size in 1_usize..17, read_size in 1_usize..17, ) { let harness = harness(2 << 20); let data_plane = harness.bootstrap.data_plane.clone(); future::block_on(async { let logical = path("/runs/self/results/property-sequence"); let mut writer = data_plane .open(&logical, OpenOptions::staged_blob(payload.len() as u64)) .await .expect("property writer"); for chunk in payload.chunks(write_size) { writer.write_all(chunk).await.expect("property write"); } writer.close().await.expect("property publish"); let mut reader = data_plane .open(&logical, OpenOptions::read_only()) .await .expect("property reader"); let mut destination = vec![0xa5_u8; read_size + 2]; let mut observed = Vec::new(); loop { destination.fill(0xa5); let count = reader .read(&mut destination[1..=read_size]) .await .expect("property read"); assert_eq!(destination[0], 0xa5); assert_eq!(destination[read_size + 1], 0xa5); if count == 0 { break; } observed.extend_from_slice(&destination[1..1 + count]); } // Kills: advance offset by requested rather than completed bytes, // touch destination outside the returned prefix, duplicate/drop data. prop_assert_eq!(observed, payload); Ok(()) })?; } } #[test] fn forbidden_actions_report_stable_errno_and_do_not_mutate_live_state() { let harness = harness(2 << 20); let data_plane = harness.bootstrap.data_plane.clone(); let logical = path("/runs/self/results/negative"); future::block_on(async { let mut writer = data_plane .open(&logical, OpenOptions::staged_blob(4)) .await .expect("negative fixture writer"); writer.write_all(b"safe").await.expect("fixture bytes"); assert_eq!( writer .read(&mut [0_u8; 1]) .await .expect_err("wrong access") .errno(), Errno::Ebadf ); writer.close().await.expect("fixture publication"); let invalid = OpenOptions { exclusive: true, ..OpenOptions::default() }; assert_eq!( data_plane .open(&logical, invalid) .await .expect_err("invalid flags") .errno(), Errno::Einval ); assert_eq!( data_plane .open( &logical, OpenOptions { nonblocking: true, ..OpenOptions::default() }, ) .await .expect_err("unsupported nonblocking") .errno(), Errno::Enotsup ); assert_eq!( data_plane .open(&path("/models/missing-raw"), OpenOptions::read_only()) .await .expect_err("missing path") .errno(), Errno::Enoent ); assert_eq!( data_plane .open( &path("/models/tiny-linear/weights"), OpenOptions::staged_blob(1), ) .await .expect_err("unauthorized write") .errno(), Errno::Eacces ); assert_eq!( data_plane .open( &logical, OpenOptions { exclusive: true, ..OpenOptions::staged_blob(4) }, ) .await .expect_err("exclusive existing") .errno(), Errno::Eexist ); let mut reader = data_plane .open(&logical, OpenOptions::read_only()) .await .expect("negative fixture reader"); assert_eq!( reader .map(MapRequest { protection: Protection::Read, sharing: Sharing::Shared, target: MapTarget::Host, offset: 3, length: 2, }) .expect_err("mapping overrun") .errno(), Errno::Einval ); assert_eq!( reader .write(b"x") .await .expect_err("read-only write") .errno(), Errno::Ebadf ); let mut bytes = [0_u8; 4]; reader .read_exact(&mut bytes) .await .expect("state unchanged"); assert_eq!(&bytes, b"safe"); reader.close().await.expect("first close"); assert_eq!( reader.close().await.expect_err("double close").errno(), Errno::Ebadf ); // Kills: omitted access checks, mutation after failed bounds checks, // silent flag downgrade, and revival after close. }); } #[test] fn exclusive_create_is_atomic_and_abort_releases_its_reservation() { let harness = harness(2 << 20); let data_plane = harness.bootstrap.data_plane.clone(); let logical = path("/runs/self/results/exclusive-race"); let options = OpenOptions { exclusive: true, ..OpenOptions::staged_blob(4) }; future::block_on(async { let first = data_plane.open(&logical, options.clone()); let second = data_plane.open(&logical, options.clone()); let (first, second) = future::zip(first, second).await; let (mut winner, loser) = match (first, second) { (Ok(winner), Err(loser)) | (Err(loser), Ok(winner)) => (winner, loser), (Ok(_), Ok(_)) => panic!("both exclusive creators succeeded"), (Err(first), Err(second)) => { panic!("both exclusive creators failed: {first}; {second}") } }; assert_eq!(loser.errno(), Errno::Eexist); winner.abort().await.expect("abort exclusive winner"); let mut replacement = data_plane .open(&logical, options) .await .expect("reservation released after abort"); replacement .abort() .await .expect("abort replacement reservation"); }); } #[test] fn raw_stream_abort_completes_before_peer_observes_broken_pipe() { let _stream_test = STREAM_TEST_LOCK.lock(); let harness = harness(2 << 20); let data_plane = harness.bootstrap.data_plane.clone(); let logical = path("/runs/self/results/raw-broken-pipe"); future::block_on(async { let seed_reader = data_plane.read_stream(&logical); let seed_writer = data_plane.write_stream(&logical); let (seed_reader, seed_writer) = future::zip(seed_reader, seed_writer).await; let mut seed_reader = seed_reader.expect("seed reader"); let mut seed_writer = seed_writer.expect("seed writer"); seed_writer.close().await.expect("seed writer close"); assert_eq!(seed_reader.read().await.expect("seed eof"), None); let reader = data_plane.open(&logical, OpenOptions::read_only()); let writer = data_plane.open( &logical, OpenOptions { access: AccessMode::WriteOnly, ..OpenOptions::default() }, ); let (reader, writer) = future::zip(reader, writer).await; let mut reader = reader.expect("raw reader"); let mut writer = writer.expect("raw writer"); reader.abort().await.expect("reader abort completion"); assert_eq!( writer .write(b"late") .await .expect_err("write after reader abort") .errno(), Errno::Epipe ); }); } #[test] fn concurrent_descriptor_opens_keep_paths_and_payloads_correlated() { let harness = harness(8 << 20); let data_plane = harness.bootstrap.data_plane.clone(); std::thread::scope(|scope| { for index in 0_u8..16 { let data_plane = data_plane.clone(); scope.spawn(move || { future::block_on(async move { let logical = path(&format!("/runs/self/results/concurrent-{index}")); let payload = [index; 32]; let mut writer = data_plane .open(&logical, OpenOptions::staged_blob(payload.len() as u64)) .await .expect("concurrent writer"); writer .write_all(&payload) .await .expect("concurrent payload"); writer.close().await.expect("concurrent publish"); }); }); } }); std::thread::scope(|scope| { for index in 0_u8..16 { let data_plane = data_plane.clone(); scope.spawn(move || { future::block_on(async move { let logical = path(&format!("/runs/self/results/concurrent-{index}")); let mut reader = data_plane .open(&logical, OpenOptions::read_only()) .await .expect("concurrent reader"); let mut payload = [0_u8; 32]; reader .read_exact(&mut payload) .await .expect("correlated read"); assert_eq!(payload, [index; 32]); }); }); } }); // Kills: accepting a grant for the wrong operation/path or crossing leases. } #[test] fn cancellation_and_transport_faults_reclaim_waiters_and_preserve_unrelated_progress() { let _stream_test = STREAM_TEST_LOCK.lock(); let harness = harness(2 << 20); let data_plane = harness.bootstrap.data_plane.clone(); let cancelled_path = path("/runs/self/results/cancelled-open"); future::block_on(async { let mut cancelled = Box::pin(data_plane.read_stream(&cancelled_path)); assert!(future::poll_once(cancelled.as_mut()).await.is_none()); drop(cancelled); std::thread::sleep(Duration::from_millis(10)); let reader = data_plane.read_stream(&cancelled_path); let writer = data_plane.write_stream(&cancelled_path); let (reader, writer) = future::zip(reader, writer).await; let mut reader = reader.expect("replacement reader"); let mut writer = writer.expect("replacement writer"); writer.close().await.expect("replacement close"); assert_eq!(reader.read().await.expect("replacement eof"), None); let mut unrelated = data_plane .open(&path("/models/second"), OpenOptions::read_only()) .await .expect("unrelated progress"); let mut bytes = [0_u8; 11]; unrelated .read_exact(&mut bytes) .await .expect("unrelated read"); assert_eq!(&bytes, b"second-blob"); }); let fault_harness = harness_with_transport(2 << 20, Arc::new(RejectingStreamTransport)); let fault_plane = fault_harness.bootstrap.data_plane.clone(); let fault_path = path("/runs/self/results/transport-fault"); future::block_on(async { let mut reader_open = Box::pin(fault_plane.read_stream(&fault_path)); assert!(future::poll_once(reader_open.as_mut()).await.is_none()); let writer_error = match fault_plane.write_stream(&fault_path).await { Ok(_) => panic!("faulted writer opened"), Err(error) => error, }; let reader_error = match reader_open.await { Ok(_) => panic!("faulted reader opened"), Err(error) => error, }; assert!(matches!( reader_error, DataPlaneError::PeerLost | DataPlaneError::StreamFault(_) )); assert!(matches!( writer_error, DataPlaneError::PeerLost | DataPlaneError::StreamFault(_) )); let mut unrelated = fault_plane .open(&path("/models/second"), OpenOptions::read_only()) .await .expect("fault isolation"); let mut bytes = [0_u8; 11]; unrelated .read_exact(&mut bytes) .await .expect("fault-isolated read"); assert_eq!(&bytes, b"second-blob"); }); // Kills: retain a cancelled waiter, complete only one matched endpoint, // and propagate one descriptor fault into unrelated operations. } #[test] fn steady_state_blob_primitives_allocate_no_heap_memory() { let harness = harness(2 << 20); let data_plane = harness.bootstrap.data_plane.clone(); future::block_on(async { let logical = path("/runs/self/results/allocation-count"); let mut writer = data_plane .open(&logical, OpenOptions::staged_blob(64)) .await .expect("allocation writer"); let payload = [7_u8; 64]; start_allocation_count(); let result = writer.write(&payload).await; let write_allocations = finish_allocation_count(); assert_eq!(result.expect("allocation-count write"), payload.len()); assert_eq!(write_allocations, 0, "blob write primitive allocated"); writer.close().await.expect("allocation publish"); let mut reader = data_plane .open(&logical, OpenOptions::read_only()) .await .expect("allocation reader"); let mut destination = [0_u8; 64]; start_allocation_count(); let result = reader.read(&mut destination).await; let read_allocations = finish_allocation_count(); assert_eq!(result.expect("allocation-count read"), destination.len()); assert_eq!(read_allocations, 0, "blob read primitive allocated"); assert_eq!(destination, payload); }); }