38bab7f62d250db395c83bd4b6b0d6c08799ef59 markd Sat Aug 15 20:01:54 2026 -0700 Make maf UI species arrays dynamic to fix crash on large mafs. refs #38039 The Codon Translation dropdown in wigMafCfgUi built its option list in a fixed nodeNames[512] stack array, so a maf with more than 511 species wrote past the end of the frame and crashed hgTrackUi. Size it from slCount of the species list instead. Three more fixed arrays in the same path silently dropped data rather than crashing: wigMafGetSpecies chopped speciesOrder into species[2000] and speciesGroups into a 1000 entry array, and wigMafSpeciesTable chopped speciesDefaultOff into words[2000]. All now count with chopLineLen and allocate. Verified by driving wigMafCfgUi directly: 700 species segfaulted before and renders now, and 3000 species previously lost everything past 2000. hgTrackUi output for hg38 multiz470way is unchanged. diff --git src/hg/lib/hui.c src/hg/lib/hui.c index 03f819249e5..19e5c9a0ddb 100644 --- src/hg/lib/hui.c +++ src/hg/lib/hui.c @@ -8127,80 +8127,83 @@ char *suffix = option + strlen(option); int suffixSize = optionSize - strlen(option); safef(suffix,suffixSize,".%s",species); } return cartUsualBooleanClosestToHome(cart,tdb, parentLevel, species,defaultState); } char **wigMafGetSpecies(struct cart *cart, struct trackDb *tdb, char *prefix, char *db, struct wigMafSpecies **list, int *groupCt) { int speciesCt = 0; char *speciesGroup = trackDbSetting(tdb, SPECIES_GROUP_VAR); char *speciesUseFile = trackDbSetting(tdb, SPECIES_USE_FILE); char *speciesOrder = trackDbSetting(tdb, SPECIES_ORDER_VAR); #define MAX_SP_SIZE 2000 -#define MAX_GROUPS 1000 char sGroup[MAX_SP_SIZE]; //Ochar *groups[20]; struct wigMafSpecies *wmSpecies, *wmSpeciesList = NULL; int group; int i; -char *species[MAX_SP_SIZE]; +char **species = NULL; char option[MAX_SP_SIZE]; *list = NULL; *groupCt = 0; /* determine species and groups for pairwise -- create checkboxes */ if (speciesOrder == NULL && speciesGroup == NULL && speciesUseFile == NULL) { if (isCustomTrack(tdb->track)) return NULL; errAbort("Track %s missing required trackDb setting: speciesOrder, speciesGroups, or speciesUseFile", tdb->track); } -char **groups = needMem(MAX_GROUPS * sizeof (char *)); +int groupsSize = (speciesGroup != NULL ? chopLineLen(speciesGroup) : 1); +char **groups = needMem(groupsSize * sizeof (char *)); *groupCt = 1; if (speciesGroup) - *groupCt = chopByWhite(speciesGroup, groups, MAX_GROUPS); + *groupCt = chopByWhite(speciesGroup, groups, groupsSize); if (speciesUseFile) { if ((speciesGroup != NULL) || (speciesOrder != NULL)) errAbort("Can't specify speciesUseFile and speciesGroup or speciesOrder"); speciesOrder = cartGetOrderFromFile(db, cart, speciesUseFile); // Not sure why this is in cart } // not tdb based so no ClosestToHome for (group = 0; group < *groupCt; group++) { if (*groupCt != 1 || !speciesOrder) { safef(sGroup, sizeof sGroup, "%s%s", SPECIES_GROUP_PREFIX, groups[group]); speciesOrder = trackDbRequiredSetting(tdb, sGroup); } - speciesCt = chopLine(speciesOrder, species); + speciesCt = chopLineLen(speciesOrder); + AllocArray(species, speciesCt); + chopByWhite(speciesOrder, species, speciesCt); for (i = 0; i < speciesCt; i++) { AllocVar(wmSpecies); wmSpecies->name = cloneString(species[i]); safecpy(option,sizeof option,prefix); wmSpecies->on = isSpeciesOn(cart, tdb, wmSpecies->name, option, sizeof option, TRUE); wmSpecies->group = group; slAddHead(&wmSpeciesList, wmSpecies); } + freez(&species); } slReverse(&wmSpeciesList); *list = wmSpeciesList; return groups; } struct wigMafSpecies * wigMafSpeciesTable(struct cart *cart, struct trackDb *tdb, char *name, char *db) { int groupCt; #define MAX_SP_SIZE 2000 char option[MAX_SP_SIZE]; int group, prevGroup; @@ -8211,52 +8214,54 @@ struct wigMafSpecies *wmSpeciesList; char **groups = wigMafGetSpecies(cart, tdb, name, db, &wmSpeciesList, &groupCt); struct wigMafSpecies *wmSpecies = wmSpeciesList; struct slName *speciesList = NULL; for(; wmSpecies; wmSpecies = wmSpecies->next) { struct slName *newName = slNameNew(wmSpecies->name); slAddHead(&speciesList, newName); } slReverse(&speciesList); int numberPerRow; boolean lineBreakJustPrinted; -char *words[MAX_SP_SIZE]; +char **words = NULL; int defaultOffSpeciesCnt = 0; if (cartOptionalString(cart, "ajax") == NULL) jsIncludeFile("utils.js",NULL); //jsInit(); puts("\n
Species selection: "); cgiContinueHiddenVar("g"); char id[256]; PLUS_BUTTON( "id", "plus_pw","cb_maf_","_maf_") MINUS_BUTTON("id","minus_pw","cb_maf_","_maf_") char prefix[512]; safef(prefix, sizeof prefix, "%s.", name); char *defaultOffSpecies = trackDbSetting(tdb, "speciesDefaultOff"); struct hash *offHash = NULL; if (defaultOffSpecies) { offHash = newHash(5); DEFAULT_BUTTON( "id", "default_pw","cb_maf_","_maf_") - int wordCt = chopLine(defaultOffSpecies, words); + int wordCt = chopLineLen(defaultOffSpecies); + AllocArray(words, wordCt); + chopByWhite(defaultOffSpecies, words, wordCt); defaultOffSpeciesCnt = wordCt; /* build hash of species that should be off */ int ii; for(ii=0; ii < wordCt; ii++) hashAdd(offHash, words[ii], NULL); } if (groupCt == 1) puts("\n
Codon highlighting:
" );
#ifdef GENE_FRAMING
safef(option, sizeof(option), "%s.%s", name, MAF_FRAME_VAR);
char *currentCodonMode = cartCgiUsualString(cart, option, MAF_FRAME_GENE);
/* Disable codon highlighting */
cgiMakeRadioButton(option, MAF_FRAME_NONE,