@@ -847,10 +847,6 @@ defmodule Exqlite.Sqlite3Test do
847847 :ok = Sqlite3 . close ( conn )
848848 end
849849
850- # Verifies that concurrent interrupt/1 and close/1 are race-free.
851- # exqlite_interrupt acquires interrupt_mutex (shared with close()) before
852- # reading conn->db, and close() acquires it (while holding the conn lock,
853- # after the running query has finished) before nulling conn->db.
854850 test "concurrent interrupt and close does not segfault" do
855851 for _ <- 1 .. 500 do
856852 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -860,10 +856,6 @@ defmodule Exqlite.Sqlite3Test do
860856 end
861857 end
862858
863- # Targets the two-concurrent-close TOCTOU:
864- # Both threads pass the `conn->db == NULL` check outside the lock.
865- # One closes and sets conn->db = NULL; the other then calls
866- # sqlite3_get_autocommit(NULL) inside the lock → segfault.
867859 test "concurrent double close does not segfault" do
868860 for _ <- 1 .. 200 do
869861 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -875,33 +867,17 @@ defmodule Exqlite.Sqlite3Test do
875867 end
876868 end
877869
878- # Targets the missing NULL guard in exqlite_last_insert_rowid.
879- # The function acquires the lock but never checks whether conn->db is NULL
880- # before calling sqlite3_last_insert_rowid(conn->db).
881- # After close() sets conn->db = NULL, the very next last_insert_rowid call
882- # acquires the lock and dereferences the NULL pointer → segfault.
883- # No concurrency is required; the crash is deterministic.
884870 test "last_insert_rowid after close does not segfault" do
885871 for _ <- 1 .. 100 do
886872 { :ok , conn } = Sqlite3 . open ( ":memory:" )
887873 :ok = Sqlite3 . execute ( conn , "create table t (id integer primary key)" )
888874 :ok = Sqlite3 . execute ( conn , "insert into t values (1)" )
889875 { :ok , 1 } = Sqlite3 . last_insert_rowid ( conn )
890876 :ok = Sqlite3 . close ( conn )
891- # Unfixed: lock acquired, sqlite3_last_insert_rowid(NULL) → segfault.
892- # Fixed: lock acquired, NULL check fires → {:error, :connection_closed}.
893877 assert { :error , :connection_closed } = Sqlite3 . last_insert_rowid ( conn )
894878 end
895879 end
896880
897- # Targets the TOCTOU in exqlite_transaction_status.
898- # The function checks !conn->db OUTSIDE the lock, then
899- # acquires the lock and calls sqlite3_get_autocommit(conn->db) inside it.
900- # If close() completes (setting conn->db = NULL) between that NULL check
901- # and the lock acquisition, transaction_status calls
902- # sqlite3_get_autocommit(NULL) → segfault.
903- # This is a distinct bug from the double-close TOCTOU (Test 1): it involves
904- # two *different* functions whose NULL checks are both outside their locks.
905881 test "concurrent close and transaction_status does not segfault" do
906882 for _ <- 1 .. 500 do
907883 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -913,10 +889,6 @@ defmodule Exqlite.Sqlite3Test do
913889 end
914890 end
915891
916- # Targets the TOCTOU in exqlite_changes:
917- # conn->db is checked outside the lock; a concurrent close() can set
918- # it to NULL between the check and the sqlite3_changes() call inside
919- # the lock → sqlite3_changes(NULL) → segfault.
920892 test "concurrent close and changes does not segfault" do
921893 for _ <- 1 .. 500 do
922894 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -928,9 +900,6 @@ defmodule Exqlite.Sqlite3Test do
928900 end
929901 end
930902
931- # Targets the missing NULL guard in exqlite_serialize:
932- # The function acquires the lock but calls sqlite3_serialize(conn->db, ...)
933- # without checking for NULL → sqlite3_serialize(NULL, ...) → segfault.
934903 test "serialize after close does not segfault" do
935904 for _ <- 1 .. 50 do
936905 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -939,9 +908,6 @@ defmodule Exqlite.Sqlite3Test do
939908 end
940909 end
941910
942- # Targets exqlite_enable_load_extension:
943- # No lock is held and no NULL check is performed; after close sets
944- # conn->db = NULL, sqlite3_enable_load_extension(NULL, ...) → segfault.
945911 test "enable_load_extension after close does not segfault" do
946912 for _ <- 1 .. 50 do
947913 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -950,9 +916,6 @@ defmodule Exqlite.Sqlite3Test do
950916 end
951917 end
952918
953- # Targets the missing NULL guard in exqlite_deserialize:
954- # Acquires the lock then calls sqlite3_deserialize(conn->db, ...) without
955- # a NULL check → sqlite3_deserialize(NULL, ...) → segfault.
956919 test "deserialize after close does not segfault" do
957920 { :ok , src } = Sqlite3 . open ( ":memory:" )
958921 { :ok , data } = Sqlite3 . serialize ( src , "main" )
@@ -965,16 +928,11 @@ defmodule Exqlite.Sqlite3Test do
965928 end
966929 end
967930
968- # Targets the missing lock release on sqlite3_malloc failure in exqlite_deserialize:
969- # sqlite3_malloc(0) returns NULL, so an empty binary triggers the error path.
970- # Without the fix the lock is never released and any subsequent call deadlocks.
971931 test "deserialize malloc failure releases the connection lock" do
972932 { :ok , conn } = Sqlite3 . open ( ":memory:" )
973933
974- # sqlite3_malloc(0) → NULL → hits the error return. Bug: lock not released.
975934 assert { :error , _ } = Sqlite3 . deserialize ( conn , "main" , << >> )
976935
977- # close/1 needs the lock. If it was leaked above, this will deadlock.
978936 task = Task . async ( fn -> Sqlite3 . close ( conn ) end )
979937 result = Task . yield ( task , 500 )
980938 Task . shutdown ( task , :brutal_kill )
@@ -983,9 +941,6 @@ defmodule Exqlite.Sqlite3Test do
983941 "close deadlocked after failed deserialize (lock not released)"
984942 end
985943
986- # Targets the missing NULL guard in exqlite_set_update_hook:
987- # Acquires the lock then calls sqlite3_update_hook(conn->db, ...) without
988- # a NULL check → sqlite3_update_hook(NULL, ...) → segfault.
989944 test "set_update_hook after close does not segfault" do
990945 for _ <- 1 .. 50 do
991946 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -994,9 +949,6 @@ defmodule Exqlite.Sqlite3Test do
994949 end
995950 end
996951
997- # Targets conn->db NULL dereference in exqlite_errmsg (connection branch).
998- # After close(), conn->db is NULL. exqlite_errmsg calls
999- # sqlite3_errmsg(conn->db) without a NULL guard → segfault.
1000952 test "errmsg conn after close does not segfault" do
1001953 for _ <- 1 .. 50 do
1002954 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -1005,9 +957,6 @@ defmodule Exqlite.Sqlite3Test do
1005957 end
1006958 end
1007959
1008- # Targets misleading return value in exqlite_errmsg (statement branch).
1009- # After release(), statement->statement is NULL. The NIF should return
1010- # nil (no error message available), not {:error, :connection_closed}.
1011960 test "errmsg stmt after release returns nil" do
1012961 for _ <- 1 .. 50 do
1013962 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -1018,10 +967,6 @@ defmodule Exqlite.Sqlite3Test do
1018967 end
1019968 end
1020969
1021- # Targets bind/2 crash when statement is released.
1022- # bind_parameter_count returns {:error, :invalid_statement} after release,
1023- # but bind/2 assumes it always returns an integer, causing a confusing
1024- # crash instead of a clean error.
1025970 test "bind after release returns error" do
1026971 for _ <- 1 .. 50 do
1027972 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -1034,11 +979,6 @@ defmodule Exqlite.Sqlite3Test do
1034979 end
1035980
1036981 describe ".step, .columns, .multi_step, .reset, .bind_* after release" do
1037- # Targets statement use-after-release in exqlite_step.
1038- # After Sqlite3.release(conn, stmt), statement->statement is set to NULL
1039- # under the connection lock. exqlite_step acquires the connection lock
1040- # and then calls sqlite3_step(statement->statement) without checking for
1041- # NULL → sqlite3_step(NULL) → segfault.
1042982 test "step after release does not segfault" do
1043983 for _ <- 1 .. 50 do
1044984 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -1050,11 +990,6 @@ defmodule Exqlite.Sqlite3Test do
1050990 end
1051991 end
1052992
1053- # Targets statement use-after-release in exqlite_columns.
1054- # After Sqlite3.release(conn, stmt), statement->statement is NULL.
1055- # exqlite_columns acquires the statement lock (= connection lock) and
1056- # calls sqlite3_column_count(statement->statement) without checking for
1057- # NULL → sqlite3_column_count(NULL) → segfault.
1058993 test "columns after release does not segfault" do
1059994 for _ <- 1 .. 50 do
1060995 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -1066,11 +1001,6 @@ defmodule Exqlite.Sqlite3Test do
10661001 end
10671002 end
10681003
1069- # Targets statement use-after-release in exqlite_multi_step.
1070- # After Sqlite3.release(conn, stmt), statement->statement is NULL.
1071- # The pre-lock check catches the case where release happened before
1072- # the call, and the inside-lock check catches the case where release
1073- # races with the lock acquisition.
10741004 test "multi_step after release does not segfault" do
10751005 for _ <- 1 .. 50 do
10761006 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -1082,10 +1012,6 @@ defmodule Exqlite.Sqlite3Test do
10821012 end
10831013 end
10841014
1085- # Targets statement use-after-release in exqlite_reset.
1086- # After Sqlite3.release(conn, stmt), statement->statement is NULL.
1087- # exqlite_reset acquires the statement lock and calls sqlite3_reset(NULL)
1088- # without checking for NULL → segfault.
10891015 test "reset after release does not segfault" do
10901016 for _ <- 1 .. 50 do
10911017 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -1097,10 +1023,6 @@ defmodule Exqlite.Sqlite3Test do
10971023 end
10981024 end
10991025
1100- # Targets statement use-after-release in exqlite_bind_parameter_count.
1101- # After Sqlite3.release(conn, stmt), statement->statement is NULL.
1102- # exqlite_bind_parameter_count acquires the statement lock and calls
1103- # sqlite3_bind_parameter_count(NULL) → segfault.
11041026 test "bind_parameter_count after release does not segfault" do
11051027 for _ <- 1 .. 50 do
11061028 { :ok , conn } = Sqlite3 . open ( ":memory:" )
@@ -1111,10 +1033,6 @@ defmodule Exqlite.Sqlite3Test do
11111033 end
11121034 end
11131035
1114- # Targets statement use-after-release in the bind_* NIFs.
1115- # After Sqlite3.release(conn, stmt), statement->statement is NULL.
1116- # Each bind_* NIF acquires the statement lock and calls the SQLite bind
1117- # function without checking for NULL → segfault.
11181036 test "bind_text after release does not segfault" do
11191037 for _ <- 1 .. 50 do
11201038 { :ok , conn } = Sqlite3 . open ( ":memory:" )
0 commit comments