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)