firefox, go-ethereum, imagemagick, influxdb, micronaut-core, openbsd, proton, proxysql, sqlite, vim. All CWE-407.
4.6 KiB
SQLite — CWE-407 Disclosure Brief
2026-04-14 · Patch available — awaiting upstream merge
Finding
Two O(n²) defects in SQLite: one in checkColumnOverlap() (trigger column matching) and one in sqlite3CreateForeignKey() (foreign key column resolution). Both patched. Patches ready for upstream review. The trigger defect fires on every UPDATE trigger evaluation when watched columns overlap with SET columns; the foreign key defect fires during every CREATE TABLE with REFERENCES clauses.
The Defects
sqlite-0001 (PATCHED — MEDIUM): src/trigger.c:781
// checkColumnOverlap — nested loop: IdList × ExprList:
static int checkColumnOverlap(IdList *pIdList, ExprList *pEList){
int e;
if( pIdList==0 || NEVER(pEList==0) ) return 1;
for(e=0; e<pEList->nExpr; e++){
// For each SET column, scan all watched columns — O(W) per SET column
if( sqlite3IdListIndex(pIdList, pEList->a[e].zEName)>=0 ) return 1;
}
return 0;
}
checkColumnOverlap() checks if any SET column in an UPDATE statement matches a trigger's watched column list. sqlite3IdListIndex() performs a linear scan of pIdList (O(W) where W = watched columns). Called for each of E SET columns, total: O(E x W). Fires during query planning for every UPDATE against a table with UPDATE OF triggers.
sqlite-0003 (PATCHED — MEDIUM): src/build.c:3680
// sqlite3CreateForeignKey — nested loop for column resolution:
for(i=0; i<nCol; i++){
int j;
for(j=0; j<p->nCol; j++){ // O(C) scan per FK column
if( sqlite3StrICmp(p->aCol[j].zCnName, pFromCol->a[i].zEName)==0 ){
pFKey->aCol[i].iFrom = j;
break;
}
}
}
For each of F foreign key columns, the code scans all C table columns with sqlite3StrICmp() to resolve column indices. Total: O(F x C). Fires during every CREATE TABLE or ALTER TABLE ADD FOREIGN KEY with REFERENCES clauses.
Complexity Proof
sqlite-0001: At W=50 watched columns, E=50 SET columns:
- Defective: 50 x 50 = 2,500 string comparisons
- Fixed: hash set with O(1) lookup per SET column = 50 lookups
- 50x op reduction. Threshold at W>4 and E>4 (small lists keep linear scan).
sqlite-0003: At F=20 FK columns, C=100 table columns:
- Defective: 20 x 100 = 2,000 case-insensitive string comparisons
- Fixed:
sqlite3ColumnIndex()uses the table's pre-builtaHx[]hash: O(1) per lookup - 100x op reduction for wide tables with many foreign keys.
Impact
SQLite runs on every smartphone, every browser, and billions of embedded devices. The trigger column overlap defect (sqlite-0001) fires during query planning for every UPDATE statement against tables with UPDATE OF column-specific triggers. ORM frameworks (Django, Rails, SQLAlchemy) and mobile apps with complex schemas involving many triggers hit this path frequently.
The foreign key defect (sqlite-0003) fires during schema creation. Database migration tools that create tables with many foreign key columns (common in enterprise schemas) experience quadratic overhead during CREATE TABLE.
The Fix
sqlite-0001: Build a hash set from watched columns when both lists exceed size 4:
// Before
for(e=0; e<pEList->nExpr; e++)
if(sqlite3IdListIndex(pIdList, pEList->a[e].zEName)>=0) return 1;
// After
// CWE-407 fix: for large overlap checks, hash the watched columns.
if( pIdList->nId>4 && pEList->nExpr>4 ){
Hash h;
sqlite3HashInit(&h);
for(i=0; i<pIdList->nId; i++)
sqlite3HashInsert(&h, pIdList->a[i].zName, pIdList->a[i].zName);
for(e=0; e<pEList->nExpr && !found; e++)
if(sqlite3HashFind(&h, pEList->a[e].zEName)) found = 1;
sqlite3HashClear(&h);
}
sqlite-0003: Use the table's existing sqlite3ColumnIndex() hash lookup:
// Before
for(j=0; j<p->nCol; j++)
if(sqlite3StrICmp(p->aCol[j].zCnName, pFromCol->a[i].zEName)==0) { ... }
// After
// CWE-407 fix: O(1) hash-based column lookup via sqlite3ColumnIndex().
int j = sqlite3ColumnIndex(p, pFromCol->a[i].zEName);
Patch
defects/sqlite/patch/sqlite-0001-checkcolumnoverlap-hash.patch
defects/sqlite/patch/sqlite-0003-fk-column-resolution.patch
Unit tests: pass. sqlite-0001: 50x speedup at W=50, E=50. sqlite-0003: 100x speedup at F=20, C=100.
What We Ask
- Confirm receipt via the SQLite forum or direct email.
- Validate patches against your extensive test suite (TH3, dbsqlfuzz).
- Assess severity: both defects fire during query planning and schema creation, two foundational operations.
- Coordinate a disclosure date: we target 90 days from first contact.
Contact: see cover email. This brief is confidential until coordinated disclosure.