fix(lociss): harden the v4 colblock reader against crafted input#71
Merged
Conversation
The v4 "colblock" decoders trusted offsets read straight from the (untrusted) file, so opening a crafted .lociss could read out of bounds or OOM — memory- safety bugs in the reader shipped in #68, not covered by the earlier audit. - DICT (codec 3): validate each dictionary offset `o0 <= o1 <= blob_len` before `assign(blob + o0, o1 - o0)` — a non-monotone offset underflowed the length to ~4 GiB → OOB read. Also widen `n_dict + 1` to size_t before the multiply so `n_dict == UINT32_MAX` can't wrap the length check to 0. - ARENA (codec 5): validate each offset against the actual blob length. (FRONTCODE was already safe — it need()-checks every step.) - Region query on a file whose stored columns omit Start/End: build_region now errors cleanly instead of decode_col indexing col_offset_ with a wrapped negative column index; decode_col also guards `c` in range. - zstd_inflate takes a max_out ceiling (derived from the block row count) so a tiny compressed chunk can't inflate to gigabytes (decompression bomb). - read_chunk failures are now sticky (read_status_) so a corrupt chunk makes the CLI exit non-zero instead of warning and continuing with exit 0. Crafted-input fixtures (tiny.v4.baddict/badarena/nocoord/zbomb.lociss) assert each path exits 1 cleanly — never a crash or hang. Valid v4/v4.1 files and the suite are unchanged. Suite 344. Systemic follow-up: an ASan/UBSan CI job + libFuzzer over the parsers (already tracked) catches this whole class. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Jul 1, 2026
Merged
This was referenced Jul 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A fresh three-axis analysis of the repo (bugs / responsiveness / functionality) surfaced memory-safety bugs in the LociSSD v4 "colblock" reader shipped in #68 — reachable by simply opening a crafted
.locissfile, and not covered by the earlier audit. This PR is Tier 0 of that roadmap: harden the reader.The decoders trusted offsets read straight from the (untrusted) on-disk payload:
dict[k].assign(blob + o0, o1 - o0)with noo0 ≤ o1 ≤ blob_lencheck; a non-monotone offset underflows the length to ~4 GiB. Alson_dict + 1was computed inuint32_t(wrap atUINT32_MAX). Now validated + widened.-rquery on a file whose stored columns omit Start/End madedecode_col(b, -1, …)indexcol_offset_with a wrapped negative column.build_regionnow errors cleanly;decode_colguards the column index.zstd_inflatehad no output ceiling; a tiny chunk could inflate to gigabytes. Now capped by a per-block bound.read_chunkfailures are now sticky (read_status_), so corrupt data makes the CLI exit non-zero instead of warning + exit 0.Verification
tiny.v4.{baddict,badarena,nocoord,zbomb}.lociss— each exits 1 cleanly, never crashes (≥128) or hangs; asserted via a newassert_exit_codehelper. Thenocoordfile still reads fine without-r.tiny.v4.lociss, DICT+FRONTCODE) and v4.1 (tiny.v41.lociss) fixtures decode unchanged. Suite 344 passed, 0 failed.assign, so the vulnerable path is unreachable for bad offsets. Local ASan is entangled with the statically-linked mimalloc (masks heap redzones); the ASan/UBSan CI job + libFuzzer over the parsers (already tracked inTODO.md → Quality/signal) is the systemic follow-up that catches this whole class automatically.From the broader analysis: next tiers are a per-source decoded-block LRU (the v4 reader decodes
Starttwice and the region count-pass re-decodes blocks), and features led by reference-aware pileup--pileup -f ref.fa.🤖 Generated with Claude Code