Skip to content

Fix false positive in Proxy.newProxyInstance interface detection#9

Merged
laeubi merged 3 commits intofix-classforname-detection-issuefrom
copilot/enhance-testcases-example
Nov 7, 2025
Merged

Fix false positive in Proxy.newProxyInstance interface detection#9
laeubi merged 3 commits intofix-classforname-detection-issuefrom
copilot/enhance-testcases-example

Conversation

Copy link
Copy Markdown

Copilot AI commented Nov 7, 2025

The Proxy.newProxyInstance detection incorrectly associated interfaces from unrelated Class[] arrays with proxy instances when the array was stored before the proxy call.

Problem

Bytecode pattern: anewarrayldcaastoreastore → (other code) → Proxy.newProxyInstance(fromField)

The inProxyArray flag and proxyInterfaces list persisted across the astore, causing interfaces from the first array to be incorrectly processed for the second proxy call.

// This would incorrectly add TestInterface imports to the proxy for Runnable
Class<?>[] unrelated = new Class<?>[] { TestInterface.class };  // Creates array inline
Supplier<String> lambda = () -> "test";  // astore breaks the pattern
Runnable proxy = Proxy.newProxyInstance(..., FIELD_ARRAY, ...);  // Different array

Changes

  • Reset inProxyArray and clear proxyInterfaces on store instructions (astore*, putstatic, putfield) that break inline array patterns
  • Reset on non-static invocations (invokespecial, invokevirtual, invokeinterface)
  • Keep invokedynamic allowed (used for InvocationHandler lambda parameter)
  • Add ProxyFalsePositive test class and testProxyFalsePositiveFixed() to verify fix

Modified: Clazz.java processCode() switch statement (32 lines)
Added: Test case demonstrating and verifying the fix (62 lines)

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • bndtools.jfrog.io
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -Xms1024m -Xmx2048m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.14-bin/38aieal9i53h9rfe7vjup95b9/gradle-8.14/lib/gradle-daemon-main-8.14.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.14-bin/38aieal9i53h9rfe7vjup95b9/gradle-8.14/lib/agents/gradle-instrumentation-agent-8.14.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.14 (dns block)
  • scans-in.gradle.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -Xms1024m -Xmx2048m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.14-bin/38aieal9i53h9rfe7vjup95b9/gradle-8.14/lib/gradle-daemon-main-8.14.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.14-bin/38aieal9i53h9rfe7vjup95b9/gradle-8.14/lib/agents/gradle-instrumentation-agent-8.14.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.14 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

Enhance the testcases with example and fix false positive

See bndtools#6921 (comment) for details


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits November 7, 2025 06:55
- Add ProxyFalsePositive test class demonstrating the false positive scenario
- Add testProxyFalsePositiveFixed test to verify the fix
- Fix false positive by resetting proxy tracking when array is stored (astore, putstatic, putfield)
- Fix false positive by resetting proxy tracking on non-static invoke calls
- Keep invokedynamic allowed since it's used for InvocationHandler lambda parameter

Co-authored-by: laeubi <1331477+laeubi@users.noreply.github.com>
- Clarify that astore resets proxy tracking, not invokedynamic
- Update test comments to accurately describe the fix mechanism
- Explain that store instructions break the inline array pattern

Co-authored-by: laeubi <1331477+laeubi@users.noreply.github.com>
Copilot AI changed the title [WIP] Enhance test cases with examples and fix false positives Fix false positive in Proxy.newProxyInstance interface detection Nov 7, 2025
Copilot AI requested a review from laeubi November 7, 2025 07:04
@laeubi laeubi marked this pull request as ready for review November 7, 2025 07:04
@laeubi laeubi merged commit d09d575 into fix-classforname-detection-issue Nov 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants