@@ -1210,29 +1210,18 @@ fn declare_cursor_with_context(
12101210 . collect :: < Result < _ , _ > > ( )
12111211 . map_err ( |e| rustler:: Error :: Term ( Box :: new ( e) ) ) ?;
12121212
1213- // Determine conn_id for cursor ownership tracking
1214- let conn_id = if id_type == transaction ( ) {
1215- // For transaction, get conn_id from TransactionEntry
1216- let txn_registry = safe_lock ( & TXN_REGISTRY , "declare_cursor_with_context txn" ) ?;
1217- let entry = txn_registry
1218- . get ( id)
1219- . ok_or_else ( || rustler:: Error :: Term ( Box :: new ( "Transaction not found" ) ) ) ?;
1220- entry. conn_id . clone ( )
1221- } else if id_type == connection ( ) {
1222- // For connection, use the id directly
1223- id. to_string ( )
1224- } else {
1225- return Err ( rustler:: Error :: Term ( Box :: new ( "Invalid id_type for cursor" ) ) ) ;
1226- } ;
1227-
1228- let ( columns, rows) = if id_type == transaction ( ) {
1229- // Use transaction registry
1213+ let ( conn_id, columns, rows) = if id_type == transaction ( ) {
1214+ // CONSOLIDATED LOCK SCOPE: Prevent TOCTOU by holding lock for both conn_id lookup and query execution
12301215 let mut txn_registry = safe_lock ( & TXN_REGISTRY , "declare_cursor_with_context txn" ) ?;
12311216 let entry = txn_registry
12321217 . get_mut ( id)
12331218 . ok_or_else ( || rustler:: Error :: Term ( Box :: new ( "Transaction not found" ) ) ) ?;
12341219
1235- TOKIO_RUNTIME . block_on ( async {
1220+ // Capture conn_id while we hold the lock
1221+ let conn_id_for_cursor = entry. conn_id . clone ( ) ;
1222+
1223+ // Execute query without releasing the lock
1224+ let ( cols, rows) = TOKIO_RUNTIME . block_on ( async {
12361225 let mut result_rows = entry
12371226 . transaction
12381227 . query ( sql, decoded_args)
@@ -1266,9 +1255,12 @@ fn declare_cursor_with_context(
12661255 }
12671256
12681257 Ok :: < _ , rustler:: Error > ( ( columns, rows) )
1269- } ) ?
1270- } else {
1271- // Use connection registry
1258+ } ) ?;
1259+
1260+ ( conn_id_for_cursor, cols, rows)
1261+ } else if id_type == connection ( ) {
1262+ // For connection, use the id directly
1263+ let conn_id_for_cursor = id. to_string ( ) ;
12721264 let conn_map = safe_lock ( & CONNECTION_REGISTRY , "declare_cursor_with_context conn" ) ?;
12731265 let client = conn_map
12741266 . get ( id)
@@ -1277,7 +1269,7 @@ fn declare_cursor_with_context(
12771269
12781270 drop ( conn_map) ;
12791271
1280- TOKIO_RUNTIME . block_on ( async {
1272+ let ( cols , rows ) = TOKIO_RUNTIME . block_on ( async {
12811273 let client_guard = safe_lock_arc ( & client, "declare_cursor_with_context client" ) ?;
12821274 let conn_guard =
12831275 safe_lock_arc ( & client_guard. client , "declare_cursor_with_context conn" ) ?;
@@ -1314,7 +1306,11 @@ fn declare_cursor_with_context(
13141306 }
13151307
13161308 Ok :: < _ , rustler:: Error > ( ( columns, rows) )
1317- } ) ?
1309+ } ) ?;
1310+
1311+ ( conn_id_for_cursor, cols, rows)
1312+ } else {
1313+ return Err ( rustler:: Error :: Term ( Box :: new ( "Invalid id_type for cursor" ) ) ) ;
13181314 } ;
13191315
13201316 let cursor_id = Uuid :: new_v4 ( ) . to_string ( ) ;
0 commit comments