Skip to content

lean_alloc_ctor crashes under mimalloc for constructors larger than MI_SMALL_SIZE_MAX #14148

Description

@cpehle

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:

7559185

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions