use crate::convert::GgufToAprConverter;
use crate::gguf::{GGUF_MAGIC, GGUF_VERSION_V3};
use proptest::prelude::*;
fn create_convertible_pygmy() -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(&GGUF_MAGIC.to_le_bytes());
data.extend_from_slice(&GGUF_VERSION_V3.to_le_bytes());
data.extend_from_slice(&4u64.to_le_bytes()); data.extend_from_slice(&5u64.to_le_bytes());
let key1 = "general.architecture";
data.extend_from_slice(&(key1.len() as u64).to_le_bytes());
data.extend_from_slice(key1.as_bytes());
data.extend_from_slice(&8u32.to_le_bytes()); let val1 = "llama";
data.extend_from_slice(&(val1.len() as u64).to_le_bytes());
data.extend_from_slice(val1.as_bytes());
let key2 = "llama.block_count";
data.extend_from_slice(&(key2.len() as u64).to_le_bytes());
data.extend_from_slice(key2.as_bytes());
data.extend_from_slice(&4u32.to_le_bytes()); data.extend_from_slice(&1u32.to_le_bytes());
let key3 = "llama.embedding_length";
data.extend_from_slice(&(key3.len() as u64).to_le_bytes());
data.extend_from_slice(key3.as_bytes());
data.extend_from_slice(&4u32.to_le_bytes());
data.extend_from_slice(&64u32.to_le_bytes());
let key4 = "llama.attention.head_count";
data.extend_from_slice(&(key4.len() as u64).to_le_bytes());
data.extend_from_slice(key4.as_bytes());
data.extend_from_slice(&4u32.to_le_bytes());
data.extend_from_slice(&4u32.to_le_bytes());
let key5 = "llama.vocab_size";
data.extend_from_slice(&(key5.len() as u64).to_le_bytes());
data.extend_from_slice(key5.as_bytes());
data.extend_from_slice(&4u32.to_le_bytes());
data.extend_from_slice(&100u32.to_le_bytes());
let t1 = "token_embd.weight";
data.extend_from_slice(&(t1.len() as u64).to_le_bytes());
data.extend_from_slice(t1.as_bytes());
data.extend_from_slice(&2u32.to_le_bytes()); data.extend_from_slice(&100u64.to_le_bytes());
data.extend_from_slice(&64u64.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&0u64.to_le_bytes());
let t2 = "blk.0.attn_q.weight";
data.extend_from_slice(&(t2.len() as u64).to_le_bytes());
data.extend_from_slice(t2.as_bytes());
data.extend_from_slice(&2u32.to_le_bytes());
data.extend_from_slice(&64u64.to_le_bytes());
data.extend_from_slice(&64u64.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&((6400 * 4) as u64).to_le_bytes());
let t3 = "blk.0.ffn_gate.weight";
data.extend_from_slice(&(t3.len() as u64).to_le_bytes());
data.extend_from_slice(t3.as_bytes());
data.extend_from_slice(&2u32.to_le_bytes());
data.extend_from_slice(&128u64.to_le_bytes());
data.extend_from_slice(&64u64.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&(((6400 + 4096) * 4) as u64).to_le_bytes());
let t4 = "output.weight";
data.extend_from_slice(&(t4.len() as u64).to_le_bytes());
data.extend_from_slice(t4.as_bytes());
data.extend_from_slice(&2u32.to_le_bytes());
data.extend_from_slice(&100u64.to_le_bytes());
data.extend_from_slice(&64u64.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&(((6400 + 4096 + 8192) * 4) as u64).to_le_bytes());
while data.len() % 32 != 0 {
data.push(0);
}
let total_floats = 6400 + 4096 + 8192 + 6400;
for i in 0..total_floats {
data.extend_from_slice(&((i as f32) * 0.001).to_le_bytes());
}
data
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(500))]
#[test]
fn byte_smasher_convert_single_bit(
byte_idx in 0usize..8,
bit_idx in 0u8..8
) {
let mut data = create_convertible_pygmy();
if byte_idx < data.len() {
data[byte_idx] ^= 1 << bit_idx;
let result = GgufToAprConverter::convert(&data);
let _ = result;
}
}
#[test]
fn byte_smasher_zero_header_field(
field in prop::sample::select(&[
(0, 4), (4, 4), ])
) {
let mut data = create_convertible_pygmy();
let (start, len) = field;
for i in start..(start + len) {
if i < data.len() {
data[i] = 0;
}
}
let result = GgufToAprConverter::convert(&data);
prop_assert!(result.is_err());
}
#[test]
fn byte_smasher_ff_ranges(
start in 0usize..8,
len in 1usize..4
) {
let mut data = create_convertible_pygmy();
let end = (start + len).min(data.len());
for i in start..end {
data[i] = 0xFF;
}
let result = GgufToAprConverter::convert(&data);
let _ = result;
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(300))]
#[test]
fn fuzz_architecture_strings(
arch in prop_oneof![
Just("llama".to_string()),
Just("gpt2".to_string()),
Just("mistral".to_string()),
Just("falcon".to_string()),
Just(String::new()),
"[a-z]{0,50}",
"[\\x00-\\xff]{0,20}",
]
) {
let mut data = Vec::new();
data.extend_from_slice(&GGUF_MAGIC.to_le_bytes());
data.extend_from_slice(&GGUF_VERSION_V3.to_le_bytes());
data.extend_from_slice(&1u64.to_le_bytes());
data.extend_from_slice(&1u64.to_le_bytes());
let key = "general.architecture";
data.extend_from_slice(&(key.len() as u64).to_le_bytes());
data.extend_from_slice(key.as_bytes());
data.extend_from_slice(&8u32.to_le_bytes()); data.extend_from_slice(&(arch.len() as u64).to_le_bytes());
data.extend_from_slice(arch.as_bytes());
let t = "test.weight";
data.extend_from_slice(&(t.len() as u64).to_le_bytes());
data.extend_from_slice(t.as_bytes());
data.extend_from_slice(&1u32.to_le_bytes());
data.extend_from_slice(&4u64.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes());
while data.len() % 32 != 0 {
data.push(0);
}
data.extend_from_slice(&[0u8; 16]);
let result = GgufToAprConverter::convert(&data);
let _ = result;
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(200))]
#[test]
fn fuzz_config_fields(
block_count in prop_oneof![
Just(0u32), Just(1u32), Just(1000u32), 0u32..100
],
embedding_length in prop_oneof![
Just(0u32), Just(1u32), Just(4096u32), 1u32..1000
],
head_count in prop_oneof![
Just(0u32), Just(1u32), Just(64u32), 1u32..32
],
vocab_size in prop_oneof![
Just(0u32), Just(1u32), Just(32000u32), 1u32..10000
],
) {
let mut data = Vec::new();
data.extend_from_slice(&GGUF_MAGIC.to_le_bytes());
data.extend_from_slice(&GGUF_VERSION_V3.to_le_bytes());
data.extend_from_slice(&1u64.to_le_bytes());
data.extend_from_slice(&5u64.to_le_bytes());
let key1 = "general.architecture";
data.extend_from_slice(&(key1.len() as u64).to_le_bytes());
data.extend_from_slice(key1.as_bytes());
data.extend_from_slice(&8u32.to_le_bytes());
let val1 = "llama";
data.extend_from_slice(&(val1.len() as u64).to_le_bytes());
data.extend_from_slice(val1.as_bytes());
let key2 = "llama.block_count";
data.extend_from_slice(&(key2.len() as u64).to_le_bytes());
data.extend_from_slice(key2.as_bytes());
data.extend_from_slice(&4u32.to_le_bytes());
data.extend_from_slice(&block_count.to_le_bytes());
let key3 = "llama.embedding_length";
data.extend_from_slice(&(key3.len() as u64).to_le_bytes());
data.extend_from_slice(key3.as_bytes());
data.extend_from_slice(&4u32.to_le_bytes());
data.extend_from_slice(&embedding_length.to_le_bytes());
let key4 = "llama.attention.head_count";
data.extend_from_slice(&(key4.len() as u64).to_le_bytes());
data.extend_from_slice(key4.as_bytes());
data.extend_from_slice(&4u32.to_le_bytes());
data.extend_from_slice(&head_count.to_le_bytes());
let key5 = "llama.vocab_size";
data.extend_from_slice(&(key5.len() as u64).to_le_bytes());
data.extend_from_slice(key5.as_bytes());
data.extend_from_slice(&4u32.to_le_bytes());
data.extend_from_slice(&vocab_size.to_le_bytes());
let t = "token_embd.weight";
data.extend_from_slice(&(t.len() as u64).to_le_bytes());
data.extend_from_slice(t.as_bytes());
data.extend_from_slice(&2u32.to_le_bytes());
data.extend_from_slice(&(vocab_size.min(1000) as u64).to_le_bytes());
data.extend_from_slice(&(embedding_length.min(1000) as u64).to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes());
while data.len() % 32 != 0 {
data.push(0);
}
let tensor_size = (vocab_size.min(1000) as usize) * (embedding_length.min(1000) as usize);
for _ in 0..tensor_size.min(10000) {
data.extend_from_slice(&1.0f32.to_le_bytes());
}
let result = GgufToAprConverter::convert(&data);
let _ = result;
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(200))]
#[test]
fn fuzz_dimension_overflow(
dim1 in prop_oneof![
Just(0u64), Just(1u64), Just(1u64 << 30),
Just(1u64 << 20), Just(1u64 << 16),
1u64..10000
],
dim2 in prop_oneof![
Just(0u64), Just(1u64), Just(1u64 << 30),
Just(1u64 << 20), Just(1u64 << 16),
1u64..10000
],
) {
let mut data = Vec::new();
data.extend_from_slice(&GGUF_MAGIC.to_le_bytes());
data.extend_from_slice(&GGUF_VERSION_V3.to_le_bytes());
data.extend_from_slice(&1u64.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes());
let t = "overflow_tensor";
data.extend_from_slice(&(t.len() as u64).to_le_bytes());
data.extend_from_slice(t.as_bytes());
data.extend_from_slice(&2u32.to_le_bytes());
data.extend_from_slice(&dim1.to_le_bytes());
data.extend_from_slice(&dim2.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&0u64.to_le_bytes());
while data.len() % 32 != 0 {
data.push(0);
}
data.extend_from_slice(&[0u8; 64]);
let result = GgufToAprConverter::convert(&data);
let _ = result;
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(200))]
#[test]
fn fuzz_quant_types(
qtype in 0u32..30
) {
let mut data = Vec::new();
data.extend_from_slice(&GGUF_MAGIC.to_le_bytes());
data.extend_from_slice(&GGUF_VERSION_V3.to_le_bytes());
data.extend_from_slice(&1u64.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes());
let t = "quant_tensor";
data.extend_from_slice(&(t.len() as u64).to_le_bytes());
data.extend_from_slice(t.as_bytes());
data.extend_from_slice(&1u32.to_le_bytes());
data.extend_from_slice(&32u64.to_le_bytes()); data.extend_from_slice(&qtype.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes());
while data.len() % 32 != 0 {
data.push(0);
}
data.extend_from_slice(&[0u8; 256]);
let result = GgufToAprConverter::convert(&data);
let _ = result;
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn fuzz_trailing_data(
extra_bytes in 0usize..1000
) {
let mut data = create_convertible_pygmy();
for _ in 0..extra_bytes {
data.push(0xAB);
}
let result = GgufToAprConverter::convert(&data);
let _ = result;
}
#[test]
fn fuzz_truncated_data(
keep_bytes in 0usize..500
) {
let mut data = create_convertible_pygmy();
data.truncate(keep_bytes);
let result = GgufToAprConverter::convert(&data);
let _ = result;
}
}