Skip to content

FlowDroid crashes when analyzing callbacks registered in a certain way #787

@wyatt-feng

Description

@wyatt-feng

Description

The current version (2.14.0 and 2.15.0-SNAPSHOT) of FlowDroid crashes when analyzing some apps that register ActivityLifecycleCallbacks in a certain way. Here is the minimal demonstration that can reliably trigger the issue and the corresponding crash log, which is very similar to #786, #770, and #748 (but I am not sure if the root cause is exactly the same).

Steps to reproduce

The demonstration consists of an activity MainActivity and an application class Demonstration, in which file the LifecycleCallbacks are implemented and registered.

MainActivity.java:

package com.example.minimaldemonstration;

import android.os.Bundle;
import android.util.Log;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import java.util.Random;

public class MainActivity extends AppCompatActivity {
    private int info;  // <----- The issue won't appear if this is a local variable.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        info = dummySource();
        dummySink(info);
    }

    @Override
    protected void onStart() {     // <----- This method does nothing but has to be here or else the issue won't appear.
        super.onStart();
    }

    public int dummySource() {
        return 0;
    }
    public void dummySink(int info) {
        Log.d("sink", String.valueOf(info));
    }
}

Demonstration.java:

package com.example.minimaldemonstration;

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class Demonstration extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        LifecycleTracker.register(this);
    }
}

class LifecycleTracker implements Application.ActivityLifecycleCallbacks {
    private static LifecycleTracker e;
    private LifecycleTracker() {}
    public static void register(Application app) {
        if (e == null) {
            e = new LifecycleTracker();
        }
        app.registerActivityLifecycleCallbacks(e);
    }
    @Override
    public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
        Log.d("Lifecycle", "onActivityCreated()");    // <----- Avoid being optimized.
    }

    @Override
    public void onActivityStarted(@NonNull Activity activity) {}

    @Override
    public void onActivityResumed(@NonNull Activity activity) {}

    @Override
    public void onActivityPaused(@NonNull Activity activity) {}

    @Override
    public void onActivityStopped(@NonNull Activity activity) {}

    @Override
    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {}

    @Override
    public void onActivityDestroyed(@NonNull Activity activity) {}
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" >
    <application
        android:name=".Demonstration"
        tools:targetApi="31" >
        <activity
            android:name=".MainActivity"
            android:exported="true" >
        </activity>
    </application>
</manifest>

SourcesAndSinks.txt:

<com.example.minimaldemonstration.MainActivity: int dummySource()> -> _SOURCE_
<com.example.minimaldemonstration.MainActivity: void dummySink(int)> -> _SINK_

Expected behavior

Log saying "Found 1 leaks from 1 sources" with no runtime exceptions.

Observed behavior

Crash log:

