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;
}