leanprover / leanprover/lean4

`lean_alloc_ctor` crashes under mimalloc for constructors larger than `MI_SMALL_SIZE_MAX`

Open
#14,148 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Lean
Stars
9.2k
Forks
990
Avg merge
1d 17h
Merged PRs (30d)
175

Description

Prerequisites
Description

lean_alloc_ctor(0, 121, 80) can crash under the mimalloc-backed runtime allocation path.

The allocation is a valid Lean-small constructor, but its size exceeds mimalloc’s mi_malloc_small limit. Lean currently calls mi_malloc_small(sz) unconditionally from
lean_alloc_small_object under LEAN_MIMALLOC.

On 64-bit platforms, the constructor size is:

sizeof(lean_ctor_object) + 121 * sizeof(void*) + 80
= 8 + 968 + 80
= 1056 bytes

Lean’s small-object limit is 4096 bytes, but mimalloc’s MI_SMALL_SIZE_MAX is 128 * sizeof(void*), i.e. 1024 bytes on 64-bit. So Lean can pass a Lean-small object that is too large for mimalloc’s direct-small allocation API.

Context

This was reproduced with a direct C runtime allocation against upstream origin/master at:

75591856794182559e5a3713bdf8aa00c52fc7ca

The relevant code path is in src/include/lean/lean.h:

#ifdef LEAN_MIMALLOC
      sz = lean_align(sz, LEAN_OBJECT_SIZE_DELTA);
      void * mem = mi_malloc_small(sz);

Possible fixes include either using mi_malloc(sz) for this path, or configuring Lean’s vendored mimalloc build so that MI_SMALL_SIZE_MAX >= LEAN_MAX_SMALL_OBJECT_SIZE.

Steps to Reproduce
  1. Save this as repro.c:
     #include <lean/lean.h>
     #include <stdio.h>

     static void fill_ctor(lean_object * o) {
         for (unsigned i = 0; i < lean_ctor_num_objs(o); i++) {
             lean_ctor_set(o, i, lean_box(i));
         }

         uint8_t * scalars = lean_ctor_scalar_cptr(o);
         for (unsigned i = 0; i < 80; i++) {
             scalars[i] = (uint8_t)i;
         }
     }

     int main(void) {
         for (unsigned iter = 0; iter < 100000; iter++) {
             lean_object * o = lean_alloc_ctor(0, 121, 80);
             fill_ctor(o);
             lean_dec(o);
         }

         puts("ok");
         return 0;
     }
  1. Build it against an affected Lean toolchain/runtime:
     cc repro.c \
       -I/path/to/lean/include \
       -L/path/to/lean/lib/lean \
       -Wl,-rpath,/path/to/lean/lib/lean \
       -lleanshared \
       -o repro
  1. Run it:
     ./repro

Actual result:

Segmentation fault / exit 139

Expected result:

ok

Contributor guide

Open the contributing guide

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 with the lean_alloc_small_object path in src/include/lean/lean.h and compile the provided repro.c against the affected runtime. Investigate the mismatch between Lean's small-object limit and mimalloc's MI_SMALL_SIZE_MAX, then verify the chosen fix by rerunning ./repro and confirming it prints "ok" without crashing.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
compilers
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.