rive-app / rive-app/rive-flutter
nativeTexture() crashes with SIGSEGV on freed MetalTextureRenderer* — missing nullptr guard
Nobody has claimed this yet.
- Dominant language
- Dart
- Stars
- 1.5k
- Forks
- 240
- PR merge metrics
- No merged PRs in 30d
Description
Description
The exported nativeTexture() function in pls_binding.mm is the only renderer API that does not guard against a nullptr renderer argument. When called with a freed or null pointer, it crashes with EXC_BAD_ACCESS (SIGSEGV) / KERN_INVALID_ADDRESS.
Every other exported function in the same file includes a null check:
// ✅ Has null check
EXPORT bool clear(MetalTextureRenderer* renderer, bool clear, uint32_t color) {
if (renderer == nullptr) { return false; }
renderer->begin(clear, color);
return true;
}
// ✅ Has null check
EXPORT rive::Renderer* makeRenderer(MetalTextureRenderer* renderer) {
if (renderer == nullptr) { return nullptr; }
return renderer->renderer();
}
// ❌ Missing null check — crashes
EXPORT void* nativeTexture(MetalTextureRenderer* renderer) {
return (__bridge void*)renderer->currentTargetTexture();
}
Crash Report
Process: XXXX - [71482]
Code Type: ARM-64 (Native)
OS Version: macOS 15.7.3 (24G419)
Crashed Thread: 65 Dispatch queue: com.airplaystudio.mixer.render
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x00006001e1d78e90
Thread 65 Crashed:
0 rive_native nativeTexture + 8
1 our_plugin [RiveTextureBridge registerRendererPointer:asSlot:]_block_invoke + 172
Reproduction Context
This crash occurs when nativeTexture() is called from a render loop on a background dispatch queue while destroyRiveRenderer() runs on the platform thread. The 1-second batched _disposeTexture() Timer in _NativeRenderTexture creates a window where the old MetalTextureRenderer* is freed but external callers may still hold the pointer.
Race window:
- performLayout() triggers makeRenderTexture() with new dimensions
- Old renderer is queued for destruction via 1-second Timer
- External render loop calls nativeTexture(oldRenderer) before onTextureChanged callback fires
- destroyRiveRenderer() frees the old renderer on the platform thread
- nativeTexture() dereferences the freed pointer → SIGSEGV
Suggested Fix
EXPORT void* nativeTexture(MetalTextureRenderer* renderer) {
+ if (renderer == nullptr) { return nullptr; }
return (__bridge void*)renderer->currentTargetTexture();
}
Environment
Package: rive_native 0.1.2
Platform: macOS 15.7.3 (ARM64)
Flutter: 3.x
File:
native/src/pls_binding.mm , line 246–249
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in native/src/pls_binding.mm around lines 246–249 and compare nativeTexture() with the other exported functions’ null checks. Trace the destroyRiveRenderer() and _disposeTexture() lifecycle described in the issue, then reproduce the render-loop and disposal timing if possible. Done means the reported nativeTexture() crash is prevented for the described invalid-renderer calls.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- objective-c
- Domain
- computer-graphics, desktop-dev
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100