RFR: 8311500: StackWalker.getCallerClass() can throw if invoked reflectively [v2]
Volker Simonis
simonis at openjdk.org
Fri Jul 7 15:48:53 UTC 2023
On Wed, 5 Jul 2023 19:14:07 GMT, Mandy Chung <mchung at openjdk.org> wrote:
>> Volker Simonis has updated the pull request incrementally with one additional commit since the last revision:
>>
>> Rename new parameter according to the HS coding conventions
>
> Thanks for catching this issue. I agree that `Method::invoke` should be skipped the caller-sensitive test in this case but the fix isn't quite right. The caller-sensitive test should apply in any batch. For example, `CSM` calls `getCallerClass` reflectively, I think the stack would look like this:
>
>
> java.lang.StackWalker::getCallerClass
> java.lang.invoke.DirectMethodHandle$Holder::invokeStatic
> java.lang.invoke.LambdaForm$MH/0x0000000800002c00::invoke
> :
> :
> jdk.internal.reflect.DirectMethodHandleAccessor::invokeImpl
> jdk.internal.reflect.DirectMethodHandleAccessor::invoke
> java.lang.reflect.Method::invoke
> CSM <--------- caller-sensitive method and UOE should be thrown
>
>
>
> In this case, UOE should be thrown.
By the way, great that you raised this issue @mlchung :) While writing a test for it I realized the the current implementation is *already failing* to trow an `UnsupportedOperationException` if `StackWalker::getCallerClass()` is called reflectively from a `@CallerSensitive` method (because the caller sensitive method is not at the first method at `index == start_index`):
[0,161s][debug][stackwalk] Start walking: mode 6 skip 0 frames batch size 6
[0,161s][debug][stackwalk]
[0,161s][debug][stackwalk] skip java.lang.StackStreamFactory$AbstractStackWalker::callStackWalk
[0,161s][debug][stackwalk] skip java.lang.StackStreamFactory$AbstractStackWalker::beginStackWalk
[0,161s][debug][stackwalk] skip java.lang.StackStreamFactory$AbstractStackWalker::walkHelper
[0,161s][debug][stackwalk] skip java.lang.StackStreamFactory$AbstractStackWalker::walk
[0,161s][debug][stackwalk] skip java.lang.StackStreamFactory$CallerClassFinder::findCaller
[0,161s][debug][stackwalk] skip java.lang.StackWalker::getCallerClass
[0,161s][debug][stackwalk] fill_in_frames limit=6 start=2 frames length=8
[0,161s][debug][stackwalk] hidden method: java.lang.invoke.DirectMethodHandle$Holder::invokeSpecial
[0,161s][debug][stackwalk] hidden method: java.lang.invoke.LambdaForm$MH/0x00000007c0084c00::invoke
[0,161s][debug][stackwalk] hidden method: java.lang.invoke.Invokers$Holder::invokeExact_MT
[0,161s][debug][stackwalk] hidden method: java.util.CallerSensitiveMethod$$InjectedInvoker/0x00000007c0085000::reflect_invoke_V
[0,161s][debug][stackwalk] hidden method: java.lang.invoke.DirectMethodHandle$Holder::invokeStatic
[0,161s][debug][stackwalk] hidden method: java.lang.invoke.LambdaForm$MH/0x00000007c0085400::invokeExact_MT
[0,161s][debug][stackwalk] hidden method: jdk.internal.reflect.DirectMethodHandleAccessor::invokeImpl
[0,161s][debug][stackwalk] 2: frame method: jdk.internal.reflect.DirectMethodHandleAccessor::invoke bci=24
[0,161s][debug][stackwalk] 2: done frame method: jdk.internal.reflect.DirectMethodHandleAccessor::invoke
[0,161s][debug][stackwalk] 3: frame method: java.lang.reflect.Method::invoke bci=90
[0,161s][debug][stackwalk] 3: done frame method: java.lang.reflect.Method::invoke
[0,161s][debug][stackwalk] 4: frame method: java.util.CallerSensitiveMethod::getCallerClass bci=110
[0,161s][debug][stackwalk] 4: done frame method: java.util.CallerSensitiveMethod::getCallerClass
[0,161s][debug][stackwalk] 5: frame method: ReflectiveGetCallerClassTest::main bci=38
[0,161s][debug][stackwalk] 5: done frame method: ReflectiveGetCallerClassTest::main
[0,161s][debug][stackwalk] fill_in_frames done frames_decoded=4 at_end=1
doStackWalk: skip 0 start 2 end 6
frame 2: class jdk.internal.reflect.DirectMethodHandleAccessor
skip: frame 2 class jdk.internal.reflect.DirectMethodHandleAccessor
next frame at 2: class jdk.internal.reflect.DirectMethodHandleAccessor (origin 2 fence 6)
skip: frame 3 class java.lang.reflect.Method
next frame at 3: class java.lang.reflect.Method (origin 3 fence 6)
next frame at 4: class java.util.CallerSensitiveMethod (origin 4 fence 6)
next frame at 5: class ReflectiveGetCallerClassTest (origin 5 fence 6)
CallerSensitiveMethod::getCallerClass() called from class ReflectiveGetCallerClassTest
Exception in thread "main" java.lang.RuntimeException: Expected UnsupportedOperationException when calling StackWalker::getCallerClass() from @CallerSensitive method reflectively
at java.base/java.util.CallerSensitiveMethod.getCallerClass(CallerSensitiveMethod.java:79)
at ReflectiveGetCallerClassTest.main(ReflectiveGetCallerClassTest.java:84)
The existing test under `test/jdk/java/lang/StackWalker/CallerSensitiveMethod/src/java.base/java/util/CSM.java` for calls from `@CallerSensitive` methods only handles direct but not reflective calls.
I'll post a patch which will hopefully fix both cases later today.
-------------
PR Comment: https://git.openjdk.org/jdk/pull/14773#issuecomment-1625607182
More information about the core-libs-dev
mailing list