Double-free in getUniformBlockIndex causes heap corruption / random crashes
Description
In flutter_angle/lib/src/desktop/wrapper.dart, RenderingContext.getUniformBlockIndex() allocates native memory via toNativeUtf8(), then creates a second Pointer that reinterprets the same memory address, and subsequently frees both pointers — resulting in a double-free.
int getUniformBlockIndex(Program program, String uniformBlockName){
var sourceString = uniformBlockName.toNativeUtf8();
var arrayPointer = Pointer<Char>.fromAddress(sourceString.address);
int i = gl.glGetUniformBlockIndex(program.id, arrayPointer);
calloc.free(arrayPointer); // <-- frees memory at `sourceString.address`
calloc.free(sourceString); // <-- frees the SAME memory again
return i;
}
Pointer<Char>.fromAddress(sourceString.address) does not allocate new memory — it only creates a differently-typed view over the address that sourceString already points to. Calling calloc.free() on both pointers frees the same block of memory twice.
Impact
Double-frees corrupt the heap's internal allocator metadata but don't necessarily crash immediately. In our case, the corruption went undetected until an unrelated free() call elsewhere in the app (triggered by an Impeller/Skia image-encode callback, e.g. toImage()) hit macOS's malloc guard, producing:
Application Specific Information:
abort() called
malloc_vreport
malloc_report
___BUG_IN_CLIENT_OF_LIBMALLOC_POINTER_BEING_FREED_WAS_NOT_ALLOCATED
This makes the bug very difficult to trace back to its source, since the crash surfaces far away (in unrelated code) from the actual double-free call site, and it may appear intermittently depending on heap layout and allocation timing.
Steps to Reproduce
- Call
RenderingContext.getUniformBlockIndex(program, blockName) for any UBO-based shader setup (e.g. lighting data via layout(std140) uniform ...).
- Continue running the app; unrelated memory operations elsewhere (e.g. taking a screenshot, calling
toImage(), or simply enough allocator churn over time) may eventually trigger abort() via the OS malloc guard.
- Crash is intermittent and not tied to any obvious action, since it depends on heap allocator state at the time of the next unrelated free.
Expected Behavior
Only the originally-allocated pointer (sourceString) should be freed once, since arrayPointer is not a separate allocation.
Suggested Fix
int getUniformBlockIndex(Program program, String uniformBlockName){
var sourceString = uniformBlockName.toNativeUtf8();
var arrayPointer = Pointer<Char>.fromAddress(sourceString.address);
int i = gl.glGetUniformBlockIndex(program.id, arrayPointer);
calloc.free(sourceString); // free only once — arrayPointer is a view, not a separate allocation
return i;
}
Environment
- flutter_angle version: 0.4.1
- Platform: macOS (Apple Silicon, arm64)
- OS: macOS 26.5.2 (25F84)
- Hardware: Mac mini (M1)
Crash Log Excerpt
Exception Type: EXC_CRASH (SIGABRT)
Termination Reason: Namespace SIGNAL, Code 6, Abort trap: 6
Thread 0 Crashed:
0 libsystem_kernel.dylib __pthread_kill
1 libsystem_pthread.dylib pthread_kill
2 libsystem_c.dylib abort
3 libsystem_malloc.dylib malloc_vreport
4 libsystem_malloc.dylib malloc_report
5 libsystem_malloc.dylib ___BUG_IN_CLIENT_OF_LIBMALLOC_POINTER_BEING_FREED_WAS_NOT_ALLOCATED
...
62 FlutterMacOS dart::DartEntry::InvokeFunction(...)
...
66 FlutterMacOS flutter::(anonymous namespace)::EncodeImageAndInvokeDataCallback(...)
(Note: the crash surfaces during an unrelated image-encode callback, not at the getUniformBlockIndex call site itself — consistent with delayed heap-corruption detection.)
Double-free in getUniformBlockIndex causes heap corruption / random crashes
Description
In
flutter_angle/lib/src/desktop/wrapper.dart,RenderingContext.getUniformBlockIndex()allocates native memory viatoNativeUtf8(), then creates a secondPointerthat reinterprets the same memory address, and subsequently frees both pointers — resulting in a double-free.Pointer<Char>.fromAddress(sourceString.address)does not allocate new memory — it only creates a differently-typed view over the address thatsourceStringalready points to. Callingcalloc.free()on both pointers frees the same block of memory twice.Impact
Double-frees corrupt the heap's internal allocator metadata but don't necessarily crash immediately. In our case, the corruption went undetected until an unrelated
free()call elsewhere in the app (triggered by an Impeller/Skia image-encode callback, e.g.toImage()) hit macOS's malloc guard, producing:This makes the bug very difficult to trace back to its source, since the crash surfaces far away (in unrelated code) from the actual double-free call site, and it may appear intermittently depending on heap layout and allocation timing.
Steps to Reproduce
RenderingContext.getUniformBlockIndex(program, blockName)for any UBO-based shader setup (e.g. lighting data vialayout(std140) uniform ...).toImage(), or simply enough allocator churn over time) may eventually triggerabort()via the OS malloc guard.Expected Behavior
Only the originally-allocated pointer (
sourceString) should be freed once, sincearrayPointeris not a separate allocation.Suggested Fix
Environment
Crash Log Excerpt
(Note: the crash surfaces during an unrelated image-encode callback, not at the getUniformBlockIndex call site itself — consistent with delayed heap-corruption detection.)