Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix StackOverflow in ThrowableProxy #300

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import ch.qos.logback.core.CoreConstants;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

Expand All @@ -33,6 +34,7 @@ public class ThrowableProxy implements IThrowableProxy {
private transient PackagingDataCalculator packagingDataCalculator;
private boolean calculatedPackageData = false;

private static final Field CAUSE_FIELD;
private static final Method GET_SUPPRESSED_METHOD;

static {
Expand All @@ -43,6 +45,13 @@ public class ThrowableProxy implements IThrowableProxy {
// ignore, will get thrown in Java < 7
}
GET_SUPPRESSED_METHOD = method;

try {
CAUSE_FIELD = Throwable.class.getDeclaredField("cause");
CAUSE_FIELD.setAccessible(true);
} catch (Exception e) {
throw new Error(e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}

private static final ThrowableProxy[] NO_SUPPRESSED=new ThrowableProxy[0];
Expand Down Expand Up @@ -72,7 +81,22 @@ public ThrowableProxy(Throwable throwable) {
if(throwableSuppressed.length > 0) {
suppressed = new ThrowableProxy[throwableSuppressed.length];
for(int i=0;i<throwableSuppressed.length;i++) {
this.suppressed[i] = new ThrowableProxy(throwableSuppressed[i]);
// It is possible for a suppressed exception to have as cause
// the exception which suppresses it.
//
// This is for instance caused by HttpURLConnection when SSL
// is used and the connection is broken unexpectedly. Closing
// the input stream in the finally block of the try-with-resource
// will throw and that exception will be suppressed but its cause
// will be the original exception that aborted the read.
//
// To avoid StackOverflow, reset the cause when it matches the
// suppressing exception.
Throwable suppressed = throwableSuppressed[i];
if (suppressed.getCause() == throwable) {
CAUSE_FIELD.set(suppressed, null);
}
this.suppressed[i] = new ThrowableProxy(suppressed);
this.suppressed[i].commonFrames = ThrowableProxyUtil
.findNumberOfCommonFrames(throwableSuppressed[i].getStackTrace(),
stackTraceElementProxyArray);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,24 @@ public void suppressedWithCause() throws InvocationTargetException, IllegalAcces
verify(ex);
}

@Test
public void suppressedCyclic() throws InvocationTargetException, IllegalAccessException
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
assumeTrue(TeztHelper.suppressedSupported()); // only execute on Java 7, would work anyway but doesn't make sense.
Exception ex = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try {
someMethod();
} catch (Exception e) {
addSuppressed(e, new Exception("foo", e));
ex = e;
}
// Disabled verification
// Java exception printing includes a [CIRCULAR REFERENCE] line in this case.
// Matching this output would require non-trivial changes to ThrowableProxy.
// For now I'm just happy that this doesn't cause a StackOverflow
//verify(ex);
}

@Test
public void suppressedWithSuppressed() throws Exception
{
Expand Down