secure-software-engineering / FlowDroid

FlowDroid Static Data Flow Tracker
GNU Lesser General Public License v2.1
1.05k stars 297 forks source link

Flowdroid can't track the taint into the method in a class? #299

Open Hxinrong opened 3 years ago

Hxinrong commented 3 years ago

The test codes are as follows, by modifying the code in <soot.jimple.infoflow.test.TypeTestCode: void arrayObjectCastTest2()>: https://github.com/secure-software-engineering/FlowDroid/blob/547e5fdb6cb5a51817155728c2ee36e38eb6181e/soot-infoflow/test/soot/jimple/infoflow/test/TypeTestCode.java#L194-L202 I only modified the Line 202:

    public void arrayObjectCastTest2() {
        Object obj = Bundle.get("foo");
        A foo2[] = (A[]) obj;
        obj = foo2[0];
        A a = (A) obj;
        a.data2 = a.data;
        ConnectionManager cm = new ConnectionManager();
        cm.publish(a.testString());
    }

A.testString() is a method which I add to the class A as a member method. It does the same action as function A.toString().

         public String testString() {
        return "data: " + data + ", data2: " + data2;
    }

Then, I run the arrayObjectCastTest2 test case. The result shows that the actual taint path is 0. And I also have done other similar tests. The test results revealed that Flowdroid can't track the taint into the methods in a class, and override member methods have the same problem.

Is it a feature of Flowdroid? And why Flowdroid is set not to track the kind of case? Thank you!

StevenArzt commented 3 years ago

The test case is actually broken. Note that Bundle.get() is implemented as follows:

return new String[] { key };

The return value is then cast to type A[]:

Object obj = Bundle.get("foo");
A foo2[] = (A[]) obj;

This shouldn't even work. FlowDroid should actually not find a leak here, because at runtime, this method would just throw an exception and terminate, and no leak would happen. In fact, finding a leak in this method is an over-approximation.

So why don't you get the over-approximation with your testString() method? The answer lies in the SPARK callgraph algorithm. There is no outgoing edge from the call to testString() to any callee. More precisely, there is no valid allocation site for the base object of the call, and therefore no propagted type information. Consequently, SPARK never registers a call edge.

In our original test case, we don't need any call edge, so we don't have the "problem". In the original case, we would need an explicit check to see that the cast is impossible.