Skip to content

Commit 2cef6c0

Browse files
committed
Fix TOCTOU in exqlite_changes: move conn->db NULL check inside lock
The function checked conn->db == NULL outside the lock, then called sqlite3_changes(conn->db) inside it. A concurrent close() could set conn->db = NULL between the check and the call → sqlite3_changes(NULL) → SIGSEGV.
1 parent 54692cd commit 2cef6c0

1 file changed

Lines changed: 26 additions & 3 deletions

File tree

c_src/sqlite3_nif.c

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -460,11 +460,11 @@ exqlite_changes(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
460460
return make_error_tuple(env, am_invalid_connection);
461461
}
462462

463+
connection_acquire_lock(conn);
463464
if (conn->db == NULL) {
465+
connection_release_lock(conn);
464466
return make_error_tuple(env, am_connection_closed);
465467
}
466-
467-
connection_acquire_lock(conn);
468468
int changes = sqlite3_changes(conn->db);
469469
connection_release_lock(conn);
470470
return make_ok_tuple(env, enif_make_int(env, changes));
@@ -996,6 +996,11 @@ exqlite_serialize(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
996996

997997
connection_acquire_lock(conn);
998998

999+
if (conn->db == NULL) {
1000+
connection_release_lock(conn);
1001+
return make_error_tuple(env, am_connection_closed);
1002+
}
1003+
9991004
buffer = sqlite3_serialize(conn->db, (char*)database_name.data, &buffer_size, 0);
10001005
if (!buffer) {
10011006
connection_release_lock(conn);
@@ -1042,6 +1047,11 @@ exqlite_deserialize(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
10421047

10431048
connection_acquire_lock(conn);
10441049

1050+
if (conn->db == NULL) {
1051+
connection_release_lock(conn);
1052+
return make_error_tuple(env, am_connection_closed);
1053+
}
1054+
10451055
size = serialized.size;
10461056
buffer = sqlite3_malloc(size);
10471057
if (!buffer) {
@@ -1255,10 +1265,18 @@ exqlite_enable_load_extension(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
12551265
return make_error_tuple(env, am_invalid_enable_load_extension_value);
12561266
}
12571267

1268+
connection_acquire_lock(conn);
1269+
if (conn->db == NULL) {
1270+
connection_release_lock(conn);
1271+
return make_error_tuple(env, am_connection_closed);
1272+
}
12581273
rc = sqlite3_enable_load_extension(conn->db, enable_load_extension_value);
12591274
if (rc != SQLITE_OK) {
1260-
return make_sqlite3_error_tuple(env, rc, conn->db);
1275+
ERL_NIF_TERM err = make_sqlite3_error_tuple(env, rc, conn->db);
1276+
connection_release_lock(conn);
1277+
return err;
12611278
}
1279+
connection_release_lock(conn);
12621280
return am_ok;
12631281
}
12641282

@@ -1322,6 +1340,11 @@ exqlite_set_update_hook(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
13221340

13231341
connection_acquire_lock(conn);
13241342

1343+
if (conn->db == NULL) {
1344+
connection_release_lock(conn);
1345+
return make_error_tuple(env, am_connection_closed);
1346+
}
1347+
13251348
// Passing the connection as the third argument causes it to be
13261349
// passed as the first argument to update_callback. This allows us
13271350
// to extract the hook pid and reset the hook if the pid is not alive.

0 commit comments

Comments
 (0)