Repository navigation
Description
We encountered a specific read pair that causes snap-aligner to segfault. I managed to extract the readpair:
R1.fastq
Details
@A00785:1408:HV5LWDRX7:2:1231:19623:9361 1:N:0:GATAGGCT+CAGTCACA
GATCTGCATGTGCCTTGTCTCTAGTCAGATTTTTCATCAGGCACTAAGGATGACTAAGAACAAAGGGACCCACATTTCAGAGGAGAGGCAAGTTAAGAACCTTAGGCTGTGTGGTTTCTTTTGTAACTGTTACTATCGGCTCTGTGTTAC
+
FFFF:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FFF:F:FFFFF
R2.fastq
Details
@A00785:1408:HV5LWDRX7:2:1231:19623:9361 2:N:0:GATAGGCT+CAGTCACA
CATTAAACCTTTAGCTAAGATTGGTTCCCTTGTAAATCTAAAGATGGTTTTGACTCTCTTGTTCCAGGGGGCAGCTGGAGTAGGTTGTTTCTAAGACAGTGGGAACCATAGCTATTGCAGAAAAGAGGCAAATTAAATACTTTCATTTG
+
,,:FFFFFFF:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:FFFFFFFFFFFFFFFFFFFFF:,FFFFFFFF:FFFFF,FF:FFFFFFFFFFFFFFFFFF,FFFFFFFFFF:FFFFFFFFFFFFFF:FFFFFFFFFFFFFFFF:F
the reference
Details
The specific command used is:
snap-aligner paired /references/Hsapiens/GRCh38/snapaligner R1.fastq R2.fastq -o FAIL.bam -t 16 -b- -sm 20 -I -hc- -S id -sa -xf 2 -R '@RG\tID:test\tSM:test\tPL:ILLUMINA'I'm not familiar with cpp and even less with the code, so I relied on AI to debug this. From what it says it seems that these reads map on different chromosomes (chr1 and chr15), and there is undefined behaviour in the incrementDistance which causes an integer overflow.
Short AI explanation:
Details
- Whenever a pair has primary alignments, updateStats is hard-coded to run.
- Biological input: FAILREAD_R1 and FAILREAD_R2 are an inter-chromosomal chimeric pair:
• Read 1 maps to Chromosome 1 at linear coordinate 215,670,569.
• Read 2 maps to Chromosome 15 at linear coordinate 2,363,154,217. - The overflow: Their coordinate difference is 215, 670, 569 - 2, 363, 154, 217 = -2, 147, 483, 648 (-2³¹).
- The crash: updateStats casts this 64-bit difference to a 32-bit (int), creating INT32_MIN. Passing this to abs() is C/C++ Undefined Behavior, which prompted GCC at -O3 to eliminate
the max(0, ...) bounds check and dereference memory 16 GB before the array.
AI explanation with code:
Details
1. Frame 0: The Exact Crash Location (SIGSEGV)
• File: PairedAligner.cpp:85-87
• Function: PairedAlignerStats::incrementDistance
• Line 86:
inline void incrementDistance(int distance) {
distanceCounts[max(0, min(MAX_DISTANCE, distance))]++; // <-- CRASH OCCURS HERE
}(GCC at -O3 eliminated max(0, ...) due to undefined behavior in abs(INT32_MIN), resulting in distanceCounts[-2147483648]++ which attempts to write 16 GB before the array).
──────
2. Frame 1: The Root Cause (32-bit Integer Truncation & UB)
• File: PairedAligner.cpp:993-995
• Function: PairedAlignerContext::updateStats
• Line 994:
if (isOneLocation(result->status[0]) && isOneLocation(result->status[1])) {
stats->incrementDistance(abs((int) (result->location[0] - result->location[1]))); // <-- ROOT CAUSE
stats->incrementScore(result->score[0], result->score[1]);
}(Subtracting the two 64-bit coordinates produces -2147483648. Casting to (int) narrows to INT32_MIN, and abs(INT32_MIN) is C/C++ Undefined Behavior).
──────
3. Frame 2: The Alignment Loop Caller
• File: PairedAligner.cpp:893-895
• Function: PairedAlignerContext::runIterationThreadImpl
• Line 894:
if (firstIsPrimary) {
updateStats((PairedAlignerStats*)stats, reads[0], reads[1], &results[0], useful0, useful1);──────
Supporting Definitions
• Histogram allocation (distanceCounts): PairedAligner.cpp:61-64
• 64-bit GenomeLocation typedef (_int64): Genome.h:247
• Safe 64-bit distance function: Genome.cpp:717-724 (DistanceBetweenGenomeLocations)
Activity
bolosky commentedon Sep 24, 2026
I'm on the road and can't really look at this until next week, but the AI summary makes sense. It looks like this bug only happens when two reads align exactly the wrong distance apart to trigger a bug that's related to the way 2s complement works. This is a one in 2^32 chance, which is probably why we haven't seen it before.
I have another bug fix to test out and push into release. Assuming this goes the way I think it will, then I'll include a fix for this in that update. I think that the simplest thing is just to leave this as 64 bit all the way through, then it can't happen. This old 32 bit stuff is left over from the earliest days of SNAP when I was trying to keep genome distances in 32 bits to save space.