On 07/31/2013 03:18 PM, Nick Williams wrote:
Okay, but I don't think @CallerSensitive is necessary to prevent MethodHandles from spoofing caller classes. I'll admit I don't know very much about MethodHandle, what it does, or how its invocation methods are different from Method.invoke(). In the code that I copied into this email from jvm.cpp last night, the third frame (frame #2) will always be returned unless it is one ignored by the security stack walk. @CallerSensitive doesn't come in to play here.

Nope. I think @CallerSensitive annotation is currently just a security feature, needed for implementation of MethodHandles.

Regards, Peter


Nick

On Jul 31, 2013, at 2:36 AM, Peter Levart wrote:

Hi Nick,

The @CallerSensitive annotation is an annotation that prevents some other infrastructure, namely the MethodHandles, to "spoof" caller classes.

Try this:

MethodHandles.Lookup lookup = MethodHandles.lookup().in(Object.class); MethodHandle mh = lookup.findStatic(Class.class, "forName", MethodType.methodType(Class.class, String.class));

...you won't be able to pretend that you are the j.l.Object that is calling method Class.forName(String)...

The annotation might have (or will have?) other effects like making sure some infrastructure-inserted frames are hidden-away just for @CallerSensitive methods which might be less optimal and not needed for normal methods.


Regards, Peter

On 07/31/2013 01:11 AM, Nick Williams wrote:
Quick question for those of you that know anything about @CallerSensitive...

After looking at the code and experimenting some, I've discovered that 
getCallerClass() doesn't actually keep going until it finds the first method 
without @CallerSensitive. It only returns the caller of the caller. So, for 
example:

Stack 1
@CallerSensitive Reflection.getCallerClass()
@CallerSensitive MyClass1.method1();
MyClass2.method2();

In this case, getCallerClass() returns MyClass2.class. BUT:

Stack 2
@CallerSensitive Reflection.getCallerClass()
@CallerSensitive MyClass1.method1();
@CallerSensitive MyClass2.method2();
MyClass3.method3();

In this case, getCallerClass() STILL returns MyClass2.class. Based on the plain-language 
meaning of @CallerSensitive, I would expect getCallerClass() to return MyClass3.class in 
the second case. But, indeed, the JavaDoc for Reflection.getCallerClass() says: 
"Returns the class of the caller of the method calling this method." So, then, 
what's the point of @CallerSensitive? Looking at the code:

    vframeStream vfst(thread);
    // Cf. LibraryCallKit::inline_native_Reflection_getCallerClass
    for (int n = 0; !vfst.at_end(); vfst.security_next(), n++) {
      Method* m = vfst.method();
      assert(m != NULL, "sanity");
      switch (n) {
      case 0:
        // This must only be called from Reflection.getCallerClass
        if (m->intrinsic_id() != vmIntrinsics::_getCallerClass) {
          THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), "JVM_GetCallerClass 
must only be called from Reflection.getCallerClass");
        }
        // fall-through
      case 1:
        // Frame 0 and 1 must be caller sensitive.
        if (!m->caller_sensitive()) {
          THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), 
err_msg("CallerSensitive annotation expected at frame %d", n));
        }
        break;
      default:
        if (!m->is_ignored_by_security_stack_walk()) {
          // We have reached the desired frame; return the holder class.
          return (jclass) JNIHandles::make_local(env, 
m->method_holder()->java_mirror());
        }
        break;
      }
    }

It seems to me that @CallerSensitive is completely pointless. This is ALWAYS 
going to return the first non-reflection frame after frame 1, regardless of 
@CallerSensitive. If @CallerSensitive were really supposed to have an actual 
purpose, it would seem to me that the last part should be this:

        if (!m->is_ignored_by_security_stack_walk() && !m->caller_sensitive()) {
          // We have reached the desired frame; return the holder class.
          return (jclass) JNIHandles::make_local(env, 
m->method_holder()->java_mirror());
        }

Am I completely missing the point here? I just don't see a reason for 
@CallerSensitive. The code could do the exact same thing it currently is 
without @CallerSensitive (except for enforcing that frame 1 is 
@CallerSensitive, which really isn't necessary if you aren't using it in 
further frames).

Thoughts?

Nick

On Jul 30, 2013, at 10:33 AM, Jochen Theodorou wrote:

Am 30.07.2013 16:16, schrieb Peter Levart:
On 07/30/2013 03:19 PM, Jochen Theodorou wrote:
Am 30.07.2013 14:17, schrieb Peter Levart:
[...]
So what would give Groovy or other language runtimes headaches when all
there was was a parameter-less getCallerClass() API? Aren't the
intermediate frames inserted by those runtimes controlled by the
runtimes? Couldn't the "surface" runtime-inserted methods capture the
caller and pass it down? I guess the problem is supporting calling the
caller-sensitive methods like Class.forName(String) and such which don't
have the overloaded variant taking caller Class or ClassLoader as an
argument...
Speaking for Groovy...
those intermediate frames are runtime controlled, yes, but passing down
the caller class is exactly the problem. Imagine I would suggest that
each and every method definition in Java automatically gets an
additional parameter for the caller class, just to have access to it
inside the method. You would not accept that for Java, would you? And so
we cannot accept that for Groovy if we want to keep integration with
Java...
Are you talking about internal Groovy implementation (the
runtime-inserted methods) or the publicly visible API?
that's the problem, it is a mix, some internal, other not. We are going to 
change that in Groovy 3

