Crash in zipfile(NULL) Due to Unhandled NULL Pointer in SQLite 3.40.0
Crash Scenario and Context in SQLite 3.40.0
A critical issue was identified in SQLite version 3.40.0 where executing the query SELECT * FROM zipfile(NULL)
results in a segmentation fault (SIGSEGV). The crash occurs due to an unhandled NULL pointer dereference within the zipfile
extension when attempting to open a file. The stack trace provided by the reporter highlights the failure at fopen+40
, indicating that the NULL value propagated to the standard library’s file-opening function without prior validation. This behavior deviates from SQLite’s typical error-handling mechanisms, which are designed to return a runtime error (e.g., "Runtime error: cannot open file:") instead of crashing the process.
The zipfile
virtual table extension, included with the SQLite command-line interface (CLI), is intended to read and parse ZIP archives. When provided with a valid filename or BLOB containing ZIP data, it constructs a virtual table representing the archive’s contents. However, passing a NULL argument bypasses internal safeguards, leading to a direct call to fopen(NULL, ...)
in the C code. While many C library implementations silently handle NULL filenames by returning a failure code, certain environments—particularly those with custom libc
implementations like the Cosmopolitan project—crash when fopen
receives a NULL pointer. This discrepancy explains why the issue manifested in the reporter’s environment but not in standard Linux builds tested by other developers.
The crash is reproducible under specific conditions:
- The SQLite CLI or application must include the
zipfile
extension (not part of the core amalgamation). - The
zipfile
function receives a NULL argument, either directly viazipfile(NULL)
or indirectly through a zero-length BLOB (e.g.,zipfile(CAST('' AS BLOB))
). - The underlying C library’s
fopen
implementation lacks NULL pointer checks, causing a segmentation fault.
Root Causes: NULL Propagation and Libc Variability
The crash stems from two interrelated factors: inadequate input validation in the zipfile
extension and variability in C library behavior.
1. Missing NULL Check in zipfile
Argument Handling
The zipfile
virtual table processes its input argument (intended as a filename or BLOB) without explicitly checking for NULL values. When the argument is NULL, the extension’s internal logic constructs a filename pointer set to NULL. This pointer is passed to fopen
, which, per the C standard, exhibits undefined behavior when given a NULL filename. In SQLite’s default error-handling paradigm, such invalid inputs should trigger a runtime error via sqlite3_result_error()
or similar APIs. However, the absence of a NULL check allows execution to proceed to fopen
, leading to a crash in environments where NULL is not tolerated.
2. Libc-Specific Behavior of fopen
The C standard does not mandate how fopen
handles NULL filenames, leading to implementation-dependent outcomes. Most mainstream C libraries (e.g., glibc on Linux, ucrt on Windows) return a NULL file pointer and set errno
to EINVAL
or EFAULT
when given a NULL filename. SQLite’s error-handling logic detects this failure and surfaces a "cannot open file" error. However, in minimal or custom C libraries—such as those used in embedded systems or specialized projects—the fopen
implementation may dereference the filename pointer without validation, causing a segmentation fault. This explains why the crash was observed in the reporter’s environment but not in standard SQLite CLI builds.
3. Prior Incidents and Code Path Similarities
A related crash involving zipfile(zeroblob('test.zip'))
was reported earlier, where a zero-length BLOB led to similar NULL pointer propagation. The zeroblob
function generates a BLOB of specified length filled with zero bytes. When passed to zipfile
, the extension attempts to interpret the BLOB as ZIP data. A zero-length BLOB is treated as an empty file, which some code paths might equate to a NULL filename. This suggests a broader issue in how zipfile
handles inputs that resolve to invalid or empty file references.
Resolution: Code Fixes, Validation, and Environment-Specific Testing
1. Code Fix in SQLite’s zipfile
Extension
The SQLite development team addressed the issue by adding an explicit NULL check in the zipfile
extension. The fix ensures that a NULL filename pointer—resulting from a NULL argument or an empty BLOB—triggers a runtime error before reaching fopen
. The relevant code change involves modifying the zipfileFilter
function to validate the input filename:
if( pFilename == NULL ){
sqlite3_result_error(context, "cannot open file: NULL pointer", -1);
return;
}
This check prevents fopen
from receiving a NULL pointer, eliminating the crash. The fix was applied to both the trunk (development branch) and the 3.40.x maintenance branch, with version numbers incremented to reflect the update (e.g., 3.40.2). Users are advised to rebuild SQLite from these branches or await an official release containing the fix.
2. Input Sanitization in Application Code
Applications invoking zipfile
should validate inputs to avoid passing NULL or empty BLOBs. For example:
SELECT * FROM zipfile(COALESCE(NULL, 'default.zip'));
or in application logic:
filename = get_user_input()
if not filename:
raise ValueError("Filename cannot be empty")
cursor.execute("SELECT * FROM zipfile(?)", (filename,))
3. Testing in Custom Libc Environments
Developers using non-standard C libraries should rigorously test SQLite extensions under memory-sanitization tools like Valgrind or AddressSanitizer. These tools detect NULL pointer dereferences and other undefined behaviors that might not manifest in standard environments. For the reported crash, running the SQLite CLI under Valgrind would surface the invalid fopen
call:
valgrind sqlite3 :memory: "SELECT * FROM zipfile(NULL)"
4. Upgrading and Patch Application
Organizations reliant on SQLite 3.40.0 should apply the official patch or upgrade to a fixed version. The patch is available in SQLite’s Fossil repository and can be cherry-picked into custom builds:
fossil update e4de5777578d27ae # Check-in containing the fix
5. Understanding SQLite Extension Risks
The incident underscores the importance of auditing extensions (e.g., zipfile
, csv
, json
) for input validation. Unlike core SQLite functions, extensions may not adhere to the same robustness standards, especially when interfacing with external libraries or system calls. Developers should review extension code when deploying in environments with atypical C libraries.
By addressing the NULL pointer propagation, validating inputs, and accounting for C library variability, this resolution ensures that zipfile
and similar extensions gracefully handle edge cases without crashing. The fix exemplifies SQLite’s commitment to robustness, even in less common execution environments.