Make JsonObjectDecoder discard everything after stream corruption
Motivation: There's no way to recover from a corrupted JSON stream. The current implementation will raise an infinite exception storm when a peer sends a large corrupted stream. Modification: Discard everything once stream corruption is detected. Result: Fixes a buffer leak Fixes exception storm
This commit is contained in:
parent
9c4a733471
commit
13a0a36db3
@ -37,11 +37,15 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class JsonObjectDecoder extends ByteToMessageDecoder {
|
public class JsonObjectDecoder extends ByteToMessageDecoder {
|
||||||
|
|
||||||
|
private static final int ST_CORRUPTED = -1;
|
||||||
|
private static final int ST_INIT = 0;
|
||||||
|
private static final int ST_DECODING_NORMAL = 1;
|
||||||
|
private static final int ST_DECODING_ARRAY_STREAM = 2;
|
||||||
|
|
||||||
private int openBraces;
|
private int openBraces;
|
||||||
private int idx;
|
private int idx;
|
||||||
|
|
||||||
private boolean isDecoding;
|
private int state;
|
||||||
private boolean isArrayStreamDecoding;
|
|
||||||
private boolean insideString;
|
private boolean insideString;
|
||||||
|
|
||||||
private final int maxObjectLength;
|
private final int maxObjectLength;
|
||||||
@ -79,6 +83,11 @@ public class JsonObjectDecoder extends ByteToMessageDecoder {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||||
|
if (state == ST_CORRUPTED) {
|
||||||
|
in.skipBytes(in.readableBytes());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// index of next byte to process.
|
// index of next byte to process.
|
||||||
int idx = this.idx;
|
int idx = this.idx;
|
||||||
int wrtIdx = in.writerIndex();
|
int wrtIdx = in.writerIndex();
|
||||||
@ -97,7 +106,7 @@ public class JsonObjectDecoder extends ByteToMessageDecoder {
|
|||||||
|
|
||||||
for (/* use current idx */; idx < wrtIdx; idx++) {
|
for (/* use current idx */; idx < wrtIdx; idx++) {
|
||||||
byte c = in.getByte(idx);
|
byte c = in.getByte(idx);
|
||||||
if (isDecoding) {
|
if (state == ST_DECODING_NORMAL) {
|
||||||
decodeByte(c, in, idx);
|
decodeByte(c, in, idx);
|
||||||
|
|
||||||
// All opening braces/brackets have been closed. That's enough to conclude
|
// All opening braces/brackets have been closed. That's enough to conclude
|
||||||
@ -115,7 +124,7 @@ public class JsonObjectDecoder extends ByteToMessageDecoder {
|
|||||||
// coming along the byte stream.
|
// coming along the byte stream.
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
} else if (isArrayStreamDecoding) {
|
} else if (state == ST_DECODING_ARRAY_STREAM) {
|
||||||
decodeByte(c, in, idx);
|
decodeByte(c, in, idx);
|
||||||
|
|
||||||
if (!insideString && (openBraces == 1 && c == ',' || openBraces == 0 && c == ']')) {
|
if (!insideString && (openBraces == 1 && c == ',' || openBraces == 0 && c == ']')) {
|
||||||
@ -146,7 +155,7 @@ public class JsonObjectDecoder extends ByteToMessageDecoder {
|
|||||||
} else if (c == '{' || c == '[') {
|
} else if (c == '{' || c == '[') {
|
||||||
initDecoding(c);
|
initDecoding(c);
|
||||||
|
|
||||||
if (isArrayStreamDecoding) {
|
if (state == ST_DECODING_ARRAY_STREAM) {
|
||||||
// Discard the array bracket
|
// Discard the array bracket
|
||||||
in.skipBytes(1);
|
in.skipBytes(1);
|
||||||
}
|
}
|
||||||
@ -154,9 +163,9 @@ public class JsonObjectDecoder extends ByteToMessageDecoder {
|
|||||||
} else if (Character.isWhitespace(c)) {
|
} else if (Character.isWhitespace(c)) {
|
||||||
in.skipBytes(1);
|
in.skipBytes(1);
|
||||||
} else {
|
} else {
|
||||||
ctx.fireExceptionCaught(
|
state = ST_CORRUPTED;
|
||||||
new CorruptedFrameException("Invalid JSON received at byte position " + idx + ". " +
|
throw new CorruptedFrameException(
|
||||||
"Hexdump: " + ByteBufUtil.hexDump(in)));
|
"invalid JSON received at byte position " + idx + ": " + ByteBufUtil.hexDump(in));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,12 +203,16 @@ public class JsonObjectDecoder extends ByteToMessageDecoder {
|
|||||||
|
|
||||||
private void initDecoding(byte openingBrace) {
|
private void initDecoding(byte openingBrace) {
|
||||||
openBraces = 1;
|
openBraces = 1;
|
||||||
isArrayStreamDecoding = openingBrace == '[' && streamArrayElements;
|
if (openingBrace == '[' && streamArrayElements) {
|
||||||
isDecoding = !isArrayStreamDecoding;
|
state = ST_DECODING_ARRAY_STREAM;
|
||||||
|
} else {
|
||||||
|
state = ST_DECODING_NORMAL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reset() {
|
private void reset() {
|
||||||
isDecoding = insideString = isArrayStreamDecoding = false;
|
insideString = false;
|
||||||
|
state = ST_INIT;
|
||||||
openBraces = 0;
|
openBraces = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user