{"record":{"id":"53785cfe69d2afdc","repo":"shadowsocks/shadowsocks-rust","slug":"bloomfilter1","errorCode":null,"errorMessage":"BloomFilter1","messagePattern":"BloomFilter1","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/shadowsocks/src/security/replay/ppbloom.rs","lineNumber":50,"sourceCode":"    blooms: [Bloom<[u8]>; 2],\n    bloom_count: [usize; 2],\n    item_count: usize,\n    current: usize,\n}\n\nimpl PingPongBloom {\n    pub fn new(ty: ServerType) -> Self {\n        let (mut item_count, fp_p) = if ty.is_local() {\n            (BF_NUM_ENTRIES_FOR_CLIENT, BF_ERROR_RATE_FOR_CLIENT)\n        } else {\n            (BF_NUM_ENTRIES_FOR_SERVER, BF_ERROR_RATE_FOR_SERVER)\n        };\n\n        item_count /= 2;\n\n        Self {\n            blooms: [\n                Bloom::new_for_fp_rate(item_count, fp_p).expect(\"BloomFilter1\"),\n                Bloom::new_for_fp_rate(item_count, fp_p).expect(\"BloomFilter2\"),\n            ],\n            bloom_count: [0, 0],\n            item_count,\n            current: 0,\n        }\n    }\n\n    // Check if data in `buf` exist.\n    //\n    // Set into the current bloom filter if not exist.\n    //\n    // Return `true` if data exist in bloom filter.\n    pub fn check_and_set(&mut self, buf: &[u8]) -> bool {\n        for bloom in &self.blooms {\n            if bloom.check(buf) {\n                return true;\n            }","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/shadowsocks/shadowsocks-rust/blob/8eb0f0a65b1d976ab6bed5787327ef86529b0435/crates/shadowsocks/src/security/replay/ppbloom.rs#L32-L68","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlet filter = Ppbloom::with_capacity(capacity, 1e-6); // capacity = 1 -> item_count 0\n// after\nlet capacity = capacity.max(2);\nlet filter = Ppbloom::with_capacity(capacity, 1e-6);","handlingStrategy":"validation","validationCode":"if capacity < 2 {\n    return Err(\"replay filter capacity must be >= 2\".to_string());\n}\nlet filter = Ppbloom::with_capacity(capacity, fp_p);","typeGuard":"fn valid_capacity(cap: usize) -> bool { cap >= 2 }","tryCatchPattern":"// constructor panics; clamp inputs at the call site instead of catching\nlet capacity = capacity.max(2);","preventionTips":["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"],"tags":["rust","shadowsocks","bloom-filter","replay-protection","panic"],"backgroundTag":"invalid-constructor-argument","analyzedSha":"8eb0f0a65b1d976ab6bed5787327ef86529b0435","analyzedAt":"2026-09-09T12:20:43.168Z","contentChangedAt":"2026-09-09T12:20:43.168Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}