tursodatabase/turso · critical

munmap failed: {}

Error message

munmap failed: {}

What it means

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.

Source

Thrown at core/storage/buffer_pool.rs:470

            PROT_READ | PROT_WRITE,
            MAP_PRIVATE | MAP_ANONYMOUS,
            -1,
            0,
        );
        if ptr == libc::MAP_FAILED {
            panic!("mmap failed: {}", std::io::Error::last_os_error());
        }
        #[cfg(target_os = "linux")]
        {
            libc::madvise(ptr, len, libc::MADV_HUGEPAGE);
        }
        ptr as *mut u8
    }

    pub unsafe fn dealloc(ptr: *mut u8, len: usize) {
        let result = munmap(ptr as *mut c_void, len);
        if result != 0 {
            panic!("munmap failed: {}", std::io::Error::last_os_error());
        }
    }
}

#[cfg(any(not(unix), miri))]
mod arena {
    pub unsafe fn alloc(len: usize) -> *mut u8 {
        let layout = std::alloc::Layout::from_size_align(len, std::mem::size_of::<u8>()).unwrap();
        unsafe { std::alloc::alloc_zeroed(layout) }
    }
    pub unsafe fn dealloc(ptr: *mut u8, len: usize) {
        let layout = std::alloc::Layout::from_size_align(len, std::mem::size_of::<u8>()).unwrap();
        unsafe { std::alloc::dealloc(ptr, layout) };
    }
}

/// Shuttle tests for concurrent buffer pool operations.
///

View on GitHub (pinned to 244cde92a7)

Solutions

  1. Store the allocation length alongside the pointer and free with exactly that pair
  2. Audit the pool bookkeeping for double-frees of arena ranges
  3. Run under Miri or ASan to catch the invalid free closer to its cause

Example fix

// before
unsafe { arena::dealloc(ptr, len_guess); } // EINVAL if len_guess != alloc len

// after
struct ArenaBlock { ptr: *mut u8, len: usize } // pair set at alloc
unsafe { arena::dealloc(block.ptr, block.len); }
Defensive patterns

Strategy: validation

Validate before calling

// keep (ptr, len) as an inseparable pair; free with exactly the alloc pair
struct ArenaBlock { ptr: *mut u8, len: usize }
fn free(block: ArenaBlock) { unsafe { arena::dealloc(block.ptr, block.len) } }

Prevention

When it happens

Trigger: 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.

Common situations: Contributor changes to the buffer pool; refactors that split or merge arena lifetimes and recompute lengths at free time.

Related errors


AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20). Data as JSON: /api/errors/e95cfbbb0a2e9dbd. Report an issue: GitHub.