This repository was archived by the owner on Apr 19, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 135
This repository was archived by the owner on Apr 19, 2023. It is now read-only.
Overhead of SecurityManager #134
Copy link
Copy link
Open
Description
Running javac(not limited to) inside nailgun process has very high contention caused by security checks, in our case mostly:
java.io.UnixFileSystem.canonicalize0(Native Method)
java.io.UnixFileSystem.canonicalize(UnixFileSystem.java:172)
java.io.File.getCanonicalPath(File.java:618)
java.io.FilePermission$1.run(FilePermission.java:215)
java.io.FilePermission$1.run(FilePermission.java:203)
java.security.AccessController.doPrivileged(Native Method)
java.io.FilePermission.init(FilePermission.java:203)
java.io.FilePermission.<init>(FilePermission.java:277)
sun.net.www.protocol.file.FileURLConnection.getPermission(FileURLConnection.java:225)
sun.misc.URLClassPath.check(URLClassPath.java:604)
We iterated over several solutions to this and settled on a custom java agent that ‘disables’ security manager. This keeps nailgun protocol intact, but in our case improves compilation speed within pantsbuild from 75 to 12 minutes.
package com.r9.nailgun;
import java.io.IOException;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.net.JarURLConnection;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
/**
* This class disables security manager without breaking nailgun protocol that depends on it.
* Implementation notes:
* Can't use transformer cause classes are already loaded.
* Can't add fields and methods or mark fields public with redefinition.
* SecurityManager is not accessible with reflection.
* Tested with nailgun 0.9.1 and java 8, should be compatible with nailgun 0.9.3 (and java 9 ???)
* Warning: This agent disables security manager. Use on your own risk.
*
* @author Justinas Dabravolskas
* @author Darius Prakaitis
*
*/
public class ExitAgent {
// This is a storage for securityManager that is used in Runtime.exit
public static volatile java.lang.SecurityManager exitSecurity;
public static void premain(String agentArgs, Instrumentation inst) {
try {
// make ExitAgent accessible to application, we probably load the second instance in
// different class loader but who cares
JarURLConnection connection = (JarURLConnection) ExitAgent.class.getClassLoader().getResource("com/r9/nailgun/ExitAgent.class").openConnection();
inst.appendToBootstrapClassLoaderSearch(connection.getJarFile());
inst.redefineClasses(new ClassDefinition(Class.forName("java.lang.System"), getSystemByteCode()));
inst.redefineClasses(new ClassDefinition(Class.forName("java.lang.Runtime"), getRuntimeByteCode()));
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
throw new RuntimeException(e1);
} catch (UnmodifiableClassException e1) {
e1.printStackTrace();
throw new RuntimeException(e1);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private static byte[] getSystemByteCode() {
try {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("java.lang.System");
// don't access volatile field at all
CtMethod getSecurityManager = cc.getDeclaredMethod("getSecurityManager", new CtClass[] {});
getSecurityManager.setBody("{return null;}");
CtMethod setSecurityManager = cc.getDeclaredMethod("setSecurityManager", new CtClass[] { cp.get("java.lang.SecurityManager") });
// make security manager available to Runtime.exit only
setSecurityManager.setBody("{com.r9.nailgun.ExitAgent.exitSecurity=$1;}");
byte[] byteCode = cc.toBytecode();
cc.detach();
return byteCode;
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
}
private static byte[] getRuntimeByteCode() {
try {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("java.lang.Runtime");
CtMethod m = cc.getDeclaredMethod("exit", new CtClass[] { CtClass.intType });
m.setBody("{SecurityManager security = com.r9.nailgun.ExitAgent.exitSecurity; if(security != null){security.checkExit($1);} Shutdown.exit($1);}");
byte[] byteCode = cc.toBytecode();
cc.detach();
return byteCode;
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
}
}
jvican, kageiit and LifeIsStrange
Metadata
Metadata
Assignees
Labels
No labels