Skip to content

fix: use-after-free in CommandQueue delete methods#419

Open
mfazekas wants to merge 1 commit intorive-app:mainfrom
mfazekas:fix/commandqueue-crash-on-teardown
Open

fix: use-after-free in CommandQueue delete methods#419
mfazekas wants to merge 1 commit intorive-app:mainfrom
mfazekas:fix/commandqueue-crash-on-teardown

Conversation

@mfazekas
Copy link
Contributor

@mfazekas mfazekas commented Feb 9, 2026

Fixes #418

Summary

Fixes EXC_BAD_ACCESS crash in processMessages() caused by premature listener deletion in CommandQueue delete methods. When a resource (e.g. ViewModelInstance) was deleted, its C++ listener was immediately deleted, but the C++ CommandQueue still held a raw pointer to it — if processMessages() ran before the queue processed the delete command, it would invoke callbacks through a dangling pointer.

The fix removes the immediate delete listener from all 6 affected methods (deleteFile, deleteArtboard, deleteViewModelInstance, deleteImage, deleteFont, deleteAudio). Listeners are already cleaned up in dealloc, and the __weak observer pattern ensures callbacks on orphaned listeners are safe no-ops.

Test plan

  • Reproduced crash with lists_demo.riv teardown test — crashed on iteration 1 without fix
  • Verified 198+ iterations with zero crashes after fix

Remove premature listener deletion from all 6 delete methods (deleteFile, deleteArtboard, deleteViewModelInstance, deleteImage, deleteFont, deleteAudio). The C++ CommandQueue still holds raw pointers to listeners after deletion, causing EXC_BAD_ACCESS when processMessages() invokes callbacks through dangling pointers. Listeners are already cleaned up in dealloc.
@dskuza
Copy link
Collaborator

dskuza commented Feb 9, 2026

Good catch! They are cleaned in dealloc. The initial implementation is in an attempt to immediately free up memory once a Rive type has been "deleted" via command queue. I wonder if there's a way to have that without deferring until a command queue is dealloc'd. In our case, a command queue won't get deallocated until a Worker is deinit'd, and it's theoretically possible for someone to allocated 100s of files, which would allocate 100s of (C++) listeners, even if those files are deleted incrementally until the Worker is finally deinit'd. The listeners themselves are quite small, so it's in the interest of good faith cleanup.

@dskuza
Copy link
Collaborator

dskuza commented Feb 9, 2026

We might be able to leverage the callbacks the command queue gives us for when a file is deleted, rather than doing it early as we currently do, if we want to clean up as types are deleted.

@mfazekas
Copy link
Contributor Author

We might be able to leverage the callbacks the command queue gives us for when a file is deleted, rather than doing it early as we currently do, if we want to clean up as types are deleted.

Added another PR, that still frees the listeners, from command queue callbacks: #420

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

EXC_BAD_ACCESS crash in CommandQueue::processMessages() on teardown after setValue

2 participants