AI and data stack advisories

Severe, 6 weeks1496Projects326

1496 severe, 6 weeks · 326 projects

GoogleGHSA-qj7j-3jp8-8ccv

SQLite: Integer truncation in findOrCreateAggInfoColumn

Google

CVE-2025-6965 · Published Aug 25, 2025

High
Fix: upgrade to 3.50.2 or later
GitHub advisory

Summary

An integer truncation vulnerability exists in SQLite's handling of aggregate queries with a very large number of distinct column references. When the number of columns processed in an aggregate context exceeds 32,767, the index used to track these columns is truncated from a 32-bit integer to a signed 16-bit integer, resulting in a negative value

Severity

High - The exploitation of this vulnerability can lead to remote code execution and potential for significant damage.

Proof of Concept

Vulnerability Details

An integer truncation vulnerability exists in SQLite's handling of aggregate queries with a very large number of distinct column references. When the number of columns processed in an aggregate context exceeds 32,767, the index used to track these columns is truncated from a 32-bit integer to a signed 16-bit integer, resulting in a negative value [1].

In debug builds, this invalid value leads to assertion failures [2][4]. In non-debug builds, the corrupted index is later used to access an array, leading to a heap-buffer-overflow. In sqlite3ExprCodeTarget, the out-of-bounds values are used to construct a potentially invalid VDBE instruction. In agginfoPersistExprCb, the out-of-bounds index read from an array [5] is followed by an out-of-bounds write to the same index [6], leading to memory corruption.

```java

static void findOrCreateAggInfoColumn(...

Affected versions

PackageAffectedFixed in
security-research
Product
< 3.50.23.50.2
Details and references

## Summary An integer truncation vulnerability exists in SQLite's handling of aggregate queries with a very large number of distinct column references. When the number of columns processed in an aggregate context exceeds 32,767, the index used to track these columns is truncated from a 32-bit integer to a signed 16-bit integer, resulting in a negative value ## Severity High - The exploitation of this vulnerability can lead to remote code execution and potential for significant damage. ## Proof of Concept ### Vulnerability Details An integer truncation vulnerability exists in SQLite's handling of aggregate queries with a very large number of distinct column references. When the number of columns processed in an aggregate context exceeds 32,767, the index used to track these columns is truncated from a 32-bit integer to a signed 16-bit integer, resulting in a negative value [1]. In debug builds, this invalid value leads to assertion failures [2][4]. In non-debug builds, the corrupted index is later used to access an array, leading to a heap-buffer-overflow. In sqlite3ExprCodeTarget, the out-of-bounds values are used to construct a potentially invalid VDBE instruction. In agginfoPersistExprCb, the out-of-bounds index read from an array [5] is followed by an out-of-bounds write to the same index [6], leading to memory corruption. ```java static void findOrCreateAggInfoColumn( Parse *pParse, /* Parsing context */ AggInfo *pAggInfo, /* The AggInfo object to search and/or modify */ Expr *pExpr /* Expr describing the column to find or insert */ ) { struct AggInfo_col *pCol; int k; [...] k = addAggInfoColumn(pParse->db, pAggInfo); if( k<0 ){ /* OOM on resize */ assert( pParse->db->mallocFailed ); return; } [...] pExpr->iAgg = (i16)k; // *** 1 *** } static int addAggInfoColumn(sqlite3 *db, AggInfo *pInfo){ int i; pInfo->aCol = sqlite3ArrayAllocate( db, pInfo->aCol, sizeof(pInfo->aCol[0]), &pInfo->nColumn, &i ); return i; } SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ [...] switch( op ){ case TK_AGG_COLUMN: { AggInfo *pAggInfo = pExpr->pAggInfo; struct AggInfo_col *pCol; assert( pAggInfo!=0 ); assert( pExpr->iAgg>=0 ); // *** 2 *** [...] pCol = &pAggInfo->aCol[pExpr->iAgg]; if( !pAggInfo->directMode ){ return AggInfoColumnReg(pAggInfo, pExpr->iAgg); }else if( pAggInfo->useSortingIdx ){ Table *pTab = pCol->pTab; sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab, pCol->iSorterColumn, target); // *** 3 *** [...] } static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ if( ALWAYS(!ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced)) && pExpr->pAggInfo!=0 ){ AggInfo *pAggInfo = pExpr->pAggInfo; int iAgg = pExpr->iAgg; Parse *pParse = pWalker->pParse; sqlite3 *db = pParse->db; assert( iAgg>=0 ); // *** 4 *** if( pExpr->op!=TK_AGG_FUNCTION ){ if( iAgg<pAggInfo->nColumn && pAggInfo->aCol[iAgg].pCExpr==pExpr // *** 5 *** ){ pExpr = sqlite3ExprDup(db, pExpr, 0); if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){ pAggInfo->aCol[iAgg].pCExpr = pExpr; // *** 6 *** } } [...] } ``` ## Further Analysis The first reproduction case triggers an out-of-bounds access in sqlite3ExprCodeTarget: ```python import math REQUIRED_REFS = 65535 COLS_PER_TABLE = 2000 GROUP_SIZE = 500 # Calculate how many times the table must be aliased. NUM_ALIASES = math.ceil(REQUIRED_REFS / COLS_PER_TABLE) # 1. Create a single wide table. create_table_sql = f"CREATE TABLE w({', '.join(f'c{i} INT' for i in range(COLS_PER_TABLE))});" insert_sql = "INSERT INTO w DEFAULT VALUES;" # 2. Build the FROM clause with self-joi

Severity from
GitHub (reviewed advisory)

More Google advisories

All Google