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