Crash found through fuzzing
(1) By Maik (maikbe) on 2022-04-30 08:49:02 [source]
Hello,
I found another crash with my grammar-based fuzzer. It is present in the current SQLite version but only occurs when assertions are turned on.
- sqlite3.c version bb2798be3fb57378
- fuzzershell.c version: a055db2a613450af
Query:
CREATE TEMP TABLE IF NOT EXISTS t0 (name CONSTRAINT constr1 PRIMARY KEY ON CONFLICT ABORT);
CREATE INDEX index1 ON t0 (FALSE COLLATE RTRIM , ~name COLLATE RTRIM ASC);
DELETE FROM t0 AS t0 INDEXED BY index1 WHERE t0.name IS (~name) COLLATE RTRIM IS NOT TRUE COLLATE RTRIM IS NOT ~name NOT IN t0 RETURNING CURRENT_TIMESTAMP name;
Output:
...
TRACE: CREATE TEMP TABLE IF NOT EXISTS t0 (name CONSTRAINT constr1 PRIMARY KEY ON CONFLICT ABORT);
TRACE: CREATE INDEX index1 ON t0 (FALSE COLLATE RTRIM , ~name COLLATE RTRIM ASC);
fuzzershell: sqlite3.c:106410: sqlite3FindInIndex: Assertion `pReq!=0 || pRhs->iColumn==XN_ROWID || pParse->nErr' failed.
Aborted (core dumped)
What do you think?
Best regards,
Maik
(2) By Richard Hipp (drh) on 2022-04-30 12:38:06 in reply to 1 [link] [source]
Thank you for the bug report.
The assertion fault does not indicate a memory error or other exploit opportunity. The crash can be avoided simply by omitting the assert().
However, the assert() does reveal the possibility of SQLite getting an incorrect answer under obscure circumstances. At a minimum, you need to have an index on an expression where that expression involves the use of a COLLATE operator, then you need to run some statement where that index on the expression is part of the right operand of an == or IS operator.
The following script demonstrates the incorrect answer:
.mode box CREATE TABLE t1(x TEXT PRIMARY KEY, y TEXT, z INT); INSERT INTO t1(x,y,z) VALUES('alpha','ALPHA',1),('bravo','charlie',1); CREATE INDEX i1 ON t1(+y COLLATE NOCASE); SELECT * FROM t1; -- Show that there are two rows in t1 DELETE FROM t1 INDEXED BY i1 WHERE x IS +y COLLATE NOCASE IN (SELECT z FROM t1) RETURNING *; -- The alpha-ALPHA row gets deleted. SELECT * FROM t1; -- the bravo-charlie row is retained.
Prior to the fix at check-in a8da85c57e07721d, the script above would delete 0 rows. The correct behavior is to delete a single row, which is what now happens with the trunk code.
(3) By Maik (maikbe) on 2022-04-30 12:52:11 in reply to 2 [link] [source]
Thanks again for the extraordinary quick reply, analysis, and fix implementation!