Skip to content

[core] Missing and invalid throws declarations #2441

@nitram84

Description

@nitram84

Issue details

Jadx generates a throws declaration for each method annotated with dalvik.annotation.Throws. But this annotation is optional an not used at runtime ( https://stackoverflow.com/questions/14062434/throws-and-annotation-for-exception-in-dalvik-bytecode ), therefore can be removed ( https://github.com/facebook/redex/blob/main/config/aggressive.config#L62 ). If throws annotation are missing, the decompiled result has compile errors.

If throws annotation are not used at runtime, there is a possibility for bytecode manipulations. I did some tests: a thrown object can be anything and not necessarily a Throwable.

In my opinion jadx has to perform the same checks like a java compiler:
Collect all thrown exceptions for each method, calculate effective throws declarations, verify all throws annotations:
For each method: Follow the call graph of each method invocation; per method; aggregate all directly thrown exceptions and exceptions thrown by method invocations, merge collected exceptions with existing throws annotations, generate missing annotations and mark method as visited.

Is there an example for a visitor where we are traversing the call graph of each method? What is the best way to mark visited methods? A new JadxAttr?

Example:

###### Class jadx.test.MissingThrowsTest (jadx.test.MissingThrowsTest)
.class public Ljadx/test/MissingThrowsTest;
.super Ljava/lang/Object;
.source "MissingThrowsTest.java"


# direct methods
.method public constructor <init>()V
    .registers 1

    .line 6
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void
.end method

.method private exceptionSource()V
    .registers 3

    .line 8
    new-instance v0, Ljava/io/FileNotFoundException;

    const-string v1, ""

    invoke-direct {v0, v1}, Ljava/io/FileNotFoundException;-><init>(Ljava/lang/String;)V

    throw v0
.end method

.method private invalidException()V
    .registers 3
    .annotation system Ldalvik/annotation/Throws;
        value = {
            Ljava/lang/String;
        }
    .end annotation

    .line 36
    new-instance v0, Ljava/io/FileNotFoundException;

    const-string v1, ""

    invoke-direct {v0, v1}, Ljava/io/FileNotFoundException;-><init>(Ljava/lang/String;)V

    throw v0
.end method


# virtual methods
.method public doSomething1(I)V
    .registers 3
    .param p1, "i"    # I

    .line 20
    const/4 v0, 0x1

    if-ne p1, v0, :cond_7

    .line 21
    invoke-virtual {p0, p1}, Ljadx/test/MissingThrowsTest;->doSomething2(I)V

    goto :goto_a

    .line 23
    :cond_7
    invoke-virtual {p0, p1}, Ljadx/test/MissingThrowsTest;->doSomething1(I)V

    .line 25
    :goto_a
    return-void
.end method

.method public doSomething2(I)V
    .registers 3
    .param p1, "i"    # I

    .line 28
    const/4 v0, 0x1

    if-ne p1, v0, :cond_7

    .line 29
    invoke-direct {p0}, Ljadx/test/MissingThrowsTest;->exceptionSource()V

    goto :goto_a

    .line 31
    :cond_7
    invoke-virtual {p0, p1}, Ljadx/test/MissingThrowsTest;->doSomething1(I)V

    .line 33
    :goto_a
    return-void
.end method

.method public mergeThrownExcetions()V
    .registers 1
    .annotation system Ldalvik/annotation/Throws;
        value = {
            Ljava/io/IOException;
        }
    .end annotation

    .line 12
    invoke-direct {p0}, Ljadx/test/MissingThrowsTest;->exceptionSource()V

    .line 13
    return-void
.end method

.method public missingThrowsAnnotation()V
    .registers 1

    .line 16
    invoke-direct {p0}, Ljadx/test/MissingThrowsTest;->exceptionSource()V

    .line 17
    return-void
.end method

Actual result:

package jadx.test;

import java.io.FileNotFoundException;
import java.io.IOException;

/* loaded from: MissingThrowsTest.smali */
public class MissingThrowsTest {
    private void exceptionSource() { // Unhandled exception: java. io. FileNotFoundException
        throw new FileNotFoundException("");
    }

    public void mergeThrownExcetions() throws IOException {
        exceptionSource();
    }

    public void missingThrowsAnnotation() { // Unhandled exception: java. io. FileNotFoundException
        exceptionSource();
    }

    public void doSomething1(int i) { // Unhandled exception: java. io. FileNotFoundException
        if (i == 1) {
            doSomething2(i);
        } else {
            doSomething1(i);
        }
    }

    public void doSomething2(int i) { // Unhandled exception: java. io. FileNotFoundException
        if (i == 1) {
            exceptionSource();
        } else {
            doSomething1(i);
        }
    }

    private void invalidException() throws String { // error: incompatible types: String cannot be converted to Throwable + Unhandled exception: java. io. FileNotFoundException
        throw new FileNotFoundException("");
    }
}

Expected result:

package jadx.test;

import java.io.FileNotFoundException;
import java.io.IOException;

/* loaded from: MissingThrowsTest.smali */
public class MissingThrowsTest {
    private void exceptionSource() throws FileNotFoundException {
        throw new FileNotFoundException("");
    }

    public void mergeThrownExcetions() throws IOException {
        exceptionSource();
    }

    public void missingThrowsAnnotation() throws FileNotFoundException {
        exceptionSource();
    }

    public void doSomething1(int i) throws FileNotFoundException {
        if (i == 1) {
            doSomething2(i);
        } else {
            doSomething1(i);
        }
    }

    public void doSomething2(int i) throws FileNotFoundException {
        if (i == 1) {
            exceptionSource();
        } else {
            doSomething1(i);
        }
    }

    // JADX WARN: Skipped invalid throws annotation
    private void invalidException() throws FileNotFoundException {
        throw new FileNotFoundException("");
    }
}

Relevant log output or stacktrace

Provide sample and class/method full name

Real world example:

https://spider-solitaire-freecell.apk.gold/

Class com.startapp.android.publish.gson.stream.JsonReader

private IOException syntaxError(String message)

Jadx version

latest git master

Metadata

Metadata

Assignees

No one assigned

    Labels

    CoreIssues in jadx-core modulebug

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions