2013-12-11 19 views
9

Ich arbeite derzeit an der Entwicklung einer kleinen Java-Anwendung, in der vertrauenswürdiger Code neben nicht vertrauenswürdigem Code ausgeführt werden muss. Um dies zu erreichen, habe ich eine benutzerdefinierte SecurityManager installiert, die SecurityException s jedes Mal wirft, wenn eine Berechtigung überprüft wird.Warum verursacht mein benutzerdefinierter SecurityManager beim 16. Mal, wenn ich ein Objekt mit Constructor.newInstance erzeuge, Ausnahmen?

Als eine Brücke zwischen dem vertrauenswürdigen und nicht vertrauenswürdigen Code habe ich einen Thread, der Constructor.newInstance() verwendet, um ein Objekt eines nicht vertrauenswürdigen Typs zu instanziieren. Zu dem Zeitpunkt, an dem dieser Aufruf ausgeführt wird, ist der Sicherheitsmanager so konfiguriert, dass er alles blockiert. Interessanterweise, die ersten 15 mal, dass ich versuche, Objekte mit Constructor.newInstance() zu erstellen, funktioniert alles gut, aber das 16. Mal bekomme ich eine SecurityException.

Ich habe es geschafft, dies zu einem einfachen Testprogramm runter:

import java.lang.reflect.*; 
import java.security.*; 

public class Main { 
    /* Track how many instances have been created so that we can see when the exception 
    * is thrown. 
    */ 
    private static int numInstances = 0; 
    public Main() { 
     System.out.println("Number created: " + ++numInstances); 
    } 

    public static void main(String[] args) { 
     /* Get the constructor for Main so that we can instantiate everything 
     * later on. 
     */ 
     Constructor<Main> ctor; 
     try { 
      ctor = Main.class.getConstructor(); 
     } catch (NoSuchMethodException e) { 
      e.printStackTrace(); 
      return; 
     } 

     /* Install a super prohibitive security manager that disallows all operations. */ 
     System.setSecurityManager(new SecurityManager() { 
      @Override 
      public void checkPermission(Permission p) { 
       /* Nothing is allowed - any permission check causes a  security 
       * exception. 
       */ 
       throw new SecurityException("Not permitted: " + p); 
      } 
     }); 

     /* Continuously create new Main objects. */ 
     try { 
      while (true) { 
       ctor.newInstance(); 
      } 
     } catch (Exception e) { 
      e.printStackTrace(); 
      return; 
     } 
    } 
} 

Dieses Programm installiert eine SecurityManager deren checkPermission immer eine Ausnahme auslöst, unabhängig davon, welche die Erlaubnis beantragt wird. Es sitzt dann in einer Schleife und verwendet ctor.newInstance(), um ein harmloses Objekt Main zu instanziieren, das die Anzahl der bisher generierten Instanzen ausgibt. Die Ausgabe dieses Programms, auf meinem System, ist wie folgt:

Number created: 1 
Number created: 2 
Number created: 3 
Number created: 4 
Number created: 5 
Number created: 6 
Number created: 7 
Number created: 8 
Number created: 9 
Number created: 10 
Number created: 11 
Number created: 12 
Number created: 13 
Number created: 14 
Number created: 15 
java.lang.SecurityException: Not permitted: ("java.lang.RuntimePermission" "createClassLoader") 
    at Main$1.checkPermission(Main.java:32) 
    at java.lang.SecurityManager.checkCreateClassLoader(SecurityManager.java:611) 
    at java.lang.ClassLoader.checkCreateClassLoader(ClassLoader.java:274) 
    at java.lang.ClassLoader.<init>(ClassLoader.java:316) 
    at sun.reflect.DelegatingClassLoader.<init>(ClassDefiner.java:72) 
    at sun.reflect.ClassDefiner$1.run(ClassDefiner.java:60) 
    at sun.reflect.ClassDefiner$1.run(ClassDefiner.java:58) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at sun.reflect.ClassDefiner.defineClass(ClassDefiner.java:57) 
    at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:399) 
    at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:396) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at sun.reflect.MethodAccessorGenerator.generate(MethodAccessorGenerator.java:395) 
    at sun.reflect.MethodAccessorGenerator.generateConstructor(MethodAccessorGenerator.java:94) 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:48) 
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526) 
    at Main.main(Main.java:39) 

