shadowsocks/shadowsocks-rust · error
BloomFilter1
Error message
BloomFilter1
What it means
Panic while constructing the dual-bloom replay filter in shadowsocks' ppbloom module: blooms::new_for_fp_rate(item_count, fp_p) fails to allocate/compute a bloom filter (the bloom crate returns Err, e.g. for zero capacity) and expect('BloomFilter1') panics. After halving, item_count must remain a positive value the bloom filter can size.
Source
Thrown at crates/shadowsocks/src/security/replay/ppbloom.rs:50
blooms: [Bloom<[u8]>; 2],
bloom_count: [usize; 2],
item_count: usize,
current: usize,
}
impl PingPongBloom {
pub fn new(ty: ServerType) -> Self {
let (mut item_count, fp_p) = if ty.is_local() {
(BF_NUM_ENTRIES_FOR_CLIENT, BF_ERROR_RATE_FOR_CLIENT)
} else {
(BF_NUM_ENTRIES_FOR_SERVER, BF_ERROR_RATE_FOR_SERVER)
};
item_count /= 2;
Self {
blooms: [
Bloom::new_for_fp_rate(item_count, fp_p).expect("BloomFilter1"),
Bloom::new_for_fp_rate(item_count, fp_p).expect("BloomFilter2"),
],
bloom_count: [0, 0],
item_count,
current: 0,
}
}
// Check if data in `buf` exist.
//
// Set into the current bloom filter if not exist.
//
// Return `true` if data exist in bloom filter.
pub fn check_and_set(&mut self, buf: &[u8]) -> bool {
for bloom in &self.blooms {
if bloom.check(buf) {
return true;
}View on GitHub (pinned to 8eb0f0a65b)
Solutions
- Pass an item_count >= 2 so that after division each bloom gets at least 1 item
- Clamp capacity to a sane minimum (e.g. max(2, item_count)) before constructing Ppbloom
- Audit the configuration value feeding with_capacity and reject 0/1 at parse time
- Check upstream shadowsocks-rust for fixes if a previously valid capacity now panics
Example fix
// before let filter = Ppbloom::with_capacity(capacity, 1e-6); // capacity = 1 -> item_count 0 // after let capacity = capacity.max(2); let filter = Ppbloom::with_capacity(capacity, 1e-6);
Defensive patterns
Strategy: validation
Validate before calling
if capacity < 2 {
return Err("replay filter capacity must be >= 2".to_string());
}
let filter = Ppbloom::with_capacity(capacity, fp_p); Type guard
fn valid_capacity(cap: usize) -> bool { cap >= 2 } Try / catch
// constructor panics; clamp inputs at the call site instead of catching let capacity = capacity.max(2);
Prevention
- Never configure replay-filter capacity below 2 (it is halved internally)
- Enforce a minimum in the config parser for UDP/relay capacity settings
- Add a property test that constructs Ppbloom across a range of capacities
- Read the ppbloom source for the item_count /= 2 behavior before sizing
When it happens
Trigger: Calling Ppbloom::with_capacity(item_count, fp_p) (public constructor) with an item_count of 0 or 1, so after item_count /= 2 the bloom filter is created with 0 items and bloom::new_for_fp_rate returns Err.
Common situations: Configuring a replay-filter capacity of 0 or 1 (e.g. from a UDP server capacity setting of 0/1); refactoring that passes uncounted client numbers; integer division dropping a capacity of 1 to 0.
Related errors
AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09).
Data as JSON: /api/errors/53785cfe69d2afdc.
Report an issue: GitHub.