This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
Re: Preliminary patch: New stack trace infrastructure
- From: Casey Marshall <csm at gnu dot org>
- To: Bryce McKinlay <mckinlay at redhat dot com>
- Cc: Java Patches <java-patches at gcc dot gnu dot org>
- Date: Sat, 27 Nov 2004 14:06:33 -0800
- Subject: Re: Preliminary patch: New stack trace infrastructure
- References: <41A6D134.9080908@redhat.com>
>>>>> "Bryce" == Bryce McKinlay <mckinlay@redhat.com> writes:
Bryce> There is still some cleanup work to be done and missing pieces
Bryce> to be filled in (most noteably, AccessController), but here is
Bryce> a preliminary snapshot of the new stack trace infrastructure
Bryce> code.
Attached is a simple implementation of VMAccessController using this;
which is mostly just a straightforward merge from Classpath. This
patch doesn't change Makefile.am, but that just adds a few files.
--
Casey Marshall || csm@gnu.org
--- stacktrace.cc.orig 2004-11-27 11:26:21.821144656 -0800
+++ stacktrace.cc 2004-11-27 00:31:40.000000000 -0800
@@ -351,3 +351,45 @@
return (JArray<StackTraceElement *>*) list->toArray (array);
}
+
+JArray<jobjectArray> *
+_Jv_StackTrace::GetClassMethodStack (_Jv_StackTrace *trace)
+{
+ jint length = 0;
+
+ UpdateNCodeMap();
+ for (int i = 0; i < trace->length; i++)
+ {
+ _Jv_StackFrame *frame = &trace->frames[i];
+ FillInFrameInfo (frame);
+
+ if (frame->klass && frame->meth)
+ length++;
+ }
+
+ jclass array_class = _Jv_GetArrayClass (&::java::lang::Object::class$, NULL);
+ JArray<jobjectArray> *result =
+ (JArray<jobjectArray> *) _Jv_NewObjectArray (2, array_class, NULL);
+ JArray<jclass> *classes = (JArray<jclass> *)
+ _Jv_NewObjectArray (length, &::java::lang::Class::class$, NULL);
+ JArray<jstring> *methods = (JArray<jstring> *)
+ _Jv_NewObjectArray (length, &::java::lang::String::class$, NULL);
+ jclass *c = elements (classes);
+ jstring *m = elements (methods);
+
+ for (int i = 0, j = 0; i < trace->length; i++)
+ {
+ _Jv_StackFrame *frame = &trace->frames[i];
+ if (!frame->klass || !frame->meth)
+ continue;
+ c[j] = frame->klass;
+ m[j] = JvNewStringUTF (frame->meth->name->chars());
+ j++;
+ }
+
+ jobjectArray *elems = elements (result);
+ elems[0] = (jobjectArray) classes;
+ elems[1] = (jobjectArray) methods;
+
+ return result;
+}
Index: include/java-stack.h
===================================================================
RCS file: /cvsroot/gcc/gcc/libjava/include/java-stack.h,v
retrieving revision 1.1
diff -u -r1.1 java-stack.h
--- include/java-stack.h 25 Nov 2004 03:47:00 -0000 1.1
+++ include/java-stack.h 27 Nov 2004 19:33:03 -0000
@@ -79,6 +79,7 @@
GetStackTraceElements (_Jv_StackTrace *trace,
java::lang::Throwable *throwable);
static jclass GetCallingClass (void);
+ static JArray<jobjectArray> *GetClassMethodStack (_Jv_StackTrace *trace);
};
#endif /* __JV_STACKTRACE_H__ */
--- /dev/null 1969-12-31 16:00:00.000000000 -0800
+++ java/security/VMAccessController.java 2004-11-27 11:32:59.595673736 -0800
@@ -0,0 +1,265 @@
+/* VMAccessController.java -- VM-specific access controller methods.
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.security;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The VM interface to the access control methods. This implementation is
+ * for libgcj; it is basically unchanged from the reference implementation,
+ * except getStack() is marked native, and implemented in C++.
+ */
+final class VMAccessController
+{
+
+ // Fields.
+ // -------------------------------------------------------------------------
+
+ /**
+ * This is a per-thread stack of AccessControlContext objects (which can
+ * be null) for each call to AccessController.doPrivileged in each thread's
+ * call stack. We use this to remember which context object corresponds to
+ * which call.
+ */
+ private static final ThreadLocal contexts = new ThreadLocal();
+
+ /**
+ * This is a Boolean that, if set, tells getContext that it has already
+ * been called once, allowing us to handle recursive permission checks
+ * caused by methods getContext calls.
+ */
+ private static final ThreadLocal inGetContext = new ThreadLocal();
+
+ /**
+ * And we return this all-permissive context to ensure that privileged
+ * methods called from getContext succeed.
+ */
+ private final static AccessControlContext DEFAULT_CONTEXT;
+ static
+ {
+ CodeSource source = new CodeSource(null, null);
+ Permissions permissions = new Permissions();
+ permissions.add(new AllPermission());
+ ProtectionDomain[] domain = new ProtectionDomain[] {
+ new ProtectionDomain(source, permissions)
+ };
+ DEFAULT_CONTEXT = new AccessControlContext(domain);
+ }
+
+ private static final boolean DEBUG = false;
+ private static void debug(String msg)
+ {
+ System.err.print(">>> VMAccessController: ");
+ System.err.println(msg);
+ }
+
+ // Constructors.
+ // -------------------------------------------------------------------------
+
+ private VMAccessController() { }
+
+ // Class methods.
+ // -------------------------------------------------------------------------
+
+ /**
+ * Relate a class (which should be an instance of {@link PrivilegedAction}
+ * with an access control context. This method is used by {@link
+ * AccessController#doPrivileged(java.security.PrivilegedAction,java.security.AccessControlContext)}
+ * to set up the context that will be returned by {@link #getContext()}.
+ * This method relates the class to the current thread, so contexts
+ * pushed from one thread will not be available to another.
+ *
+ * @param acc The access control context.
+ */
+ static void pushContext (AccessControlContext acc)
+ {
+ if (DEBUG)
+ debug("pushing " + acc);
+ LinkedList stack = (LinkedList) contexts.get();
+ if (stack == null)
+ {
+ stack = new LinkedList();
+ contexts.set(stack);
+ }
+ stack.addFirst(acc);
+ }
+
+ /**
+ * Removes the relation of a class to an {@link AccessControlContext}.
+ * This method is used by {@link AccessController} when exiting from a
+ * call to {@link
+ * AccessController#doPrivileged(java.security.PrivilegedAction,java.security.AccessControlContext)}.
+ */
+ static void popContext()
+ {
+ if (DEBUG)
+ debug("popping context");
+
+ // Stack should never be null, nor should it be empty, if this method
+ // and its counterpart has been called properly.
+ LinkedList stack = (LinkedList) contexts.get();
+ if (stack != null)
+ {
+ stack.removeFirst();
+ if (stack.isEmpty())
+ contexts.set(null);
+ }
+ }
+
+ /**
+ * Examine the method stack of the currently running thread, and create
+ * an {@link AccessControlContext} filled in with the appropriate {@link
+ * ProtectionDomain} objects given this stack.
+ *
+ * @return The context.
+ */
+ static AccessControlContext getContext()
+ {
+ // If we are already in getContext, but called a method that needs
+ // a permission check, return the all-permissive context so methods
+ // called from here succeed.
+ //
+ // XXX is this necessary? We should verify if there are any calls in
+ // the stack below this method that require permission checks.
+ Boolean inCall = (Boolean) inGetContext.get();
+ if (inCall != null && inCall.booleanValue())
+ {
+ if (DEBUG)
+ debug("already in getContext");
+ return DEFAULT_CONTEXT;
+ }
+
+ inGetContext.set(Boolean.TRUE);
+
+ Object[][] stack = getStack();
+ Class[] classes = (Class[]) stack[0];
+ String[] methods = (String[]) stack[1];
+
+ if (DEBUG)
+ debug(">>> got trace of length " + classes.length);
+
+ HashSet domains = new HashSet();
+ HashSet seenDomains = new HashSet();
+ AccessControlContext context = null;
+ int privileged = 0;
+
+ // We walk down the stack, adding each ProtectionDomain for each
+ // class in the call stack. If we reach a call to doPrivileged,
+ // we don't add any more stack frames. We skip the first three stack
+ // frames, since they comprise the calls to getStack, getContext,
+ // and AccessController.getContext.
+ for (int i = 3; i < classes.length && privileged < 2; i++)
+ {
+ Class clazz = classes[i];
+ String method = methods[i];
+
+ if (DEBUG)
+ {
+ debug(">>> checking " + clazz + "." + method);
+ debug(">>> loader = " + clazz.getClassLoader());
+ }
+
+ // If the previous frame was a call to doPrivileged, then this is
+ // the last frame we look at.
+ if (privileged == 1)
+ privileged = 2;
+
+ if (clazz.equals (AccessController.class)
+ && method.equals ("doPrivileged"))
+ {
+ // If there was a call to doPrivileged with a supplied context,
+ // return that context.
+ LinkedList l = (LinkedList) contexts.get();
+ if (l != null)
+ context = (AccessControlContext) l.getFirst();
+ privileged = 1;
+ }
+
+ ProtectionDomain domain = clazz.getProtectionDomain();
+
+ if (domain == null)
+ continue;
+ if (seenDomains.contains(domain))
+ continue;
+ seenDomains.add(domain);
+
+ // Create a static snapshot of this domain, which may change over time
+ // if the current policy changes.
+ domains.add(new ProtectionDomain(domain.getCodeSource(),
+ domain.getPermissions()));
+ }
+
+ if (DEBUG)
+ debug("created domains: " + domains);
+
+ ProtectionDomain[] result = (ProtectionDomain[])
+ domains.toArray(new ProtectionDomain[domains.size()]);
+
+ // Intersect the derived protection domain with the context supplied
+ // to doPrivileged.
+ if (context != null)
+ context = new AccessControlContext(result, context,
+ IntersectingDomainCombiner.SINGLETON);
+ // No context was supplied. Return the derived one.
+ else
+ context = new AccessControlContext(result);
+
+ inGetContext.set(Boolean.FALSE);
+ return context;
+ }
+
+ /**
+ * Returns a snapshot of the current call stack as a pair of arrays:
+ * the first an array of classes in the call stack, the second an array
+ * of strings containing the method names in the call stack. The two
+ * arrays match up, meaning that method <i>i</i> is declared in class
+ * <i>i</i>. The arrays are clean; it will only contain Java methods,
+ * and no element of the list should be null.
+ *
+ * @return A pair of arrays describing the current call stack. The first
+ * element is an array of Class objects, and the second is an array
+ * of Strings comprising the method names.
+ */
+ private static native Object[][] getStack();
+}
--- /dev/null 1969-12-31 16:00:00.000000000 -0800
+++ java/security/IntersectingDomainCombiner.java 2004-11-26 00:06:43.000000000 -0800
@@ -0,0 +1,82 @@
+/* IntersectingDomainCombiner.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.security;
+
+import java.util.HashSet;
+
+/**
+ * A trivial implementation of {@link DomainCombiner} that produces the
+ * intersection of the supplied {@link ProtectionDomain} objects.
+ */
+final class IntersectingDomainCombiner implements DomainCombiner
+{
+
+ // Contstant.
+ // -------------------------------------------------------------------------
+
+ static final IntersectingDomainCombiner SINGLETON = new IntersectingDomainCombiner();
+
+ // Constructor.
+ // -------------------------------------------------------------------------
+
+ private IntersectingDomainCombiner()
+ {
+ }
+
+ // Methods.
+ // -------------------------------------------------------------------------
+
+ public ProtectionDomain[] combine (ProtectionDomain[] currentDomains,
+ ProtectionDomain[] assignedDomains)
+ {
+ HashSet newDomains = new HashSet ();
+ for (int i = 0; i < currentDomains.length; i++)
+ {
+ if (currentDomains[i] == null)
+ continue;
+ for (int j = 0; j < assignedDomains.length; j++)
+ {
+ if (currentDomains[i].equals (assignedDomains[j]))
+ newDomains.add (currentDomains[i]);
+ }
+ }
+ return (ProtectionDomain[])
+ newDomains.toArray(new ProtectionDomain[newDomains.size()]);
+ }
+}
Index: java/security/AccessControlContext.java
===================================================================
RCS file: /cvsroot/gcc/gcc/libjava/java/security/AccessControlContext.java,v
retrieving revision 1.3
diff -u -r1.3 AccessControlContext.java
--- java/security/AccessControlContext.java 9 Jul 2004 15:43:01 -0000 1.3
+++ java/security/AccessControlContext.java 27 Nov 2004 19:36:00 -0000
@@ -37,6 +37,8 @@
package java.security;
+import java.util.HashSet;
+
/**
* AccessControlContext makes system resource access decsion
* based on permission rights.
@@ -53,8 +55,8 @@
*/
public final class AccessControlContext
{
- private ProtectionDomain protectionDomain[];
- private DomainCombiner combiner;
+ private final ProtectionDomain[] protectionDomains;
+ private final DomainCombiner combiner;
/**
* Construct a new AccessControlContext with the specified
@@ -65,29 +67,12 @@
*/
public AccessControlContext(ProtectionDomain[]context)
{
- int i, j, k, count = context.length, count2 = 0;
- for (i = 0, j = 0; i < count; i++)
- {
- for (k = 0; k < i; k++)
- if (context[k] == protectionDomain[i])
- break;
- if (k != i) //it means previous loop did not complete
- continue;
-
- count2++;
- }
-
- protectionDomain = new ProtectionDomain[count2];
- for (i = 0, j = 0; i < count2; i++)
- {
- for (k = 0; k < i; k++)
- if (context[k] == protectionDomain[i])
- break;
- if (k != i) //it means previous loop did not complete
- continue;
-
- protectionDomain[j++] = context[i];
- }
+ HashSet domains = new HashSet (context.length);
+ for (int i = 0; i < context.length; i++)
+ domains.add (context[i]);
+ protectionDomains = (ProtectionDomain[])
+ domains.toArray (new ProtectionDomain[domains.size()]);
+ combiner = null;
}
/**
@@ -99,7 +84,17 @@
public AccessControlContext(AccessControlContext acc,
DomainCombiner combiner)
{
- this(acc.protectionDomain);
+ // XXX check permission to call this.
+ AccessControlContext acc2 = AccessController.getContext();
+ protectionDomains = combiner.combine (acc2.protectionDomains,
+ acc.protectionDomains);
+ this.combiner = combiner;
+ }
+
+ AccessControlContext (ProtectionDomain[] domains, AccessControlContext acc,
+ DomainCombiner combiner)
+ {
+ protectionDomains = combiner.combine (domains, acc.protectionDomains);
this.combiner = combiner;
}
@@ -123,11 +118,11 @@
*/
public void checkPermission(Permission perm) throws AccessControlException
{
- for (int i = 0; i < protectionDomain.length; i++)
- if (protectionDomain[i].implies(perm) == true)
- return;
-
- throw new AccessControlException("Permission not granted");
+ if (protectionDomains.length == 0)
+ throw new AccessControlException ("permission not granted");
+ for (int i = 0; i < protectionDomains.length; i++)
+ if (!protectionDomains[i].implies(perm))
+ throw new AccessControlException ("permission not granted");
}
/**
@@ -146,13 +141,21 @@
{
AccessControlContext acc = (AccessControlContext) obj;
- if (acc.protectionDomain.length != protectionDomain.length)
+ if (acc.protectionDomains.length != protectionDomains.length)
return false;
- for (int i = 0; i < protectionDomain.length; i++)
- if (acc.protectionDomain[i] != protectionDomain[i])
- return false;
- return true;
+ int i, j;
+ for (i = 0; i < protectionDomains.length; i++)
+ {
+ for (j = 0; j < acc.protectionDomains.length; j++)
+ {
+ if (acc.protectionDomains[j].equals (protectionDomains[i]))
+ break;
+ }
+ if (j == acc.protectionDomains.length)
+ return false;
+ }
+ return true;
}
return false;
}
@@ -165,8 +168,8 @@
public int hashCode()
{
int h = 0;
- for (int i = 0; i < protectionDomain.length; i++)
- h ^= protectionDomain[i].hashCode();
+ for (int i = 0; i < protectionDomains.length; i++)
+ h ^= protectionDomains[i].hashCode();
return h;
}
Index: java/security/AccessController.java
===================================================================
RCS file: /cvsroot/gcc/gcc/libjava/java/security/AccessController.java,v
retrieving revision 1.5
diff -u -r1.5 AccessController.java
--- java/security/AccessController.java 20 Apr 2004 14:44:54 -0000 1.5
+++ java/security/AccessController.java 27 Nov 2004 19:36:00 -0000
@@ -47,11 +47,6 @@
* And provides a <code>getContext()</code> method which gives the access
* control context of the current thread that can be used for checking
* permissions at a later time and/or in another thread.
- * <p>
- * XXX - Mostly a stub implementation at the moment. Needs native support
- * from the VM to function correctly. XXX - Do not forget to think about
- * how to handle <code>java.lang.reflect.Method.invoke()</code> on the
- * <code>doPrivileged()</code> methods.
*
* @author Mark Wielaard (mark@klomp.org)
* @since 1.2
@@ -95,7 +90,15 @@
*/
public static Object doPrivileged(PrivilegedAction action)
{
- return action.run();
+ VMAccessController.pushContext(null);
+ try
+ {
+ return action.run();
+ }
+ finally
+ {
+ VMAccessController.popContext();
+ }
}
/**
@@ -113,9 +116,17 @@
* @return the result of the <code>action.run()</code> method.
*/
public static Object doPrivileged(PrivilegedAction action,
- AccessControlContext context)
+ AccessControlContext context)
{
- return action.run();
+ VMAccessController.pushContext(context);
+ try
+ {
+ return action.run();
+ }
+ finally
+ {
+ VMAccessController.popContext();
+ }
}
/**
@@ -137,14 +148,18 @@
public static Object doPrivileged(PrivilegedExceptionAction action)
throws PrivilegedActionException
{
-
+ VMAccessController.pushContext(null);
try
{
- return action.run();
+ return action.run();
}
catch (Exception e)
{
- throw new PrivilegedActionException(e);
+ throw new PrivilegedActionException(e);
+ }
+ finally
+ {
+ VMAccessController.popContext();
}
}
@@ -167,31 +182,40 @@
* is thrown in the <code>run()</code> method.
*/
public static Object doPrivileged(PrivilegedExceptionAction action,
- AccessControlContext context)
+ AccessControlContext context)
throws PrivilegedActionException
{
-
+ VMAccessController.pushContext(context);
try
{
- return action.run();
+ return action.run();
}
catch (Exception e)
{
- throw new PrivilegedActionException(e);
+ throw new PrivilegedActionException(e);
+ }
+ finally
+ {
+ VMAccessController.popContext();
}
}
/**
* Returns the complete access control context of the current thread.
- * <p>
- * XXX - Should this include all the protection domains in the call chain
- * or only the domains till the last <code>doPrivileged()</code> call?
- * <p>
- * XXX - needs native support. Currently returns an empty context.
+ * The returned object encompasses all {@link ProtectionDomain} objects
+ * for all classes in the current call stack, or the set of protection
+ * domains until the last call to {@link
+ * #doPrivileged(java.security.PrivilegedAction)}.
+ *
+ * <p>Additionally, if a call was made to {@link
+ * #doPrivileged(java.security.PrivilegedAction,java.security.AccessControlContext)}
+ * that supplied an {@link AccessControlContext}, then that context
+ * will be intersected with the calculated one.
+ *
+ * @return The context.
*/
public static AccessControlContext getContext()
{
- // For now just return an new empty context
- return new AccessControlContext(new ProtectionDomain[0]);
+ return VMAccessController.getContext();
}
}
--- /dev/null 1969-12-31 16:00:00.000000000 -0800
+++ java/security/natVMAccessController.cc 2004-11-27 00:20:59.000000000 -0800
@@ -0,0 +1,24 @@
+/* natVMAccessController.java -- methods for getting the current call stack.
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+ This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
+details. */
+
+
+#include <gcj/cni.h>
+#include <java-stack.h>
+
+#include <java/lang/Object.h>
+#include <java/security/VMAccessController.h>
+
+
+JArray<jobjectArray> *
+java::security::VMAccessController::getStack ()
+{
+ _Jv_StackTrace *trace = _Jv_StackTrace::GetStackTrace ();
+ return _Jv_StackTrace::GetClassMethodStack (trace);
+}
+