Review request for 7198429: need checked categorization of caller-sensitive methods in the JDK

Mandy Chung mandy.chung at oracle.com
Tue Apr 2 19:07:43 UTC 2013


On 4/1/13 5:24 PM, John Rose wrote:
> On Mar 27, 2013, at 10:35 AM, Mandy Chung <mandy.chung at oracle.com 
> <mailto:mandy.chung at oracle.com>> wrote:
>
>> This is the JDK change for JEP 176: JEP 176: Mechanical Checking of 
>> Caller-Sensitive Methods [1].  Christian has posted the webrev for 
>> the hotspot VM change a couple weeks ago [2].
>>
>> Webrev at:
>> http://cr.openjdk.java.net/~mchung/jdk8/webrevs/7198429/webrev.00/ 
>> <http://cr.openjdk.java.net/%7Emchung/jdk8/webrevs/7198429/webrev.00/>
>
> This is very good work.  It makes certain aspects of the system much 
> easier to understand.
>
> I have reviewed it all, with the following comments:
>
Thanks for the review.
> The transform moves caller sensitivity into a clearly visible 
> position.  In many cases, the call to Reflection.getCallerClass now 
> precedes the check of the security manager.  For some customer codes, 
> this may cause a performance regression, although it will probably be 
> in the noise in most cases.

Agree.  I have restored the cases that should find caller class lazily.

> If necessary, we can replace some uses of R.getCC() by something like 
> (S.getSecurityManager() == null ? null : R.getCC()), recovering the 
> original laziness of the stack check.  This won't be necessary 
> everywhere.  Note that the server compiler can usually remove the 
> stack walk.  We may need to put something similar into the client 
> compiler.
>
> In the case of the reflective calls (Field, Constructor, Method), the 
> check of the caller class is gated by both AccessibleObject.override 
> and quickCheckMemberAccess, and you have preserved this for 
> Constructor and Method (which are probably the most important 
> performance case).  Consider gating it also for Field, as noted below.
>
> Per-file comments:
>
> -- Class.java
>
> (See below about checking for checkMemberAccess override.)
>

Done.
> -- ClassLoader.java
>
> Did you consider using Class.cast to get a runtime check instead of 
> @SuppressWarnings("unchecked")?
>

Good point - I have modified it to do runtime check using Class.asSubclass.
> -- MethodHandleNatives.java
>
> Was this deletion intentional or was it something Eclipse or Netbeans did?
>   -import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
>
It was unintentional and I fixed it differently when I realized the 
reference of IMPL_LOOKUP.  I now have restored to the original import 
static to be consistent with other files.

> Good cleanups!
>
> You can remove this since rAPC is static (error in original code):
>          case "registerAsParallelCapable":
>              return canBeCalledVirtual(mem, java.lang.ClassLoader.class);
>

Done.

> Note that the subsequent call to canBeCalledVirtual will disqualify 
> mem because it is static.
>

Yes.
> -- MethodHandles.Lookup
>
> Now that stack walking is out of the picture, make all the 
> constructors private, and remove this scary comment:
>>          * <p>
>>          * Also, don't make it private, lest javac interpose
>>          * an access$N method.
>
Done.

> I like this comment, and the manual inlining technique (in this very 
> special case):
>   // Inlined SecurityManager.checkMemberAccess
> Since it tends to get lost in the previous comment block, I suggest 
> you put it after the open brace of the inlined body.
>
> Also, both parameters should be inlined at the top of the block, not 
> just "which", so the rest of the block can a more or less verbatim copy.
>
>   +      { // Inlined SecurityManager.checkMemberAccess
>   +        final Class<?> clazz = refc/defc;
>   +        final int which = Member.PUBLIC/PRIVATE;
>

I considered that too and made the change. I actually took out the 
redundant check (which != PUBLIC) since we know the value.

> As previously noted, smgr.getClass() == SecurityManager.class is too 
> strict.  Suggest hoisting the nasty logic into a boolean, and using it 
> twice:
>
> boolean standardCMA = (smgr.getClass() == SecurityManager.class);
> if (!standardCMA) try {
>   standardCMA = smgr.getClass().getDeclaredMethod("checkMemberAccess", 
> ...).getDeclaringClass() == SecurityManager.class;
> } catch (ReflectiveOperationException ex) { throw new InternalError(ex); }
>
> if (standardCMA) {  // // Inlined SecurityManager.checkMemberAccess
>   final Class<?> clazz = refc/defc;
>   final int which = Member.PUBLIC/PRIVATE;
>   ...
> } else {
>   smgr.checkMemberAccess(...);
> }
>

OK.  I modified a little.  For subclass case, I can simply check if 
Class.getDeclaredMethod finds the method; if exists, it's overrided; 
otherwise, NoSuchMethodException thrown indicates using the standard one.

> (And a similar comment for Class.java.)
>
Done.

> Lots of trouble for a corner case!
>
> -- Field.java
>
> Consider making R.getCC more lazy as follows:
>
> +    return getFieldAccessor(obj, needsMemberAccessCheck() ? 
> Reflection.getCallerClass() : clazz).get(obj);
>
> +  private boolean needsMemberAccessCheck() {
> +    return !override && !Reflection.quickCheckMemberAccess(clazz, 
> modifiers);
> +  }
>
> (This might affect the performance of serialization.)
>
>
> -- CallerSensitiveFinder.java
>
> This is a remarkable test.  Does it statically determine whether the 
> annotations are missing?

Yes - that's the purpose to catch if any use of 
Reflection.getCallerClass() but not missing @CS annotation.

>  Does it process all of rt.jar?  How long does it take to complete?
>

It processes all JRE classes (i.e. rt.jar, jar files in JAVA_HOME/lib 
and JAVA_HOME/lib/ext).

I have made it done the minimal necessary work only.  It takes 6 seconds 
on my MacMini (2GHz core i7 and 8GB memory) to parse 24243 classes.  It 
takes 5-14 seconds on the jprt machines and here are some sample timing:
    linux_i586 jdk_lang took 14 mins and CallerSensitiveFinder took 8.5 sec
    macosx_x64 jdk_lang took 20.5 mins and CallerSensitiveFinder took 
14.5 sec
    solaris_i586 jdk_lang took 15.5 mins and CallerSensitiveFinder took 
10 sec
    windows_x64 jdk_lang took 16.5 mins and CallerSensitiveFinder took 
10 sec

> And (I have to ask, though I'm sure the answer will be yes) have you 
> run it on broken inputs (such as 
> boot.GetCallerClass.missingCallerSensitiveAnnotation) and verified 
> that it barks at them?
>
I ran it on an older JDK that calls Reflection.getCallerClass(int) and 
no @sun.reflect.CallerSensitive exist.

The test is currently tailored-made to only parses JRE classes.  I can 
easily extend it to parse additional classes like boot.GetCallerClass 
that is a good test case to this tool itself.  Will do so.

I'll post a new webrev once I finish the testing.

thanks for the detailed review.
Mandy



More information about the core-libs-dev mailing list