mirror of
https://github.com/milvus-io/milvus.git
synced 2026-07-21 10:15:43 +00:00
fix: prevent use-after-free in TantivyIndexWrapper::finish() (#49845)
## Summary Fix a use-after-free bug in `TantivyIndexWrapper::finish()` that causes intermittent SEGFAULT (signal 139) in `json_stats_test` CI builds. **Root cause**: The Rust FFI function `tantivy_finish_index` always consumes and frees the `IndexWriterWrapper` via `Box::from_raw`, regardless of success or failure. But the C++ side only set `writer_ = nullptr` **after** the `AssertInfo` check: ```cpp auto res = RustResultWrapper(tantivy_finish_index(writer_)); // Rust frees writer_ AssertInfo(res.result_->success, ...); // throws on error writer_ = nullptr; // never reached if AssertInfo throws ``` When `finish()` returned an error (e.g., I/O issues during commit), `AssertInfo` threw an exception while `writer_` still pointed to already-freed Rust memory. The destructor chain then called `tantivy_free_index_writer(writer_)` on the dangling pointer → **use-after-free → SEGFAULT**. **Fix**: Null `writer_` before the FFI call so the destructor safely skips `free()` even when `finish()` throws. **Observed in CI**: ciloop #6762 (SIGSEGV, rc=139) and #6684 (assertion failure), both in `json_stats_test`. Intermittent (~1.8% failure rate). issue: https://github.com/milvus-io/milvus/issues/49135 ## Test plan - [ ] `json_stats_test` no longer crashes with SIGSEGV in CI - [ ] Existing C++ unit tests pass Signed-off-by: Li Liu <li.liu@zilliz.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Li Liu <li.liu@zilliz.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
66fa592752
commit
9f501f9bea
+9
-2
@@ -678,11 +678,18 @@ struct TantivyIndexWrapper {
|
||||
return;
|
||||
}
|
||||
|
||||
auto res = RustResultWrapper(tantivy_finish_index(writer_));
|
||||
// Null writer_ before the FFI call because tantivy_finish_index
|
||||
// always consumes (frees) the Rust-side IndexWriterWrapper via
|
||||
// Box::from_raw, regardless of success or failure. If we leave
|
||||
// writer_ pointing at the now-freed memory and the AssertInfo
|
||||
// below throws, the destructor path will call free() on a
|
||||
// dangling pointer (use-after-free / SEGFAULT).
|
||||
auto w = writer_;
|
||||
writer_ = nullptr;
|
||||
auto res = RustResultWrapper(tantivy_finish_index(w));
|
||||
AssertInfo(res.result_->success,
|
||||
"failed to finish index: {}",
|
||||
res.result_->error);
|
||||
writer_ = nullptr;
|
||||
finished_ = true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user