This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Preliminary patch: New stack trace infrastructure


>>>>> "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);
+}
+

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]