6c676a0e9ed642676008731762141e0c16822787
braney
  Sat Aug 22 09:31:14 2026 -0700
vcf: tighten validation of the genotype allele index, refs #38155

New parseAlleleIx returns missing data for a GT allele index that the record
has no allele for, and for a value that will not fit the field.  It checks the
value as an int, before it reaches the field.  vcfParseGenotypes and
vcfParseGenotypesGtOnly both go through it, and a check after the per-genotype
loop covers the PL and SGT fallbacks, which choose an index without consulting
the allele count.

hapIxA and hapIxB become signed char.  Plain char is unsigned on some
architectures, and the negative missing-data value did not stay negative there.
Size and field offsets are unchanged, and x86 code generation is identical.

Adds a lib/tests case for malformed and boundary genotype indexes.

diff --git src/inc/vcf.h src/inc/vcf.h
index e5f210e80fd..f83e273dc66 100644
--- src/inc/vcf.h
+++ src/inc/vcf.h
@@ -44,33 +44,35 @@
     };
 
 struct vcfInfoElement
 /* A single INFO column component; each row's INFO column may contain multiple components. */
     {
     char *key;			// An identifier described by a struct vcfInfoDef
     int count;			// Number of data values following id
     union vcfDatum *values;	// Array of data values following id
     bool *missingData;		// Array of flags for missing data values ("." instead of number)
     };
 
 struct vcfGenotype
 /* A single component of the optional GENOTYPE column. */
     {
     char *id;			// Name of individual/sample (pointer to vcfFile genotypeIds) or .
-    char hapIxA;		// Index of one haplotype's allele: 0=reference, 1=alt, 2=other alt
-				// *or* if negative, missing data
-    char hapIxB;		// Index of other haplotype's allele, or if negative, missing data
+    signed char hapIxA;		// Index of one haplotype's allele: 0=reference, 1=alt, 2=other alt
+				// *or* if negative, missing data.  Explicitly signed: plain char is
+				// unsigned on some architectures, and the missing-data value has to
+				// stay negative there too.
+    signed char hapIxB;		// Index of other haplotype's allele, or if negative, missing data
     bool isPhased;		// True if haplotypes are phased
     bool isHaploid;		// True if there is only one haplotype (e.g. chrY)
     int infoCount;		// Number of components named in FORMAT column
     struct vcfInfoElement *infoElements;	// Array of info components for this genotype call
     };
 
 struct vcfRecord
 /* A VCF data row (or list of rows). */
 {
     struct vcfRecord *next;
     char *chrom;		// Reference assembly sequence name
     unsigned int chromStart;	// Start offset in chrom
     unsigned int chromEnd;	// End offset in chrom
     char *name;			// Variant name from ID column
     int alleleCount;		// Number of alleles (reference + alternates)