Skip to content

Commit cb89655

Browse files
Fix Infinite Loops in ExceptionsHelper#unwrap
* Keep track of all seen exceptions and break out on loops * Closes #42340
1 parent 1991ee8 commit cb89655

File tree

2 files changed

+40
-13
lines changed

2 files changed

+40
-13
lines changed

server/src/main/java/org/elasticsearch/ExceptionsHelper.java

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import java.util.Arrays;
3939
import java.util.Collections;
4040
import java.util.HashSet;
41+
import java.util.IdentityHashMap;
4142
import java.util.LinkedList;
4243
import java.util.List;
4344
import java.util.Objects;
@@ -185,21 +186,26 @@ public static <T extends Throwable> T useOrSuppress(T first, T second) {
185186
* @return Corruption indicating exception if one is found, otherwise {@code null}
186187
*/
187188
public static IOException unwrapCorruption(Throwable t) {
188-
if (t != null) {
189-
do {
190-
for (Class<?> clazz : CORRUPTION_EXCEPTIONS) {
191-
if (clazz.isInstance(t)) {
192-
return (IOException) t;
193-
}
189+
return t == null ? null : doUnwrapCorruption(t, Collections.newSetFromMap(new IdentityHashMap<>()));
190+
}
191+
192+
private static IOException doUnwrapCorruption(Throwable t, Set<Throwable> seen) {
193+
do {
194+
if (seen.add(t) == false) {
195+
return null;
196+
}
197+
for (Class<?> clazz : CORRUPTION_EXCEPTIONS) {
198+
if (clazz.isInstance(t)) {
199+
return (IOException) t;
194200
}
195-
for (Throwable suppressed : t.getSuppressed()) {
196-
IOException corruptionException = unwrapCorruption(suppressed);
197-
if (corruptionException != null) {
198-
return corruptionException;
199-
}
201+
}
202+
for (Throwable suppressed : t.getSuppressed()) {
203+
IOException corruptionException = doUnwrapCorruption(suppressed, seen);
204+
if (corruptionException != null) {
205+
return corruptionException;
200206
}
201-
} while ((t = t.getCause()) != null);
202-
}
207+
}
208+
} while ((t = t.getCause()) != null);
203209
return null;
204210
}
205211

@@ -213,7 +219,11 @@ public static IOException unwrapCorruption(Throwable t) {
213219
*/
214220
public static Throwable unwrap(Throwable t, Class<?>... clazzes) {
215221
if (t != null) {
222+
final Set<Throwable> seen = Collections.newSetFromMap(new IdentityHashMap<>());
216223
do {
224+
if (seen.add(t) == false) {
225+
return null;
226+
}
217227
for (Class<?> clazz : clazzes) {
218228
if (clazz.isInstance(t)) {
219229
return t;

server/src/test/java/org/elasticsearch/ExceptionsHelperTests.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.elasticsearch.test.ESTestCase;
3636
import org.elasticsearch.transport.RemoteClusterAware;
3737

38+
import java.io.IOException;
3839
import java.util.Optional;
3940

4041
import static org.elasticsearch.ExceptionsHelper.MAX_ITERATIONS;
@@ -211,4 +212,20 @@ public void testUnwrapCorruption() {
211212
withSuppressedException.addSuppressed(new RuntimeException());
212213
assertThat(ExceptionsHelper.unwrapCorruption(withSuppressedException), nullValue());
213214
}
215+
216+
public void testSuppressedCycle() {
217+
RuntimeException e1 = new RuntimeException();
218+
RuntimeException e2 = new RuntimeException();
219+
e1.addSuppressed(e2);
220+
e2.addSuppressed(e1);
221+
ExceptionsHelper.unwrapCorruption(e1);
222+
}
223+
224+
public void testCauseCycle() {
225+
RuntimeException e1 = new RuntimeException();
226+
RuntimeException e2 = new RuntimeException(e1);
227+
e1.initCause(e2);
228+
ExceptionsHelper.unwrap(e1, IOException.class);
229+
ExceptionsHelper.unwrapCorruption(e1);
230+
}
214231
}

0 commit comments

Comments
 (0)