pascal-lab/Tai-e

Reflection analysis for Method.invoke(Object, Object[]) miss target when the second argument is generated by ArrayList.toArray(T[])

Opened this issue · 1 comments

Overall Description

A reproducible case "ListToArray.java" is shown in the following with the explanations for the bug. (The jdk8 compilable java source is in the attachment)

public class ListToArray {

    public static void foo(String[] args) {}

    public static void main(String[] args) throws Exception {
        Method m = ListToArray.class.getMethod("foo", String[].class);
        List<String> list = new ArrayList<>();
        list.add("hello");
        m.invoke(null, new Object[]{list.toArray(new String[0])});
    }
}

In ListToArray.java, m.invoke(...) invokes ListToArray.foo method, according to jdk docs of API ArrayList.toArray(T[]), the argument of the invocation is a String array created by list.toArray(new String[0]) containing one String type element "hello".

In analyzing the above case, tai-e failed to analyze the invocation m.invoke, i.e. <ListToArray: void foo(java.lang.String[])> is not reachable

ListToArray.zip

Current Behavior

Only <ListToArray: void main(java.lang.String[])> is in the dumped output/reachable-methods.txt

Expected Behavior

In the dumped output/reachable-methods.txt

<ListToArray: void main(java.lang.String[])> 
<ListToArray: void foo(java.lang.String[])> 

Tai-e Version

331589a

Tai-e Arguments

-cp /path/to/ListToArray.class
-m ListToArray
-java 8
-ap
-a pta=cs:ci
-a cg=dump-methods:true
-scope=REACHABLE

JDK Version

Corretto-17.0.4

System Environment

Windows 10

Additional Information

When analyzing m.invoke(null, new Object[]{list.toArray(new String[0])}); (tir in the following) Tai-e successfully resolved m($r2), i.e. <ListToArray: void foo(java.lang.String[])>, but failed to analyze the pts of $r4 elements and $r6 (pts is empty) because tai-e failed to analyze ArrayList.toArray(T[]), this may trigger typeMatcher.isUnmatched(invoke, target) -> true in methodInvoke method in ReflectiveActionModel.java.

[12@L12] $r4 = newarray java.lang.Object[%intconst0];
[13@L12] $r5 = newarray java.lang.String[%intconst1];
[14@L12] $r6 = invokeinterface $r3.<java.util.List: java.lang.Object[] toArray(java.lang.Object[])>($r5);
[15@L12] $r4[%intconst1] = $r6;
[16@L12] invokevirtual $r2.<java.lang.reflect.Method: java.lang.Object invoke(java.lang.Object,java.lang.Object[])>(%nullconst, $r4);

Maybe a proper model to <java.util.List: java.lang.Object[] toArray(java.lang.Object[])> will solve the problem.

Additional Information (Modified)

My fault, I misunderstand how tai-e compute type info of an invoke, a proper model to <java.util.List: java.lang.Object[] toArray(java.lang.Object[])> only solves the empty pts problem of $r4 element and $r6.

When analyzing m.invoke(null, new Object[]{list.toArray(new String[0])}); (tir in the following) Tai-e successfully resolved m (i.e. $r2) as <ListToArray: void foo(java.lang.String[])>, but failed to analyze the pts of $r4 elements and $r6 (pts is empty) because tai-e failed to analyze ArrayList.toArray(T[]), this may trigger typeMatcher.isUnmatched(invoke, target) -> true in methodInvoke method in ReflectiveActionModel.java.

[12@L12] $r4 = newarray java.lang.Object[%intconst0];
[13@L12] $r5 = newarray java.lang.String[%intconst1];
[14@L12] $r6 = invokeinterface $r3.<java.util.List: java.lang.Object[] toArray(java.lang.Object[])>($r5);
[15@L12] $r4[%intconst1] = $r6;
[16@L12] invokevirtual $r2.<java.lang.reflect.Method: java.lang.Object invoke(java.lang.Object,java.lang.Object[])>(%nullconst, $r4);

Maybe a proper model to <java.util.List: java.lang.Object[] toArray(java.lang.Object[])> will solve the problem.