Skip to content

[Bug] Internal smali assembler silently emits invalid DEX when .catch regions are nested/overlapping #238

Description

@lindongbin

Describe the bug
When rebuilding with -dex-lib = internal (the default), the smali assembler accepts nested (overlapping) .catch try-regions and emits the overlapping ranges verbatim as try_items. The DEX spec requires try items within a code_item to be sorted, in-range and non-overlapping — so the produced dex is invalid.

Official smali (baksmali/smali, also used by apktool) supports nested .catch by flattening overlaps into disjoint try ranges with merged handler sets. APKEditor's internal assembler does neither: the build succeeds with zero warnings ("Saved to: …") and the invalid dex is shipped. On device, ART then refuses to open the whole dex file, so every class in that dex becomes unresolvable at runtime (NoClassDefFoundError on a perfectly valid, unrelated class). The d command happily disassembles such a file back — nothing in the toolchain ever complains.

To Reproduce
Steps to reproduce the behavior:

  1. Used version APKEditor 1.4.9, ARSCLib 1.3.9 and 1.4.0 (both affected), OpenJDK 26.0.1+8-34, device Android 13
  2. Operating system Windows 11 x64
  3. Command — see attached apkeditor-nestedcatch-repro.zip (includes prebuilt nested.apk / flat.apk / nested_jf.apk):
build_repro.bat <path-to-apkeditor.jar>

It builds two ready-made mini projects (identical semantics, different catch layout) and validates the produced dexes with dexcheck.py (standalone, no deps):

  • mini_nested/ — one class, one method, outer .catch region fully containing an inner one (legal smali):
    :try_outer
    invoke-static {p0}, Landroid/graphics/Color;->parseColor(Ljava/lang/String;)I
    ...
    :try_inner
    invoke-static {p1}, Ljava/lang/Float;->parseFloat(Ljava/lang/String;)F
    :try_end_inner
    .catch Ljava/lang/Throwable; {:try_inner .. :try_end_inner} :inner_label
    :inner_label
    ...
    :try_end_outer
    .catch Ljava/lang/Throwable; {:try_outer .. :try_end_outer} :outer_label
  • mini_flat/ — same code split into two disjoint regions (the workaround).

Observed output:

=== nested ===
00.465 I: [BUILD] Saved to: nested.apk          (success, no diagnostic)
PROBLEMS (1):
  - LRepro;#0: try overlap/unsorted start=0 prev_end=13

=== flat ===
00.466 I: [BUILD] Saved to: flat.apk
STRUCTURE OK

Log/Stacktrace
Installing the nested variant's real-world counterpart (same construct inside an app's own class) on an Android 13 device:

FATAL EXCEPTION: main
java.lang.NoClassDefFoundError: Failed resolution of: Lf/a/a/a/a;
    at androidx.preference.PreferenceManager.getDefaultSharedPreferencesName(SourceFile:1)
    at androidx.preference.PreferenceManager.getDefaultSharedPreferences(SourceFile:1)
    ...
Caused by: java.lang.ClassNotFoundException: f.a.a.a.a

The class is present in the rebuilt dex's class_defs table (verified by direct dex parsing); the whole dex is simply rejected by ART because of the invalid try-item table. Replacing the nested regions with disjoint ones makes the identical app work.

Used apk file
Not shareable (proprietary, 169 MB). However the issue is fully reproducible without it — see the attached minimal reproduction project apkeditor-nestedcatch-repro.zip (~16 KB: mini projects, prebuilt apks, validator, one-click script, README).

Additional context

  • Disjoint side-by-side .catch regions, and multiple handlers over one region, are handled fine — only genuinely nested/overlapping ranges produce the corrupt output.
  • The tool's own -dex-lib jf (JesusFreke/smali) backend assembles the identical mini_nested source into 3 correctly flattened, non-overlapping try_items (STRUCTURE OK). So the expected behavior is implemented inside the same codebase; only the default internal assembler emits the overlap silently.
  • Suggested fix: at minimum hard-error on overlapping catch regions (naming class/method/labels); ideally flatten them like official smali does.

apkeditor-nestedcatch-repro.zip

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggood first issueGood for newcomers

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions