pascal-lab/Tai-e

The CLI output detected the taint flow, but no flow in the dot file.

Closed this issue · 3 comments

source code

public class Main {
    public static void main(String[] args) {
        test1();
    }

    public static void test1(){
        String a = getSourceString();

        String [] b;
        b = a.split(":");

        TestSink.testStringSink(b[0]);
    }
    public static String getSourceString(){
        return "test1:test2";
    }
}

IR

    public static void test1() {
        java.lang.String $r0, %stringconst0, $r2;
        java.lang.String[] $r1;
        int %intconst1;
        [0@L12] $r0 = invokestatic <org.example.Main: java.lang.String getSourceString()>();
        [1@L15] %stringconst0 = ":";
        [2@L15] $r1 = invokevirtual $r0.<java.lang.String: java.lang.String[] split(java.lang.String)>(%stringconst0);
        [3@L17] %intconst1 = 0;
        [4@L17] $r2 = $r1[%intconst1];
        [5@L17] invokestatic <org.example.TestSink: void testStringSink(java.lang.String)>($r2);
        [6@L18] return;
    }

tai-e options

  • options
analyses:
  pta: cs:ci;implicit-entries:true;distinguish-string-constants:null;reflection-inference:solar;taint-config:java-benchmarks/common-text/taint-config.yml;
  • taint-config
sources:
  - { kind: call, method: "<org.example.Main: java.lang.String getSourceString()>", index: result }
sinks:
  - { method: "<org.example.TestSink: void testStringSink(java.lang.String)>", index: 0 }

transfers:
  - { method: "<java.lang.String: java.lang.String[] split(java.lang.String)>", from: base, to: "result[*]" }

output

  • cli
TaintConfig:
sources:
  CallSource{<org.example.Main: java.lang.String getSourceString()>/result(java.lang.String)}
  CallSource{<org.example.Main: java.lang.String[] getSourceStringArray()>/result(java.lang.String[])}

sinks:
  <org.example.TestSink: void testStringSink(java.lang.String)>/0

[Pointer analysis] elapsed time: 5.47s
Detected 1 taint flow(s):
TaintFlow{<org.example.Main: void test1()>[0@L11] $r0 = invokestatic org.example.Main.getSourceString()/result -> <org.example.Main: void test1()>[5@L16] invokestatic org.example.TestSink.testStringSink($r2)/0}
TFGDumper starts ...
Source nodes:
VarNode{<org.example.Main: void test1()>/$r0}
Sink nodes:
VarNode{<org.example.Main: void test1()>/$r2}
  • dot
    image

question

  • Is the output without edge in dot file as expected?
    I think expected result should have at least 2 point flow edge: $r0 -> $r1 and $r1 -> $r2

There is essentially no $r0 to $r1 edge in OFG ($r1 is an array); that's why it's not showing up.

One actual propagation chain in TFG is:

VarNode{<org.example.Main: void test1()>/$r0}
ArrayIndexNode{NewObj{<java.util.regex.Pattern: java.lang.String[] split(java.lang.CharSequence,int)>[33@L1015] newarray java.lang.String[%intconst1]}}
VarNode{<org.example.Main: void test1()>/$r2}

You could try reversing the switch

/**
* Whether only track taint flow in application code.
*/
private final boolean onlyApp = true;

which is used to filter the edge from app to non-app when traveling the OFG

Node target = edge.target();
if (!onlyApp || isApp(target)) {
tfg.addEdge(edge);

to get the complete graph.

One actual propagation chain is:

VarNode{<org.example.Main: void test1()>/$r0}
ArrayIndexNode{NewObj{<java.util.regex.Pattern: java.lang.String[] split(java.lang.CharSequence,int)>[33@L1015] newarray java.lang.String[%intconst1]}}
VarNode{<org.example.Main: void test1()>/$r2}

Is there a taint-config rule that can get this "actual propagation chain"?

The above actual propagation chain I mentioned, one of shortest paths from $r0 to $r2, is reflected in the complete TFG. You can modify the source code to reversing the switch) to produce it.

Besides, if you think the obtained TFG too large for checking conveniently (that's why we haven't made the switch a configurable option for the time being), you can modify the buildComplete method to search (BFS/Shortest-path/...) the paths you want.

private TaintFlowGraph buildComplete() {