Skip to content

Commit 27b9314

Browse files
authored
StackOverflowError in isClosedChannelException (#802)
1 parent 3178486 commit 27b9314

File tree

2 files changed

+49
-5
lines changed

2 files changed

+49
-5
lines changed

src/main/java/hudson/remoting/Channel.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2050,17 +2050,23 @@ public static void dumpDiagnosticsForAll(@NonNull PrintWriter w) {
20502050
* anywhere in the exception or suppressed exception chain.
20512051
*/
20522052
public static boolean isClosedChannelException(@CheckForNull Throwable t) {
2053-
if (t instanceof ClosedChannelException) {
2053+
return _isClosedChannelException(t, new HashSet<>());
2054+
}
2055+
2056+
private static boolean _isClosedChannelException(@CheckForNull Throwable t, Set<Throwable> seen) {
2057+
if (t == null) {
2058+
return false;
2059+
} else if (!seen.add(t)) {
2060+
return false;
2061+
} else if (t instanceof ClosedChannelException) {
20542062
return true;
20552063
} else if (t instanceof ChannelClosedException) {
20562064
return true;
20572065
} else if (t instanceof EOFException) {
20582066
return true;
2059-
} else if (t == null) {
2060-
return false;
20612067
} else {
2062-
return isClosedChannelException(t.getCause())
2063-
|| Stream.of(t.getSuppressed()).anyMatch(Channel::isClosedChannelException);
2068+
return _isClosedChannelException(t.getCause(), seen)
2069+
|| Stream.of(t.getSuppressed()).anyMatch(x -> _isClosedChannelException(x, seen));
20642070
}
20652071
}
20662072

src/test/java/hudson/remoting/ChannelTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package hudson.remoting;
22

3+
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.is;
35
import static org.junit.jupiter.api.Assertions.assertEquals;
46
import static org.junit.jupiter.api.Assertions.assertFalse;
57
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
@@ -11,13 +13,15 @@
1113

1214
import edu.umd.cs.findbugs.annotations.NonNull;
1315
import hudson.remoting.util.GCTask;
16+
import java.io.EOFException;
1417
import java.io.IOException;
1518
import java.io.ObjectInputStream;
1619
import java.io.ObjectStreamException;
1720
import java.io.PrintWriter;
1821
import java.io.StringWriter;
1922
import java.net.URL;
2023
import java.net.URLClassLoader;
24+
import java.nio.channels.ClosedChannelException;
2125
import java.util.ArrayList;
2226
import java.util.Collection;
2327
import java.util.concurrent.ExecutorService;
@@ -28,6 +32,7 @@
2832
import org.jenkinsci.remoting.RoleChecker;
2933
import org.jenkinsci.remoting.SerializableOnlyOverRemoting;
3034
import org.junit.jupiter.api.Disabled;
35+
import org.junit.jupiter.api.Test;
3136
import org.junit.jupiter.params.ParameterizedTest;
3237
import org.junit.jupiter.params.provider.MethodSource;
3338
import org.jvnet.hudson.test.Issue;
@@ -483,4 +488,37 @@ private void assertFailsWithChannelClosedException(Channel channel, TestRunnable
483488
}
484489
fail("Expected ChannelClosedException, but the call has completed without any exception");
485490
}
491+
492+
@Test
493+
public void isClosedChannelException() {
494+
assertThat(Channel.isClosedChannelException(null), is(false));
495+
assertThat(Channel.isClosedChannelException(new IOException()), is(false));
496+
assertThat(Channel.isClosedChannelException(new ClosedChannelException()), is(true));
497+
assertThat(Channel.isClosedChannelException(new ChannelClosedException((Channel) null, null)), is(true));
498+
assertThat(Channel.isClosedChannelException(new EOFException()), is(true));
499+
assertThat(Channel.isClosedChannelException(new RuntimeException(new ClosedChannelException())), is(true));
500+
{
501+
var main = new RuntimeException();
502+
main.addSuppressed(new ClosedChannelException());
503+
assertThat(Channel.isClosedChannelException(main), is(true));
504+
}
505+
{
506+
var level2 = new RuntimeException(new ClosedChannelException());
507+
var level3 = new RuntimeException();
508+
level3.addSuppressed(level2);
509+
assertThat(Channel.isClosedChannelException(new RuntimeException(level3)), is(true));
510+
}
511+
{
512+
var cycle1 = new RuntimeException();
513+
var cycle2 = new RuntimeException(cycle1);
514+
cycle1.addSuppressed(cycle2);
515+
assertThat(Channel.isClosedChannelException(cycle2), is(false));
516+
}
517+
{
518+
var cycle1 = new ClosedChannelException();
519+
var cycle2 = new RuntimeException(cycle1);
520+
cycle1.addSuppressed(cycle2);
521+
assertThat(Channel.isClosedChannelException(cycle2), is(true));
522+
}
523+
}
486524
}

0 commit comments

Comments
 (0)