Skip to content

Fix Dart 3.13 ObjectPool decode: parse early base-object clusters - #25

Merged
trufae merged 1 commit into
radareorg:mainfrom
fatalSec:fix/dart-313-early-clusters
Sep 15, 2026
Merged

trufae merged 1 commit into
radareorg:mainfrom
fatalSec:fix/dart-313-early-clusters

Conversation

@fatalSec

Copy link
Copy Markdown
Contributor

Dart 3.13 dropped the separate VM snapshot. The VM singletons that used to be base objects (empty_object_pool, empty_var_descriptors, the preallocated errors, ...) are now written by CreateEarlyClusters as ordinary clusters at the start of the combined snapshot. On a real 3.13 app this broke the fill walk in two ways.

Three cluster types gained serializers in 3.13 and had no CID kind:

  • LocalVarDescriptors: alloc is a count plus one entry count per object; fill is the entry count, one name ref per entry, then per entry four Read<int32_t> (index_kind and three token positions) and one Read<int64_t> scope_id. It is not an inline-bytes class.
  • ApiError: fixed-size alloc, one ref.
  • UnwindError: fixed-size alloc, one ref plus a bool.

LocalVarDescriptors was read as a simple count, desyncing the stream at cluster 5, so every later cluster, including the global ObjectPool, was left not_parsed and r2flutter -p reported "PP not resolved".

Add the three kinds to the CID model, all version tables and the table generator. Their positions (CompressedStackMaps + 1, LanguageError - 1, UnhandledException + 1) hold in class_id.h from 2.10 through 3.13, and older serializers never emit these clusters, so earlier snapshots are unaffected. Layouts verified against dart-lang/sdk 3.13.0 app_snapshot.cc.

The early empty ObjectPool also meant modern_resolve_pp_slot stopped at the first pool cluster, which is no longer the global pool; skip zero-length pools there.

On the iOS AuthPass 1.9.12 build (Dart 3.13.0, cws=8) all 1140 clusters now fill, -p resolves 44666 entries, PP slots resolve to their targets and -AAA emits valued PP comments. Adds a synthetic r2r test (early-clusters-313) that fails with "PP not resolved" before this change.

Dart 3.13 dropped the separate VM snapshot. The VM singletons that used
to be base objects (empty_object_pool, empty_var_descriptors, the
preallocated errors, ...) are now written by CreateEarlyClusters as
ordinary clusters at the start of the combined snapshot. On a real 3.13
app this broke the fill walk in two ways.

Three cluster types gained serializers in 3.13 and had no CID kind:

- LocalVarDescriptors: alloc is a count plus one entry count per object;
  fill is the entry count, one name ref per entry, then per entry four
  Read<int32_t> (index_kind and three token positions) and one
  Read<int64_t> scope_id. It is not an inline-bytes class.
- ApiError: fixed-size alloc, one ref.
- UnwindError: fixed-size alloc, one ref plus a bool.

LocalVarDescriptors was read as a simple count, desyncing the stream at
cluster 5, so every later cluster, including the global ObjectPool, was
left not_parsed and `r2flutter -p` reported "PP not resolved".

Add the three kinds to the CID model, all version tables and the table
generator. Their positions (CompressedStackMaps + 1, LanguageError - 1,
UnhandledException + 1) hold in class_id.h from 2.10 through 3.13, and
older serializers never emit these clusters, so earlier snapshots are
unaffected. Layouts verified against dart-lang/sdk 3.13.0
app_snapshot.cc.

The early empty ObjectPool also meant modern_resolve_pp_slot stopped at
the first pool cluster, which is no longer the global pool; skip
zero-length pools there.

On the iOS AuthPass 1.9.12 build (Dart 3.13.0, cws=8) all 1140 clusters
now fill, -p resolves 44666 entries, PP slots resolve to their targets
and -AAA emits valued PP comments. Adds a synthetic r2r test
(early-clusters-313) that fails with "PP not resolved" before this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KmxPHTVwSkr2HTHJHQsord
@trufae
trufae merged commit 5c4a51d into radareorg:main Sep 15, 2026
8 checks passed
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.

2 participants