From 49d0e76272cd1814cfcef7768c4d7987d6ac771f Mon Sep 17 00:00:00 2001 From: Skylot Date: Sat, 4 Jun 2022 23:25:52 +0100 Subject: [PATCH] fix: support all-catch in multi-catch (#1510) --- .../core/dex/trycatch/ExceptionHandler.java | 21 ++++++---- .../dex/visitors/AttachTryCatchVisitor.java | 4 +- .../blocks/BlockExceptionHandler.java | 2 +- .../trycatch/TestTryCatchMultiException.java | 2 - .../trycatch/TestTryCatchMultiException2.java | 33 ++++++++++++++++ .../TestTryCatchMultiException2.smali | 38 +++++++++++++++++++ 6 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchMultiException2.java create mode 100644 jadx-core/src/test/smali/trycatch/TestTryCatchMultiException2.smali diff --git a/jadx-core/src/main/java/jadx/core/dex/trycatch/ExceptionHandler.java b/jadx-core/src/main/java/jadx/core/dex/trycatch/ExceptionHandler.java index 619bceb19..ff8fbe3bc 100644 --- a/jadx-core/src/main/java/jadx/core/dex/trycatch/ExceptionHandler.java +++ b/jadx-core/src/main/java/jadx/core/dex/trycatch/ExceptionHandler.java @@ -14,9 +14,9 @@ import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.IContainer; +import jadx.core.dex.nodes.MethodNode; import jadx.core.utils.InsnUtils; import jadx.core.utils.Utils; -import jadx.core.utils.exceptions.JadxRuntimeException; public class ExceptionHandler { @@ -33,9 +33,14 @@ public class ExceptionHandler { private boolean removed = false; - public ExceptionHandler(int addr, @Nullable ClassInfo type) { + public static ExceptionHandler build(MethodNode mth, int addr, @Nullable ClassInfo type) { + ExceptionHandler eh = new ExceptionHandler(addr); + eh.addCatchType(mth, type); + return eh; + } + + private ExceptionHandler(int addr) { this.handlerOffset = addr; - addCatchType(type); } /** @@ -43,7 +48,7 @@ public class ExceptionHandler { * * @param type - null for 'all' or 'Throwable' handler */ - public boolean addCatchType(@Nullable ClassInfo type) { + public boolean addCatchType(MethodNode mth, @Nullable ClassInfo type) { if (type != null) { if (catchTypes.contains(type)) { return false; @@ -51,14 +56,16 @@ public class ExceptionHandler { return catchTypes.add(type); } if (!this.catchTypes.isEmpty()) { - throw new JadxRuntimeException("Null type added to not empty exception handler: " + this); + mth.addDebugComment("Throwable added to exception handler: '" + catchTypeStr() + "', keep only Throwable"); + catchTypes.clear(); + return true; } return false; } - public void addCatchTypes(Collection types) { + public void addCatchTypes(MethodNode mth, Collection types) { for (ClassInfo type : types) { - addCatchType(type); + addCatchType(mth, type); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/AttachTryCatchVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/AttachTryCatchVisitor.java index ff1723f41..159b4f166 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/AttachTryCatchVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/AttachTryCatchVisitor.java @@ -133,7 +133,7 @@ public class AttachTryCatchVisitor extends AbstractVisitor { ExcHandlerAttr excHandlerAttr = insn.get(AType.EXC_HANDLER); if (excHandlerAttr != null) { ExceptionHandler handler = excHandlerAttr.getHandler(); - if (handler.addCatchType(type)) { + if (handler.addCatchType(mth, type)) { // exist handler updated (assume from same try block) - don't add again return null; } @@ -143,7 +143,7 @@ public class AttachTryCatchVisitor extends AbstractVisitor { } else { insn = insertNOP(insnByOffset, handlerOffset); } - ExceptionHandler handler = new ExceptionHandler(handlerOffset, type); + ExceptionHandler handler = ExceptionHandler.build(mth, handlerOffset, type); mth.addExceptionHandler(handler); insn.addAttr(new ExcHandlerAttr(handler)); return handler; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockExceptionHandler.java b/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockExceptionHandler.java index 0ebba28d8..ccab8b091 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockExceptionHandler.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockExceptionHandler.java @@ -549,7 +549,7 @@ public class BlockExceptionHandler { if (handler == resultHandler) { return false; } - resultHandler.addCatchTypes(handler.getCatchTypes()); + resultHandler.addCatchTypes(mth, handler.getCatchTypes()); handler.markForRemove(); return true; }); diff --git a/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchMultiException.java b/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchMultiException.java index 3d89d00d1..57febf7aa 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchMultiException.java +++ b/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchMultiException.java @@ -25,8 +25,6 @@ public class TestTryCatchMultiException extends IntegrationTest { @Test public void test() { - // printDisassemble(); - // setFallback(); noDebugInfo(); ClassNode cls = getClassNode(TestCls.class); String code = cls.getCode().toString(); diff --git a/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchMultiException2.java b/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchMultiException2.java new file mode 100644 index 000000000..6d117aaae --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchMultiException2.java @@ -0,0 +1,33 @@ +package jadx.tests.integration.trycatch; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.SmaliTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +@SuppressWarnings("CommentedOutCode") +public class TestTryCatchMultiException2 extends SmaliTest { + + // @formatter:off + /* + public static boolean test() { + try { + Class cls = Class.forName("c"); + return ((Boolean) cls.getMethod("b", new Class[0]).invoke(cls, new Object[0])).booleanValue(); + } catch (ClassNotFoundException | NoSuchMethodException | Exception | Throwable unused) { + // java compiler don't allow shadow subclasses in multi-catch + // in this case leave only Throwable + return false; + } + } + */ + // @formatter:on + + @Test + public void test() { + assertThat(getClassNodeFromSmali()) + .code() + .containsOne("} catch (Throwable unused) {"); + } +} diff --git a/jadx-core/src/test/smali/trycatch/TestTryCatchMultiException2.smali b/jadx-core/src/test/smali/trycatch/TestTryCatchMultiException2.smali new file mode 100644 index 000000000..9cf7edc0a --- /dev/null +++ b/jadx-core/src/test/smali/trycatch/TestTryCatchMultiException2.smali @@ -0,0 +1,38 @@ +.class public Ltrycatch/TestTryCatchMultiException2; +.super Ljava/lang/Object; + + +.method public static test()Z + .registers 5 + + :try_start_b + const-string v0, "c" + invoke-static {v0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class; + move-result-object v1 + + const/4 v0, 0x0 + const-string v2, "b" + new-array v3, v0, [Ljava/lang/Class; + invoke-virtual {v1, v2, v3}, Ljava/lang/Class;->getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method; + move-result-object v2 + + new-array v3, v0, [Ljava/lang/Object; + invoke-virtual {v2, v1, v3}, Ljava/lang/reflect/Method;->invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; + move-result-object v1 + + check-cast v1, Ljava/lang/Boolean; + invoke-virtual {v1}, Ljava/lang/Boolean;->booleanValue()Z + + move-result v1 + :try_end_2f + .catch Ljava/lang/ClassNotFoundException; {:try_start_b .. :try_end_2f} :catch_30 + .catch Ljava/lang/NoSuchMethodException; {:try_start_b .. :try_end_2f} :catch_30 + .catch Ljava/lang/Exception; {:try_start_b .. :try_end_2f} :catch_30 + .catchall {:try_start_b .. :try_end_2f} :catchall_30 + + return v1 + + :catch_30 + :catchall_30 + return v0 +.end method