microsoft / microsoft/mimalloc

SegFault: mi_free_size_aligned / mi_free_size on small over-aligned allocations v3

Open
#1,400 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C
Stars
13.4k
Forks
1.2k
Avg merge
4d 45m
Merged PRs (30d)
13

Description

In mimalloc v3 release builds, `mi_free_size(p, size)` routes every `size <= MI_SMALL_SIZE_MAX` through `mi_free_small`, which locates the page by aligning `p` down to `MI_SMALL_PAGE_SIZE` instead of doing a page-map lookup. That is only valid when the block lives in a small page. A small block with a large alignment is over-allocated (`size + alignment - 1`) and ends up in a medium page, so the computed page pointer is garbage and the process crashes.

`mi_free_size_aligned(p, size, alignment)` receives the alignment but discards it and defers to `mi_free_size`, so calling it with exactly the documented inputs ("the size of the object as allocated", "the requested alignment at the allocation") is enough to trigger the crash.

### Reproducer

```c
#include
#include

int main(void) {
const size_t size = 8;
const size_t alignment = 16 * 1024;
void* ptrs[200];
for (int i = 0; i < 200; i++) {
ptrs[i] = mi_malloc_aligned(size, alignment);
}
for (int i = 0; i < 200; i++) {
mi_free_size_aligned(ptrs[i], size, alignment); // SIGSEGV
}
printf("ok\n");
return 0;
}
```

```
$ cc -O2 -DNDEBUG -DMI_DEBUG=0 -Iinclude src/static.c repro.c -o repro && ./repro
Segmentation fault (exit 139)
```

A single allocation does not always crash: the first block happens to sit in the first 64 KiB chunk of the medium page, where the aligned-down address coincides with the page header. With a handful of blocks the crash is deterministic.

The debug build detects the exact condition and recovers:

```
$ cc -O0 -DMI_DEBUG=3 -DMI_SHOW_ERRORS=1 -Iinclude src/static.c repro.c -o repro_dbg && ./repro_dbg
mimalloc: error: thread 0x...: pointer 0x020000100000 is freed with mi_free_size but the given size 8 is less than the allocated block size 20480
(maybe a `new[]` was matched with `delete` instead of `delete[]`?)
...
ok
```

### Expected

`mi_free_size_aligned` (and `mi_free_size`) should be safe for any pointer returned by the matching allocation call with the same size and alignment.

### Environment

- mimalloc v3.5.2 (same code present on current `dev3`)
- macOS 26 (Darwin 25.6.0), arm64, Apple clang 21
- Default build options, `MI_SECURE` off, `MI_GUARDED` off (both of those disable `MI_PAGE_META_SMALL_IS_ALIGNED` and avoid the crash). v2 is unaffected because its `mi_free_size` is a plain `mi_free`.

Found while evaluating `mi_free_size_aligned` for `GlobalAlloc::dealloc` in a Rust wrapper, where the allocator must accept any power-of-two alignment.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/static.c at mi_free_size_aligned and mi_free_size, using the issue's C reproducer and release-build command to observe the failure. Trace how the supplied size and alignment reach the freeing path, then rerun the reproducer in the stated release configuration; done means matching aligned allocations can be freed without a crash.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
backend, performance
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.