sanitizes bloom filters to avoid division by zero (#13714)
Pull requests received over the wire can cause a validator to panic because of division by zero in bloom filters: https://github.com/solana-labs/solana/blob/af08ba93e/runtime/src/bloom.rs#L86-L88
This commit is contained in:
parent
856693ac1f
commit
a8c29505f0
@ -79,7 +79,7 @@ impl CrdsFilter {
|
|||||||
let seed: u64 = seed.checked_shl(64 - mask_bits).unwrap_or(0x0);
|
let seed: u64 = seed.checked_shl(64 - mask_bits).unwrap_or(0x0);
|
||||||
seed | (!0u64).checked_shr(mask_bits).unwrap_or(!0x0) as u64
|
seed | (!0u64).checked_shr(mask_bits).unwrap_or(!0x0) as u64
|
||||||
}
|
}
|
||||||
pub fn max_items(max_bits: f64, false_rate: f64, num_keys: f64) -> f64 {
|
fn max_items(max_bits: f64, false_rate: f64, num_keys: f64) -> f64 {
|
||||||
let m = max_bits;
|
let m = max_bits;
|
||||||
let p = false_rate;
|
let p = false_rate;
|
||||||
let k = num_keys;
|
let k = num_keys;
|
||||||
@ -93,28 +93,26 @@ impl CrdsFilter {
|
|||||||
let buf = item.as_ref()[..8].try_into().unwrap();
|
let buf = item.as_ref()[..8].try_into().unwrap();
|
||||||
u64::from_le_bytes(buf)
|
u64::from_le_bytes(buf)
|
||||||
}
|
}
|
||||||
pub fn test_mask_u64(&self, item: u64, ones: u64) -> bool {
|
fn test_mask(&self, item: &Hash) -> bool {
|
||||||
let bits = item | ones;
|
|
||||||
bits == self.mask
|
|
||||||
}
|
|
||||||
pub fn test_mask(&self, item: &Hash) -> bool {
|
|
||||||
// only consider the highest mask_bits bits from the hash and set the rest to 1.
|
// only consider the highest mask_bits bits from the hash and set the rest to 1.
|
||||||
let ones = (!0u64).checked_shr(self.mask_bits).unwrap_or(!0u64);
|
let ones = (!0u64).checked_shr(self.mask_bits).unwrap_or(!0u64);
|
||||||
let bits = Self::hash_as_u64(item) | ones;
|
let bits = Self::hash_as_u64(item) | ones;
|
||||||
bits == self.mask
|
bits == self.mask
|
||||||
}
|
}
|
||||||
pub fn add(&mut self, item: &Hash) {
|
#[cfg(test)]
|
||||||
|
fn add(&mut self, item: &Hash) {
|
||||||
if self.test_mask(item) {
|
if self.test_mask(item) {
|
||||||
self.filter.add(item);
|
self.filter.add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn contains(&self, item: &Hash) -> bool {
|
#[cfg(test)]
|
||||||
|
fn contains(&self, item: &Hash) -> bool {
|
||||||
if !self.test_mask(item) {
|
if !self.test_mask(item) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
self.filter.contains(item)
|
self.filter.contains(item)
|
||||||
}
|
}
|
||||||
pub fn filter_contains(&self, item: &Hash) -> bool {
|
fn filter_contains(&self, item: &Hash) -> bool {
|
||||||
self.filter.contains(item)
|
self.filter.contains(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ use bv::BitVec;
|
|||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
use rand::{self, Rng};
|
use rand::{self, Rng};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use solana_sdk::sanitize::{Sanitize, SanitizeError};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
use std::{cmp, hash::Hasher, marker::PhantomData};
|
use std::{cmp, hash::Hasher, marker::PhantomData};
|
||||||
@ -45,7 +46,16 @@ impl<T: BloomHashIndex> fmt::Debug for Bloom<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: BloomHashIndex> solana_sdk::sanitize::Sanitize for Bloom<T> {}
|
impl<T: BloomHashIndex> Sanitize for Bloom<T> {
|
||||||
|
fn sanitize(&self) -> Result<(), SanitizeError> {
|
||||||
|
// Avoid division by zero in self.pos(...).
|
||||||
|
if self.bits.is_empty() {
|
||||||
|
Err(SanitizeError::InvalidValue)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: BloomHashIndex> Bloom<T> {
|
impl<T: BloomHashIndex> Bloom<T> {
|
||||||
pub fn new(num_bits: usize, keys: Vec<u64>) -> Self {
|
pub fn new(num_bits: usize, keys: Vec<u64>) -> Self {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user