185 lines
6.0 KiB
Rust
185 lines
6.0 KiB
Rust
#![no_main]
|
|
|
|
use libfuzzer_sys::fuzz_target;
|
|
use announcement::Announcement;
|
|
use std::sync::{Arc, Barrier};
|
|
use std::thread;
|
|
|
|
fuzz_target!(|data: &[u8]| {
|
|
if data.len() < 4 {
|
|
return;
|
|
}
|
|
|
|
// Extract test parameters from fuzz data
|
|
let num_threads = (data[0] % 8) as usize + 2; // 2-9 threads
|
|
let announce_delay = data[1] % 3; // 0-2 delay pattern
|
|
let pattern = data[2] % 4;
|
|
|
|
match pattern {
|
|
0 => {
|
|
// Test concurrent listeners
|
|
let (announcer, announcement) = Announcement::new();
|
|
let barrier = Arc::new(Barrier::new(num_threads + 1));
|
|
|
|
let handles: Vec<_> = (0..num_threads)
|
|
.map(|i| {
|
|
let listener = announcement.listener();
|
|
let barrier = barrier.clone();
|
|
|
|
thread::spawn(move || {
|
|
// Synchronize start
|
|
barrier.wait();
|
|
|
|
// All threads try to listen
|
|
let value = listener.try_listen();
|
|
|
|
// Return thread id and value
|
|
(i, value)
|
|
})
|
|
})
|
|
.collect();
|
|
|
|
// Start all threads together
|
|
barrier.wait();
|
|
|
|
// Announce after threads are started
|
|
match announce_delay {
|
|
0 => {
|
|
// Immediate announce
|
|
let _ = announcer.announce(42u32);
|
|
}
|
|
1 => {
|
|
// Short delay
|
|
thread::sleep(std::time::Duration::from_micros(10));
|
|
let _ = announcer.announce(42u32);
|
|
}
|
|
_ => {
|
|
// Drop without announcing
|
|
drop(announcer);
|
|
}
|
|
}
|
|
|
|
// Collect results
|
|
for handle in handles {
|
|
let (id, value) = handle.join().unwrap();
|
|
|
|
if announce_delay < 2 {
|
|
// Should get value if we announced
|
|
// (might be None if thread checked before announce)
|
|
if let Some(v) = value {
|
|
assert_eq!(v, 42u32, "Thread {} got wrong value", id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
1 => {
|
|
// Test concurrent clone and listener creation
|
|
let (announcer, announcement) = Announcement::new();
|
|
|
|
let handles: Vec<_> = (0..num_threads)
|
|
.map(|_| {
|
|
let ann_clone = announcement.clone();
|
|
|
|
thread::spawn(move || {
|
|
// Create listener from clone
|
|
let listener = ann_clone.listener();
|
|
|
|
// Try to read
|
|
listener.try_listen()
|
|
})
|
|
})
|
|
.collect();
|
|
|
|
// Announce while threads are creating listeners
|
|
let _ = announcer.announce(42u32);
|
|
|
|
// All should eventually see the value
|
|
for handle in handles {
|
|
let value = handle.join().unwrap();
|
|
// May or may not have seen the value depending on timing
|
|
if let Some(v) = value {
|
|
assert_eq!(v, 42u32);
|
|
}
|
|
}
|
|
}
|
|
2 => {
|
|
// Test concurrent is_announced checks
|
|
let (announcer, announcement) = Announcement::new();
|
|
|
|
let handles: Vec<_> = (0..num_threads)
|
|
.map(|_| {
|
|
let ann_clone = announcement.clone();
|
|
|
|
thread::spawn(move || {
|
|
// Check announced state multiple times
|
|
let mut states = vec![];
|
|
for _ in 0..10 {
|
|
states.push(ann_clone.is_announced());
|
|
}
|
|
states
|
|
})
|
|
})
|
|
.collect();
|
|
|
|
// Announce in the middle
|
|
thread::sleep(std::time::Duration::from_micros(5));
|
|
let _ = announcer.announce(42u32);
|
|
|
|
// Collect all state observations
|
|
for handle in handles {
|
|
let states = handle.join().unwrap();
|
|
|
|
// States should only transition from false to true, never back
|
|
let mut seen_true = false;
|
|
for state in states {
|
|
if seen_true {
|
|
assert!(state, "is_announced went from true to false");
|
|
}
|
|
if state {
|
|
seen_true = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ => {
|
|
// Test concurrent close checks
|
|
let (announcer, announcement) = Announcement::<u32>::new();
|
|
|
|
let handles: Vec<_> = (0..num_threads)
|
|
.map(|_| {
|
|
let ann_clone = announcement.clone();
|
|
|
|
thread::spawn(move || {
|
|
// Check closed state multiple times
|
|
let mut states = vec![];
|
|
for _ in 0..10 {
|
|
states.push(ann_clone.is_closed());
|
|
}
|
|
states
|
|
})
|
|
})
|
|
.collect();
|
|
|
|
// Close in the middle
|
|
thread::sleep(std::time::Duration::from_micros(5));
|
|
announcer.close();
|
|
|
|
// Collect all state observations
|
|
for handle in handles {
|
|
let states = handle.join().unwrap();
|
|
|
|
// States should only transition from false to true, never back
|
|
let mut seen_true = false;
|
|
for state in states {
|
|
if seen_true {
|
|
assert!(state, "is_closed went from true to false");
|
|
}
|
|
if state {
|
|
seen_true = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|