Review request for 7198429: need checked categorization of caller-sensitive methods in the JDK
Peter Levart
peter.levart at gmail.com
Tue Apr 2 22:00:57 UTC 2013
On 04/02/2013 09:07 PM, Mandy Chung wrote:
> 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.
Hi Mandy,
There could be:
public class SM1 extends SecurityManager {
@Override
public void checkMemberAccess(Class<?> clazz, int which) {...
and:
public class SM2 extends SM1 { ... // no checkMemberAccess override
now if if you take SM2.class.getDeclaredMethod("checkMemberAccess", ...)
it will throw NoSuchMethodException, but the method is overriden in
SM1. So I think it's better to use what John suggested (although not
using getDeclaredMethod, but getMethod instead):
smgr.getClass().getMethod("checkMemberAccess", ...).getDeclaringClass()
== SecurityManager.class
Regards, Peter
>
>> (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