* Fix overflow in entry hash count verification (cherry picked from commitd611337394
) * clippy (cherry picked from commit01a4889b53
) Co-authored-by: Justin Starry <justin@solana.com>
This commit is contained in:
@ -622,7 +622,7 @@ impl EntrySlice for [Entry] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for entry in self {
|
for entry in self {
|
||||||
*tick_hash_count += entry.num_hashes;
|
*tick_hash_count = tick_hash_count.saturating_add(entry.num_hashes);
|
||||||
if entry.is_tick() {
|
if entry.is_tick() {
|
||||||
if *tick_hash_count != hashes_per_tick {
|
if *tick_hash_count != hashes_per_tick {
|
||||||
warn!(
|
warn!(
|
||||||
@ -900,52 +900,117 @@ mod tests {
|
|||||||
fn test_verify_tick_hash_count() {
|
fn test_verify_tick_hash_count() {
|
||||||
let hashes_per_tick = 10;
|
let hashes_per_tick = 10;
|
||||||
let tx = Transaction::default();
|
let tx = Transaction::default();
|
||||||
let tx_entry = Entry::new(&Hash::default(), 1, vec![tx]);
|
|
||||||
let full_tick_entry = Entry::new_tick(hashes_per_tick, &Hash::default());
|
let no_hash_tx_entry = Entry {
|
||||||
let partial_tick_entry = Entry::new_tick(hashes_per_tick - 1, &Hash::default());
|
transactions: vec![tx.clone()],
|
||||||
|
..Entry::default()
|
||||||
|
};
|
||||||
|
let single_hash_tx_entry = Entry {
|
||||||
|
transactions: vec![tx.clone()],
|
||||||
|
num_hashes: 1,
|
||||||
|
..Entry::default()
|
||||||
|
};
|
||||||
|
let partial_tx_entry = Entry {
|
||||||
|
num_hashes: hashes_per_tick - 1,
|
||||||
|
transactions: vec![tx.clone()],
|
||||||
|
..Entry::default()
|
||||||
|
};
|
||||||
|
let full_tx_entry = Entry {
|
||||||
|
num_hashes: hashes_per_tick,
|
||||||
|
transactions: vec![tx.clone()],
|
||||||
|
..Entry::default()
|
||||||
|
};
|
||||||
|
let max_hash_tx_entry = Entry {
|
||||||
|
transactions: vec![tx],
|
||||||
|
num_hashes: u64::MAX,
|
||||||
|
..Entry::default()
|
||||||
|
};
|
||||||
|
|
||||||
let no_hash_tick_entry = Entry::new_tick(0, &Hash::default());
|
let no_hash_tick_entry = Entry::new_tick(0, &Hash::default());
|
||||||
let single_hash_tick_entry = Entry::new_tick(1, &Hash::default());
|
let single_hash_tick_entry = Entry::new_tick(1, &Hash::default());
|
||||||
|
let partial_tick_entry = Entry::new_tick(hashes_per_tick - 1, &Hash::default());
|
||||||
|
let full_tick_entry = Entry::new_tick(hashes_per_tick, &Hash::default());
|
||||||
|
let max_hash_tick_entry = Entry::new_tick(u64::MAX, &Hash::default());
|
||||||
|
|
||||||
let no_ticks = vec![];
|
// empty batch should succeed if hashes_per_tick hasn't been reached
|
||||||
let mut tick_hash_count = 0;
|
let mut tick_hash_count = 0;
|
||||||
assert!(no_ticks.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
let mut entries = vec![];
|
||||||
|
assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
||||||
assert_eq!(tick_hash_count, 0);
|
assert_eq!(tick_hash_count, 0);
|
||||||
|
|
||||||
// validation is disabled when hashes_per_tick == 0
|
// empty batch should fail if hashes_per_tick has been reached
|
||||||
let no_hash_tick = vec![no_hash_tick_entry.clone()];
|
tick_hash_count = hashes_per_tick;
|
||||||
assert!(no_hash_tick.verify_tick_hash_count(&mut tick_hash_count, 0));
|
assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
||||||
assert_eq!(tick_hash_count, 0);
|
|
||||||
|
|
||||||
// validation is disabled when hashes_per_tick == 0
|
|
||||||
let tx_and_no_hash_tick = vec![tx_entry.clone(), no_hash_tick_entry];
|
|
||||||
assert!(tx_and_no_hash_tick.verify_tick_hash_count(&mut tick_hash_count, 0));
|
|
||||||
assert_eq!(tick_hash_count, 0);
|
|
||||||
|
|
||||||
let single_tick = vec![full_tick_entry];
|
|
||||||
assert!(single_tick.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
|
||||||
assert_eq!(tick_hash_count, 0);
|
|
||||||
assert!(!single_tick.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick - 1));
|
|
||||||
assert_eq!(tick_hash_count, hashes_per_tick);
|
assert_eq!(tick_hash_count, hashes_per_tick);
|
||||||
tick_hash_count = 0;
|
tick_hash_count = 0;
|
||||||
|
|
||||||
let ticks_and_txs = vec![tx_entry.clone(), partial_tick_entry.clone()];
|
// validation is disabled when hashes_per_tick == 0
|
||||||
assert!(ticks_and_txs.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
entries = vec![max_hash_tx_entry.clone()];
|
||||||
|
assert!(entries.verify_tick_hash_count(&mut tick_hash_count, 0));
|
||||||
assert_eq!(tick_hash_count, 0);
|
assert_eq!(tick_hash_count, 0);
|
||||||
|
|
||||||
let partial_tick = vec![partial_tick_entry];
|
// partial tick should fail
|
||||||
assert!(!partial_tick.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
entries = vec![partial_tick_entry.clone()];
|
||||||
|
assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
||||||
assert_eq!(tick_hash_count, hashes_per_tick - 1);
|
assert_eq!(tick_hash_count, hashes_per_tick - 1);
|
||||||
tick_hash_count = 0;
|
tick_hash_count = 0;
|
||||||
|
|
||||||
let tx_entries: Vec<Entry> = (0..hashes_per_tick - 1).map(|_| tx_entry.clone()).collect();
|
// full tick entry should succeed
|
||||||
let tx_entries_and_tick = [tx_entries, vec![single_hash_tick_entry]].concat();
|
entries = vec![no_hash_tx_entry, full_tick_entry.clone()];
|
||||||
assert!(tx_entries_and_tick.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
||||||
assert_eq!(tick_hash_count, 0);
|
assert_eq!(tick_hash_count, 0);
|
||||||
|
|
||||||
let too_many_tx_entries: Vec<Entry> =
|
// oversized tick entry should fail
|
||||||
(0..hashes_per_tick).map(|_| tx_entry.clone()).collect();
|
assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick - 1));
|
||||||
assert!(!too_many_tx_entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
|
||||||
assert_eq!(tick_hash_count, hashes_per_tick);
|
assert_eq!(tick_hash_count, hashes_per_tick);
|
||||||
|
tick_hash_count = 0;
|
||||||
|
|
||||||
|
// partial tx entry without tick entry should succeed
|
||||||
|
entries = vec![partial_tx_entry];
|
||||||
|
assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
||||||
|
assert_eq!(tick_hash_count, hashes_per_tick - 1);
|
||||||
|
tick_hash_count = 0;
|
||||||
|
|
||||||
|
// full tx entry with tick entry should succeed
|
||||||
|
entries = vec![full_tx_entry.clone(), no_hash_tick_entry];
|
||||||
|
assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
||||||
|
assert_eq!(tick_hash_count, 0);
|
||||||
|
|
||||||
|
// full tx entry with oversized tick entry should fail
|
||||||
|
entries = vec![full_tx_entry.clone(), single_hash_tick_entry.clone()];
|
||||||
|
assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
||||||
|
assert_eq!(tick_hash_count, hashes_per_tick + 1);
|
||||||
|
tick_hash_count = 0;
|
||||||
|
|
||||||
|
// full tx entry without tick entry should fail
|
||||||
|
entries = vec![full_tx_entry];
|
||||||
|
assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
||||||
|
assert_eq!(tick_hash_count, hashes_per_tick);
|
||||||
|
tick_hash_count = 0;
|
||||||
|
|
||||||
|
// tx entry and a tick should succeed
|
||||||
|
entries = vec![single_hash_tx_entry.clone(), partial_tick_entry];
|
||||||
|
assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
||||||
|
assert_eq!(tick_hash_count, 0);
|
||||||
|
|
||||||
|
// many tx entries and a tick should succeed
|
||||||
|
let tx_entries: Vec<Entry> = (0..hashes_per_tick - 1)
|
||||||
|
.map(|_| single_hash_tx_entry.clone())
|
||||||
|
.collect();
|
||||||
|
entries = [tx_entries, vec![single_hash_tick_entry]].concat();
|
||||||
|
assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
||||||
|
assert_eq!(tick_hash_count, 0);
|
||||||
|
|
||||||
|
// check overflow saturation should fail
|
||||||
|
entries = vec![full_tick_entry.clone(), max_hash_tick_entry];
|
||||||
|
assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
||||||
|
assert_eq!(tick_hash_count, u64::MAX);
|
||||||
|
tick_hash_count = 0;
|
||||||
|
|
||||||
|
// check overflow saturation should fail
|
||||||
|
entries = vec![max_hash_tx_entry, full_tick_entry];
|
||||||
|
assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
|
||||||
|
assert_eq!(tick_hash_count, u64::MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Reference in New Issue
Block a user