diff options
author | Christopher Vogt <oss.nsp@cvogt.org> | 2016-10-07 00:24:14 -0400 |
---|---|---|
committer | Christopher Vogt <oss.nsp@cvogt.org> | 2016-10-07 20:05:57 -0400 |
commit | 8529a70358921af282920c216a81609292c8ba6a (patch) | |
tree | 04a23a27b41c55fc6c5ab42c79406d2f41e14b79 | |
parent | 95728f6041a32e081a1f4129b8483ef622179b45 (diff) | |
download | cbt-8529a70358921af282920c216a81609292c8ba6a.tar.gz cbt-8529a70358921af282920c216a81609292c8ba6a.tar.bz2 cbt-8529a70358921af282920c216a81609292c8ba6a.zip |
Fix exit code trapping across classloaders
A SecurityManager is once installed globally and stays the same across
all classloaders. TrapSecurityManager was installed this way, but it
looked up the `trapExitCode` in it's own classloader, while
classes in other classloaders (when cbt was using another version of
cbt) were writing it to their own classloader. This flag needs to be in
a global place instead to fix this, so we'll put it straight into the
TrapSecurityManager itself.
-rw-r--r-- | nailgun_launcher/NailgunLauncher.java | 7 | ||||
-rw-r--r-- | nailgun_launcher/Stage0Lib.java | 10 | ||||
-rw-r--r-- | nailgun_launcher/TrapSecurityManager.java | 52 | ||||
-rw-r--r-- | nailgun_launcher/TrappedExitCode.java | 8 | ||||
-rw-r--r-- | stage1/Stage1Lib.scala | 8 |
5 files changed, 57 insertions, 28 deletions
diff --git a/nailgun_launcher/NailgunLauncher.java b/nailgun_launcher/NailgunLauncher.java index 5a70312..37bfa06 100644 --- a/nailgun_launcher/NailgunLauncher.java +++ b/nailgun_launcher/NailgunLauncher.java @@ -23,13 +23,6 @@ public class NailgunLauncher{ public final static SecurityManager initialSecurityManager = System.getSecurityManager(); - public final static ThreadLocal<Boolean> trapExitCode = - new ThreadLocal<Boolean>() { - @Override protected Boolean initialValue() { - return false; - } - }; - public static String TARGET = System.getenv("TARGET"); private static String NAILGUN = "nailgun_launcher/"; private static String STAGE1 = "stage1/"; diff --git a/nailgun_launcher/Stage0Lib.java b/nailgun_launcher/Stage0Lib.java index 11a9aab..452bdae 100644 --- a/nailgun_launcher/Stage0Lib.java +++ b/nailgun_launcher/Stage0Lib.java @@ -20,21 +20,21 @@ public class Stage0Lib{ } public static int runMain(String cls, String[] args, ClassLoader cl) throws Throwable{ - Boolean trapExitCodeBefore = NailgunLauncher.trapExitCode.get(); + Boolean trapExitCodeBefore = TrapSecurityManager.trapExitCode().get(); try{ - NailgunLauncher.trapExitCode.set(true); + TrapSecurityManager.trapExitCode().set(true); cl.loadClass(cls) .getMethod("main", String[].class) .invoke( null, (Object) args); return 0; }catch( InvocationTargetException exception ){ Throwable cause = exception.getCause(); - if(cause instanceof TrappedExitCode){ - return ((TrappedExitCode) cause).exitCode; + if(TrapSecurityManager.isTrappedExit(cause)){ + return TrapSecurityManager.exitCode(cause); } throw exception; } finally { - NailgunLauncher.trapExitCode.set(trapExitCodeBefore); + TrapSecurityManager.trapExitCode().set(trapExitCodeBefore); } } diff --git a/nailgun_launcher/TrapSecurityManager.java b/nailgun_launcher/TrapSecurityManager.java index fada878..1626787 100644 --- a/nailgun_launcher/TrapSecurityManager.java +++ b/nailgun_launcher/TrapSecurityManager.java @@ -9,6 +9,33 @@ would be Nailgun's if running on Nailgun. If we do not delegate to Nailgun, it s could in some cases kill the server process */ public class TrapSecurityManager extends ProxySecurityManager{ + public static ThreadLocal<Boolean> trapExitCode(){ + // storing the flag in the installed security manager + // instead of e.g. a static member is necessary because + // we run multiple versions of CBT with multiple TrapSecurityManager classes + // but we need to affect the installed one + SecurityManager sm = System.getSecurityManager(); + if(sm instanceof TrapSecurityManager){ + return ((TrapSecurityManager) sm)._trapExitCode; + } else { + try{ + @SuppressWarnings("unchecked") + ThreadLocal<Boolean> res = + (ThreadLocal<Boolean>) sm.getClass().getMethod("trapExitCode").invoke(null); + return res; + } catch(Exception e) { + throw new RuntimeException(e); + } + } + } + + private final ThreadLocal<Boolean> _trapExitCode = + new ThreadLocal<Boolean>() { + @Override protected Boolean initialValue() { + return false; + } + }; + public TrapSecurityManager(){ super(NailgunLauncher.initialSecurityManager); } @@ -19,21 +46,38 @@ public class TrapSecurityManager extends ProxySecurityManager{ Calling .super leads to ClassNotFound exteption for a lambda. Calling to the previous SecurityManager leads to a stack overflow */ - if(!NailgunLauncher.trapExitCode.get()){ + if(!TrapSecurityManager.trapExitCode().get()){ super.checkPermission(permission); } } public void checkPermission( Permission permission, Object context ){ /* Does this methods need to be overidden? */ - if(!NailgunLauncher.trapExitCode.get()){ + if(!TrapSecurityManager.trapExitCode().get()){ super.checkPermission(permission, context); } } + + private static final String prefix = "[TrappedExit] "; + @Override public void checkExit( int status ){ - if(NailgunLauncher.trapExitCode.get()){ - throw new TrappedExitCode(status); + if(TrapSecurityManager.trapExitCode().get()){ + // using a RuntimeException and a prefix here instead of a custom + // exception type because this is thrown by the installed TrapSecurityManager + // but other versions of cbt need to be able to catch it, that do not have access + // to that version of the TrapSecurityManager class + throw new RuntimeException(prefix+status); } super.checkExit(status); } + + public static boolean isTrappedExit( Throwable t ){ + return t instanceof RuntimeException && t.getMessage().startsWith(prefix); + } + + public static int exitCode( Throwable t ){ + assert(isTrappedExit(t)); + return Integer.parseInt( t.getMessage().substring(prefix.length()) ); + } + } diff --git a/nailgun_launcher/TrappedExitCode.java b/nailgun_launcher/TrappedExitCode.java deleted file mode 100644 index 154db27..0000000 --- a/nailgun_launcher/TrappedExitCode.java +++ /dev/null @@ -1,8 +0,0 @@ -package cbt; -import java.security.*; -public class TrappedExitCode extends SecurityException{ - public int exitCode; - public TrappedExitCode(int exitCode){ - this.exitCode = exitCode; - } -} diff --git a/stage1/Stage1Lib.scala b/stage1/Stage1Lib.scala index 7620fd8..eea9be6 100644 --- a/stage1/Stage1Lib.scala +++ b/stage1/Stage1Lib.scala @@ -23,7 +23,7 @@ object CatchTrappedExitCode{ def unapply(e: Throwable): Option[ExitCode] = { Option(e) flatMap { case i: InvocationTargetException => unapply(i.getTargetException) - case e: TrappedExitCode => Some( ExitCode(e.exitCode) ) + case e if TrapSecurityManager.isTrappedExit(e) => Some( ExitCode(TrapSecurityManager.exitCode(e)) ) case _ => None } } @@ -312,16 +312,16 @@ ${files.sorted.mkString(" \\\n")} } def trapExitCode( code: => ExitCode ): ExitCode = { - val trapExitCodeBefore = NailgunLauncher.trapExitCode.get + val trapExitCodeBefore = TrapSecurityManager.trapExitCode().get try{ - NailgunLauncher.trapExitCode.set(true) + TrapSecurityManager.trapExitCode().set(true) code } catch { case CatchTrappedExitCode(exitCode) => logger.stage1(s"caught exit code $exitCode") exitCode } finally { - NailgunLauncher.trapExitCode.set(trapExitCodeBefore) + TrapSecurityManager.trapExitCode().set(trapExitCodeBefore) } } |