a1c0911798f9e50f0af79db624b8de840af4854f braney Wed Sep 9 13:02:54 2026 -0700 hgc: zero the stack refLink in doKnownGene so the details page stops reading uninitialised memory doKnownGene builds a refLink two ways. When the accession contains NM_ it loads a real row. Otherwise it uses a plain struct on the stack and fills in three of the eight fields. A struct declared that way is not zeroed, and two of the remaining fields are read further down: prKnownGeneInfo tests omimId and prints an OMIM link built from it, and geneShowPosAndLinksPal passes protAcc to hGenBankHaveSeq as a string. No knownGene name on hg38, hg19 or mm39 contains NM_, so every knownGene details page on those assemblies takes the else branch. The OMIM link appears and disappears between requests to the same URL and carries a meaningless number when it does. The protAcc read is worse: run hgc from the command line on such a page and it segfaults in strlen, reached through checkIfInTable. Zero the struct before filling it in. The fields that are not set then read as absent, which is what the else branch already means. Also guard the hGenBankHaveSeq call on a NULL pepName, because a NULL string argument to sqlSafef reaches sqlCheckError, whose default level is abort. Note for anyone moving code between these two functions later: prRefGeneInfo dereferences rl->product, and doKnownGene does not call it. A zeroed struct would be a NULL dereference there. Found with valgrind, refs #38316. refs #38317 diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c index 6c1dc70566b..658b00ada8a 100644 --- src/hg/hgc/hgc.c +++ src/hg/hgc/hgc.c @@ -3239,31 +3239,31 @@ while ((row = sqlNextRow(sr)) != NULL) { struct yaleGencodeAssoc *ya = yaleGencodeAssocLoad(row); safef(buffer, sizeof buffer, "%s/%s",yaleUrl,ya->yaleId); printf("<B>Yale pseudogene:</B> <a href=\"%s\" target=\"_blank\">%s</a><br>\n", buffer, ya->yaleId); } sqlFreeResult(&sr); hFreeConn(&conn); } } printf("<H3>Links to sequence:</H3>\n"); printf("<UL>\n"); -if ((pepTable != NULL) && hGenBankHaveSeq(srcDb, pepName, pepTable)) +if ((pepTable != NULL) && (pepName != NULL) && hGenBankHaveSeq(srcDb, pepName, pepTable)) { puts("<LI>\n"); hgcAnchorSomewhere(pepClick, pepName, pepTable, seqName); printf("Predicted Protein</A> \n"); puts("</LI>\n"); foundPep = TRUE; } if (!foundPep) { char *autoTranslate = trackDbSetting(tdb, "autoTranslate"); if (autoTranslate == NULL || differentString(autoTranslate, "0")) { puts("<LI>\n"); /* put out correct message to describe translated mRNA */ if ( sameString(geneTable, "ensGene") @@ -14338,30 +14338,33 @@ sqlRnaName = replaceChars(rnaName, "'", "''"); } /* get refLink entry */ if (strstr(rnaName, "NM_") != NULL) { sqlSafef(query, sizeof(query), "select * from %s where mrnaAcc = '%s'", refLinkTable, sqlRnaName); sr = sqlGetResult(conn, query); if ((row = sqlNextRow(sr)) == NULL) errAbort("Couldn't find %s in %s table - this accession may no longer be available.", rnaName, refLinkTable); rl = refLinkLoad(row); sqlFreeResult(&sr); } else { + /* No refLink row for this accession. Zero the whole struct so the fields we + * do not fill in read as absent, rather than as whatever was on the stack. */ + ZeroVar(&rlR); rlR.name = strdup(kgId); rlR.mrnaAcc = strdup(kgId); rlR.locusLinkId = 0; rl = &rlR; } cartWebStart(cart, database, "Known Gene"); printf("<table border=0>\n<tr>\n"); prKnownGeneInfo(conn, tdb, rnaName, sqlRnaName, rl); printf("</tr>\n</table>\n"); /* optional summary text */ summary = getRefSeqSummary(conn, kgId); if (summary != NULL)