...
[main] INFO soot.jimple.infoflow.android.SetupApplication - Constructing the callgraph...
[main] ERROR soot.jimple.spark.solver.PropWorklist - Note that we log, but continue in case of SPARK problems
[main] INFO soot.jimple.infoflow.android.callbacks.DefaultCallbackAnalyzer - Running incremental callback analysis for 1 components...
[main] INFO soot.jimple.infoflow.android.callbacks.DefaultCallbackAnalyzer - Incremental callback analysis done.
[main] INFO soot.jimple.infoflow.memory.MemoryWarningSystem - Shutting down the memory warning system...
[main] INFO soot.jimple.infoflow.android.SetupApplication - Callback analysis terminated normally
[main] INFO soot.jimple.infoflow.android.SetupApplication - Entry point calculation done.
[main] INFO soot.jimple.infoflow.android.source.AccessPathBasedSourceSinkManager - Created a SourceSinkManager with 2 sources, 2 sinks, and 81 callback methods.
[main] INFO soot.jimple.infoflow.android.SetupApplication - Collecting callbacks and building a callgraph took 0 seconds
[main] INFO soot.jimple.infoflow.android.SetupApplication - Running data flow analysis on /Users/wyattfeng/Workspace/minimaldemonstration/app/build/outputs/apk/debug/app-debug.apk with 2 sources and 2 sinks...
[main] INFO soot.jimple.infoflow.InfoflowConfiguration - Implicit flow tracking is NOT enabled
[main] INFO soot.jimple.infoflow.InfoflowConfiguration - Exceptional flow tracking is enabled
[main] INFO soot.jimple.infoflow.InfoflowConfiguration - Running with a maximum access path length of 5
[main] INFO soot.jimple.infoflow.InfoflowConfiguration - Using path-agnostic result collection
[main] INFO soot.jimple.infoflow.InfoflowConfiguration - Recursive access path shortening is enabled
[main] INFO soot.jimple.infoflow.InfoflowConfiguration - Taint analysis enabled: true
[main] INFO soot.jimple.infoflow.InfoflowConfiguration - Using alias algorithm FlowSensitive
[main] INFO soot.jimple.infoflow.memory.MemoryWarningSystem - Registered a memory warning system for 14,745.6 MiB
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Callgraph construction took 0 seconds
[main] INFO soot.jimple.infoflow.codeOptimization.InterproceduralConstantValuePropagator - Removing side-effect free methods is disabled
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Dead code elimination took 0.013935166 seconds
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Callgraph has 111 edges
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Starting Taint Analysis
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Using context- and flow-sensitive solver
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Using context- and flow-sensitive solver
[main] WARN soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Running with limited join point abstractions can break context-sensitive path builders
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Looking for sources and sinks...
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Source lookup done, found 1 sources and 1 sinks.
[FlowDroid] ERROR heros.solver.CountingThreadPoolExecutor - Worker thread execution failed: null
java.lang.NullPointerException
	at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:904)
	at com.google.common.cache.LocalCache.get(LocalCache.java:4016)
	at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:4040)
	at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4989)
	at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4996)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.getOrCreateUnitGraph(AbstractJimpleBasedICFG.java:130)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.isStartPoint(AbstractJimpleBasedICFG.java:160)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.isStartPoint(AbstractJimpleBasedICFG.java:51)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isStartPoint(InfoflowCFG.java:213)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isStartPoint(InfoflowCFG.java:51)
	at soot.jimple.toolkits.ide.icfg.BackwardsInterproceduralCFG.isExitStmt(BackwardsInterproceduralCFG.java:67)
	at soot.jimple.toolkits.ide.icfg.BackwardsInterproceduralCFG.isExitStmt(BackwardsInterproceduralCFG.java:38)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isExitStmt(InfoflowCFG.java:208)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isExitStmt(InfoflowCFG.java:51)
	at soot.jimple.infoflow.solver.fastSolver.IFDSSolver$PathEdgeProcessingTask.runInternal(IFDSSolver.java:750)
	at soot.jimple.infoflow.solver.fastSolver.LocalWorklistTask.run(LocalWorklistTask.java:27)
	...
[FlowDroid] ERROR heros.solver.CountingThreadPoolExecutor - Worker thread execution failed: null
java.lang.NullPointerException
	at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:904)
	at com.google.common.cache.LocalCache.get(LocalCache.java:4016)
	at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:4040)
	at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4989)
	at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4996)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.getOrCreateUnitGraph(AbstractJimpleBasedICFG.java:130)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.isStartPoint(AbstractJimpleBasedICFG.java:160)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.isStartPoint(AbstractJimpleBasedICFG.java:51)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isStartPoint(InfoflowCFG.java:213)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isStartPoint(InfoflowCFG.java:51)
	at soot.jimple.toolkits.ide.icfg.BackwardsInterproceduralCFG.isExitStmt(BackwardsInterproceduralCFG.java:67)
	at soot.jimple.toolkits.ide.icfg.BackwardsInterproceduralCFG.isExitStmt(BackwardsInterproceduralCFG.java:38)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isExitStmt(InfoflowCFG.java:208)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isExitStmt(InfoflowCFG.java:51)
	at soot.jimple.infoflow.solver.fastSolver.IFDSSolver$PathEdgeProcessingTask.runInternal(IFDSSolver.java:750)
	at soot.jimple.infoflow.solver.fastSolver.LocalWorklistTask.run(LocalWorklistTask.java:27)
	...
Exception in thread "FlowDroid" [main] INFO soot.jimple.infoflow.memory.MemoryWarningSystem - Shutting down the memory warning system...
java.lang.NullPointerException
	at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:904)
	at com.google.common.cache.LocalCache.get(LocalCache.java:4016)
	at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:4040)
	at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4989)
	at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4996)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.getOrCreateUnitGraph(AbstractJimpleBasedICFG.java:130)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.isStartPoint(AbstractJimpleBasedICFG.java:160)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.isStartPoint(AbstractJimpleBasedICFG.java:51)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isStartPoint(InfoflowCFG.java:213)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isStartPoint(InfoflowCFG.java:51)
	at soot.jimple.toolkits.ide.icfg.BackwardsInterproceduralCFG.isExitStmt(BackwardsInterproceduralCFG.java:67)
	at soot.jimple.toolkits.ide.icfg.BackwardsInterproceduralCFG.isExitStmt(BackwardsInterproceduralCFG.java:38)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isExitStmt(InfoflowCFG.java:208)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isExitStmt(InfoflowCFG.java:51)
	at soot.jimple.infoflow.solver.fastSolver.IFDSSolver$PathEdgeProcessingTask.runInternal(IFDSSolver.java:750)
	at soot.jimple.infoflow.solver.fastSolver.LocalWorklistTask.run(LocalWorklistTask.java:27)
	...
Exception in thread "FlowDroid" java.lang.NullPointerException
	at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:904)
	at com.google.common.cache.LocalCache.get(LocalCache.java:4016)
	at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:4040)
	at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4989)
	at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4996)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.getOrCreateUnitGraph(AbstractJimpleBasedICFG.java:130)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.isStartPoint(AbstractJimpleBasedICFG.java:160)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.isStartPoint(AbstractJimpleBasedICFG.java:51)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isStartPoint(InfoflowCFG.java:213)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isStartPoint(InfoflowCFG.java:51)
	at soot.jimple.toolkits.ide.icfg.BackwardsInterproceduralCFG.isExitStmt(BackwardsInterproceduralCFG.java:67)
	at soot.jimple.toolkits.ide.icfg.BackwardsInterproceduralCFG.isExitStmt(BackwardsInterproceduralCFG.java:38)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isExitStmt(InfoflowCFG.java:208)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isExitStmt(InfoflowCFG.java:51)
	at soot.jimple.infoflow.solver.fastSolver.IFDSSolver$PathEdgeProcessingTask.runInternal(IFDSSolver.java:750)
	at soot.jimple.infoflow.solver.fastSolver.LocalWorklistTask.run(LocalWorklistTask.java:27)
	...
[main] ERROR soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - Exception during data flow analysis
java.lang.RuntimeException: There were exceptions during IFDS analysis. Exiting.
	at soot.jimple.infoflow.solver.fastSolver.IFDSSolver.runExecutorAndAwaitCompletion(IFDSSolver.java:263)
	at soot.jimple.infoflow.solver.fastSolver.IFDSSolver.awaitCompletionComputeValuesAndShutdown(IFDSSolver.java:230)
	at soot.jimple.infoflow.solver.fastSolver.IFDSSolver.solve(IFDSSolver.java:202)
	at soot.jimple.infoflow.AbstractInfoflow.runTaintAnalysis(AbstractInfoflow.java:1204)
	at soot.jimple.infoflow.AbstractInfoflow.runAnalysis(AbstractInfoflow.java:881)
	at soot.jimple.infoflow.AbstractInfoflow.runAnalysis(AbstractInfoflow.java:814)
	at soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow.runAnalysis(SetupApplication.java:1358)
	at soot.jimple.infoflow.android.SetupApplication.processEntryPoint(SetupApplication.java:1656)
	at soot.jimple.infoflow.android.SetupApplication.runInfoflow(SetupApplication.java:1585)
	at soot.jimple.infoflow.android.SetupApplication.runInfoflow(SetupApplication.java:1532)
	at soot.jimple.infoflow.android.SetupApplication.runInfoflow(SetupApplication.java:1500)
	at edu.ucr.cs.cresp.Main.main(Main.java:39)
Caused by: java.lang.NullPointerException
	at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:904)
	at com.google.common.cache.LocalCache.get(LocalCache.java:4016)
	at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:4040)
	at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4989)
	at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4996)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.getOrCreateUnitGraph(AbstractJimpleBasedICFG.java:130)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.isStartPoint(AbstractJimpleBasedICFG.java:160)
	at soot.jimple.toolkits.ide.icfg.AbstractJimpleBasedICFG.isStartPoint(AbstractJimpleBasedICFG.java:51)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isStartPoint(InfoflowCFG.java:213)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isStartPoint(InfoflowCFG.java:51)
	at soot.jimple.toolkits.ide.icfg.BackwardsInterproceduralCFG.isExitStmt(BackwardsInterproceduralCFG.java:67)
	at soot.jimple.toolkits.ide.icfg.BackwardsInterproceduralCFG.isExitStmt(BackwardsInterproceduralCFG.java:38)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isExitStmt(InfoflowCFG.java:208)
	at soot.jimple.infoflow.solver.cfg.InfoflowCFG.isExitStmt(InfoflowCFG.java:51)
	at soot.jimple.infoflow.solver.fastSolver.IFDSSolver$PathEdgeProcessingTask.runInternal(IFDSSolver.java:750)
	at soot.jimple.infoflow.solver.fastSolver.LocalWorklistTask.run(LocalWorklistTask.java:27)
	...
[main] INFO soot.jimple.infoflow.android.SetupApplication - Found 0 leaks from 0 sources

Process finished with exit code 0

I tried to solve the issue by myself, but the root cause seems to be more complicated than I had thought.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions