From 0c52ec969548b277038624ff9405e4e3b44a14c5 Mon Sep 17 00:00:00 2001 From: erhant Date: Fri, 19 Jun 2026 18:50:02 +0300 Subject: [PATCH] fix(indexer): stop FFI integration tests segfaulting on query_account MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The indexer FFI test helper borrowed `ctx.runtime()` via `Runtime::from_borrowed` and then moved `ctx` (and its by-value `tokio::runtime::Runtime` field) into the tuple returned from `setup()`. That move relocates the runtime, leaving the raw pointer the indexer stored dangling. Sync queries never touch the runtime, so they passed; `query_account` is the only path that `block_on`s it, so it dereferenced freed stack memory → SIGSEGV (hence only the two `indexer_ffi_state_consistency*` tests crashed). Pass a null runtime so the FFI creates and owns its own — the same lifetime path the production module uses (`start_indexer(nullptr, …)`) — instead of borrowing a runtime whose address isn't stable across the move. --- .../tests/indexer_ffi_helpers/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/integration_tests/tests/indexer_ffi_helpers/mod.rs b/integration_tests/tests/indexer_ffi_helpers/mod.rs index 73d7bf57..e9527269 100644 --- a/integration_tests/tests/indexer_ffi_helpers/mod.rs +++ b/integration_tests/tests/indexer_ffi_helpers/mod.rs @@ -40,10 +40,7 @@ unsafe extern "C" { ) -> InitializedIndexerServiceFFIResult; } -pub fn setup_indexer_ffi( - runtime: &Runtime, - bedrock_addr: SocketAddr, -) -> Result<(IndexerServiceFFI, TempDir)> { +pub fn setup_indexer_ffi(bedrock_addr: SocketAddr) -> Result<(IndexerServiceFFI, TempDir)> { let temp_indexer_dir = tempfile::tempdir().context("Failed to create temp dir for indexer home")?; @@ -63,8 +60,9 @@ pub fn setup_indexer_ffi( file.flush()?; let res = - // SAFETY: lib function ensures validity of value. - unsafe { start_indexer(std::ptr::from_ref(runtime), CString::new(config_path.to_str().unwrap())?.as_ptr()) }; + // SAFETY: null runtime → the FFI creates and owns its own tokio runtime, + // so there is no external runtime whose address we must keep stable. + unsafe { start_indexer(std::ptr::null(), CString::new(config_path.to_str().unwrap())?.as_ptr()) }; if res.error.is_error() { anyhow::bail!("Indexer FFI error {:?}", res.error); @@ -79,9 +77,11 @@ pub fn setup_indexer_ffi( pub fn setup() -> Result<(BlockingTestContext, IndexerServiceFFI, TempDir)> { let ctx = TestContext::builder().disable_indexer().build_blocking()?; - // Safety: ctx runtime is valid for the lifetime of the returned Runtime - let runtime = unsafe { Runtime::from_borrowed(ctx.runtime()) }; - let (indexer_ffi, indexer_dir) = setup_indexer_ffi(&runtime, ctx.ctx().bedrock_addr())?; + // Don't borrow `ctx.runtime()`: `ctx` (and its by-value tokio runtime) is + // moved into the returned tuple, which would leave any pointer into it + // dangling. Pass a null runtime so the FFI owns its own — the same path the + // production module uses. + let (indexer_ffi, indexer_dir) = setup_indexer_ffi(ctx.ctx().bedrock_addr())?; Ok((ctx, indexer_ffi, indexer_dir)) }