On Jul 30, 2013, at 7:45 AM, Peter Levart wrote:

> I think there are various techniques to use new API when running on JDK8 and 
> still use old API when running on JDK6 or 7. One way is to have 2 classes 
> implementing the same interface or extending basic class where one is 
> compiled with JDK6/7 and the other with JDK8. During runtime the "correct" 
> implementation is choosen, but code only depends on the interface/basic class…

Now there's an interesting idea I hadn't considered. It sure wouldn't be easy, 
though. It would make it harder for projects to fix bugs related to this when 
discovered, and still slow down adoption of Java 8.

> 
> There are other tricks.
> 
> But I have an idea. What if Reflection.getCallerClass(int) is restored in 
> JDK7 and JDK8 without a @CallerSensitive annotation on it. In order to 
> prevent inadvertent internal JDK usage, the method could throw exception when 
> called from any internal JDK class…

I think this MUST happen in Java 7, because adding a public API isn't an option 
there. As for Java 8, I'm almost done with StackTraceFrame and associated C/C++ 
code, and it includes both an @CallerSensitive getCallerClass() method and a 
getCallerClass(int) method. While I would prefer your option for Java 8 over 
what we have now (obviously), the public StackTraceFrame API is a better 
approach by the mere fact of being public (and universally available on all 
JVMs).

Nick

