Skip to content

Missing call graph edges for JDK dynamic proxies created via Proxy.newProxyInstance() #2287

Description

@mohPYdev

Note: This is the artifact of a research project designed to help static analysis developers reason about and localize the unsoundness issues of their tool.

Description

Summary

SPARK does not correctly model dynamic proxies created through java.lang.reflect.Proxy.newProxyInstance(). Although Soot includes a virtual edge summary intended to model this API, the summary is registered as an instance invocation even though Proxy.newProxyInstance() is a static method. Consequently, the summary is never applied during call graph construction, and the proxy dispatch chain is omitted from the generated call graph.

As a result, calls made through dynamically generated proxy objects do not resolve to the corresponding InvocationHandler or generated proxy methods.


Minimal Reproduction

I have attached a Maven-based minimal project that creates a JDK dynamic proxy using Proxy.newProxyInstance(...) and immediately invokes a method through the returned proxy.

How to reproduce

Run SPARK call graph construction on the attached project using the provided Maven setup.
soot version: 4.4.1
java version: 8

The script to run the tool using JCG interface.

attachment.zip

python analysis_interface.py --framework SOOT --algorithm SPARK --project reproducing --type static --reproducing_test_case_path [path to test case directory with jcg.conf] --boundary_id 1

Observed Behavior

  • SPARK encounters the static call to Proxy.newProxyInstance(...)

  • No virtual edge summary is applied to the call site

  • The returned proxy object is therefore not connected to its generated proxy class or InvocationHandler

  • Subsequent interface dispatch through the proxy cannot be resolved

  • Consequently, the call graph contains no edge to:

    com.sun.proxy.$Proxy0.bar(Object)
    

Expected Behavior

For programs using JDK dynamic proxies:

  • The analysis should recognize calls to Proxy.newProxyInstance(...)

  • The proxy creation should be connected to the generated proxy class and its associated InvocationHandler

  • Subsequent interface dispatch through the proxy should resolve correctly

  • A call graph edge should exist, for example:

    {
      "caller": "com.example.App.main(String[])",
      "callee": "com.sun.proxy.$Proxy0.bar(Ljava/lang/Object;)Ljava/lang/Object;"
    }

Notes on Implementation (inspected behavior)

From code inspection:

  • Soot contains a virtual edge summary for Proxy.newProxyInstance(...) in virtualedges.xml

  • However, the summary is registered with:

    • invoketype="instance"
  • Proxy.newProxyInstance(...) is a static method

  • During call graph construction:

    • static call sites query staticinvokeEdges via getVirtualEdgesMatchingFunction(...)
    • instance call sites query instanceinvokeEdges via getVirtualEdgesMatchingSubSig(...)
  • Because the proxy summary is stored as an instance edge, the lookup for the static call returns null

  • As a result, no proxy-related virtual edges are generated, and the proxy dispatch chain is omitted from the call graph

Relevant locations:

  • virtualedges.xml
  • OnFlyCallGraphBuilder.java
  • VirtualEdgesSummaries.java

Impact

If the omission is unintended, it may affect analyses that rely on accurate modeling of Java's dynamic proxy mechanism. Dynamic proxies are widely used in Java frameworks such as Spring, RMI, JDK proxy-based AOP, and numerous dependency injection libraries. Missing proxy dispatch edges can lead to incomplete call graphs and reduce the effectiveness of downstream analyses such as vulnerability detection, dependency analysis, and framework behavior modeling.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions