# Monday, 02 June 2008
« Introducing CallerID Part 2 | Main | New Development Snapshot »
Introducing CallerID Part 3

This is the last part of a three part backward running series on the design, implementation and performance results of the CallerID feature.

How To Pass Information From Caller To Callee

The obvious solution is to use a method parameter, but in this case there is a slight complication: We can't necessarily trust the caller to provide truthful information.

The first scheme I came up with was something like this:

class CallSiteExample {
  private static CallerID __callerID;

  static void method() {
    Class.forName("...", ref __callerID);
  }
}

Here CallerID would be a value type defined in IKVM.Runtime and its content (a Class and ClassLoader reference field) would only be accessible to the runtime and core class library. Inside the callee there would be a check to see if the required field in the CallerID value had been initialized and if not, it would walk up the stack to determine the caller and store the result in the CallerID value.

The obvious downside of this scheme is that it would still require a stack walk the first time a call site does a call. A less obvious downside is that this approach makes it harder to pass the CallerID to another method. This is used in a couple of places where the callee doesn't do the actual work of looking at the caller. Take for exampe java.lang.reflect.Field.get():

@ikvm.internal.HasCallerID
public Object get(Object obj)
    throws IllegalArgumentException, IllegalAccessException
{
    return getFieldAccessor(obj, CallerID.getCallerID()).get(obj);
}

Here the actual access check is in getFieldAccessor (actually, it in turn calls another method to do the security check) and I used the CallerID.getCallerID() intrinsic to pass the implicit callerID parameter explicitly to this other method. Doing this with the value type byref approach would have made this a lot harder.

Nested Types

The design sketched above is actually fairly old, but I had never implemented it due to a nagging feeling that it wasn't quite right. I think my primary concern was that it didn't actually get rid of the stack walk, it only reduced them to at most one per caller. Last week as I was helping someone with a problem that turned out to be caused by a stack walking issue on Mono running on ARM I realized that I should get rid of the stack walk altogether and came up with the trick of using a nested type to create an unforgeable token to represent a class.

Reflection

Another instance of passing around the CallerID is found in reflection. When you call a method via reflection, the immediate caller of that method will, of course, be some VM internal method or Method.invoke() depending on the exact implementation, so the stack walking mechanism that the JDK (and previous IKVM versions) use skips all stack frames related to reflection to get to the real caller. However, with the new scheme, Method.invoke() already gets passed in the CallerID, so it would be a shame not to use this and, of course, it is trivial to pass this value along to the call stub which can then (if required) pass it along to the method being called.

Why the Explicit HasCallerID Annotation?

I could have made ikvmc add the implicit CallerID parameter to every method that calls one of the intrinsics that return the caller, but this has three downsides. The first is that it would have made compiling the core class library slower, because ikvmc would have had to check every method to see if it calls one of these intrinsics (the .NET method signature gets determined way before the method body is processed). The second is that since the additional parameter is, in a sense, part of the public API it should be a conscious decision. The third reasons is that the implicit approach doesn't work with methods that indirectly look at the caller, like the Field.get() example above, so I would have needed to add a mechanism for that anyway.

Limitations

The current implementation doesn't support adding a CallerID parameter to constructors. This is simply the result of the fact that there aren't any constructors that require CallerID that I'm aware of. Another limitation is that the HasCallerID annotation is only recognized for static methods or instance methods on final classes. This means that ClassLoader.getParent() doesn't currently have CallerID support, even though it does want to look at the caller if there is a security manager installed.

Security Considerations

While it's generally not possible to inject a nested type into an arbitrary type, it may be possible to trick another (dynamic) CLR language into creating such a type. However, I think the risk of that is sufficiently low to make it acceptable.

Another risk is that someone may steal a CallerID object from another type and try to impersonate that type. Since .NET 2.0 SP1 it is actually possible for partially trusted code to use reflection to access private fields if you have the RestrictedMemberAccess permission and both assemblies have the same security grant set. However, since this requires .NET reflection (the callerID field isn't visible from the Java reflection APIs) and there already are several ways to bypass Java's security model by directly using .NET APIs this is also a non-issue.

Finally, it is not possible for untrusted Java code to use the HasCallerID annotation, because ikvmc only recognizes the HasCallerID annotation while compiling the core class library and the IKVM runtime only recognizes CallerID methods in the core class library.

Monday, 02 June 2008 08:20:41 (W. Europe Daylight Time, UTC+02:00)  #    Comments [2]