From 08a335c77465c01b22bf2eb1a81c264093064529 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 9 Sep 2026 04:03:37 -0700 Subject: [PATCH] fix(runtime): append new VamanaIndex virtuals instead of inserting them get_memory_usage() and get_memory_breakdown() were added between reconstruct_at() and save() in #345, shifting every later vtable slot. VamanaIndex is a pure abstract interface passed across the shared-library boundary, so a consumer compiled against v0.4.0 dispatches save() to get_memory_usage(), leaves the returned Status unconstructed, and frees a garbage pointer in its destructor. Observed as a SIGSEGV inside jemalloc during index serialization in Milvus, whose knowhere is built against a 0.4.0-era runtime-bindings nightly. Nothing catches this at link time: virtual dispatch never resolves by symbol name, so ldd -r and exported-symbol comparison both pass. SOVERSION is PROJECT_VERSION_MAJOR, which stays 0 across 0.4.0 and 0.5.0, so a v0.4.0 consumer resolves SONAME libsvs_runtime.so.0 to a 0.5.0 library and loads it. Moving both declarations after save() restores the v0.4.0 slot indices. No tagged release carries the inserted order -- it has shipped only in nightlies and on rls/v0.5.0 -- so consumers built against those nightlies need a rebuild. Co-Authored-By: Claude Opus 5 --- bindings/cpp/include/svs/runtime/vamana_index.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/bindings/cpp/include/svs/runtime/vamana_index.h b/bindings/cpp/include/svs/runtime/vamana_index.h index 0f11edec6..5c2dc3c36 100644 --- a/bindings/cpp/include/svs/runtime/vamana_index.h +++ b/bindings/cpp/include/svs/runtime/vamana_index.h @@ -98,12 +98,6 @@ struct SVS_RUNTIME_API VamanaIndex { // Reconstruct `n` vectors by ID into `output` buffer (n * dim floats). virtual Status reconstruct_at(size_t n, const size_t* ids, float* output) noexcept = 0; - // Return the index memory usage in bytes. - virtual size_t get_memory_usage() const noexcept = 0; - - // Return the bytes allocated by each index component. - virtual Status get_memory_breakdown(MemoryBreakdown* out) const noexcept = 0; - // Utility function to check storage kind support static Status check_storage_kind(StorageKind storage_kind) noexcept; @@ -120,6 +114,13 @@ struct SVS_RUNTIME_API VamanaIndex { static Status destroy(VamanaIndex* index) noexcept; virtual Status save(std::ostream& out) const noexcept = 0; + + // Return the index memory usage in bytes. + virtual size_t get_memory_usage() const noexcept = 0; + + // Return the bytes allocated by each index component. + virtual Status get_memory_breakdown(MemoryBreakdown* out) const noexcept = 0; + static Status load( VamanaIndex** index, std::istream& in, MetricType metric, StorageKind storage_kind ) noexcept;