From 31fc681e079e70ce74273c364f2d837211debbde Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Fri, 2 Sep 2016 14:03:00 -0400 Subject: [PATCH] Handle security exception from logging stack trace When logging a stack trace, Log4j attempts to load the classes in the stack trace. Yet, it might not have permissions to load some of these classes and a security exception will be thrown. Log4j does not handle this exception and this exception can unwind to the caller. This causes the original exception to be lost, and can even unwind the JVM. This commit fixes this by treating these security exceptions as another possible reason that a class can not be loaded. --- .../logging/log4j/core/impl/ThrowableProxy.java | 4 ++ .../log4j/core/impl/ThrowableProxyTest.java | 58 ++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java index 91093bf..798d5f1 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java @@ -568,6 +568,8 @@ public class ThrowableProxy implements Serializable { return initializeClass(className); } catch (final NoClassDefFoundError ignored) { return initializeClass(className); + } catch (final SecurityException ignored) { + return initializeClass(className); } return clazz; } @@ -579,6 +581,8 @@ public class ThrowableProxy implements Serializable { return null; } catch (final NoClassDefFoundError ignore) { return null; + } catch (final SecurityException ignore) { + return null; } } diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ThrowableProxyTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ThrowableProxyTest.java index 98e3ec7..51c4fd1 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ThrowableProxyTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ThrowableProxyTest.java @@ -27,10 +27,20 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.net.BindException; +import java.net.InetSocketAddress; +import java.net.SocketPermission; +import java.nio.channels.ServerSocketChannel; +import java.security.CodeSource; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; import java.util.HashMap; import java.util.Map; import java.util.Stack; +import javax.management.MBeanPermission; +import javax.management.MBeanServerPermission; import javax.xml.bind.DatatypeConverter; import org.apache.logging.log4j.LogManager; @@ -123,6 +133,54 @@ public class ThrowableProxyTest { } } + @Test + public void testLogStackTraceWithClassThatWillCauseSecurityException() throws IOException { + class SimplePolicy extends Policy { + + private final Permissions permissions; + + public SimplePolicy(Permissions permissions) { + this.permissions = permissions; + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return permissions; + } + + } + + final SecurityManager sm = System.getSecurityManager(); + try { + final Permissions permissions = new Permissions(); + + // you know, for binding + permissions.add(new SocketPermission("localhost:9300", "listen,resolve")); + + /** + * the JUnit test runner uses reflection to invoke the test; while leaving this + * permission out would display the same issue, it's clearer to grant this + * permission and show the real issue that would arise + */ + // TODO: other JDKs might need a different permission here + permissions.add(new RuntimePermission("accessClassInPackage.sun.reflect")); + + // for restoring the security manager after test execution + permissions.add(new RuntimePermission("setSecurityManager")); + + Policy.setPolicy(new SimplePolicy(permissions)); + System.setSecurityManager(new SecurityManager()); + ServerSocketChannel.open().bind(new InetSocketAddress("localhost", 9300)); + ServerSocketChannel.open().bind(new InetSocketAddress("localhost", 9300)); + fail("expected a java.net.BindException"); + } catch (final BindException e) { + new ThrowableProxy(e); + } finally { + // restore the security manager + System.setSecurityManager(sm); + } + } + // DO NOT REMOVE THIS COMMENT: // UNCOMMENT WHEN GENERATING SERIALIZED THROWABLEPROXY FOR #testSerializationWithUnknownThrowable // public static class DeletedException extends Exception { -- 2.9.0