One solution for
internal implementation of Groovy could be (speaking by heart since I
don't know the internals of Groovy) for the "surface" public API method
which doesn't have to have the special caller parameter, to capture the
caller with getCallerClass() parameterless API (possibly enclosed with a
quick check confirming that it might actually be needed) and bind it to
a ThreadLocal, then use this ThreadLocal down at the end...
confirming that it might actually be needed is a problem. In the old fallback 
path we don't know what we call until after we are deep in runtime code, and 
there it is too late. In the other paths we could mark those methods in a 
@CallerSensitive style and do it in that case only.

and the good integration with Java is one of the key points of
Groovy. Even if we make something like that @CallerSensitive and add the
parameter only in those cases, we break being able to override methods.
I guess I don't know every Groovy need to obtain the caller class. I
thought the problem was to support calling caller-sensitive methods in
Java API (like Class.forName(String)) from within Groovy code, where
there are runtime-inserted frames between the "call-site" and the target
method. Are there any other needs?
ok, there is a misunderstanding...

if we call a Java implemented method from Groovy, which is using 
getCallerClass() it may or may not work. In general this does not work and our 
problem is not about that at all. With the change to let getCallerClass() 
ignore some reflective frames it will work actually better as long as we use 
our custom callsite caching implementation, it will not work if indy is used or 
the fallback path.

To be able to call a method Class#forName(String), we need to "replace" it with 
an implementation of our own, which we do with an approach similar to extension methods 
(only that ours can hide existing implementation methods for groovy). And in there we 
need to get to the caller class

Our problem though is @Grab which is an annotation to add elements to the 
classpath while running a script.

Plus, before Groovy3 is not done we have to support several call paths.
And the oldest one, which is still a fallback, does not support
transporting the caller class through the runtime layers at all.
Changing here is a breaking change.
Could you describe those call-paths? Examples of Groovy code and to what
it gets translated (equivalent Java code at call site) with a brief
description of what each intermediate layer (between the call-site and
the target method) does and at which point the caller class is extracted...
the code generated at the call site depends on several factors actually... The 
call site code itself is usually not very informative

I start with Groovy 1.0, since that is basically the fallback path. Here 
this.foo() translates more or less to
ScriptBytecodeAdapter.invokeMethod0(staticCallerClass, this,"foo")
which basically does this.getMetaClass().invokeMethod(staticCallerClass, this, 
"foo"). The problem is that the meta class might be user supplied and the code 
executed in invokeMethod as well. The invocation is then finally done by reflection. That 
means we have frames from ScriptBytecodeAdapter, from the meta class, as well as maybe 
frames from a custom meta class and reflection frames. At the level of 
ScriptBytecodeAdapter there is a means of transporting the caller class, but that is the 
static one. Once there is a subclass, this information is different from what is needed 
here and it cannot simply be exchanged. Even if the bytecode adapter is changed, we 
cannot change the public API for MetaClass#invokeMethod now. And then the information 
would be lost.

In later versions of Groovy (since 1.6) we introduced a custom call site caching technique, which 
uses runtime generated classes to create a helper class per call site and is then used for 
invocation. At the callsite we basically have something like callsiteArray[i].invoke(..). Here 
again the staticCallerClass can be found. In this version we are able to "get" the method 
we want to invoke, before invoking it (bypassing MetaClass#invokeMethod). But to be able to get the 
method, certain conditions have to be met (like no user supplied meta class). If they are not met, 
then we do basically the same path as in 1.0, only that we don't use ScriptBytecodeAdapter. Instead 
We use our CallSite class as entrance point, which then makes the call to the meta class. In the 
"efficent" case we have now frames from the callsite handling code between the callsite 
and the target method only. This includes reflection in the first instantiation, later the 
generated class is used so it reduces to two frame
s of which one is the Callsite entrance point, the other a frame form the 
generated method. In the fallback case we have frames from the callsite 
handling code, plus meta class code, plus reflection of course. Again the 
fallback case prevents us from transporting the caller information to the 
target method. If we ignore the fallback case, then we could here maybe use the 
Threadlocal information. It will require a new callsite interface for the 
bytecode though, meaning this code will not work for precompiled grovvy of 
older version, excluding from getting into Groovy 2.1.x, since it would be a 
breaking change. The earliest version for that would be Groovy 2.2.0, which is 
almost in RC now. Effectively it would mean we would have to do a 2.3.0 very 
soon after most probably.

In Groovy 2 we added an indy implementation, which replaces the callsite caching code.  
At the callsite we have here basically invokedynamic "foo" with 
IndyInterface#bootstrap. bootstrap will first introduce a target for 
IndyInterface#selectMethod, since I need the runtime types instead of the static ones. 
The static caller class information is here part of the bootstrap method as Lookup 
object, added by invokedynamic itself. After selectMethod is done we have an initial 
invocation using invokeExact and later invocations by the handle stored in the callsite. 
Of course the same conditions as for the callsite caching above have to be met, meaning 
the fallback path might appear. That makes initially one IndyInterface frame, then 
invokedynamic and lambda related frames, then optionally the traget method, or in the 
fallback case the meta class frames plus reflection


bye Jochen

--
Jochen "blackdrag" Theodorou - Groovy Project Tech Lead
blog:http://blackdragsview.blogspot.com/
german groovy discussion newsgroup: de.comp.lang.misc
For Groovy programming sources visithttp://groovy-lang.org




_______________________________________________
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev

Reply via email to