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

  1. Pass an item_count >= 2 so that after division each bloom gets at least 1 item
  2. Clamp capacity to a sane minimum (e.g. max(2, item_count)) before constructing Ppbloom
  3. Audit the configuration value feeding with_capacity and reject 0/1 at parse time
  4. 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

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.