{"record":{"id":"e95cfbbb0a2e9dbd","repo":"tursodatabase/turso","slug":"munmap-failed","errorCode":null,"errorMessage":"munmap failed: {}","messagePattern":"munmap failed: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"core/storage/buffer_pool.rs","lineNumber":470,"sourceCode":"            PROT_READ | PROT_WRITE,\n            MAP_PRIVATE | MAP_ANONYMOUS,\n            -1,\n            0,\n        );\n        if ptr == libc::MAP_FAILED {\n            panic!(\"mmap failed: {}\", std::io::Error::last_os_error());\n        }\n        #[cfg(target_os = \"linux\")]\n        {\n            libc::madvise(ptr, len, libc::MADV_HUGEPAGE);\n        }\n        ptr as *mut u8\n    }\n\n    pub unsafe fn dealloc(ptr: *mut u8, len: usize) {\n        let result = munmap(ptr as *mut c_void, len);\n        if result != 0 {\n            panic!(\"munmap failed: {}\", std::io::Error::last_os_error());\n        }\n    }\n}\n\n#[cfg(any(not(unix), miri))]\nmod arena {\n    pub unsafe fn alloc(len: usize) -> *mut u8 {\n        let layout = std::alloc::Layout::from_size_align(len, std::mem::size_of::<u8>()).unwrap();\n        unsafe { std::alloc::alloc_zeroed(layout) }\n    }\n    pub unsafe fn dealloc(ptr: *mut u8, len: usize) {\n        let layout = std::alloc::Layout::from_size_align(len, std::mem::size_of::<u8>()).unwrap();\n        unsafe { std::alloc::dealloc(ptr, layout) };\n    }\n}\n\n/// Shuttle tests for concurrent buffer pool operations.\n///","sourceCodeStart":452,"sourceCodeEnd":488,"githubUrl":"https://github.com/tursodatabase/turso/blob/244cde92a7df7f9b8b8b7a4075c35a12977e303e/core/storage/buffer_pool.rs#L452-L488","documentation":"Arena dealloc unmaps the region and panics if munmap fails - in practice EINVAL from a wrong pointer/length pair (double unmap, a len differing from the allocation length, or a corrupted pointer). This is a memory-management invariant break, not an environmental condition.","triggerScenarios":"Calling arena::dealloc with a len other than the one passed to alloc; freeing the same region twice; pointer arithmetic bugs in buffer-pool bookkeeping.","commonSituations":"Contributor changes to the buffer pool; refactors that split or merge arena lifetimes and recompute lengths at free time.","solutions":["Store the allocation length alongside the pointer and free with exactly that pair","Audit the pool bookkeeping for double-frees of arena ranges","Run under Miri or ASan to catch the invalid free closer to its cause"],"exampleFix":"// before\nunsafe { arena::dealloc(ptr, len_guess); } // EINVAL if len_guess != alloc len\n\n// after\nstruct ArenaBlock { ptr: *mut u8, len: usize } // pair set at alloc\nunsafe { arena::dealloc(block.ptr, block.len); }","handlingStrategy":"validation","validationCode":"// keep (ptr, len) as an inseparable pair; free with exactly the alloc pair\nstruct ArenaBlock { ptr: *mut u8, len: usize }\nfn free(block: ArenaBlock) { unsafe { arena::dealloc(block.ptr, block.len) } }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never recompute lengths at free time; carry them from allocation","Run the storage crate under Miri/ASan in CI to catch invalid frees early"],"tags":["memory","munmap","buffer-pool","double-free","unix","panic"],"backgroundTag":"memory-deallocation-failed","analyzedSha":"244cde92a7df7f9b8b8b7a4075c35a12977e303e","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}