Close the last of the five defects
This commit is contained in:
parent
bd4ccf19b5
commit
724e9b0433
1 changed files with 15 additions and 19 deletions
34
README.md
34
README.md
|
|
@ -42,7 +42,7 @@ RFC 8878 is the specification. Of it:
|
||||||
| Huffman weights | Both the direct nibble form and the FSE-coded form. |
|
| Huffman weights | Both the direct nibble form and the FSE-coded form. |
|
||||||
| Sequence tables | Predefined, RLE, FSE-compressed and repeat, for all three of literal length, match length and offset. |
|
| Sequence tables | Predefined, RLE, FSE-compressed and repeat, for all three of literal length, match length and offset. |
|
||||||
| Repeated offsets | Yes, including the no-literals shift. |
|
| Repeated offsets | Yes, including the no-literals shift. |
|
||||||
| Content checksum | Parsed and **skipped, never verified**. |
|
| Content checksum | Verified. XXH64 over the output, compared with the stored low 32 bits. Hashed only when the frame carries one. |
|
||||||
| Dictionaries | Refused. |
|
| Dictionaries | Refused. |
|
||||||
| Skippable frames, several frames in one buffer | Refused. Reading one frame is the whole API, and input left over after it is an error. |
|
| Skippable frames, several frames in one buffer | Refused. Reading one frame is the whole API, and input left over after it is an error. |
|
||||||
| Streaming, or decoding part of a frame | Not offered. |
|
| Streaming, or decoding part of a frame | Not offered. |
|
||||||
|
|
@ -51,26 +51,16 @@ It targets native Dart and not the web: the literals header reads a five-byte
|
||||||
field with a 32-bit shift, and the bit reader carries 56 bits in an `int`.
|
field with a 32-bit shift, and the bit reader carries 56 bits in an `int`.
|
||||||
Both are exact on the VM and on AOT, and neither is on JavaScript.
|
Both are exact on the VM and on AOT, and neither is on JavaScript.
|
||||||
|
|
||||||
## Known defects
|
## Defects found and closed, 2026-09-21
|
||||||
|
|
||||||
Found by two audits on 2026-09-21, each with a reproducer. Four are fixed; the
|
Two audits, each finding with a reproducer. All five are fixed.
|
||||||
fifth is the one that matters most and is still open.
|
|
||||||
|
|
||||||
### Open
|
One decision is still open, and it is not a defect in this package:
|
||||||
|
`hgtz.py` writes no checksum, so nothing in a `.hgtz` is covered by
|
||||||
**The content checksum is parsed and never verified.** Over 600,000 mutations of
|
the verification below. Making it write one costs four bytes a block and a
|
||||||
real block data, 34% decoded into wrong bytes with no error raised. This is the
|
re-transcode of every tile. It is narrower than it sounds, because
|
||||||
only mechanism that would catch a wrong decode from anything else, which is what
|
the downloader verifies a SHA-256 over the whole container before it keeps the
|
||||||
makes it the one worth closing. Two halves: verify the XXH64 when the flag is
|
download, so only corruption at rest on the phone is uncovered.
|
||||||
set, and have `hgtz.py` start writing one, which costs four bytes a
|
|
||||||
block and a re-transcode of the archive.
|
|
||||||
|
|
||||||
Narrower than it first looks for this project. the downloader verifies a
|
|
||||||
SHA-256 over the whole container before it renames the `.part`, so a tile damaged
|
|
||||||
on the way to the phone is already caught. What is left is corruption at rest
|
|
||||||
after that check.
|
|
||||||
|
|
||||||
### Fixed 2026-09-21
|
|
||||||
|
|
||||||
1. **`Frame_Content_Size` sized the output allocation with no ceiling.** A
|
1. **`Frame_Content_Size` sized the output allocation with no ceiling.** A
|
||||||
16-byte frame can ask for 4 EiB. One bit flipped in the frame descriptor of a
|
16-byte frame can ask for 4 EiB. One bit flipped in the frame descriptor of a
|
||||||
|
|
@ -93,6 +83,12 @@ after that check.
|
||||||
bytes were stepped over rather than read.
|
bytes were stepped over rather than read.
|
||||||
4. **RLE blocks skipped the block-size ceiling**, so 97 bytes of input returned
|
4. **RLE blocks skipped the block-size ceiling**, so 97 bytes of input returned
|
||||||
44 MB of output that a caller cannot tell from a good tile.
|
44 MB of output that a caller cannot tell from a good tile.
|
||||||
|
5. **The content checksum was parsed and never verified.** Over 600,000
|
||||||
|
mutations of real block data, 34% decoded into wrong bytes with no error
|
||||||
|
raised, because a damaged match or literal length usually still produces
|
||||||
|
plausible output of the right length. Every other guard catches a frame that
|
||||||
|
cannot be parsed; this is the only one that catches a frame that parses and
|
||||||
|
is wrong.
|
||||||
|
|
||||||
Over-permissive against libzstd, with no wrong output, so lower priority: a
|
Over-permissive against libzstd, with no wrong output, so lower priority: a
|
||||||
compressed block over `blockSizeMax` is accepted, the Huffman weight table is
|
compressed block over `blockSizeMax` is accepted, the Huffman weight table is
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue