SQLite

View Ticket
Login
2017-08-17
14:48 Fixed ticket [be436a7f]: Use-after-free on schema change where RTREE is used inside of a trigger plus 5 other changes (artifact: f77fb5a0 user: drh)
14:47
Remove an unnecessary branch from the [be436a7f4587ce517ddc] fix. (check-in: fb6ca99b user: drh tags: trunk)
14:12
Add test cases for ticket [be436a7f4587ce517] using virtual table modules fts5 and rtree. (check-in: 2101b420 user: dan tags: trunk)
02:26
Defer schema resets when the query planner is running. Proposed fix for ticket [be436a7f4587ce517]. (check-in: a7bc7752 user: drh tags: trunk)
02:25 New ticket [be436a7f] Use-after-free on schema change where RTREE is used inside of a trigger. (artifact: 580eefd3 user: drh)

Ticket Hash: be436a7f4587ce517ddc36f3670073563ecc6622
Title: Use-after-free on schema change where RTREE is used inside of a trigger
Status: Fixed Type: Code_Defect
Severity: Severe Priority: Immediate
Subsystem: Unknown Resolution: Fixed
Last Modified: 2017-08-17 14:48:10
Version Found In:
User Comments:
drh added on 2017-08-17 02:25:13:

If a virtual table invokes sqlite3_step() as part of its xConnect method (as RTREE does) and if that virtual table is used inside of a trigger and if an external schema change occurs on the first use of that trigger, then the xConnect method will provoke a schema reset that will delete a pointer to the trigger out from under the query planner, resulting in a use-after-free and a probable segfault. The following C code demonstrates the problems:

#include <stdio.h>
#include <unistd.h>
#include "sqlite3.h"

int main(int argc, char *argv){
  sqlite3 *db1, *db2;
  unlink("test.db");
  printf("VERSION: %s\n", sqlite3_libversion());
  sqlite3_open("test.db", &db1);
  sqlite3_exec(db1, 
   "CREATE VIRTUAL TABLE r1 USING rtree(id, x1, x2, y1, y2);\n"
   "CREATE TABLE t1(id, x1, x2, y1, y2);\n"
   "CREATE TABLE log(l);\n"
   "CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN\n"
   "  INSERT INTO r1 VALUES(new.id, new.x1, new.x2, new.y1, new.y2);\n"
   "  INSERT INTO log VALUES('r1: ' || new.id);\n"
   "END;", 0, 0, 0);
  sqlite3_close(db1);
  sqlite3_open("test.db", &db1);
  sqlite3_open("test.db", &db2);
  sqlite3_exec(db1, "INSERT INTO log VALUES('startup');", 0, 0, 0);
  sqlite3_exec(db2, "CREATE TABLE newtab(a,b);", 0, 0, 0);
  sqlite3_exec(db1, "INSERT INTO t1 VALUES(1,2,3,4,5);", 0, 0, 0);
  sqlite3_close(db1);
  sqlite3_close(db2);
  return 0;
}

This problem appears to have existed in the virtual table implementation forever. The test program above first began to fail with check-in [ebc9433f] on 2010-02-16 (SQLite version 3.6.23) because that check-in was the first to cause RTREE to invoke sqlite3_step() from within its xConnect method.