On Sep 9, 2013, at 12:57 PM, Peter Levart wrote:

> 
> On 09/09/2013 07:02 PM, David Chase wrote:
>> Take this lightly informed suggestion with a grain of salt, but why not, for 
>> purposes of performance and security,
>> change the logging-specific getCallerClass methods so that their "class" 
>> references are instead wrapped in some sort of proxy object that only 
>> forwards certain operations quickly without a security check?  For example, 
>> equals, hashcode, and toString are probably not security-sensitive.
>> 
>> i.e.
>> class SafeClass {
>>  private final Class clazz;
>>  public SafeClass(Class clazz) { this.clazz = class; }
>>  public String toString() { return clazz.toString(); }
>>  public int hashCode() { return clazz.hashCode(); }
>>  public boolean equals(Object o) { return clazz.equals(o); }
>>  public Class maybeWeLetYouLookAtTheRealClass() { ... a bunch of security 
>> checks ... }
>> }
>> 
>> If necessary, do the same for classloaders.
>> And them, no security checks needed, as long as the "safe" methods are 
>> enough to get the job done.
> 
> Hi,
> 
> This is a good idea. It got me thinking that there are a bunch of methods in 
> j.l.Class that are not security-sensitive. So instead of proxy-ing those in a 
> wrapper SafeClass, let's just identify "unsafe" methods 1st. If the security 
> checks that are planned for obtaining j.l.Class instances from call-stack are 
> transferred to those unsafe methods instead, then holding a reference to a 
> j.l.Class instance becomes a security-nonissue.

Yes! This is what I have been saying. If there are unsafe methods in Class<?> 
not protected from restricted access, then security checks need to be added to 
*those* methods. Otherwise, once malicious code gets a Class<?> all bets are 
off. Don't restrict access to the Class<?>, just to its unsafe methods 
(instantiation, invoking Constructors, invoking Methods, etc.).

> Just a quick example - presumably the "unsafe" methods of j.l.Class are: 
> get(Declared)Method(s), get(Declared)Field(s) and get(Declared)Constructor(s)

I would actually argue that those aren't unsafe. The *invoke* methods on 
Constructors/Methods are what are unsafe.

> , because they enable you to call/access public methods/fields/constructors 
> of a class represented by j.l.Class object. If this class is from a 
> restricted package (say sun.misc) then you could get access to restricted 
> methods/fields/instances. Now if the security checks for obtaining reflective 
> object would include checking the "class visibility/restrictability", then 
> j.l.Class object of say sun.misc.Unsafe class would not represent any 
> security issue. It's just about ******delaying******* the security check. 
> What do you think?

(asterisks mine) Yes. Please. Let's do this. :-)

> Are there any other security-sensitive j.l.Class methods? Are there any 
> public methods in JDK that take j.l.Class instances and delegate to internal 
> logic assuming that the caller can only pass-in security-pre-checked 
> j.l.Class instances?

A legitimate concern that would need to be researched to identify if this will 
be a problem.

