From c9d10acab275b7fdd5e9ccf8e2d50865c53978d0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 21 Mar 2026 16:42:32 +0000 Subject: [PATCH] fix: copy bech32 cache key to prevent SIGSEGV from mmap'd zero-copy data cacheBech32Addr stored the caller-provided cacheKey (created via UnsafeBytesToStr) directly in the global LRU cache. UnsafeBytesToStr produces a string that shares backing memory with the original []byte slice. When that slice originates from a MemIAVL zero-copy read, its backing memory is mmap'd from a snapshot file on disk. During snapshot rotation, Tree.ReplaceWith() closes the old snapshot which calls munmap(), unmapping the memory region. Any LRU cache keys still pointing into that region become dangling pointers. The next map lookup in the LRU triggers memeqbody on unmapped memory, causing a SIGSEGV that halts the node. Fix: use string(addr) instead of cacheKey when inserting into the cache. string([]byte) always allocates a heap-backed copy, so the cache key survives independently of the original slice's lifetime. Performance impact: zero on the hot path (cache hits still use the zero-alloc UnsafeBytesToStr for lookup). On cache misses, one extra 20-byte allocation is negligible next to the bech32 encoding already performed. Co-authored-by: Masih H. Derkani --- sei-cosmos/types/address.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sei-cosmos/types/address.go b/sei-cosmos/types/address.go index a9faf2937c..4a507b58f4 100644 --- a/sei-cosmos/types/address.go +++ b/sei-cosmos/types/address.go @@ -661,11 +661,19 @@ func addressBytesFromHexString(address string) ([]byte, error) { } // cacheBech32Addr is not concurrency safe. Concurrent access to cache causes race condition. -func cacheBech32Addr(prefix string, addr []byte, cache *simplelru.LRU[string, string], cacheKey string) string { +func cacheBech32Addr(prefix string, addr []byte, cache *simplelru.LRU[string, string], _ string) string { bech32Addr, err := bech32.ConvertAndEncode(prefix, addr) if err != nil { panic(err) } - cache.Add(cacheKey, bech32Addr) + // Use string(addr) instead of the caller-provided cacheKey to guarantee the + // map key is backed by heap-allocated memory. Callers create cacheKey via + // UnsafeBytesToStr, which shares the backing array of addr. When addr + // originates from a MemIAVL zero-copy read the backing memory is mmap'd; + // after a snapshot rotation the region is munmap'd, turning any map key that + // still points there into a dangling pointer and causing SIGSEGV during + // subsequent lookups. string(addr) always copies, so the cache key survives + // independently of the original slice's lifetime. + cache.Add(string(addr), bech32Addr) return bech32Addr }