table_helper: fix use-after-free on prepared-statement invalidation

insert() held no local strong ref to the prepared modification_statement
across the suspension in execute(). On a single shard:

1. Fiber A suspends inside _insert_stmt->execute().
2. DROP TABLE / DROP KEYSPACE on the target, or LRU eviction, removes
   the prepared_statements_cache entry, releasing its strong ref.
3. Fiber B re-enters cache_table_info(), sees _prepared_stmt
   (checked_weak_ptr) invalidated, and runs _insert_stmt = nullptr,
   releasing the last strong ref. The modification_statement is freed.
4. Fiber A resumes inside execute() and touches freed *this.

Pin strong ref to _insert_stmt locally before the suspension.
This commit is contained in:
Marcin Maliszkiewicz
2026-04-21 17:12:48 +02:00
parent 1ca97f0c0a
commit aa18c3ed4a

View File

@@ -152,9 +152,13 @@ future<> table_helper::insert(cql3::query_processor& qp, service::migration_mana
break;
}
}
// Pin a strong ref locally: while we suspend in execute(), a concurrent
// insert() on this shard may reset _insert_stmt to nullptr if the
// prepared_statements_cache entry gets invalidated, freeing the object.
auto stmt = _insert_stmt;
auto opts = opt_maker();
opts.prepare(_prepared_stmt->bound_names);
co_await _insert_stmt->execute(qp, qs, opts, std::nullopt);
co_await stmt->execute(qp, qs, opts, std::nullopt);
}
future<> table_helper::setup_keyspace(cql3::query_processor& qp, service::migration_manager& mm, std::string_view keyspace_name, sstring replication_strategy_name,