shadowsocks/shadowsocks-rust · error

BloomFilter2

Error message

BloomFilter2

What it means

Identical to BloomFilter1 but for the second of the two rotating bloom filters in ppbloom's Self constructor. Bloom::new_for_fp_rate for the second filter failed (most commonly zero item capacity after item_count /= 2) and expect('BloomFilter2') panics.

Source

Thrown at crates/shadowsocks/src/security/replay/ppbloom.rs:51

    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. Ensure item_count is at least 2 before construction
  2. Guard capacity at the configuration layer (minimum allowed replay filter size)
  3. Replace expect with error propagation in a wrapper if dynamic capacities are unavoidable
  4. Update to a shadowsocks-rust version that validates capacity in the constructor

Example fix

// before
Ppbloom::with_capacity(0, FP_P)
// after
if capacity < 2 { capacity = 2; }
Ppbloom::with_capacity(capacity, FP_P)
Defensive patterns

Strategy: validation

Validate before calling

let item_count = item_count.max(2);
// both bloom filters now receive at least 1 item after the internal /= 2

Try / catch

// clamp before construction; do not attempt to catch this panic
let capacity = if capacity < 2 { 2 } else { capacity };

Prevention

When it happens

Trigger: Public constructor Self::with_capacity with item_count 0 or 1, causing the second Bloom::new_for_fp_rate(item_count, fp_p) call to fail after the halving step.

Common situations: Zero/near-zero capacity settings flowing from server config; unit tests constructing Ppbloom with tiny capacities; capacity values reduced by earlier refactors without updating call sites.

Related errors


AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09). Data as JSON: /api/errors/bea68ecb7d5315be. Report an issue: GitHub.