I have create a simple plugin system that allows others upload their plugin's jar, and the plugin system will load it and execute some code in it.
the plugin system will get a subclass of Function<Input, Output> to execute the loaded plugin logic, but I do not want that Function to create new Thread or do some danger action like System.exit. how can I forbid this action?
I have found the AccessController or SecurityManager in Java, how to use it to implement my intent.
Like you said, you can add a security Manager. Something like below: You can put your code in try catch block and catch your custom security exception thrown. This code below runs in loop and keeps on calling System.exit(1);
import java.security.Permission;
public class TestPreventSystemExit {
public static void main(String[] args) {
forbidSystemExitCall();
while (true) {
try {
System.exit(1);
} catch (Exception ex) {
}
}
}
private static class PreventExitException extends SecurityException {
}
private static void forbidSystemExitCall() {
final SecurityManager securityManager = new SecurityManager() {
public void checkPermission(Permission permission) {
if (permission.getName().indexOf("exitVM") >= 0) {
System.out.println("Why you did this to me? :)");
throw new PreventExitException();
}
}
};
System.setSecurityManager(securityManager);
}
}
For System.exit() - see the other answer.
For preventing the starting of threads: possible, but requires to extend the SecurityManager class - see here.
AccessController is more about how a client would write code that is potentially checked. It is not something that you, as the "owner" of the JVM can make usage of (see here). So it doesn't help with your problem.
Related
I'm seeing a rather odd issue.
I created some standard Java loggers (using Logger.getLogger(), a FileHandle, and a SimpleFormatter.)
Those work fine, and output the log file as expected.
Then, I used some classes from the Gigaspaces API (com.gigaspaces.gs-openspaces - included via a Maven dependency), which includes its own logging.
After that, all of the output of my loggers ended up inside the Gigaspaces log file (e.g. ~/.m2/repository/com/gigaspaces/logs/2017-03-27~12.46-gigaspaces-service-135.60.146.142-23534.log) instead of in the appropriate log files that they are supposed to be using.
If I then create more loggers after I've initialised Gigaspaces, these new loggers work as expected. Only loggers created before initialising gigaspaces are affected.
I tried poking around in the code for Gigaspaces a little bit, there's a lot of code in there. I didn't see anything immediately obvious.
Am I doing something wrong with setting up my loggers? It doesn't seem right that a library can steal the output from pre-existing loggers that are unrelated to its classes.
The below short test program demonstrates the problem:
Logger testLog = Logger.getLogger("testlog");
try {
FileHandler fh = new FileHandler("testlog.log");
fh.setFormatter(new SimpleFormatter());
testLog.addHandler(fh);
}
catch (Exception e) {
// Not important
e.printStackTrace();
}
testLog.log(Level.INFO, "This appears in the main log file");
// Spin up gigaspaces, even by trying to connect to a space that doesn't exist
UrlSpaceConfigurer testSpaceConfigurer = new UrlSpaceConfigurer("jini://*/*/testSpace?locators=127.0.01").lookupTimeout(1);
try {
GigaSpace g = new GigaSpaceConfigurer(testSpaceConfigurer).gigaSpace();
}
catch (Exception e) {
// This will throw an exception, just ignore it.
}
testSpaceConfigurer.close();
testLog.log(Level.INFO, "This appears in the (wrong) gigaspaces log file");
You have to pin the "testlog" logger or you risk losing all of the changes you apply to it.
Modifying loggers requires permissions. One option might be to use a custom security manager that doesn't let GigaSpaces redirect your logging.
If GigaSpaces is calling LogManager.reset(), then one hacky, smelly, dirty way to get around removing your handler is to extend FileHandler and override equals.
public class GigaSpaces {
//Pin the logger
private static final Logger testLog = Logger.getLogger("testlog");
static {
try {
FileHandler fh = new FileHandler("testlog.log") {
public boolean equals(Object o) {
return false; //Pure Evil.
}
};
fh.setFormatter(new SimpleFormatter());
testLog.addHandler(fh);
}
catch (Exception e) {
// Not important
e.printStackTrace();
}
}
public void foo() throws Throwable {
testLog.log(Level.INFO, "This appears in the main log file");
// Spin up gigaspaces, even by trying to connect to a space that doesn't exist
UrlSpaceConfigurer testSpaceConfigurer = new UrlSpaceConfigurer("jini://*/*/testSpace?locators=127.0.01").lookupTimeout(1);
try {
GigaSpace g = new GigaSpaceConfigurer(testSpaceConfigurer).gigaSpace();
}
catch (Exception e) {
// This will throw an exception, just ignore it.
} finally {
testSpaceConfigurer.close();
}
testLog.log(Level.INFO, "This appears in the (wrong) gigaspaces log file");
}
}
Overriding the security manager, as suggested by jmehrens, seems to be the way to go.
I was able to stop Gigaspaces from stealing the logging by denying it permission to run the reset() methods on the LogManager, thusly:
// Silly hack to keep gigaspaces from STEALING ALL OUR LOGS
static {
System.setSecurityManager(new SecurityManager() {
#Override
public void checkPermission(Permission p) {
if (p instanceof LoggingPermission) {
for (StackTraceElement stackTraceElement : new Exception().getStackTrace()) {
if (stackTraceElement.getMethodName().equalsIgnoreCase("reset") && stackTraceElement.getClassName().equalsIgnoreCase("java.util.logging.LogManager")) {
throw new SecurityException("No resetting the logger! It is forbidden.");
}
}
}
}
});
}
I this case, I just added the override in a static block of the class that creates my gigaspace instances, but anywhere before initialising them should do.
I want to individually log every unique error I have, as searching though a dozen log files each +10k lines in length is time wasting and tedious.
I catch all exceptions I possibly can, but oftentimes other threads or libraries will shoot off their own errors without any way to process them myself.
Is there any workaround for this?
(E.G. an event for when printStackTrace() is called.)
Is there any workaround for this?
(E.G. an event for when printStackTrace() is called.)
Remap System.err to intercept throwables. If you look at the source code for Throwable.printStackTrace() you'll see that it indirectly calls System.err.println(this);
For example:
import java.io.PrintStream;
public class SpyPrintStream extends PrintStream {
public static void main(String[] args) {
System.setErr(new SpyPrintStream(System.err));
System.setOut(new SpyPrintStream(System.out));
new Exception().printStackTrace();
}
public SpyPrintStream(PrintStream src) {
super(src);
}
#Override
public void println(Object x) {
if (x instanceof Throwable) {
super.println("Our spies detected "+ x.getClass().getName());
}
super.println(x);
}
}
Keep in mind there is all kinds of issues with using this code and it is not going to work in cases where printStackTrace is called with stream that is not standard stream.
You could always do a deep dive into java.lang.instrument if you really want to trap all exceptions.
I catch all exceptions I possibly can, but oftentimes other threads or libraries will shoot off their own errors without any way to process them myself.
Most libraries either throw exceptions back to the caller or use a logging framework. Capture the exception or configure the logging framework.
I want to individually log every unique error I have, as searching though a dozen log files each +10k lines in length is time wasting and tedious.
Logging frameworks include options to deal with this. DuplicateMessageFilter is an example.
Food for thought:
public class DemoClass {
private Map<String, Exception> myExceptions = new HashMap<>();
public void demoMethod() {
try {
// throwing an exception for illustration
throw new IOException("some message");
} catch (IOException e) {
myExceptions.putIfAbsent(e.getLocalizedMessage(), e);
// actually handle the exception
...
}
}
public void finished() {
for (Exception e : myExceptions.values()) {
e.printStackTrace();
}
}
}
You could store any exception you haven't seen yet. If your specific scenario allows for a better way to ensure you only save an exception only once you should prefer that over mapping by Exception.getLocalizedMessage()
Some 3rd party library swallowed an Exception:
String getAnswer(){
try{
// do stuff, modify instance state, maybe throw some exceptions
// ...
return computeAnswer();
}catch (SomeException e){
return null;
}
}
As much as I want to change it into:
String getAnswer() throws SomeException{
// do stuff, modify instance state, maybe throw some exceptions
// ...
return computeAnswer();
}
I can't, because the library is already packaged into a jar. So, is there a way to bring the exception back?
I don't need to rethrow, a stacktrace with exception and message would work too.
I don't think reflection would help here, Unsafe perhaps?
Yes I know I can use a debugger to find out what's happening, but that wouldn't be very useful if I need the exception at runtime for logging and stuff like that
You can do it without reflection or AOP. The main idea is to throw another (unchecked) exception in the constructor of SomeException. There are some limitations (see at the end of this answer) but I hope it fits your needs.
You need to replace the SomeException with a new version (just create a SomeException.java file in the original package but in your src directory) with something like :
package com.3rdpartylibrary;
public class SomeException extends Exception {
public static class SomeExceptionWrapperException extends RuntimeException {
public SomeExceptionWrapperException(final SomeException ex) {
super(ex.getMessage(), ex);
}
}
public SomeException(final String message) {
super(message);
throw new SomeExceptionWrapperException(this); //<=== the key is here
}
}
The SomeExceptionWrapperException has to be unchecked (inherit from RuntimeException or Error). It will be our wrapper to carry the SomeException accross the ugly 3rd party catch(...)
Then you can catch the SomeExceptionWrapperException in your code (and eventually rethrow the original SomeException:
//original, unmodifiable 3rdParty code, here as a example
public String getAnswer() {
try {
//some code
throw new SomeException("a message");
} catch (final SomeException e) {
return null;
}
}
//a wrapper to getAnswer to unwrapp the `SomeException`
public String getAnswerWrapped() throws SomeException {
try {
return getAnswer();
} catch (final SomeExceptionWrapperException e) {
throw (SomeException) e.getCause();
}
}
#Test(expected = SomeException.class)
public void testThrow() throws SomeException {
final String t = getAnswerWrapped();
}
The test will be green as the original SomeException, will be thrown.
Limitations:
This solution will not work if either :
if SomeException is in java.lang as you cannot replace java.lang classes (or see Replacing java class?)
if the 3rd party method has a catch(Throwable e) (which will be horrible and should motivate you to ignore the full 3rd party library)
To solve this based on your constraints I would use aspects (something like AspectJ) and attach it to the creation of your exception, logging (or having it call some arbitrary) method then.
http://www.ibm.com/developerworks/library/j-aspectj/
If all you're looking for is to log the stacktrace + exception message, you could do that at the point you're throwing your exception.
See Get current stack trace in Java to get the stack trace. You can simply use Throwable.getMessage() to get the message and write it out.
But if you need the actual Exception within your code, you could try and add the exception into a ThreadLocal.
To do this, you would need a class like this that can store the exception:
package threadLocalExample;
public class ExceptionKeeper
{
private static ThreadLocal<Exception> threadLocalKeeper = new ThreadLocal<Exception>();
public static Exception getException()
{
return threadLocalKeeper.get();
}
public static void setException(Exception e)
{
threadLocalKeeper.set(e);
}
public static void clearException()
{
threadLocalKeeper.set(null);
}
}
... then in your code which throws the Exception, the code that the 3rd party library calls, you can do something like this to record the exception before you throw it:
package threadLocalExample;
public class ExceptionThrower
{
public ExceptionThrower()
{
super();
}
public void doSomethingInYourCode() throws SomeException
{
boolean someBadThing = true;
if (someBadThing)
{
// this is bad, need to throw an exception!
SomeException e = new SomeException("Message Text");
// but first, store it in a ThreadLocal because that 3rd party
// library I use eats it
ExceptionKeeper.setException(e);
// Throw the exception anyway - hopefully the library will be fixed
throw e;
}
}
}
... then in your overall code, the one that calls the third party library, it can setup and use the ThreadLocal class like this:
package threadLocalExample;
import thirdpartylibrary.ExceptionEater;
public class MainPartOfTheProgram
{
public static void main(String[] args)
{
// call the 3rd party library function that eats exceptions
// but first, prepare the exception keeper - clear out any data it may have
// (may not need to, but good measure)
ExceptionKeeper.clearException();
try
{
// now call the exception eater. It will eat the exception, but the ExceptionKeeper
// will have it
ExceptionEater exEater = new ExceptionEater();
exEater.callSomeThirdPartyLibraryFunction();
// check the ExceptionKeeper for the exception
Exception ex = ExceptionKeeper.getException();
if (ex != null)
{
System.out.println("Aha! The library ate my exception, but I found it");
}
}
finally
{
// Wipe out any data in the ExceptionKeeper. ThreadLocals are real good
// ways of creating memory leaks, and you would want to start from scratch
// next time anyway.
ExceptionKeeper.clearException();
}
}
}
Beware of ThreadLocals. They have their use, but they are a great way of creating memory leaks. So if your application has a lot of threads that would execute this code, be sure to look at the memory footprint and make sure the ThreadLocals aren't taking up too much memory. Being sure to clear out the ThreadLocal's data when you know you no longer need it should prevent that.
JVMTI agent can help. See the related question.
I've made an agent that calls Throwable.printStackTrace() for every thrown exception, but you may easily change the callback to invoke any other Java method.
A rather dirty trick that could do the job with less effort than AOP or de-/recompile the JAR:
If you can copy the source code, you can create a patched version of the class in question with your version of the getAnswer method. Then put it on your classpath before the third party library that contains the unwanted version of getAnswer.
Problems could arise if SomeException is not a RuntimeException and other third party code calls getAnswer. In this situation I am not sure how the resulting behavior will be. But you could circumvent this by wrapping SomeException in a custom RuntimeException.
Could you not just use a reference variable to call that method, if the result is a null, then you can just display a message/call an exception, whatever you want?
if you're using maven, you would exclude packages of the library.
Dependency Exclusions.
I hope to be helpful
If you have the source to the throwing class, you can add it "in the original package but in your src directory" using the technique as #Benoît has pointed out. Then just change
return null;
to
return e;
or
e.printStackTrace();
etc.
This would be quicker then making a new Exception.
A program that I've developed is crashing the JVM occasionally due to this bug: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8029516. Unfortunately the bug has not been resolved by Oracle and the bug report says that there are no known workarounds.
I've tried to modify the example code from the bug report by calling .register(sWatchService, eventKinds) in the KeyWatcher thread instead, by adding all pending register request to a list that I loop through in the KeyWatcher thread but it's still crashing. I'm guessing this just had the same effect as synchronizing on sWatchService (like the submitter of the bug report tried).
Can you think of any way to get around this?
From comments:
It appears that we have an issue with I/O cancellation when there is a pending ReadDirectoryChangesW outstanding.
The statement and example code indicate that the bug is triggered when:
There is a pending event that has not been consumed (it may or may not be visible to WatchService.poll() or WatchService.take())
WatchKey.cancel() is called on the key
This is a nasty bug with no universal workaround. The approach depends on the specifics of your application. Consider pooling watches to a single place so you don't need to call WatchKey.cancel(). If at one point the pool becomes too large, close the entire WatchService and start over. Something similar to.
public class FileWatcerService {
static Kind<?>[] allEvents = new Kind<?>[] {
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY
};
WatchService ws;
// Keep track of paths and registered listeners
Map<String, List<FileChangeListener>> listeners = new ConcurrentHashMap<String, List<FileChangeListener>>();
Map<WatchKey, String> keys = new ConcurrentHashMap<WatchKey, String>();
boolean toStop = false;
public interface FileChangeListener {
void onChange();
}
public void addFileChangeListener(String path, FileChangeListener l) {
if(!listeners.containsKey(path)) {
listeners.put(path, new ArrayList<FileChangeListener>());
keys.put(Paths.get(path).register(ws, allEvents), path);
}
listeners.get(path).add(l);
}
public void removeFileChangeListener(String path, FileChangeListener l) {
if(listeners.containsKey(path))
listeners.get(path).remove(l);
}
public void start() {
ws = FileSystems.getDefault().newWatchService();
new Thread(new Runnable() {
public void run() {
while(!toStop) {
WatchKey key = ws.take();
for(FileChangeListener l: listeners.get(keys.get(key)))
l.onChange();
}
}
}).start();
}
public void stop() {
toStop = true;
ws.close();
}
}
I've managed to create a workaround though it's somewhat ugly.
The bug is in JDK method WindowsWatchKey.invalidate() that releases native buffer while the subsequent calls may still access it. This one-liner fixes the problem by delaying buffer clean-up until GC.
Here is a compiled patch to JDK. In order to apply it add the following Java command-line flag:
-Xbootclasspath/p:jdk-8029516-patch.jar
If patching JDK is not an option in your case, there is still a workaround on the application level. It relies on the knowledge of Windows WatchService internal implementation.
public class JDK_8029516 {
private static final Field bufferField = getField("sun.nio.fs.WindowsWatchService$WindowsWatchKey", "buffer");
private static final Field cleanerField = getField("sun.nio.fs.NativeBuffer", "cleaner");
private static final Cleaner dummyCleaner = Cleaner.create(Thread.class, new Thread());
private static Field getField(String className, String fieldName) {
try {
Field f = Class.forName(className).getDeclaredField(fieldName);
f.setAccessible(true);
return f;
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
public static void patch(WatchKey key) {
try {
cleanerField.set(bufferField.get(key), dummyCleaner);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
Call JDK_8029516.patch(watchKey) right after the key is registred, and it will prevent watchKey.cancel() from releasing the native buffer prematurely.
You might not be able to work around the problem itself but you could deal with the error and handle it. I don't know your specific situation but I could imagine the biggest issue is the crash of the whole JVM. Putting all in a try block does not work because you cannot catch a JVM crash.
Not knowing more about your project makes it difficult to suggest a good/acceptable solution, but maybe this could be an option: Do all the file watching stuff in a separate JVM process. From your main process start a new JVM (e.g. using ProcessBuilder.start()). When the process terminates (i.e. the newly started JVM crashes), restart it. Obviously you need to be able to recover, i.e. you need to keep track of what files to watch and you need to keep this data in your main process too.
Now the biggest remaining part is to implement some communication between the main process and the file watching process. This could be done using standard input/output of the file watching process or using a Socket/ServerSocket or some other mechanism.
I'm building a SWING application and also need to write a custom SecurityManager. If I write an empty class which extends SecurityManager like this
public class Sandbox extends SecurityManager {}
it works fine, meaning that the GUI is rendered correctly and all privileges like I/O are revoked.
However I need to customize the checkPermission method and whenever I override it nothing works anymore...
Why even something like this shouldn't work??
public class Sandbox extends SecurityManager {
#Overide
public void checkPermission(Permission perm) {
super.checkPermission(perm);
}
}
Update: a very basic example that shows the problem is this
public static void main(String[] args) {
System.setSecurityManager(new SecurityManager() {
#Override
public void checkPermission(Permission p) {
if (some_condition_here) {
// Do something here
} else {
// Resort to default implementation
super.checkPermission(p);
}
}
});
new JFrame().setVisible(true);
}
Removing the "checkPermission" method the application works correctly, but I really can't get my head around this.
The permissions are granted based on all the code on the stack. All callers must have the required permission. If you override the method and call the superclass method, your code is on the stack as well which implies that your codebase (where your custom SecurityManager belongs to) must have the permission you (your callers) ask for.
That’s the difference between overriding or not. If you don’t override that method only the (possibly privileged) caller’s code is on the stack and it will get the requested permission. If you override that method your code is also on the stack and must have the permission as well.
So if you want to implement a custom SecurityManager which invokes the inherited check method you must configure the inherited (policy based) logic to give your SecurityManager all permissions it should be able to grant. It’s recommended to separate the SecurityManager from the rest of the application into a different codebase so only the SecurityManager and nothing else gets the generous permissions.
If you call the superclass' checkPermission(p) you didn't have to override the class in the first place. Comment it out, then it works.
The superclas' calls java.security.AccessController.checkPermission(perm) and that seems to throw a java.security.AccessControlException, when not invoked by java.lang.SecurityManager
in my case it says:
Could not load Logmanager "null"
java.security.AccessControlException: access denied (java.util.PropertyPermission java.util.logging.manager read)
etc.
public class SecurityManagerExample
{
public static void main(String[] args)
{
System.setSecurityManager(new SecurityManager()
{
#Override
public void checkPermission(Permission p)
{
//super.checkPermission(p);
}
});
new JFrame().setVisible(true);
}
}
I found a tutorial on how to write a security manager. I'd also recommend you to go through the java doc and the examples provided by oracle.
UPDATE
Take a look at the method summary and override the functionality you want to forbid.
As I found out you also need to explicitly allow the functionality you want to have.
Here an example:
public class SecurityManagerExample
{
public static void main(String[] args)
{
System.setSecurityManager(new SecurityManager()
{
#Override
public void checkWrite(String file) {
// no exception is thrown, i. e. creating files is allowed in general
}
#Override
public void checkDelete(String file)
{
if (file.equals("test.xml"))
{
throw new SecurityException("Not allowed to delete test.xml!");
}
}
});
File f = new File("test.xml");
try
{
f.createNewFile();
}
catch (IOException e)
{
}
f.delete();
}
}
OUTPUT
Exception in thread "main" java.lang.SecurityException: Not allowed to delete test.xml!
at main.SecurityManagerExample$1.checkDelete(SecurityManagerExample.java:60)
at java.io.File.delete(File.java:902)
at main.SecurityManagerExample.main(SecurityManagerExample.java:74)