Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/hpax/huffman.ex
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ defmodule HPAX.Huffman do
end
end

# Anything left over here is 8 or more bits that don't match any Huffman code (the clauses
# above only match a complete code, the empty binary, or up to 7 bits of valid EOS padding).
# This can only happen with a malformed/malicious encoding, since a real encoder never
# produces output with more than 7 trailing bits that aren't a complete code.
def decode(<<_rest::bitstring>>) do
throw({:hpax, {:protocol_error, :invalid_huffman_encoding}})
end

## Helpers

@compile {:inline, take_significant_bits: 3}
Expand Down
13 changes: 13 additions & 0 deletions test/hpax/huffman_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,17 @@ defmodule HPAX.HuffmanTest do
assert Huffman.decode(encoded) == :huffman.decode(encoded)
end
end

describe "decode/1 with malformed input" do
test "raises a catchable :hpax error instead of a FunctionClauseError when the trailing bits don't correspond to any complete code" do
# 8+ bits of all 1s is a valid prefix of the 30-bit EOS code but is neither a complete
# code nor <= 7 bits of valid EOS padding, so it can't come from a real encoder
for n_bits <- [8, 9, 15, 16, 23, 29] do
invalid = for _ <- 1..n_bits, into: <<>>, do: <<1::1>>

assert catch_throw(Huffman.decode(invalid)) ==
{:hpax, {:protocol_error, :invalid_huffman_encoding}}
end
end
end
end
Loading