fix: error on degenerate Huffman code in zlib inflate instead of panicking
decode_symbol walked the Huffman tree while either child existed, then unwrapped the child selected by the current bit. A malformed/incomplete code table (fully attacker-controlled, since search results arrive zlib-compressed from peers) can leave a node with only one child; reading the opposite bit unwrapped None and panicked, unwinding out of the actor job. It now returns a decode error, which propagates as a CompressionError and is handled gracefully.
This commit is contained in:
@@ -170,11 +170,12 @@ fn decode_symbol(
|
|||||||
let mut node = &t.root;
|
let mut node = &t.root;
|
||||||
while node.left.is_some() || node.right.is_some() {
|
while node.left.is_some() || node.right.is_some() {
|
||||||
let b = r.read_bit()?;
|
let b = r.read_bit()?;
|
||||||
node = if b != 0 {
|
let next = if b != 0 { &node.right } else { &node.left };
|
||||||
node.right.as_ref().unwrap()
|
// A degenerate/incomplete Huffman code (attacker-controlled) can point
|
||||||
} else {
|
// at a missing child; error out instead of unwrap-panicking.
|
||||||
node.left.as_ref().unwrap()
|
node = next
|
||||||
};
|
.as_ref()
|
||||||
|
.ok_or("Invalid Huffman code".to_string())?;
|
||||||
}
|
}
|
||||||
node.symbol.ok_or("No symbol found".to_string())
|
node.symbol.ok_or("No symbol found".to_string())
|
||||||
}
|
}
|
||||||
@@ -364,6 +365,18 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decode_symbol_on_degenerate_tree_errors_instead_of_panicking() {
|
||||||
|
// A malformed Huffman table from an untrusted peer can produce a node
|
||||||
|
// with only one child. Inserting codeword "0" gives the root a left
|
||||||
|
// child but no right child; decoding a '1' bit must error rather than
|
||||||
|
// unwrap-panic.
|
||||||
|
let mut tree = HuffmanTree::new();
|
||||||
|
tree.insert(0, 1, 42);
|
||||||
|
let mut reader = BitReader::new(vec![0b0000_0001]); // first bit = 1
|
||||||
|
assert!(decode_symbol(&mut reader, &tree).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bitreader_read_bits() {
|
fn test_bitreader_read_bits() {
|
||||||
let data = vec![0b11010010, 0b10110101];
|
let data = vec![0b11010010, 0b10110101];
|
||||||
|
|||||||
Reference in New Issue
Block a user