Repository navigation
Fix dangling references in the labeled aligner's annotation buffer - #690
Merged
Merged
Conversation
Two places in AnnotationBuffer read through references or iterators that an insertion into the same container could invalidate. LabeledAligner keeps one buffer across reads, so its containers keep growing, and both bugs surface only after many align() calls on the same aligner object, which is how a language binding drives it. - fetch_queued_annotations: on CANONICAL graphs, a node whose canonical counterpart was already registered was inserted into node_to_cols_ and then checked through the iterator to that counterpart. When the insert reallocated the map, the read went through a dangling iterator, the node could be left unqueued, and filter_seeds later dereferenced its null label pointer (SIGSEGV after ~980 simulated 400 bp reads on a 24-genome graph). Copy the value before inserting. - The labeled extender holds references to cached label sets (get_cached_column_set, get_labels_and_coords) while it caches new ones in the same loop. Store the label sets in a deque, so caching never moves the existing ones. Tests: references to cached label sets stay valid while 10,000 new sets are cached; queueing both strands of a 5 kbp sequence in one batch on a CANONICAL graph fetches labels for every node. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It builds a CANONICAL graph and reverse-complements the query. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 28, 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.
Problem
LabeledAlignersegfaults after manyalign()calls on one aligner object. A language binding drives the aligner this way. The batched CLI is not affected, because it builds a new aligner per batch.Reproduction:
LabeledAligner,align()called once per read.Root cause, found with an assertion-enabled build:
assert(val != nannot)fails at the end ofAnnotationBuffer::fetch_queued_annotations.Fixes
Two dangling-reference bugs, both in
AnnotationBuffer, the aligner's per-object label cache:fetch_queued_annotations, CANONICAL graphs (the crash).node_to_cols_withtry_emplace.find_b.find_bdangles. The node can be left unqueued and keep the "not fetched" marker.LabeledAligner::filter_seedsthen dereferences a null label pointer; the guardingassertis compiled out in Release.LabeledExtenderholds references fromget_cached_column_set()andget_labels_and_coords()while it caches new intersected label sets in the same loop.VectorSetwith astd::dequecontainer), so caching never moves existing sets.hm/align_nomutex(414cfb0), which re-fetched the reference per iteration; that commit never reached master.Tests
LabeledAlignerTest.CachedColumnSetsKeepTheirAddress: references to a cached set and to a node's labels stay valid while 10,000 new sets are cached.LabeledAlignerTest.FetchBothStrandsInOneBatch: on a CANONICAL graph, queueing the forward and reverse-complement paths of a 5 kbp sequence in one batch fetches labels for every node.-O2build: 14/14 test instances pass. The full suites were not run locally.CachedColumnSetsKeepTheirAddressfails on 7/7 graph and annotation types.FetchBothStrandsInOneBatchexercises the iterator path, but it cannot catch that bug deterministically: the bug is a read after reallocation, which needs a sanitizer to detect reliably. The crash reproduction below is the evidence for that fix.Before / after (benchmark harness, branch
bench/align-harness)Setup:
align_bench, one aligner reused across reads.8b77fc8f9)r980, SIGSEGV)r980CRASHr980aligned to its true genome, score 826Multi-threaded CLI
metagraph align -p 8, CANONICAL graph, 945 long ONT reads (3/5/10% error). Master8b77fc8f9vs this PR, 3 interleaved repeats each; median wall (per-repeat values), peak RSS.🤖 Generated with Claude Code