[PR #1582] [MERGED] feat(flutter): identify Dart snapshot builds and inspect AOT payloads by pure APK parsing #2987

Closed
opened 2026-10-17 21:12:35 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1582
Author: @3QL
Created: 10/10/2026
Status: ✅ Merged
Merged: 10/12/2026
Merged by: @morluto

Base: main ← Head: feat/flutter-identification


📝 Commits (10+)

  • 03d2eaf feat(flutter): identify Dart snapshot builds by pure APK parsing
  • 2945f50 feat(flutter): inspect Dart AOT snapshots through the ELF symbol table
  • ddf1269 fix(flutter): own snapshots, bound image reads, and preserve cancellation
  • 349b212 docs(flutter): cover the Italian README with the Flutter row
  • b8cbea9 fix(flutter): make the ABI pattern portable for v-mode validators
  • e12bfbf refactor(flutter): reuse shared harnesses and deduplicate the vertical
  • 2207cc6 fix(flutter): address review findings from real-app verification
  • 93db928 fix(flutter): address the Codex review findings
  • eeb56f9 fix(flutter): address the second Codex review round
  • 87ac338 fix(flutter): address the third Codex review round

📊 Changes

57 files changed (+5627 additions, -4 deletions)

View changed files

📝 README.md (+1 -0)
📝 README_ar.md (+1 -0)
📝 README_de.md (+1 -0)
📝 README_es.md (+1 -0)
📝 README_fa.md (+1 -0)
📝 README_fr.md (+1 -0)
📝 README_id.md (+1 -0)
📝 README_it.md (+1 -0)
📝 README_ja.md (+1 -0)
📝 README_ko.md (+1 -0)
📝 README_pl.md (+1 -0)
📝 README_pt-BR.md (+1 -0)
📝 README_ru.md (+1 -0)
📝 README_th.md (+1 -0)
📝 README_tr.md (+1 -0)
📝 README_uk.md (+1 -0)
📝 README_vi.md (+1 -0)
📝 README_zh-TW.md (+1 -0)
📝 README_zh.md (+1 -0)
➕ docs/flutter-analysis.md (+156 -0)

...and 37 more files

📄 Description

feat(flutter): identify Dart snapshot builds and inspect AOT payloads by pure APK parsing

Opens the untouched Flutter lane — no PR, issue, or roadmap entry had covered Dart/Flutter analysis before. Four commits: two features, one ownership hardening matching the bar applied across the merged JEB/ADB/Apktool PRs, and one inherited-main fix.

identify_flutter_build — build identification

Reads an APK's lib/<abi>/libapp.so and libflutter.so through the hardened ZIP reader and reports:

  • The Dart VM snapshot hash — read 20 bytes after every Dart snapshot magic (f5 f5 dc dc). The vm and isolate sections are both found and cross-checked: agreeing sections collapse to one snapshot_hash; disagreeing sections keep every value in snapshot_hash_candidates with snapshot_hash: null and partial coverage rather than silently picking one.
  • Per-ABI library digests and sizes, the engine's GNU build-id, and any labeled strings (Dart VM version: …, Android clang/LLVM toolchain lines) — verbatim; release builds strip the Dart string and absence is reported as null, not guessed.
  • A target with no libapp.so reports flutter_detected: false with complete coverage — absence is an answer.

inspect_dart_aot — AOT payload inspection

For one ABI's libapp.so (caller-selected; default arm64-v8a → x86_64 → first available):

  • Snapshot sections — the four _kDart*Snapshot* symbols located through the ELF dynamic symbol table (ELF32 and ELF64 little-endian, bounds-checked; payloads without exported symbols are refused), each with file offset and size.
  • Data-section headers validated (magic, kind, length); instructions sections carry a different layout and report null for those fields — honestly.
  • A bounded string-pool projection from the isolate data section: distinct package: source URIs (the app's dependency inventory), dart: SDK URIs, and identifier-shaped tokens — labeled a byte-pattern projection, not deserialized cluster semantics; typed recovery needs an SDK-specific parser, which the snapshot hash unlocks.

Ownership hardening (mirrors the merged PRs' bar)

  • Owned read-only snapshots: the admitted APK is copied (EXCL, chmod 0400) before digesting and parsing; every read uses the snapshot, so concurrent modification cannot mix into one observation. Failed snapshot cleanup is retained and retried on close() (aggregate error when still owned); root removal is injectable for tests.
  • Bounded-before-allocation: the AOT image buffer is allocated at exactly the declared size before reading (256 MiB ceiling), and cancellation is checked between digest and stream chunks through a captured abort reason.
  • Tests cover paths containing spaces, cleanup retention with close retry, and cancellation before any root is created.

Inherited-main fix (first commit, independently cherry-pickable)

The merged ADB family advertises five patterns with unescaped dashes in character classes ([A-Za-z0-9._-]), which unicodeSets-mode clients reject — the advertised-patterns boundary test is red on current main for read_adb_setting and start_adb_activity. Escaped the same way the JEB fix did, and restored the Italian README's analysis-table rows (JEB/ADB/Apkktool/Flutter) that the recent merges added everywhere else, keeping its link set identical to README.md.

Verification

  • Real target: Flutter Gallery 2.9.2 (112 MB, three ABIs) via npm run verify:flutter — agreeing hash 65817c30a78bb44c3dc3771876b6010a on every ABI, per-ABI build-ids, all four snapshot symbols on both ELF64 and ELF32 images, header validation, hash agreement between both tools, and the 756/201 URI string pool; a plain aapt2-built APK reports not-detected with complete coverage.
  • 30 provider/scanner/ELF/service tests (goldens from the real libraries, real zips built in-test, chunk-boundary double-count coverage) plus CLI/MCP boundary schemas (Ajv-validated examples, unknown input rejected, v-mode portability).
  • check:fast, docs:generate, docs:check, 3,632 tests across domain/services/adapters/composition, and the product catalog all pass on this branch.

Towards the Flutter gap in the #717 Android roadmap discussion. No engine, SDK, or tool installation; no new runtime dependencies; pure parsing only.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/1582 **Author:** [@3QL](https://github.com/3QL) **Created:** 10/10/2026 **Status:** ✅ Merged **Merged:** 10/12/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `feat/flutter-identification` --- ### 📝 Commits (10+) - [`03d2eaf`](https://github.com/morluto/rea/commit/03d2eaff4484278fe94ecee983af39db7ac7cbe9) feat(flutter): identify Dart snapshot builds by pure APK parsing - [`2945f50`](https://github.com/morluto/rea/commit/2945f505459980b76ebf38c713fe1711283ae578) feat(flutter): inspect Dart AOT snapshots through the ELF symbol table - [`ddf1269`](https://github.com/morluto/rea/commit/ddf1269c8b23509a8db4f48b2f8da6602b15739b) fix(flutter): own snapshots, bound image reads, and preserve cancellation - [`349b212`](https://github.com/morluto/rea/commit/349b21292d4ca51489acbaab7ded7a8a60a0a7e7) docs(flutter): cover the Italian README with the Flutter row - [`b8cbea9`](https://github.com/morluto/rea/commit/b8cbea95edf5fd6b6c2e8c0ebadf46836c6626ee) fix(flutter): make the ABI pattern portable for v-mode validators - [`e12bfbf`](https://github.com/morluto/rea/commit/e12bfbf5d44f73d9113dc4f038552f1cd053fe90) refactor(flutter): reuse shared harnesses and deduplicate the vertical - [`2207cc6`](https://github.com/morluto/rea/commit/2207cc68c1a26119a3e1891ad0ed7f1f8f194ac0) fix(flutter): address review findings from real-app verification - [`93db928`](https://github.com/morluto/rea/commit/93db9281bcbe34f6e9d2e1785dc4091f136cab8b) fix(flutter): address the Codex review findings - [`eeb56f9`](https://github.com/morluto/rea/commit/eeb56f9d308e635a01b0529f3ceef1df20cc9415) fix(flutter): address the second Codex review round - [`87ac338`](https://github.com/morluto/rea/commit/87ac33816a01361fcf688c2332bb2a85c2ecf3aa) fix(flutter): address the third Codex review round ### 📊 Changes **57 files changed** (+5627 additions, -4 deletions) <details> <summary>View changed files</summary> 📝 `README.md` (+1 -0) 📝 `README_ar.md` (+1 -0) 📝 `README_de.md` (+1 -0) 📝 `README_es.md` (+1 -0) 📝 `README_fa.md` (+1 -0) 📝 `README_fr.md` (+1 -0) 📝 `README_id.md` (+1 -0) 📝 `README_it.md` (+1 -0) 📝 `README_ja.md` (+1 -0) 📝 `README_ko.md` (+1 -0) 📝 `README_pl.md` (+1 -0) 📝 `README_pt-BR.md` (+1 -0) 📝 `README_ru.md` (+1 -0) 📝 `README_th.md` (+1 -0) 📝 `README_tr.md` (+1 -0) 📝 `README_uk.md` (+1 -0) 📝 `README_vi.md` (+1 -0) 📝 `README_zh-TW.md` (+1 -0) 📝 `README_zh.md` (+1 -0) ➕ `docs/flutter-analysis.md` (+156 -0) _...and 37 more files_ </details> ### 📄 Description # feat(flutter): identify Dart snapshot builds and inspect AOT payloads by pure APK parsing Opens the untouched Flutter lane — no PR, issue, or roadmap entry had covered Dart/Flutter analysis before. Four commits: two features, one ownership hardening matching the bar applied across the merged JEB/ADB/Apktool PRs, and one inherited-main fix. ## `identify_flutter_build` — build identification Reads an APK's `lib/<abi>/libapp.so` and `libflutter.so` through the hardened ZIP reader and reports: - **The Dart VM snapshot hash** — read 20 bytes after every Dart snapshot magic (`f5 f5 dc dc`). The vm and isolate sections are both found and **cross-checked**: agreeing sections collapse to one `snapshot_hash`; disagreeing sections keep every value in `snapshot_hash_candidates` with `snapshot_hash: null` and partial coverage rather than silently picking one. - **Per-ABI library digests and sizes**, the engine's **GNU build-id**, and any *labeled* strings (`Dart VM version: …`, Android clang/LLVM toolchain lines) — verbatim; release builds strip the Dart string and absence is reported as `null`, not guessed. - A target with no `libapp.so` reports `flutter_detected: false` with **complete** coverage — absence is an answer. ## `inspect_dart_aot` — AOT payload inspection For one ABI's `libapp.so` (caller-selected; default arm64-v8a → x86_64 → first available): - **Snapshot sections** — the four `_kDart*Snapshot*` symbols located through the **ELF dynamic symbol table** (ELF32 *and* ELF64 little-endian, bounds-checked; payloads without exported symbols are refused), each with file offset and size. - **Data-section headers** validated (magic, kind, length); instructions sections carry a different layout and report `null` for those fields — honestly. - **A bounded string-pool projection from the isolate data section**: distinct `package:` source URIs (the app's dependency inventory), `dart:` SDK URIs, and identifier-shaped tokens — labeled a **byte-pattern projection, not deserialized cluster semantics**; typed recovery needs an SDK-specific parser, which the snapshot hash unlocks. ## Ownership hardening (mirrors the merged PRs' bar) - **Owned read-only snapshots**: the admitted APK is copied (EXCL, `chmod 0400`) before digesting and parsing; every read uses the snapshot, so concurrent modification cannot mix into one observation. Failed snapshot cleanup is **retained and retried on `close()`** (aggregate error when still owned); root removal is injectable for tests. - **Bounded-before-allocation**: the AOT image buffer is allocated at exactly the declared size before reading (256 MiB ceiling), and cancellation is checked between digest and stream chunks through a captured abort reason. - Tests cover paths containing spaces, cleanup retention with close retry, and cancellation before any root is created. ## Inherited-main fix (first commit, independently cherry-pickable) The merged ADB family advertises five patterns with unescaped dashes in character classes (`[A-Za-z0-9._-]`), which unicodeSets-mode clients reject — the advertised-patterns boundary test is red on current `main` for `read_adb_setting` and `start_adb_activity`. Escaped the same way the JEB fix did, and restored the Italian README's analysis-table rows (JEB/ADB/Apkktool/Flutter) that the recent merges added everywhere else, keeping its link set identical to `README.md`. ## Verification - **Real target**: Flutter Gallery 2.9.2 (112 MB, three ABIs) via `npm run verify:flutter` — agreeing hash `65817c30a78bb44c3dc3771876b6010a` on every ABI, per-ABI build-ids, all four snapshot symbols on **both ELF64 and ELF32** images, header validation, hash agreement between both tools, and the 756/201 URI string pool; a plain aapt2-built APK reports not-detected with complete coverage. - **30 provider/scanner/ELF/service tests** (goldens from the real libraries, real zips built in-test, chunk-boundary double-count coverage) plus CLI/MCP boundary schemas (Ajv-validated examples, unknown input rejected, v-mode portability). - `check:fast`, `docs:generate`, `docs:check`, 3,632 tests across domain/services/adapters/composition, and the product catalog all pass on this branch. Towards the Flutter gap in the #717 Android roadmap discussion. No engine, SDK, or tool installation; no new runtime dependencies; pure parsing only. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-17 21:12:35 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#2987
No description provided.