Nach the Javadoc for RuntimePermission, die createClassLoader Erlaubnis mit hohen Risiken behaftet zu gewähren:

Dies ist eine extrem gefährliche Erlaubnis zu erteilen. Schädliche Anwendungen, die ihre eigenen Klassenladeprogramme instanziieren können, können dann ihre eigenen Rogue-Klassen in das System laden. Diese neu geladenen Klassen können vom Klassenlader in jede Schutzdomäne platziert werden, wodurch den Klassen automatisch die Berechtigungen für diese Domäne erteilt werden.

Ich habe zwei Fragen:

  1. Was speziell diesen Fehler verursacht? Warum bekomme ich zum 16. Mal eine Anfrage für einen Classloader? Ich vermute, das hat damit zu tun, dass Java versucht, die Reflexion zu optimieren, indem ich Bytecode erzeuge, um das Objekt direkt zu instantiieren, aber ich bin mir nicht sicher.

  2. Ohne Whitelisting das Privileg createClassLoader, das gefährlich ist, gibt es eine Möglichkeit, die nicht vertrauenswürdigen Objekte aus vertrauenswürdigem Code zu instanziieren?

  3. Gehe ich grundsätzlich auf die falsche Weise?

Vielen Dank!

+0

Ich habe versucht, Ihren Code, das gleiche Verhalten. Wenn Sie die throw-Ausnahme entfernen und eine print-Anweisung hinzufügen, wird die checkPermission dreimal aufgerufen, bevor die 16. Instanz erstellt wird. –

Antwort

11

Check out this at GrepCode:

72 privatestaticint  inflationThreshold = 15;

15 ist der Standardwert für die Inflation Schwelle, die Anzahl der reflektierenden Anrufe, bevor eine aggressivere Optimierung in NativeConstructorAccessorImpl eingeführt:

47 if (++numInvocations > ReflectionFactory. inflationThreshold()) {
48 ConstructorAccessorImpl acc = (ConstructorAccessorImpl)
49 newMethodAccessorGenerator().
50 generateConstructor (c. getDeclaringClass(),
51 c. getParameterTypes(),
52 c. getExceptionTypes(),
53 c. getModifiers());
54 parent. setDelegate (acc);

Und das insbesondere Code bewirkt, dass ein neuer Klassenlader instanziiert wird, was bei der 16. Iteration zu Ihrer Ausnahme führt.Bytecode Generation geschieht in der MethodAccessorGenerator Klasse, und das ist das Interessanteste:

387 // Load class
388 vec. trim();
389 finalbyte[] bytes = vec. getData();
390 // Note: the class loader is the only thing that really matters
391 // here -- it's important to get the generated code into the
392 // same namespace as the target class. Since the generated code
393 // is privileged anyway, the protection domain probably doesn't
394 // matter.
395 return AccessController. doPrivileged (
396 newPrivilegedAction < MagicAccessorImpl >() {
397 publicMagicAccessorImpl run() {
398 try {
399 return (MagicAccessorImpl)
400 ClassDefiner. defineClass
401 (generatedName,
402 bytes,
403 0,
404 bytes.length,
405 declaringClass. getClassLoader()). newInstance();
406 } catch (InstantiationException e) {
407 throw (InternalError)
408 newInternalError(). initCause (e);
409 } catch (IllegalAccessException e) {
410 throw (InternalError)
411 newInternalError(). initCause (e);
412 }
413 }
414 });

Wie für diese Erlaubnis gewähren, Sie haben immer noch die Wahl sorgfältig eine Schutzdomäne für Ihren Code bilden, zu dem Sie die gewähren Erlaubnis, ohne es dem fremden Code zu gewähren.

+0

Detektiv Marko Topolnik, auf dem Fall! –

+0

Eine Versuchung, der ich nicht widerstehen konnte :) –

+0

Das ist fantastisch. Ich lerne immer noch über Schutzdomänen und entschuldige mich, wenn das eine dumme Frage ist, aber wie würde ich die Domäne konstruieren, um zu verhindern, dass der nicht vertrauenswürdige Code Classloader verwendet, während mein vertrauenswürdiger Code sie für den newInstance-Aufruf verwendet? Oder würde das Ganze in eine Schutzdomäne gesetzt, in der Klassenloader-Privilegien widerrufen werden, weil die interne Implementierung nicht versucht, sich zu inflatieren? – templatetypedef