Skip to content

fix: bind symbols via @Native, restore Flutter compatibility (0.4.1) - #14

Merged
jwinarske merged 1 commit into
mainfrom
jw/native-assets
Aug 14, 2026
Merged

fix: bind symbols via @Native, restore Flutter compatibility (0.4.1)#14
jwinarske merged 1 commit into
mainfrom
jw/native-assets

Conversation

@jwinarske

Copy link
Copy Markdown
Contributor

Two fixes, both surfaced by actually smoke-testing the Flutter example. 0.4.1.

1. Published 0.4.0 is unusable from Flutter

hooks >=2.1.0 depends on meta ^1.19.0
flutter_test from sdk depends on meta 1.18.0
→ version solving failed

Every Flutter app depends on flutter_test, so no Flutter project on an SDK pinning meta 1.18.0 (3.44.x) can depend on appstream_dart 0.4.0 at all. The bundled example is one of them — that is how this was found. CI missed it because its Flutter job runs 3.47.0.

Widening to hooks: '>=1.0.2 <3.0.0' and code_assets: '>=1.0.0 <2.0.0' fixes it. Verified resolution in both directions:

Context hooks code_assets meta
standalone dart pub get 2.1.0 1.2.1 1.19.0
example under Flutter 3.44.2 1.0.2 1.0.0 1.18.0

The build hook is source-compatible with both majors, and pana still scores 160/160 — a standalone resolve still picks the latest, which is what that check measures.

2. The code asset was built and never used

hook/build.dart published libappstream.so as a code asset, but the VM consults its asset table only for @Native declarations — a plain DynamicLibrary.open(name) never sees it. So bindings.dart compensated with a seven-step runtime search. Comments in hook/build.dart and lib/src/appstream_native.dart claimed @Native was already in use; it was not.

Symbols are now @Native externals bound to @DefaultAsset, and the search chain is deleted (~180 lines): a /proc/self/maps scan, a glob through .dart_tool/hooks_runner/ internals, and candidates derived from Platform.script, the executable, and the CWD.

Security note: those CWD-relative candidates meant the process would load libappstream.so from ./lib, ./build, or ./src/build. Running an application from a directory an attacker can write to was enough to get a library of their choosing loaded. Removing them closes that.

The public API is unchanged — AppstreamBindings lives in lib/src/, is not exported, and appears only as a local inside a private function.

Verification

Exercised on three runtimes against the real 48 MB Flathub catalog, not just unit tests:

  • dart run / dart test — 45/45
  • Flutter example under GTK on wayland-0 — full import, 4677 components, 319 languages
  • Same example on ivi-homescreen on wayland-0 — same result

Resolution goes through NativeAssetsManifest.json, which maps the asset to the plain soname libappstream.so, so the final dlopen uses the system loader. GTK works via the runner's RPATH=$ORIGIN/lib; embedders whose binary sits outside the bundle need it on LD_LIBRARY_PATH. The README now documents this, including that flutter build bundle does not do Linux native-asset packaging — use flutter build linux.

Performance is unchanged, measured rather than assumed:

Bindings Run 1 Run 2
@Native 416 ms 411 ms
DynamicLibrary.lookupFunction 414 ms 409 ms

Expected: the FFI boundary is crossed twice per parse, and components stream back over the Dart port via Dart_PostCObject_DL, so the binding mechanism cannot move throughput. Argument passing, port protocol, and SQLITE_TRANSIENT handling are byte-identical.

clang-tidy clean, clang-format applied last, 149/149 C++ tests, dart analyze --fatal-infos clean, dart pub publish --dry-run 0 warnings.

Relationship to open PRs

Independent of #12 and #13 (disjoint files); merges in any order.

Two fixes, both surfaced by smoke-testing the Flutter example.

Flutter compatibility. hooks ^2.1.0 requires meta ^1.19.0, but flutter_test
from the Flutter SDK pins meta 1.18.0 on 3.44.x. Every Flutter app depends
on flutter_test, so 0.4.0 could not be resolved by any of them — the
bundled example included, which is how this was found. Widening to
hooks '>=1.0.2 <3.0.0' and code_assets '>=1.0.0 <2.0.0' resolves to
2.1.0/1.2.1 standalone and 1.0.2/1.0.0 under an older Flutter; the build
hook is source-compatible with both majors. pana still scores 160/160,
since a standalone resolve still picks the latest.

Native symbol resolution. hook/build.dart already published libappstream.so
as a code asset, but the VM consults its asset table only for @Native
declarations, so that asset was built and then never used. bindings.dart
instead searched for the library at runtime. The symbols are now @Native
externals bound to @DefaultAsset, and the search chain is gone.

That chain was ~180 lines: a /proc/self/maps scan, a glob through
.dart_tool/hooks_runner internals, and paths derived from Platform.script,
the executable, and the current directory. The CWD-relative entries meant
the process would load libappstream.so from ./lib, ./build, or
./src/build — so running an application from a directory an attacker could
write to was enough to get a library of their choosing loaded. Removing
them closes that.

Comments in hook/build.dart and lib/src/appstream_native.dart described the
@Native mechanism as though it already existed; they are now accurate.
README documents how the library is located and what embedders outside the
bundle, such as ivi-homescreen, need on LD_LIBRARY_PATH.

The public API is unchanged: AppstreamBindings lives in lib/src/, is not
exported, and appears only as a local in a private function.

Verified on three runtimes over the real 48 MB Flathub catalog: dart
run/dart test (45/45), the Flutter example under GTK on wayland-0, and the
same example on ivi-homescreen on wayland-0 — both importing all 4677
components. Parse throughput is unchanged (416/411 ms with @Native vs
414/409 ms before); the FFI boundary is crossed twice per parse and
components stream over the Dart port, so binding mechanism cannot move it.
clang-tidy clean, clang-format applied last, 149/149 C++ tests,
dart analyze --fatal-infos clean, publish dry-run 0 warnings.
@jwinarske
jwinarske merged commit 347abd9 into main Aug 14, 2026
12 checks passed
@jwinarske
jwinarske deleted the jw/native-assets branch August 14, 2026 17:28
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.

1 participant