
Clickbait. The CVE is AI slop.
Entschuldigung für den Clickbait-Reponamen, aber diese CVE ist kompletter, von LLM halluzinierter Müll – ehrlich gesagt traurig zu sehen, dass eine CNA das mit so inkonsistenten Behauptungen akzeptiert 💔
CVE-2026-51302 ist nicht echt 😱
Das veröffentlichte SQL reproduziert unter AddressSanitizer in keiner SQLite-3.41-Version einen Use-after-Free. Noch wichtiger: Die angegebene Grundursache ist mit dem betroffenen Quellcode völlig unvereinbar:
exprComputeOperands() existiert in SQLite 3.41.0, 3.41.1 und 3.41.2 nicht;regFree1 ist eine Ganzzahl-Kennung eines virtuellen Maschinenregisters, kein Zeiger auf Heap-Speicher;sqlite3ReleaseTempReg() macht ein Register zur Wiederverwendung verfügbar und hinterlässt keinen hängenden C-Zeiger in regFree1;exprComputeOperands() enthalten, werden die Operanden berechnet, bevor die temporären Register freigegeben werden – entgegen der im Advisory angegebenen Reihenfolge; undexprComputeOperands() eingeführt wurde.Dieser CVE-Eintrag sollte abgelehnt werden, und ich werde eine CNA-Dispute bei MITRE einreichen 😉
Das Advisory bezeichnet die betroffene Version als „SQLite 3.41" und liefert diese Abfrage:
SELECT CASE WHEN (1+3) THEN (5*8) ELSE (2/0) END FROM test;
Es behauptet, dass:
sqlite3ReleaseTempReg() Heap-Speicher freigibt, der mit regFree1 verbunden ist;regFree1 als hängende Referenz zurückbleibt;exprComputeOperands() anschließend auf den freigegebenen Speicher zugreift; undDas SQLite-3.41.0-Amalgam definiert die Funktion wie folgt:
SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse *pParse, int iReg){
if( iReg ){
sqlite3VdbeReleaseRegisters(pParse, iReg, 1, 0, 0);
if( pParse->nTempReg<ArraySize(pParse->aTempReg) ){
pParse->aTempReg[pParse->nTempReg++] = iReg;
}
}
}
Die Funktion erhält iReg als int. Sie speichert diese Ganzzahl in pParse->aTempReg, sodass die Registernummer erneut zugewiesen werden kann:
SQLITE_PRIVATE int sqlite3GetTempReg(Parse *pParse){
if( pParse->nTempReg==0 ){
return ++pParse->nMem;
}
return pParse->aTempReg[--pParse->nTempReg];
}
Dabei handelt es sich um die Verwaltung der Registerlebensdauer während der VDBE-Programmerzeugung. Das Advisory beschreibt regFree1, als wäre es ein weiterhin vorhandener Zeiger auf freigegebenen Heap-Speicher. Das ist es nicht:
int regFree1 = 0, regFree2 = 0;
int r1, r2;
r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, ®Free1);
r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, ®Free2);
codeCompare(pParse, pL, pR, opx, r1, r2, addrDone, p5, isCommuted);
sqlite3ReleaseTempReg(pParse, regFree1);
sqlite3ReleaseTempReg(pParse, regFree2);
Der erzeugte Vergleich verbraucht die Registerkennungen, bevor sie zur Wiederverwendung freigegeben werden.
Eine Blame-Suche im Git-Spiegel ergab, dass exprComputeOperands() durch folgenden Commit eingeführt wurde:
e24f20a4f5a6d26cdaece58eff77619a4ee757b9
2025-06-30T10:30:47Z
Factor out the code that tries to avoid evaluating subquery operands if the other operand is NULL into a subroutine, so that it can be more easily reused by other parts of the code generator.
Die Funktion fehlt in allen drei offiziellen SQLite-3.41-Amalgamen. Eine Schwachstelle in SQLite 3.41 kann keinen Ausführungspfad durch eine Funktion nehmen, die erst 2025 eingeführt wurde – ernsthaft, MITRE? 🥲
Im modernen SQLite-Quellcode sieht die gekürzte Aufrufreihenfolge in sqlite3ExprIfTrue() wie folgt aus:
addrIsNull = exprComputeOperands(
pParse, pExpr, &r1, &r2, ®Free1, ®Free2);
codeCompare(
pParse, pExpr->pLeft, pExpr->pRight, op,
r1, r2, dest, jumpIfNull, ExprHasProperty(pExpr, EP_Commuted));
/* Other switch cases and generated-bytecode handling occur here. */
sqlite3ReleaseTempReg(pParse, regFree1);
sqlite3ReleaseTempReg(pParse, regFree2);
exprComputeOperands() erzeugt die Registerkennungen. Der Aufrufer verbraucht sie und gibt sie anschließend frei. Das Advisory behauptet stattdessen, dass sqlite3ReleaseTempReg() zuerst läuft und exprComputeOperands() später auf das freigegebene Objekt zugreift.
exprComputeOperands() wurde eingeführt, um die Auswertung eines teuren Subquery-Operanden zu vermeiden, wenn der andere Operand NULL ist. Der veröffentlichte Ausdruck:
CASE WHEN (1+3) THEN (5*8) ELSE (2/0) END
enthält arithmetische Operationen und einen CASE-Ausdruck, aber keinen Subquery-Operanden. Die für diese Abfrage angegebene Begründung entspricht nicht den Aufrufbedingungen der Funktion.
vm übrigens
Debian GNU/Linux 13 (trixie)
Clang 19.1.7
AddressSanitizer enabled
Optimisation level: -O1
Frame pointers retained
Sanitizer recovery disabled
Amalgam-SHAs:
3.41.0 146ce189b67fdbefbf2d72cdc81e198d07ff643614cc9102e9bf063255e8e7e1
3.41.1 df0d54bf246521360c8148f64e7e5ad07a4665b4f902339e844f4c493d535ff5
3.41.2 01df06a84803c1ab4d62c64e995b151b2dbcf5dbc93bbc5eee213cb18225d987
3.53.3 646421e12aac110282ef8cc68f1a62d4bb15fc7b8f09da0b53e29ee690500431