Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
"crates/flutterdec-disasm-arm64",
"crates/flutterdec-ir",
"crates/flutterdec-decompiler",
"crates/flutterdec-serwalker",
]
resolver = "2"

Expand Down
9 changes: 9 additions & 0 deletions crates/flutterdec-serwalker/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "flutterdec-serwalker"
version.workspace = true
edition.workspace = true
license.workspace = true

[dependencies]
goblin.workspace = true
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether `goblin` is referenced anywhere inside the new crate.
fd -t f -e rs . crates/flutterdec-serwalker | xargs rg -nP '\bgoblin\b'

Repository: caverav/flutterdec

Length of output: 46


Remove the unused goblin dependency from this crate.

The goblin crate is declared in Cargo.toml but is not referenced anywhere in the source files. Unused dependencies increase build time and dependency surface; drop it until actually needed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/flutterdec-serwalker/Cargo.toml` at line 8, Remove the unused goblin
dependency entry from this crate's Cargo.toml: locate the 'goblin'
workspace/dependency declaration (the line containing "goblin.workspace = true"
/ the 'goblin' dependency) and delete that entry so the crate no longer declares
goblin until it's actually used; then run cargo check to verify there are no
missing references.

paste = "1.0.15"
98 changes: 98 additions & 0 deletions crates/flutterdec-serwalker/src/cluster/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use crate::constants::{ClassId, ClassId::*, SIGNED_M, UNSIGNED_M};
use crate::raw_object::*;
use crate::stream::Stream;
use crate::DECLARE_FIXED_LENGTH_CLUSTER;
use crate::DECLARE_VARIABLE_LENGTH_CLUSTER;
use crate::FFI_TYPES_LIST;

type Smi = i32;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Smi is i32 here but i64 in raw_object/mod.rs — pick one.

crates/flutterdec-serwalker/src/raw_object/mod.rs line 2 declares type Smi = i64; (with comment "Using i64 for Smi fields to be decompressed") and is used by every struct field marked // Smi. This file declares its own type Smi = i32; and uses it as the return type of the public read_and_decompress_smi. The decompressed value is therefore returned at i32 width while every consumer in raw_object stores it at i64 width — an obvious foot-gun that will silently truncate large Dart Smis on 64-bit targets.

Centralize this in constants.rs (or raw_object) as a single pub type Smi = i64; and import it in both modules.

♻️ Suggested fix
-type Smi = i32;
+use crate::raw_object::Smi;

…and make Smi pub in raw_object/mod.rs:

-type Smi = i64; // Using i64 for Smi fields to be decompressed
+pub type Smi = i64; // Using i64 for Smi fields to be decompressed
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/flutterdec-serwalker/src/cluster/mod.rs` at line 6, The Smi alias is
inconsistent: cluster/mod.rs defines type Smi = i32 while raw_object/mod.rs uses
type Smi = i64, causing silent truncation; fix by centralizing a single pub type
Smi = i64 (either in a new constants.rs or make it pub in raw_object/mod.rs),
remove the local type alias in cluster/mod.rs, import that pub Smi where needed,
and change the public function signature of read_and_decompress_smi (and any
other uses) to return the centralized Smi type so all modules use i64
consistently.


pub trait Cluster {
fn is_fixed_len(&self) -> bool;
fn read_alloc(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize;
fn read_fill(&mut self, stream: &mut Stream) -> usize;
}

pub fn read_smi(stream: &mut Stream) -> Smi {
let raw_smi = stream.read_modified_leb128(SIGNED_M); // smis are always written as signed numbers

raw_smi as Smi
}

macro_rules! FFI_CASE_PATTERN {
( $( $ffi_type:ident ),* ) => {
$( $ffi_type )|*
};
}

pub fn decide_cluster(class_id: ClassId) -> Result<Box<dyn Cluster>, &'static str> {
match class_id {
// we assume compressed pointers, it supports only Android for now...
IllegalCid => Err("Not a supported class (illegal class)..."),
FFI_TYPES_LIST!(FFI_CASE_PATTERN) => Err("To do..."),
_ => Err("Not a supported class..."),
}
}

// These are the objects that call ReadAllocFixedSize during deserialization,
// whose fill cluster size is uniquely determined by sizeof(Object) * num_of_objects
// and alloc cluster size is tags (MULEB128) + num_of_objects (MULEB128)

DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameters<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(PatchClass<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(Function<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(ClosureData<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(FfiTrampolineData<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(Field<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(Script<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(Library<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(Namespace<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(KernelProgramInfo<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(UnlinkedCall, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(ICData, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(MegamorphicCache, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(SubtypeTestCache, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(LoadingUnit<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(LanguageError, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(UnhandledException, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(LibraryPrefix, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(Type<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(FunctionType<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(RecordType, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameter<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(Closure<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(Double, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(Int32x4, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(GrowableObjectArray<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(TypedDataView<'a>, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(ExternalTypedData, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(StackTrace, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(RegExp, |_self, last_ref_id, stream| { 1 });
DECLARE_FIXED_LENGTH_CLUSTER!(WeakProperty, |_self, last_ref_id, stream| { 1 });

DECLARE_VARIABLE_LENGTH_CLUSTER!(Map);
DECLARE_VARIABLE_LENGTH_CLUSTER!(Set);
DECLARE_VARIABLE_LENGTH_CLUSTER!(Instance<'a>);
DECLARE_VARIABLE_LENGTH_CLUSTER!(TypedData<'a>);
DECLARE_VARIABLE_LENGTH_CLUSTER!(Class<'a>);
DECLARE_VARIABLE_LENGTH_CLUSTER!(TypeArguments<'a>);
DECLARE_VARIABLE_LENGTH_CLUSTER!(Code<'a>);
DECLARE_VARIABLE_LENGTH_CLUSTER!(ObjectPool);
DECLARE_VARIABLE_LENGTH_CLUSTER!(ExceptionHandlers<'a>);
DECLARE_VARIABLE_LENGTH_CLUSTER!(Context<'a>);
DECLARE_VARIABLE_LENGTH_CLUSTER!(ContextScope);
DECLARE_VARIABLE_LENGTH_CLUSTER!(Mint);
DECLARE_VARIABLE_LENGTH_CLUSTER!(Float32x4);
DECLARE_VARIABLE_LENGTH_CLUSTER!(Float64x2);
DECLARE_VARIABLE_LENGTH_CLUSTER!(Record);
DECLARE_VARIABLE_LENGTH_CLUSTER!(Array<'a>);
DECLARE_VARIABLE_LENGTH_CLUSTER!(WeakArray<'a>);
DECLARE_VARIABLE_LENGTH_CLUSTER!(ImmutableArray);
DECLARE_VARIABLE_LENGTH_CLUSTER!(ConstMap);
DECLARE_VARIABLE_LENGTH_CLUSTER!(ConstSet);
DECLARE_VARIABLE_LENGTH_CLUSTER!(CodeSourceMap);
DECLARE_VARIABLE_LENGTH_CLUSTER!(CompressedStackMaps);
DECLARE_VARIABLE_LENGTH_CLUSTER!(PcDescriptors);
DECLARE_VARIABLE_LENGTH_CLUSTER!(OneByteString);
DECLARE_VARIABLE_LENGTH_CLUSTER!(TwoByteString);
DECLARE_VARIABLE_LENGTH_CLUSTER!(_String);
Loading
Loading