SQLite - Integer Overflow in FTS5 Extension
SQLite FTS5扩展存在整数溢出漏洞,当计算墓碑指针数组大小时使用32位整数导致溢出。攻击者可利用此漏洞通过执行特定查询或处理受控数据库文件触发溢出,导致堆缓冲区溢出。 2025-8-14 23:59:13 Author: github.com(查看原文) 阅读量:4 收藏

Summary

An integer overflow exists in the FTS5 extension. It occurs when the size of an array of tombstone pointers is calculated and truncated into a 32-bit integer. A pointer to partially controlled data can then be written out of bounds.

Severity

Moderate - The overflow can be triggered by either an attacker who is able to execute arbitrary queries or an attacker that can make an application process a controlled SQLite DB file.

Proof of Concept

echo "SELECT * FROM articles WHERE articles MATCH 'whatever'" | ./sqlite3 /tmp/poc.sql
=================================================================
==3811642==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x5030000012f0 at pc 0x55eafca6599b bp 0x7ffdd1591570 sp 0x7ffdd1591568
READ of size 8 at 0x5030000012f0 thread T0

Further Analysis

The vulnerability occurs in fts5SegIterAllocTombstone() when nByte is calculated as the total size for an array of pointers:

https://github.com/sqlite/sqlite/blob/d2b9cc099d66fe220eb1c4883f865ec94cb37f52/ext/fts5/fts5_index.c#L1952-L1964

static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
  const int nTomb = pIter->pSeg->nPgTombstone;
  if( nTomb>0 ){
    int nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1);
    Fts5TombstoneArray *pNew;
    pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
    if( pNew ){
      pNew->nTombstone = nTomb;
      pNew->nRef = 1;
      pIter->pTombArray = pNew;
    }
  }
}

https://github.com/sqlite/sqlite/blob/d2b9cc099d66fe220eb1c4883f865ec94cb37f52/ext/fts5/fts5_index.c#L566-L568

/* Size (in bytes) of an Fts5TombstoneArray holding up to N tombstones */
#define SZ_FTS5TOMBSTONEARRAY(N) \
  (offsetof(Fts5TombstoneArray,apTombstone)+(N)*sizeof(Fts5Data*))

nByte is a signed 32-bit integer. While the multiplication done in the SZ_FTS5TOMBSTONEARRAY macro is a 64-bit multiplication, the result gets truncated to 32-bit should the resulting value exceed 2^32 - 1.

pIter->pSeg->nPgTombstone is a fully attacker controlled, 32-bit value that is stored within the metadata tables for a FTS5 table:

https://github.com/sqlite/sqlite/blob/837dc09bce7de8971c7488b70cf5da93c60fbed0/ext/fts5/fts5_index.c#L1159

i += fts5GetVarint32(&pData[i], pSeg->nPgTombstone);

By setting nPgTombstone to e.g. 1610612738, nByte becomes 32. As a result, only 32 bytes are allocated for an array that is supposed to fit 1610612738 entries.

This array is then used in the fts5MultiIterIsDeleted() function. An array index is calculated using the Rowid of the current context and is bound checked by the number of Tombstone pointers. Since nTombstone is the original value that was not overflowed, the iPg entry can point out of bounds. If the entry at iPg is 0, a structure is allocated and a pointer to it is written out of bounds:

https://github.com/sqlite/sqlite/blob/837dc09bce7de8971c7488b70cf5da93c60fbed0/ext/fts5/fts5_index.c#L3302-L3314

 if( pSeg->pLeaf && pArray ){
    /* Figure out which page the rowid might be present on. */
    int iPg = ((u64)pSeg->iRowid) % pArray->nTombstone;
    assert( iPg>=0 );

    /* If tombstone hash page iPg has not yet been loaded from the
    ** database, load it now. */
    if( pArray->apTombstone[iPg]==0 ){
      pArray->apTombstone[iPg] = fts5DataRead(pIter->pIndex,
          FTS5_TOMBSTONE_ROWID(pSeg->pSeg->iSegid, iPg)
      );
      if( pArray->apTombstone[iPg]==0 ) return 0;
    }

Fix can be found here: https://sqlite.org/src/info/63595b74956a9391

Timeline

Date reported: 07/15/2025
Date fixed: 07/16/2025
Date disclosed: 08/15/2025


文章来源: https://github.com/google/security-research/security/advisories/GHSA-v2c8-vqqp-hv3g
如有侵权请联系:admin#unsafe.sh