SQLite Bug Forum

sha1_query Zeroblob NULL Pointer Dereference DoS
Login

sha1_query Zeroblob NULL Pointer Dereference DoS

(1) By sdjasj on 2026-06-16 07:42:23 [source]

Summary

A NULL pointer dereference vulnerability exists in the sha1_query() SQL function implemented in ext/misc/sha1.c (also compiled into the CLI via src/shell.c.in). When the query passed to sha1_query() returns a zero-length BLOB column (e.g., zeroblob(0) or X''), sqlite3_column_blob() returns a NULL pointer per SQLite API contract, but sha1QueryFunc() passes this NULL pointer to hash_step() without checking for NULL. The hash_step() function then calls memcpy(dst, NULL, 0), which is undefined behavior per the C standard (C11 §7.24.1). On UBSan/hardened builds this causes an abort (DoS); on normal builds it is undefined behavior that could crash on platforms with guard pages or strict memcpy implementations.

The single-value sha1() function correctly checks for NULL (if( pData==0 ) return;) before calling hash_step(), but sha1_query() lacks this check — a classic "function returns NULL.

Vulnerable Code

/* ext/misc/sha1.c:375-387 — sha1QueryFunc, SQLITE_BLOB case */
case SQLITE_BLOB: {
    int n2 = sqlite3_column_bytes(pStmt, i);
    const unsigned char *z2 = sqlite3_column_blob(pStmt, i);
    /* BUG: z2 can be NULL for zero-length BLOBs; no NULL check */
    hash_step_vformat(&cx,"B%d:",n2);
    hash_step(&cx, z2, n2);    /* NULL dereference: memcpy(dst, NULL, 0) */
    break;
}

/* ext/misc/sha1.c:375-380 — sha1QueryFunc, SQLITE_TEXT case */
case SQLITE_TEXT: {
    int n2 = sqlite3_column_bytes(pStmt, i);
    const unsigned char *z2 = sqlite3_column_text(pStmt, i);
    /* NOTE: sqlite3_column_text() for empty TEXT returns non-NULL,
       but OOM can cause NULL return which is also unchecked */
    hash_step_vformat(&cx,"T%d:",n2);
    hash_step(&cx, z2, n2);    /* potential NULL dereference on OOM */
    break;
}

/* ext/misc/sha1.c:156-179 — hash_step, the sink function */
static void hash_step(
  SHA1Context *p,
  const unsigned char *data,    /* may be NULL */
  unsigned int len              /* may be 0 */
){
  unsigned int i, j;
  j = p->count[0];
  if( (p->count[0] += len << 3) < j ){
    p->count[1] += (len>>29)+1;
  }
  j = (j >> 3) & 63;
  if( (j + len) > 63 ){
    (void)memcpy(&p->buffer[j], data, (i = 64-j));  /* reads from NULL if data==NULL and j+len>63 */
    SHA1Transform(p->state, p->buffer);
    for(; i + 63 < len; i += 64){
      SHA1Transform(p->state, &data[i]);
    }
    j = 0;
  }else{
    i = 0;
  }
  (void)memcpy(&p->buffer[j], &data[i], len - i);   /* UB: memcpy(dst, NULL, 0) */
}

/* Compare with the correct NULL check in sha1Func: */
/* ext/misc/sha1.c:266-271 — sha1Func, which DOES check for NULL */
if( eType==SQLITE_BLOB ){
    pData = (const unsigned char*)sqlite3_value_blob(argv[0]);
}else{
    pData = (const unsigned char*)sqlite3_value_text(argv[0]);
}
if( pData==0 ) return;    /* ← NULL check present in sha1(), but missing in sha1QueryFunc() */
hash_step(&cx, pData, nByte);

PoC

-- Trigger NULL pointer dereference in sha1_query via zero-length BLOB
-- On UBSan/hardened builds: runtime error: null pointer passed as argument 2
-- On normal builds: undefined behavior (typically no-op, but could crash)

-- PoC 1: Simple zeroblob trigger
SELECT sha1_query('SELECT zeroblob(0)');

-- PoC 2: Empty BLOB literal
SELECT sha1_query('SELECT X''');

-- PoC 3: Zero-length BLOB from table
CREATE TABLE t(b BLOB);
INSERT INTO t VALUES(zeroblob(0));
SELECT sha1_query('SELECT b FROM t');

-- PoC 4: Multiple zero-length BLOBs in result set
SELECT sha1_query('SELECT zeroblob(0), zeroblob(0), 42');

Fix

Add NULL checks for sqlite3_column_blob() and sqlite3_column_text() return values in sha1QueryFunc(), matching the pattern used in sha1Func():

case SQLITE_BLOB: {
    int n2 = sqlite3_column_bytes(pStmt, i);
    const unsigned char *z2 = sqlite3_column_blob(pStmt, i);
    if( z2==0 && n2==0 ) break;    /* skip zero-length NULL BLOBs */
    hash_step_vformat(&cx,"B%d:",n2);
    hash_step(&cx, z2, n2);
    break;
}
case SQLITE_TEXT: {
    int n2 = sqlite3_column_bytes(pStmt, i);
    const unsigned char *z2 = sqlite3_column_text(pStmt, i);
    if( z2==0 ) break;              /* skip NULL TEXT (OOM) */
    hash_step_vformat(&cx,"T%d:",n2);
    hash_step(&cx, z2, n2);
    break;
}
Status Moderators and the post's owner may change the status of this thread unless it is still. pending moderation. See /help/forum-statuses

(2) By dan on 2026-06-16 10:53:42 in reply to 1 [link] [source]

Thanks. Should now be fixed here:

https://sqlite.org/src/info/2063926473