Skip to content

Commit ba1ff7a

Browse files
authored
Guard against unbounded grow of suppressed exceptions storage (#13351)
Motivation: We should guard against unbounded grows of suppressed exceptions in our SSLEngine implementation to prevent possible OOME Modifications: - Check if a suppressed exception was already added for a specific error code before adding it again - Ensure we always null out pending exception field. Result: Fixes #13350
1 parent c1f8cb0 commit ba1ff7a

File tree

3 files changed

+66
-13
lines changed

3 files changed

+66
-13
lines changed

common/src/main/java/io/netty/util/internal/EmptyArrays.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,7 @@ public final class EmptyArrays {
3737
public static final X509Certificate[] EMPTY_X509_CERTIFICATES = {};
3838
public static final javax.security.cert.X509Certificate[] EMPTY_JAVAX_X509_CERTIFICATES = {};
3939

40+
public static final Throwable[] EMPTY_THROWABLES = {};
41+
4042
private EmptyArrays() { }
4143
}

common/src/main/java/io/netty/util/internal/ThrowableUtil.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,12 @@ public static void addSuppressed(Throwable target, List<Throwable> suppressed) {
7676
addSuppressed(target, t);
7777
}
7878
}
79+
80+
@SuppressJava6Requirement(reason = "Throwable getSuppressed is only available for >= 7. Has check for < 7.")
81+
public static Throwable[] getSuppressed(Throwable source) {
82+
if (!haveSuppressed()) {
83+
return EmptyArrays.EMPTY_THROWABLES;
84+
}
85+
return source.getSuppressed();
86+
}
7987
}

handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,20 +1077,17 @@ private SSLException shutdownWithError(String operations, int sslError) {
10771077
}
10781078

10791079
private SSLException shutdownWithError(String operation, int sslError, int error) {
1080-
String errorString = SSL.getErrorString(error);
10811080
if (logger.isDebugEnabled()) {
1081+
String errorString = SSL.getErrorString(error);
10821082
logger.debug("{} failed with {}: OpenSSL error: {} {}",
10831083
operation, sslError, error, errorString);
10841084
}
10851085

10861086
// There was an internal error -- shutdown
10871087
shutdown();
1088-
if (handshakeState == HandshakeState.FINISHED) {
1089-
return new SSLException(errorString);
1090-
}
10911088

1092-
SSLHandshakeException exception = new SSLHandshakeException(errorString);
1093-
// If we have a handshakeException stored already we should include it as well to help the user debug things.
1089+
SSLException exception = newSSLExceptionForError(error);
1090+
// If we have a pendingException stored already we should include it as well to help the user debug things.
10941091
if (pendingException != null) {
10951092
exception.initCause(pendingException);
10961093
pendingException = null;
@@ -1360,15 +1357,12 @@ private boolean needWrapAgain(int stackError) {
13601357
// This is needed so we ensure close_notify etc is correctly send to the remote peer.
13611358
// See https://github.com/netty/netty/issues/3900
13621359
if (SSL.bioLengthNonApplication(networkBIO) > 0) {
1363-
// we seems to have data left that needs to be transferred and so the user needs
1360+
// we seem to have data left that needs to be transferred and so the user needs
13641361
// call wrap(...). Store the error so we can pick it up later.
1365-
String message = SSL.getErrorString(stackError);
1366-
SSLException exception = handshakeState == HandshakeState.FINISHED ?
1367-
new SSLException(message) : new SSLHandshakeException(message);
13681362
if (pendingException == null) {
1369-
pendingException = exception;
1370-
} else {
1371-
ThrowableUtil.addSuppressed(pendingException, exception);
1363+
pendingException = newSSLExceptionForError(stackError);
1364+
} else if (shouldAddSuppressed(pendingException, stackError)) {
1365+
ThrowableUtil.addSuppressed(pendingException, newSSLExceptionForError(stackError));
13721366
}
13731367
// We need to clear all errors so we not pick up anything that was left on the stack on the next
13741368
// operation. Note that shutdownWithError(...) will cleanup the stack as well so its only needed here.
@@ -1378,6 +1372,23 @@ private boolean needWrapAgain(int stackError) {
13781372
return false;
13791373
}
13801374

1375+
private SSLException newSSLExceptionForError(int stackError) {
1376+
String message = SSL.getErrorString(stackError);
1377+
return handshakeState == HandshakeState.FINISHED ?
1378+
new OpenSslException(message, stackError) : new OpenSslHandshakeException(message, stackError);
1379+
}
1380+
1381+
private static boolean shouldAddSuppressed(Throwable target, int errorCode) {
1382+
for (Throwable suppressed: ThrowableUtil.getSuppressed(target)) {
1383+
if (suppressed instanceof NativeSslException &&
1384+
((NativeSslException) suppressed).errorCode() == errorCode) {
1385+
/// An exception with this errorCode was already added before.
1386+
return false;
1387+
}
1388+
}
1389+
return true;
1390+
}
1391+
13811392
private SSLEngineResult sslReadErrorResult(int error, int stackError, int bytesConsumed, int bytesProduced)
13821393
throws SSLException {
13831394
if (needWrapAgain(stackError)) {
@@ -2661,4 +2672,36 @@ public String toString() {
26612672
'}';
26622673
}
26632674
}
2675+
2676+
private interface NativeSslException {
2677+
int errorCode();
2678+
}
2679+
2680+
private static final class OpenSslException extends SSLException implements NativeSslException {
2681+
private final int errorCode;
2682+
2683+
OpenSslException(String reason, int errorCode) {
2684+
super(reason);
2685+
this.errorCode = errorCode;
2686+
}
2687+
2688+
@Override
2689+
public int errorCode() {
2690+
return errorCode;
2691+
}
2692+
}
2693+
2694+
private static final class OpenSslHandshakeException extends SSLHandshakeException implements NativeSslException {
2695+
private final int errorCode;
2696+
2697+
OpenSslHandshakeException(String reason, int errorCode) {
2698+
super(reason);
2699+
this.errorCode = errorCode;
2700+
}
2701+
2702+
@Override
2703+
public int errorCode() {
2704+
return errorCode;
2705+
}
2706+
}
26642707
}

0 commit comments

Comments
 (0)