> 
> Regards, Peter
> 
> 
> On 07/30/2013 02:32 PM, Nick Williams wrote:
>> "For caller-sensitive methods, the approach taken with new 
>> Reflection.getCallerClass() is the right one, I think. There's no need to 
>> support a fragile API when caller-sensitivity is concerned, so the lack of 
>> "int" parameter, combined with annotation for marking such methods is 
>> correct approach, I think."
>> 
>> Not when the code running on Java 8 was compiled on Java 6 or 7 and thus 
>> can't be annotated @CallerSensitive. In these cases, use of any new public 
>> API must use reflection (sudo code: If Java 8, use new public 
>> caller-sensitive API), so it needs code that it can pass a number into.
>> 
>> Nick
>> 
>> On Jul 30, 2013, at 7:17 AM, Peter Levart wrote:
>> 
>>> 
>>> On 07/27/2013 09:01 PM, Nick Williams wrote:
>>>> All,
>>>> 
>>>> In the last two months, there have been a number of discussions 
>>>> surrounding stack traces, Classes on the stack trace, and caller classes 
>>>> [1], [2], [3]. These are all related discussions and the solution to them 
>>>> is equally related, so I wanted to consolidate it all into this one 
>>>> discussion where I hope we can finalize on a solution and get it 
>>>> implemented for Java 8.
>>>> 
>>>> In a nut shell, here are the underlying needs that I have seen expressed 
>>>> through many, many messages:
>>>> 
>>>> - Some code needs to get the Class of the caller of the current method, 
>>>> skipping any reflection methods.
>>>> - Some code needs to get the Class of the caller /n/ stack frames before 
>>>> the current method, skipping any reflection methods.
>>>> - Some code needs to get the current stack trace, populated with Classes, 
>>>> Executables, file names, line numbers, and native flags instead of the 
>>>> String class names and String method names in StackTraceElement. This 
>>>> /should/ include any reflection methods, just like StackTraceElement[]s.
>>>> - Some code needs to get the stack trace from when a Throwable was 
>>>> created, populated with Classes, Executables, file names, line numbers, 
>>>> and native flags instead of the String class names and String method names 
>>>> in StackTraceElement. This /should/ include any reflection methods, just 
>>>> like StackTraceElement[]s.
>>>> - There needs to be a reflection way to achieve all of this since some 
>>>> libraries (e.g., Log4j) need to be compiled against Java 6 but run on 7 
>>>> and 8 (and thus can't use @CallerSensitive).
>>>> 
>>>> I believe the solutions to these needs are all related. Importantly, I 
>>>> think it is very important that action be taken in Java 8 due to the 
>>>> changes made to sun.reflect.Reflection#getCallerClass(...). While we all 
>>>> understand that relying on private sun.* APIs is not safe, the fact is 
>>>> that many people have relied on sun.reflect.Reflection#getCallerClass(...) 
>>>> due to the fact that there is simply no other way to do this in the 
>>>> standard API. This includes Log4j 2, Logback, SLF4j, and Groovy, some 
>>>> features of which will stop working correctly in Java 7 >= u25.
>>> 
>>> Hi,
>>> 
>>> The needs described above may seem related, but from what I see in this 
>>> commit:
>>> 
>>>     http://hg.openjdk.java.net/jdk8/tl/jdk/rev/da6addef956e
>>> 
>>> my observations are as following (please comment if I missed or 
>>> misunderstood anything):
>>> 
>>> sun.reflect.Reflection.getCallerClass(int) is/was used internally in JDK 
>>> more or less for different purposes than outside the JDK. Inside it was 
>>> used basically for implementing security-sensitive checks like 
>>> optimizations for public methods which can avoid calling SecurityManager 
>>> API wen called from withing JDK classes. It was used for security-unrelated 
>>> purposes too, like for example Class.forName(String) or 
>>> ResourceBundle.getBundle(String). All internal JDK uses do share one common 
>>> thing though: it is very important that the right direct caller of a 
>>> caller-sensitive method is established, since any failure to do so can have 
>>> devastating effect on security or correctness.
>>> 
>>> The API taking an "int" to count the frames between the top of the 
>>> call-stack to the indirect caller was convenient, but too fragile to 
>>> support such important use cases. Every time some code was refactored, 
>>> there was danger that some call-frame was inadvertently inserted or 
>>> removed. So I think it was decided to "cripple" the API to only support 
>>> obtaining the immediate caller of the method making call to the 
>>> Reflection.getCallerClass() and all uses modified accordingly to make the 
>>> internal JDK code more robust to refactorings.
>>> 
>>> And there's also MethodHandles which are early-bound. Meaning that the 
>>> caller is established and bound when the MethodHandle instance is 
>>> looked-up. The "lookupClass" which is associated with the Lookup object and 
>>> used in permission checks when obtaining MHs is also used as the bound 
>>> caller class when the MH is invoked. Now there's a method:
>>> 
>>> java.lang.invoke.MethodHandles.Lookup {
>>>     public java.lang.invoke.MethodHandles.Lookup in(java.lang.Class<?> 
>>> requestedLookupClass)
>>> 
>>> that returns a Lookup object which reports a different "lookupClass" and 
>>> has capabilities to lookup MHs which are combined from capabilities of the 
>>> original Lookup object and new lookupClass (tipicaly less, never more). 
>>> Most of such Lookup objects are prevented from looking-up MHs for 
>>> caller-sensitive methods, since they could be used to "pose" as a caller 
>>> that is not the one having obtained the MH and therefore gain access to 
>>> restricted resource, for example:
>>> 
>>> MethodHandle mh = MethodHandles.lookup().in(Object.class).lookupXXX(....)
>>> 
>>> ...such mh could be used to pose as being called from withing Object if 
>>> allowed to be obtained for caller-sensitive methods. So here comes 
>>> @CallerSensitive annotation to mark such methods and prevent such lookups 
>>> (before that - in JDK7, all internal caller-sensitive methods were 
>>> hand-maintained in a list).
>>> 
>>> So this is, what I think, the background and rationale for changing the API.
>>> 
>>> For outside JDK use, I think there are two main needs, which are actually 
>>> distinct:
>>> 
>>> a) the caller-sensitive methods
>>> b) anything else that is not caller-sensitive, but wants to fiddle with the 
>>> call-stack
>>> 
>>> For caller-sensitive methods, the approach taken with new 
>>> Reflection.getCallerClass() is the right one, I think. There's no need to 
>>> support a fragile API when caller-sensitivity is concerned, so the lack of 
>>> "int" parameter, combined with annotation for marking such methods is 
>>> correct approach, I think. The refactorings to support this change in JDK 
>>> show that this API is adequate. The "surface" public API methods must 
>>> capture the caller class and pass it down the internal API where it can be 
>>> used.
>>> 
>>> 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...
>>> 
>>> John Rose suggested to "capture" the caller in the "surface" method and 
>>> bind it with a MethodHandle and then pass such MH down the runtime API and 
>>> finally call that method via MH. For that to work, two problems would have 
>>> to be resolved first:
>>> 
>>> 1) the runtime-inserted "surface" method would have to be annotated with 
>>> @CallerSensitive so that illegal "posers" could be prevented
>>> 2) this "surface" method would have to be given permission to "pose as" the 
>>> caller class when looking-up the MethodHandle of the target 
>>> caller-sensitive method.
>>> 
>>> The 1st part is not a problem I think, but the 2nd part is a problem. What 
>>> makes the runtime-inserted "surface" method so special that it can be 
>>> allowed to "pose" as its caller?
>>> 
>>> Now that is the question for mlvm-dev mailing list: Isn't preventing almost 
>>> all Lookup objects obtained by Lookup.in(RequestedLookupClass.class) from 
>>> obtaining MHs of @CallerSensitive methods too restrictive? 
>>> 
>>> Currently classes are only allowed to "pose as" it's nest-mate classes - 
>>> the classes that share the same outermost enclosing class, since the check 
>>> to look-up @CallerSensitive methods is based on the ability to look-up 
>>> PRIVATE members. So the ability to "pose as" another class when binding 
>>> caller already exists even for @CallerSensitive methods, just the 
>>> restriction is too conservative, isn't it?
>>> 
>>> Perhaps a class that is visible from the calling class could be allowed to 
>>> look-up MHs of @CallerSensitive methods and "pose" as the calling class, 
>>> bearing all other security checks for combined abilities have passed. For 
>>> example, why wouldn't class A be allowed to "pose as" class B if they are 
>>> loaded by the same ClassLoader or if class B is loaded by a ClassLoader 
>>> that directly or indirectly delegates to the ClassLoader of class A?
>>> 
>>> These are my thoughts about caller-sensitivity and why I think it requires 
>>> special restricted API. Anything else that needs to examine the whole 
>>> call-stack is a separate need that is not infected by the strict 
>>> constraints of caller-sensitivity and for that purpose an API like the one 
>>> presented below (StackTraceFrame) is a good starting-point, maybe it just 
>>> doesn't need the static getCallerFrame() method which suggests that it's 
>>> use is for implementing caller-sensitive methods.
>>> 
>>> Regards, Peter
>>> 
>>>> I would point out that this could all easily be solved simply by adding a 
>>>> getElementClass() method to StackTraceElement, but there was strong 
>>>> opposition to this, largely due to serialization issues. Since that is 
>>>> apparently not an option, I propose the following API, based on the 
>>>> various discussions in the last two months, StackTraceElement, and the API 
>>>> that .NET provides to achieve the same needs as listed above:
>>>> 
>>>> CallerSensitive.java:
>>>> package java.lang;
>>>> 
>>>> /** Previously private API, now public */
>>>> public @interface CallerSensitive {
>>>>     ...
>>>> }
>>>> 
>>>> StackTraceFrame.java:
>>>> package java.lang;
>>>> 
>>>> import java.util.Objects.
>>>> 
>>>> public final class StackTraceFrame {
>>>>     private final Class<?> declaringClass;
>>>>     private final Executable executable;
>>>>     private final String fileName;
>>>>     private final int lineNumber;
>>>> 
>>>>     public StackTraceFrame(Class<?> declaringClass, Executable executable, 
>>>> String fileName, int lineNumber) {
>>>>         this.declaringClass = Objects.requireNonNull(declaringClass, 
>>>> "Declaring class is null");
>>>>         this.executable = Objects.requireNonNull(executable, "Executable 
>>>> is null");
>>>>         this.fileName = fileName;
>>>>         this.lineNumber = lineNumber;
>>>>     }
>>>> 
>>>>     public Class<?> getDeclaringClass() {
>>>>         return this.declaringClass;
>>>>     }
>>>> 
>>>>     public Executable getExecutable() {
>>>>         return this.executable;
>>>>     }
>>>> 
>>>>     public String getFileName() {
>>>>         return this.fileName;
>>>>     }
>>>> 
>>>>     public int getLineNumber() {
>>>>         return this.lineNumber;
>>>>     }
>>>> 
>>>>     public boolean isNative() {
>>>>         return this.lineNumber == -2;
>>>>     }
>>>> 
>>>>     public String toString() { /* Same as StackTraceElement */ }
>>>>     public boolean equals() { /* Ditto */ }
>>>>     public int hashCode() { /* Ditto */ }
>>>> 
>>>>     /** Uses @CallerSensitive */
>>>>     public static native StackTraceFrame getCallerFrame();
>>>> 
>>>>     /** Works like Java < 7u25 sun.reflect.Reflection#getCallerClass() */
>>>>     public static native StackTraceFrame getCallerFrame(int skipFrames);
>>>> 
>>>>     public static native StackTraceFrame[] getCurrentStackTrace();
>>>> }
>>>> 
>>>> Throwable.java:
>>>> package java.lang;
>>>> 
>>>> ...
>>>> 
>>>> public class Throwable {
>>>>     ...
>>>>     public synchronized Throwable fillInStackTraceFrames() { ... }
>>>> 
>>>>     private native Throwable fillInStackTraceFrames(int dummy);
>>>> 
>>>>     public StackTraceFrame[] getStackTraceFrames() {
>>>>         return this.getOurStackTraceFrames().clone();
>>>>     }
>>>> 
>>>>     private synchronized StackTraceFrame[] getOurStackTraceFrames() { ... }
>>>>     ...
>>>> }
>>>> 
>>>> Furthermore, I propose that we restore the behavior of 
>>>> sun.reflect.Reflection#getCallerClass(int) /just for Java 7/ since the 
>>>> proposed above solution cannot be added to Java 7.
>>>> 
>>>> I would love if we could quickly coalesce around this solution or a 
>>>> derivative thereof so that it can be implemented before Feature Complete. 
>>>> The absence of any replacement or alternative for 
>>>> sun.reflect.Reflection#getCallerClass(int) will be a serious issue in Java 
>>>> 8 that will cause hardships for many projects.
>>>> 
>>>> [1] 
>>>> http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-June/018049.html
>>>> [2] 
>>>> http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-June/018349.html,
>>>>  http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-July/019098.html
>>>> [3] 
>>>> http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-July/018855.html
>>> 
>> 
> 

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

Reply via email to