> Regards, Peter
> 
>> David
>> 
>> On 2013-09-09, at 10:54 AM, Mandy Chung <mandy.ch...@oracle.com> wrote:
>> 
>>> Nick,
>>> 
>>> Do you have any information to some of the questions I asked below that can 
>>> help the API discussion?
>>> 
>>> We need to decide on the permission check and also the hotspot change has 
>>> to be integrated and the jdk change will need to wait until it shows in a 
>>> promoted build.  Schedule-wise, to make JDK 8, we should finalize the API 
>>> this week so that you can update the patch soon.
>>> 
>>> Mandy
>>> 
>>> On 9/3/2013 5:02 PM, Mandy Chung wrote:
>>>> Nick,
>>>> 
>>>> I skimmed through the changes.  Congratulations for your first patch 
>>>> making changes in both hotspot and jdk code.
>>>> 
>>>> In my mind, the Log4J use case accessing Class instance to obtain 
>>>> additional information for diagnosability is different than the use case 
>>>> of obtaining the caller's class / loader to lookup resources.  While the 
>>>> new APIs might be in the same class, I will discuss them individually and 
>>>> keep us focus one at a time.
>>>> 
>>>> Ralph has pointed out [1] that Log4j also needs the ability to find an 
>>>> appropriate ClassLoader which is used for logging separation (thank you 
>>>> Ralph). That will be the caller-sensitivity (i.e. obtain caller's 
>>>> class/loader) discussion.
>>>> 
>>>> There are a couple of RFEs:
>>>> https://bugs.openjdk.java.net/browse/JDK-4942697
>>>> https://bugs.openjdk.java.net/browse/JDK-6349551
>>>> 
>>>> Performance is critical for Log4j to traverse the stack trace and obtain 
>>>> Class information.  I like your patch to speed up the generation of 
>>>> StackTraceElement[] (and StackTraceFrame[] - essentially same code with 
>>>> different type). java.util.logging.LogRecord has workaround the 
>>>> performance overhead and go to a specific frame directly and avoid the 
>>>> cost of generating the entire array. JDK-6349551 requests to lazily 
>>>> instantiate the StackTraceElement object unless that frame is requested.  
>>>> Does Log4J always walk all frames and log all information?  Do you just 
>>>> log only some max number of frames rather than the entire stack trace?
>>>> 
>>>> Class<?> getDeclaringClass() method is the key method you need to enhance 
>>>> the diagnosability.  This method opens up a new way to access a Class 
>>>> instance that untrusted code wouldn't be able in the past.  A permission 
>>>> check is needed as Alan points out early.  Performance as well as logging 
>>>> framework can't access all class loaders are two factors to be considered 
>>>> when defining the permission check.
>>>> 
>>>> I saw your other comment about permission check (cut-n-paste below).  It 
>>>> seems to me that you don't agree a permission check is needed for the 
>>>> getDeclaringClass() method (regardless of which class it belongs to) and 
>>>> if that's the case, no point to continue.
>>>> 
>>>> I want to make it very clear that I have agreed to take this on and 
>>>> provide a replacement of sun.reflect.Reflection.getCallerClass(int) in JDK 
>>>> 8 to address the use cases.  It will take time for the API and security 
>>>> discussion and please be prepared for that (also I am working on other 
>>>> things at the same time).
>>>> 
>>>> The second comment on your patch is that there are lot of duplicated code 
>>>> in hotspot in order to support two similar but different types 
>>>> (StackTraceFrame and StackTraceElement). Serialization is the reason 
>>>> leading you to have a new StackTraceFrame class.  Maybe some refactoring 
>>>> can help but this is the cost of having the VM directly creating the 
>>>> instance.  One other option, as suggested in the previous thread, is to 
>>>> keep the declaring class in the StackTraceElement as a transient field.  
>>>> If we add the getDeclaringClass method in the StackTraceElement class, it 
>>>> would need to specify to be optional that it only returns the Class 
>>>> instance when it's available.
>>>> 
>>>> There are currently three different ways to get a stack trace:
>>>> 1. Throwable.getStackTrace()
>>>> 2. Thread.getStackTrace() and Thread.getAllStackTraces()
>>>> 3. java.lang.management.ThreadMXBean.getThreadInfo(long id, int 
>>>> maxDepth).getStackTrace() and multiple thread IDs version
>>>> (a) local (b) remote
>>>> 
>>>> Since it's a new StackTraceFrame class, you have to provide a new method 
>>>> replacing #1 and #2.  I don't see any need to provide a new API equivalent 
>>>> to Thread.getAllStackTraces() and java.lang.management.
>>>> 
>>>> The proposal I originally have last week was to keep declaring class as 
>>>> transient and add a method in StackTraceElement with a permission check at 
>>>> every call. Tom raises a good question about the cost of permission check. 
>>>>  Would that be a concern to Log4j? Is log4j on bootclasspath or extension 
>>>> directory?  I assume not. So for log4j to work with security manager 
>>>> installed, it would have torequire the application to grant certain 
>>>> permissions - can you confirm?  For example it calls 
>>>> sun.reflect.Reflection.getCallerClass method that will require 
>>>> RuntimePermission("accessClassInPackage.sun.reflect") permission. Calling 
>>>> Class.getProtectionDomain and Class.getClassLoader() requires 
>>>> RuntimePermission("getProtectionDomain") and
>>>> RuntimePermission("getClassLoader") respectively.  That gives me an 
>>>> impression that permission check on individual stack frame might be a 
>>>> non-issue?
>>>> 
>>>> Mandy
>>>> [1] 
>>>> http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-September/020525.html
>>>> 
>>>> On 9/3/13 5:24 AM, Nick Williams wrote:
>>>>> On Sep 3, 2013, at 4:07 AM, Alan Bateman wrote:
>>>>>>> I'm not voicing any objection to any kind of security/permissions 
>>>>>>> checks in these methods. Before I can pass judgement one way or 
>>>>>>> another, I'd want to know 1) specifically what type of permissions 
>>>>>>> check you are looking for, and 2) what you're looking to achieve with 
>>>>>>> said permissions check.
>>>>>> I would say this is TBD and start by asking the question as to whether 
>>>>>> there are concerns about leaking reference to Class objects that 
>>>>>> untrusted code wouldn't normally be able to get a reference to. Tom 
>>>>>> brings up the cost of the permission check and also whether any API 
>>>>>> should be tied to class loader. There are clearly discussion points here 
>>>>>> that could potentially influence the API.
>>>>> As I have said before, there are MANY ways to get a Class object that 
>>>>> aren't security checked. It's when you try to USE that class object to 
>>>>> impersonate it or invoke methods that security checks begin to happen. As 
>>>>> they should!
>>>>> 
>>>>> Nick
>>>> On 9/1/13 1:16 AM, Nick Williams wrote:
>>>>> I have completed and am proposing a patch for replacing 
>>>>> sun.reflect.Reflection#getCallerClass(...) with a public API in Java 8. I 
>>>>> saw no point in duplicating an issue, even though it's over 10 years old, 
>>>>> so I'm indicating that this is a patch for 4851444 
>>>>> (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4851444).
>>>>> 
>>>>> I welcome and solicit support/+1s and a sponsor. Someone about a month 
>>>>> ago had mentioned that they would be willing to be a sponsor if the patch 
>>>>> looked good, but I can't remember who it was and I can't find the email. 
>>>>> I want to say it was someone with RedHat, but my memory could be faulty, 
>>>>> so please don't hold it against me if I'm wrong.
>>>>> 
>>>>> *Summary of Changes*
>>>>> Added the new class java.lang.StackTraceFrame. It's a virtual mirror of 
>>>>> StackTraceElement, except that it contains a Class<?> declaringClass 
>>>>> property instead of a String className property. Since the list members 
>>>>> expressed reluctance to add methods to Thread and Throwable, 
>>>>> StackTraceFrame also contains several static methods for retrieving 
>>>>> Classes and StackTraceFrames from the stack. These methods are as follows:
>>>>> 
>>>>> Class<?> getCallerClass(): Retrieves the class of the caller of the 
>>>>> method calling getCallerClass(). This is identical to the new 
>>>>> Reflection#getCallerClass() added in Java 7u25/8.
>>>>> 
>>>>> Class<?> getCallerClass(int): Retrieves the class of the caller n frames 
>>>>> down the stack. This is identical to the old 
>>>>> Reflection#getCallerClass(int) that was deprecated in Java 7u25 and 
>>>>> removed in Java 8.
>>>>> 
>>>>> StackTraceFrame getCallerFrame(): Retrieves the StackTraceFrame of the 
>>>>> line of code that called the method calling getCallerClass(). This is 
>>>>> similar to the new Reflection#getCallerClass() added in Java 7u25/8, 
>>>>> except that it returns a StackTraceFrame.
>>>>> 
>>>>> StackTraceFrame getCallerFrame(int): Retrieves the StackTraceFrame of the 
>>>>> caller n frames down the stack. This is similar to the old 
>>>>> Reflection#getCallerClass(int), except that it returns a StackTraceFrame.
>>>>> 
>>>>> StackTraceFrame[] getCurrentStackTrace(): Functionally equivalent to 
>>>>> Thread#getStackTrace(), except that it returns an array of 
>>>>> StackTraceFrames.
>>>>> 
>>>>> StackTraceFrame[] getStackTrace(Throwable throwable): Functionally 
>>>>> equivalent to Throwable#getStackTrace(), except that it returns an array 
>>>>> of StackTraceFrames. It uses the same save point (backtrace) created when 
>>>>> the Throwable is created that Throwable#getStackTrace() uses when it's 
>>>>> first called. It caches the array of StackTraceFrames in the Throwable 
>>>>> just like the array of StackTraceElements are cached, so that multiple 
>>>>> calls for the same Throwable are fast.
>>>>> 
>>>>> As a result of this change, sun.reflect.CallerSensitive has been moved to 
>>>>> java.lang.CallerSensitive.
>>>>> 
>>>>> I spent considerable time reviewing, revising, considering, and testing 
>>>>> these changes. I included several unit tests that establish the proper 
>>>>> behavior. I also spent considerable time benchmarking the changes. While 
>>>>> benchmarking, I noticed some things. First, getCallerClass() and 
>>>>> getCallerClass(int) are as fast as their counterparts in 
>>>>> sun.reflect.Reflection, and they easily inline when appropriate. Second, 
>>>>> getCallerFrame() and getCallerFrame(int) are /almost/ as fast as the 
>>>>> Class versions, but there is some additional overhead for the 
>>>>> construction of the StackTraceFrame. This is minuscule (1,000,000 
>>>>> invocations consume around 500 ms total on my machine). At this point, 
>>>>> all of the benchmarking was as expected.
>>>>> 
>>>>> However, I then encountered a surprise. The getCurrentStackTrace() and 
>>>>> getStackTrace(Throwable) methods executed (again, 1,000,000 times) in 
>>>>> about 70% of the time that Thread#getStackTrace() and 
>>>>> Throwable#getStackTrace() did, respectively. Theoretically, they should 
>>>>> have executed in the same amount of time, not faster. After extensive 
>>>>> analysis, I discovered (what I considered to be) a serious flaw in how 
>>>>> the stack trace is filled in within Throwable (which also affects how 
>>>>> Thread#getStackTrace() works).
>>>>> 
>>>>> Instead of simply iterating over the entire save point and filling in the 
>>>>> Throwable stack trace in native code (which is what I did when I 
>>>>> implemented the StackTraceFrame methods), the Java code in Throwable 
>>>>> first called a native method to figure out how deep the stack was, then 
>>>>> called another native method once for every frame in the stack to 
>>>>> retrieve each element individually. This native method that is called 
>>>>> repeatedly iterates over the entire backtrace once for each call, 
>>>>> stopping only when it finds the matching element (so it's O(1) for the 
>>>>> first call, O(2) for the second call, O(3) for the third call, and so 
>>>>> on). While my StackTraceFrame methods were iterating over the backtrace 
>>>>> exactly 1 time (O(n)), the Throwable methods were iterating over the 
>>>>> backtrace 1+(n/2) times (worse than O(nlogn) but not as bad as O(n^2)). 
>>>>> This problem would not have been extremely apparent over small stack 
>>>>> traces (the 30% improvement was a stack trace of 6 elements), but over a 
>>>>> large (200+ elements) stack traces the performance difference would be 
>>>>> huge and noticeable. Seeing a room for improvement, I refactored the code 
>>>>> that fills in the stack trace for Throwable, improving its performance 
>>>>> accordingly to match the performance of the StackTraceFrame methods.
>>>>> 
>>>>> I'm very pleased with how this turned out, and both the unit tests and my 
>>>>> extensive functional testing show that the new class and its methods are 
>>>>> working great. I just need someone willing to review and sponsor my patch!
>>>>> 
>>>>> *The Code Changes*
>>>>> I couldn't get WebRev to run without all kinds of errors. So I ran `hg 
>>>>> diff -g` on every repository in the forest with changes. Here are the 
>>>>> four patch files for the four repositories that had changes (no other 
>>>>> repositories had changes):
>>>>> 
>>>>> https://java.nicholaswilliams.net/Java8GetCallerClass/jdk8.patch
>>>>> https://java.nicholaswilliams.net/Java8GetCallerClass/jdk8_jdk.patch
>>>>> https://java.nicholaswilliams.net/Java8GetCallerClass/jdk8_hotspot.patch
>>>>> https://java.nicholaswilliams.net/Java8GetCallerClass/jdk8_nashorn.patch
>>>>> 
>>>>> I believe I have followed all of the procedures as closely as possible. I 
>>>>> await feedback and hope for some support on this, so that we can get a 
>>>>> public replacement for this method in Java 8. Let me know if you have any 
>>>>> questions.
>>>>> 
>>>>> Thanks!
>>>>> 
>>>>> Nick
> 

Reply via email to