Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

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

オープン
#1,423 コメント 6 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 3 日以内に返信

@Tyagiquamar がすでに取り組んでいます。

2026年10月8日 から。

  • #1427 @Tyagiquamar による — オープン

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
65/100
issue の種類
バグ
明瞭さ
おおむね明確
活発さ
活発
技術スタック
c
領域
backend

調査の方向性

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.

索引モデルが issue の本文から書いたものです。

説明

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.

主要言語
C
スター
13.4k
フォーク
1.2k
平均マージ
3日 20時間
マージ済み PR(30日)
8

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

microsoft/mimalloc のほかの issue

microsoft/mimalloc の issue をすべて見る

似ている issue

C の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。