de609b7489963f32ad474619cb3dff0283ef7e76 braney Sun Aug 23 15:13:46 2026 -0700 vcf: bound the genotype allele index by the field it is stored in, refs #38155 parseAlleleIx checked the index against alleleCount only. A record can hold up to VCF_MAX_INFO alleles, so an index of 128 or more could pass that check and then narrow on the way into the signed char field. The narrowed value was sometimes another real allele of the record: with 260 ALT alleles, index 260 came out as 4. The parser then reported a genotype the VCF never named. The check now also rejects an index above SCHAR_MAX, so an index too large for the field reads as missing data. SCHAR_MAX and not CHAR_MAX, because CHAR_MAX is 255 on the unsigned char platforms that the field is declared signed for. Every other assignment to hapIxA and hapIxB in this file is a literal in the range -1 to 2, so parseAlleleIx was the only path that could carry an out-of-range value. New test vcfParseManyAlleles, with a record of 260 ALT alleles. Without the fix, GT 128/1 reads as -128/1, 130/130 as -126/-126, and 260/260 as 4/4. diff --git src/lib/tests/expected/vcfParseManyAlleles.out src/lib/tests/expected/vcfParseManyAlleles.out new file mode 100644 index 00000000000..ecf2cc55c6d --- /dev/null +++ src/lib/tests/expected/vcfParseManyAlleles.out @@ -0,0 +1,11 @@ +chr1 999 manyAlleles alleleCount=261 + S0 hapIxA=127 hapIxB=127 isHaploid=0 isPhased=0 + S1 hapIxA=-1 hapIxB=1 isHaploid=0 isPhased=0 + S2 hapIxA=-1 hapIxB=-1 isHaploid=0 isPhased=0 + S3 hapIxA=-1 hapIxB=0 isHaploid=0 isPhased=0 + S4 hapIxA=0 hapIxB=1 isHaploid=0 isPhased=1 + S5 hapIxA=-1 hapIxB=2 isHaploid=0 isPhased=0 + S6 hapIxA=-1 hapIxB=-1 isHaploid=0 isPhased=0 + S7 hapIxA=-1 hapIxB=-1 isHaploid=0 isPhased=0 + alleleCounts (last is missing data): 2 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 + phased=1 diploid=8