rive-app / rive-app/rive-flutter

rive_native: support environments that prohibit runtime executable memory for FFI callbacks

Open
#656 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Dart
Stars
1.5k
Forks
240
PR merge metrics
No merged PRs in 30d

Description

Submission checklist
  • This is present in the latest rive (0.14.11) and rive_native (0.1.11) packages.
  • I searched the documentation, forums, and existing issues.
  • I found no existing issue covering this.
Description

Rive Flutter requires the Dart VM to allocate executable memory for FFI callback trampolines. On GrapheneOS, an app using Rive aborts when the per-app Dynamic code loading from memory restriction is enabled:

virtual_memory_posix.cc: error: mprotect failed: 13 (Permission denied)

This came up while investigating ente/ente#3436. The equivalent blocker in sqlite3.dart was fixed in simolus3/sqlite3.dart#396.

Rive-specific cause

Normal Rive usage reaches Dart FFI callbacks, not only optional scripting APIs:

  • FFIRiveFile in rive_native/lib/src/ffi/rive_ffi.dart registers the view-model callbacks with Pointer.fromFunction when a .riv file is decoded.
  • FFIFlutterFactory in rive_native/lib/src/ffi/flutter_renderer_ffi.dart registers its renderer callbacks with Pointer.fromFunction.
  • Other APIs use NativeCallable, including state-machine, asset-loader, scripting, and Luau callbacks.

Despite accepting only static functions, Pointer.fromFunction uses the same Dart VM FFI callback trampoline mechanism as NativeCallable, as confirmed in sqlite3.dart#396. The VM allocates a trampoline page and changes it to executable (Dart VM source). When that operation is prohibited, the failure is fatal and cannot be caught by the Flutter application.

Reproduction

A Linux machine with systemd can reproduce the restriction without GrapheneOS. In a minimal Flutter app, initialize Rive and decode a bundled .riv file:

await rive.RiveNative.init();
final data = await rootBundle.load('assets/example.riv');
await rive.File.decode(
  data.buffer.asUint8List(),
  riveFactory: rive.Factory.flutter,
);

Build the Linux application, then run it with executable-memory creation disabled:

flutter build linux --release
systemd-run --user --scope --property=MemoryDenyWriteExecute=yes \
  ./build/linux/x64/release/bundle/<app-name>

Loading the Rive file triggers the mprotect failure. The same restriction is exposed on GrapheneOS as Dynamic code loading from memory.

Expected behavior

Rive Flutter should provide a path that does not require Dart FFI callback trampolines or newly executable anonymous mappings, so ordinary .riv loading and rendering work under MemoryDenyWriteExecute=yes and the equivalent GrapheneOS restriction.

Replacing NativeCallable with Pointer.fromFunction is not sufficient; the callback boundary needs a statically compiled native bridge or another design that avoids Dart FFI callbacks requiring runtime executable memory.

Environment
  • rive: 0.14.11
  • rive_native: 0.1.11
  • Flutter: 3.38.10
  • Dart: 3.10.9
  • Affected: Android with GrapheneOS memory DCL disabled; reproducible on Linux with MemoryDenyWriteExecute=yes

Contributor guide

No contributing guide indexed for this repository

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 FFIRiveFile in rive_native/lib/src/ffi/rive_ffi.dart and FFIFlutterFactory in rive_native/lib/src/ffi/flutter_renderer_ffi.dart, then reproduce with the minimal Flutter app and systemd-run command shown. Trace the Pointer.fromFunction and NativeCallable callback paths. Done means ordinary .riv loading and rendering work under MemoryDenyWriteExecute=yes without runtime executable-memory allocation.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, dart, flutter, linux
Domain
desktop-dev, mobile-dev, operating-systems, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.