3.5.1+: mi_free_size uses mi_free_small for small sizes with MI_PAGE_META_IS_ALIGNED, corrupting the heap when a larger block is freed with a small size
Maintainer thường phản hồi trong vòng 5 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 65/100
Hướng nghiên cứu
Start in src/free.c at the mi_free_size branch changed between v3.5.0 and v3.5.1, then build and run the supplied C++ reproducer with the stated release configurations. Compare the page-alignment and page-map paths, and verify that the completed change prevents heap corruption while preserving appropriate handling of an undersized delete.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Summary
Since v3.5.1, mi_free_size (used by sized operator delete(void*, size_t)) takes the mi_free_small path for any size <= MI_SMALL_SIZE_MAX when MI_PAGE_META_IS_ALIGNED is set. mi_free_small finds the page by aligning the pointer down instead of using the page map, so if the block actually lives in a larger page, the "page" header is garbage and the heap is corrupted (SEGV in _mi_page_free_collect / mi_page_update_sample_countdown via mi_free_try_collect_mt, or corrupted thread-free list (possibly due to a cross-thread double free)).
Up to v3.5.0 the same code worked in practice (the #if was MI_PAGE_META_SMALL_IS_ALIGNED only, so the regular page-map lookup was used).
I realize the caller is technically at fault: the block was allocated with malloc(2000+) but released with a sized delete whose size is tiny (std::unique_ptr<T> default deleter on a variable-size record with a flexible array member, sizeof(T)==4). This is UB per the C++ standard, and we are fixing our code. I'm reporting because (a) this was a silent behavior change in a patch release, and a hard-to-diagnose crash in release builds, and (b) debug and secure builds of 3.5.3 do not report it (no mi_free_size ... less than the allocated block size message and no crash in our real application), so it is very hard to find. Perhaps release builds could keep the page-map lookup for mi_free_size, or the docs could mention the requirement.
Reproducer
#include <cstdlib>
#include <cstring>
#include <memory>
#include <thread>
#include <vector>
#include <cstdio>
struct Extra { unsigned a; unsigned char data[]; };
int main() {
for (int round = 0; round < 200; ++round) {
std::vector<std::unique_ptr<Extra>> v;
for (int i = 0; i < 2000; ++i) {
size_t sz = 2000 + (i % 7) * 3000; // > MI_SMALL_SIZE_MAX
auto* p = static_cast<Extra*>(malloc(sz));
memset(p, 0, sz);
v.emplace_back(p); // default_delete -> operator delete(p, sizeof(Extra)==4)
}
std::thread t([&] { v.clear(); }); // cross-thread free
t.join();
std::vector<void*> junk; // churn the same pages
for (int i = 0; i < 5000; ++i) junk.push_back(malloc(3000));
for (auto* j : junk) free(j);
}
puts("done");
}
Build (shared mimalloc, release, default CMake options, -DMI_USE_CXX=ON):
g++ -O2 -fsized-deallocation repro.cpp -o repro /path/to/libmimalloc.so.3 -pthread
Results (Rocky Linux 9.8, glibc 2.34, GCC 15.2, x86_64, Release builds from the GitHub tags)
| mimalloc | result |
|---|---|
| v3.4.5 | done |
| v3.5.0 | done |
| v3.5.1 | SIGSEGV |
| v3.5.3 | SIGSEGV |
| v3.5.4 | SIGSEGV |
In our real application (a gRPC server, free from a different thread) the same change shows: 3.4.5 and 3.5.0 pass, 3.5.1/3.5.2/3.5.3/3.5.4 crash (several full runs each), while v3.5.3 built with MI_DEBUG_FULL=ON or MI_SECURE=ON ran clean. MI_PROFILE=OFF does not help.
Diffing v3.5.0..v3.5.1, src/free.c changes mi_free_size from #if MI_PAGE_META_SMALL_IS_ALIGNED to #if MI_PAGE_META_SMALL_IS_ALIGNED || MI_PAGE_META_IS_ALIGNED for the size <= MI_SMALL_SIZE_MAX branch calling mi_free_small(p), which I believe is the trigger.
- Ngôn ngữ chính
- C
- Star
- 13.4k
- Fork
- 1.2k
- Merge trung bình
- 4 ngày 8 giờ
- Pull request đã merge (30 ngày)
- 8
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của microsoft/mimalloc
-
stale
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
microsoft/mimalloc#1113 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 5 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
microsoft/mimalloc#886 · 4 bình luận · 1 reaction ·
Maintainer thường phản hồi trong vòng 5 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 64/100
microsoft/mimalloc#368 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 5 ngày
-
Intermittent SIGSEGV / memory corruption under WSL2 with mimalloc 3.3.2 as Rust global allocatorĐang mở
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 8/100
Maintainer thường phản hồi trong vòng 5 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
microsoft/mimalloc#1405 · 2 bình luận · 1 reaction ·
Maintainer thường phản hồi trong vòng 5 ngày
Tất cả issue của microsoft/mimalloc
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
FujiNetWIFI/fujinet-lib#186 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
VIEW_TYPE set for computer playersCó thể đã có người làm @Loobinex đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
Maintainer thường phản hồi trong vòng 2 ngày
-
bug
Độ khó 2/5 Dưới một giờ Mức phù hợp với người mới 78/100
brunodev85/winlator-app#90 ·