use super::*;
use crate::{PathLocation, ShapeParams, SinkConstraintKind, WitnessRequest};
fn dummy_request(
sink_kind: SinkConstraintKind,
shape_params: Option<ShapeParams>,
) -> WitnessRequest {
WitnessRequest {
source: PathLocation {
file: "src/foo.c".to_string(),
byte_start: 0,
byte_end: 16,
},
sink: PathLocation {
file: "src/foo.c".to_string(),
byte_start: 100,
byte_end: 116,
},
statements: Vec::new(),
sink_kind,
shape_params,
}
}
#[test]
fn sprintf_unbounded_witness_overflows_dst_with_canary_padding() {
let dst_capacity = 64;
let w = sprintf_unbounded_witness(dst_capacity).expect("64-byte capacity witnesses");
assert_eq!(w.bytes.len(), dst_capacity + 16);
assert!(w.bytes.iter().all(|b| *b == b'A'));
assert!(matches!(w.entrypoint_kind, EntrypointKind::Cli));
}
#[test]
fn sprintf_unbounded_render_byte_count_matches_bytes() {
for dst_capacity in [0usize, 1, 64, 4096] {
let w = sprintf_unbounded_witness(dst_capacity).expect("small capacity witnesses");
let total = dst_capacity + CANARY_PADDING;
assert_eq!(
w.bytes.len(),
total,
"byte length must equal dst_capacity + CANARY_PADDING"
);
assert!(
w.render.contains(&format!("\"A\" * {total}")),
"render must state the exact byte count {total}, got: {}",
w.render
);
}
}
#[test]
fn gets_witness_render_matches_stack_overflow_bytes() {
for dst_capacity in [0usize, 1, 64] {
let w = gets_witness(dst_capacity).expect("small capacity witnesses");
let total = dst_capacity + CANARY_PADDING;
assert_eq!(w.bytes.len(), total);
assert!(
w.render.contains(&format!("\"A\" * {total}")),
"gets render must state {total}, got: {}",
w.render
);
}
}
#[test]
fn stack_overflow_witness_refuses_capacity_that_overflows_or_exceeds_cap() {
assert!(stack_overflow_witness(usize::MAX).is_none());
assert!(stack_overflow_witness(usize::MAX - CANARY_PADDING).is_none());
assert!(stack_overflow_witness(MAX_WITNESS_BYTES).is_none());
assert!(sprintf_unbounded_witness(usize::MAX).is_none());
assert!(gets_witness(usize::MAX).is_none());
let max_ok = MAX_WITNESS_BYTES - CANARY_PADDING;
let w = stack_overflow_witness(max_ok).expect("capacity at the cap boundary witnesses");
assert_eq!(w.bytes.len(), MAX_WITNESS_BYTES);
}
#[test]
fn sprintf_unbounded_dispatches_via_try_trivial_witness() {
let req = dummy_request(
SinkConstraintKind::StackOverflowSprintfUnbounded,
Some(ShapeParams::StackOverflow { dst_capacity: 128 }),
);
let out = try_trivial_witness(&req).expect("sprintf shape witnesses trivially");
assert_eq!(out.bytes.len(), 128 + 16);
}
#[test]
fn alloc_arith_wraps_emits_smallest_count_for_u32_arch() {
let w = alloc_arith_wraps_witness(24, 4).expect("encoder produces witness");
let count_bytes: [u8; 4] = w.bytes[..4].try_into().unwrap();
let n = u32::from_le_bytes(count_bytes);
let wrapped = (n as u64).wrapping_mul(24) & 0xFFFF_FFFF;
assert!(wrapped < (n as u64) * 24);
let write_len = w.bytes.len() - 4;
assert!(write_len as u64 > wrapped);
}
#[test]
fn alloc_arith_wraps_emits_smallest_count_for_u64_arch() {
let w = alloc_arith_wraps_witness(1024, 8).expect("encoder produces witness");
let count_bytes: [u8; 8] = w.bytes[..8].try_into().unwrap();
let n = u64::from_le_bytes(count_bytes);
let wrapped = (n as u128).wrapping_mul(1024) % (1u128 << 64);
assert!(wrapped < (n as u128) * 1024 || wrapped == 0);
}
#[test]
fn alloc_arith_wraps_returns_none_for_unit_struct_size() {
assert!(alloc_arith_wraps_witness(1, 8).is_none());
}
#[test]
fn alloc_arith_wraps_dispatches_via_try_trivial_witness() {
let req = dummy_request(
SinkConstraintKind::HeapOverflowAllocArithWraps,
Some(ShapeParams::AllocArithWraps {
struct_size: 16,
ptr_width_bytes: 8,
}),
);
let out = try_trivial_witness(&req).expect("alloc-wrap witnesses trivially");
assert_eq!(out.bytes.len() - 8 > 0, true);
}
#[test]
fn oob_write_index_positive_overflow_emits_capacity_as_first_illegal_index() {
let capacity = 32;
let w = oob_write_index_witness(capacity, false, 4).expect("encoder produces witness");
let idx_bytes: [u8; 4] = w.bytes[..4].try_into().unwrap();
let idx = u32::from_le_bytes(idx_bytes);
assert_eq!(idx, capacity as u32);
assert_eq!(w.bytes.len(), 5);
assert_eq!(w.bytes[4], b'C');
}
#[test]
fn oob_write_index_signed_variant_emits_negative_one() {
let w = oob_write_index_witness(32, true, 4).expect("encoder produces witness");
let idx_bytes: [u8; 4] = w.bytes[..4].try_into().unwrap();
let idx = i32::from_le_bytes(idx_bytes);
assert_eq!(idx, -1);
assert_eq!(w.bytes.len(), 5);
}
#[test]
fn oob_write_index_emits_8_byte_index_on_64bit_sink() {
let capacity = 256;
let w = oob_write_index_witness(capacity, false, 8).expect("encoder produces witness");
let idx_bytes: [u8; 8] = w.bytes[..8].try_into().unwrap();
let idx = u64::from_le_bytes(idx_bytes);
assert_eq!(idx, capacity as u64);
assert_eq!(w.bytes.len(), 9);
}
#[test]
fn oob_write_index_signed_variant_emits_8_byte_negative_one() {
let w = oob_write_index_witness(32, true, 8).expect("encoder produces witness");
let idx_bytes: [u8; 8] = w.bytes[..8].try_into().unwrap();
let idx = i64::from_le_bytes(idx_bytes);
assert_eq!(idx, -1);
assert_eq!(w.bytes.len(), 9);
}
#[test]
fn oob_write_index_returns_none_when_capacity_exceeds_index_width() {
assert!(
oob_write_index_witness((u32::MAX as usize).wrapping_add(1), false, 4).is_none()
|| (u32::MAX as usize).checked_add(1).is_none()
);
}
#[test]
fn oob_write_index_returns_none_for_unsupported_width() {
assert!(oob_write_index_witness(32, false, 2).is_none());
assert!(oob_write_index_witness(32, true, 16).is_none());
}
#[test]
fn oob_write_index_dispatches_via_try_trivial_witness() {
let req = dummy_request(
SinkConstraintKind::OobWriteUnboundedIndex,
Some(ShapeParams::OobWriteIndex {
capacity: 256,
signed: false,
index_width_bytes: 4,
}),
);
let out = try_trivial_witness(&req).expect("OOB-index witnesses trivially");
let idx = u32::from_le_bytes(out.bytes[..4].try_into().unwrap());
assert_eq!(idx, 256);
}
#[test]
fn alloc_arith_wraps_admits_zero_struct_size() {
let w = alloc_arith_wraps_witness(0, 8).expect("ZST shape produces a witness");
let count_bytes: [u8; 8] = w.bytes[..8].try_into().unwrap();
let n = u64::from_le_bytes(count_bytes);
assert_eq!(n, 1);
assert!(w.bytes.len() > 8);
}
#[test]
fn alloc_arith_wraps_returns_none_for_struct_size_one() {
assert!(alloc_arith_wraps_witness(1, 8).is_none());
}
#[test]
fn try_trivial_witness_returns_none_when_shape_params_missing() {
let req = dummy_request(SinkConstraintKind::StackOverflowSprintfUnbounded, None);
assert!(try_trivial_witness(&req).is_none());
}
#[test]
fn try_trivial_witness_returns_none_when_shape_params_wrong_variant() {
let req = dummy_request(
SinkConstraintKind::StackOverflowStrcpy,
Some(ShapeParams::OobWriteIndex {
capacity: 32,
signed: false,
index_width_bytes: 4,
}),
);
assert!(try_trivial_witness(&req).is_none());
}