231 lines
9.0 KiB
Diff
231 lines
9.0 KiB
Diff
|
|
From de072d314af8f5f269c8abec6930652af67bc8e6 Mon Sep 17 00:00:00 2001
|
||
|
|
From: Tatu Saloranta <tatu.saloranta@iki.fi>
|
||
|
|
Date: Fri, 4 Dec 2020 16:27:55 -0800
|
||
|
|
Subject: [PATCH] Fix eager allocation aspect of #186
|
||
|
|
|
||
|
|
---
|
||
|
|
.../dataformat/cbor/CBORGenerator.java | 2 +-
|
||
|
|
.../jackson/dataformat/cbor/CBORParser.java | 115 ++++++++++++++----
|
||
|
|
2 files changed, 90 insertions(+), 27 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORGenerator.java b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORGenerator.java
|
||
|
|
index f813f4c..1215c86 100644
|
||
|
|
--- a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORGenerator.java
|
||
|
|
+++ b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORGenerator.java
|
||
|
|
@@ -190,7 +190,7 @@ public class CBORGenerator extends GeneratorBase
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Number of elements remaining in the current complex structure (if any),
|
||
|
|
- * when writing defined-length Arrays, Objects; marker {@link #INDEFINITE_LENGTH}
|
||
|
|
+ * when writing defined-length Arrays, Objects; marker {code INDEFINITE_LENGTH}
|
||
|
|
* otherwise.
|
||
|
|
*/
|
||
|
|
protected int _currentRemainingElements = INDEFINITE_LENGTH;
|
||
|
|
diff --git a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java
|
||
|
|
index 2412c7c..ec89f9b 100644
|
||
|
|
--- a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java
|
||
|
|
+++ b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java
|
||
|
|
@@ -63,6 +63,10 @@ public final class CBORParser extends ParserMinimalBase
|
||
|
|
private final static double MATH_POW_2_10 = Math.pow(2, 10);
|
||
|
|
private final static double MATH_POW_2_NEG14 = Math.pow(2, -14);
|
||
|
|
|
||
|
|
+ // 2.11.4: [dataformats-binary#186] Avoid OOME/DoS for bigger binary;
|
||
|
|
+ // read only up to 250k
|
||
|
|
+ protected final static int LONGEST_NON_CHUNKED_BINARY = 250_000;
|
||
|
|
+
|
||
|
|
/*
|
||
|
|
/**********************************************************
|
||
|
|
/* Configuration
|
||
|
|
@@ -1500,13 +1504,15 @@ public final class CBORParser extends ParserMinimalBase
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
- private int _readAndWriteBytes(OutputStream out, int total) throws IOException
|
||
|
|
+ private int _readAndWriteBytes(OutputStream out, final int total) throws IOException
|
||
|
|
{
|
||
|
|
int left = total;
|
||
|
|
while (left > 0) {
|
||
|
|
int avail = _inputEnd - _inputPtr;
|
||
|
|
if (_inputPtr >= _inputEnd) {
|
||
|
|
- loadMoreGuaranteed();
|
||
|
|
+ if (!loadMore()) {
|
||
|
|
+ _reportIncompleteBinaryRead(total, total-left);
|
||
|
|
+ }
|
||
|
|
avail = _inputEnd - _inputPtr;
|
||
|
|
}
|
||
|
|
int count = Math.min(avail, left);
|
||
|
|
@@ -2219,33 +2225,55 @@ public final class CBORParser extends ParserMinimalBase
|
||
|
|
// either way, got it now
|
||
|
|
return _inputBuffer[_inputPtr++];
|
||
|
|
}
|
||
|
|
-
|
||
|
|
+
|
||
|
|
+ /**
|
||
|
|
+ * Helper called to complete reading of binary data ("byte string") in
|
||
|
|
+ * case contents are needed.
|
||
|
|
+ */
|
||
|
|
@SuppressWarnings("resource")
|
||
|
|
protected byte[] _finishBytes(int len) throws IOException
|
||
|
|
{
|
||
|
|
+ // Chunked?
|
||
|
|
// First, simple: non-chunked
|
||
|
|
- if (len >= 0) {
|
||
|
|
+ if (len <= 0) {
|
||
|
|
if (len == 0) {
|
||
|
|
return NO_BYTES;
|
||
|
|
}
|
||
|
|
- byte[] b = new byte[len];
|
||
|
|
- if (_inputPtr >= _inputEnd) {
|
||
|
|
- loadMoreGuaranteed();
|
||
|
|
+ return _finishChunkedBytes();
|
||
|
|
+ }
|
||
|
|
+ // Non-chunked, contiguous
|
||
|
|
+ if (len > LONGEST_NON_CHUNKED_BINARY) {
|
||
|
|
+ // [dataformats-binary#186]: avoid immediate allocation for longest
|
||
|
|
+ return _finishLongContiguousBytes(len);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ final byte[] b = new byte[len];
|
||
|
|
+ final int expLen = len;
|
||
|
|
+ if (_inputPtr >= _inputEnd) {
|
||
|
|
+ if (!loadMore()) {
|
||
|
|
+ _reportIncompleteBinaryRead(expLen, 0);
|
||
|
|
}
|
||
|
|
- int ptr = 0;
|
||
|
|
- while (true) {
|
||
|
|
- int toAdd = Math.min(len, _inputEnd - _inputPtr);
|
||
|
|
- System.arraycopy(_inputBuffer, _inputPtr, b, ptr, toAdd);
|
||
|
|
- _inputPtr += toAdd;
|
||
|
|
- ptr += toAdd;
|
||
|
|
- len -= toAdd;
|
||
|
|
- if (len <= 0) {
|
||
|
|
- return b;
|
||
|
|
- }
|
||
|
|
- loadMoreGuaranteed();
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ int ptr = 0;
|
||
|
|
+ while (true) {
|
||
|
|
+ int toAdd = Math.min(len, _inputEnd - _inputPtr);
|
||
|
|
+ System.arraycopy(_inputBuffer, _inputPtr, b, ptr, toAdd);
|
||
|
|
+ _inputPtr += toAdd;
|
||
|
|
+ ptr += toAdd;
|
||
|
|
+ len -= toAdd;
|
||
|
|
+ if (len <= 0) {
|
||
|
|
+ return b;
|
||
|
|
+ }
|
||
|
|
+ if (!loadMore()) {
|
||
|
|
+ _reportIncompleteBinaryRead(expLen, ptr);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
+ }
|
||
|
|
|
||
|
|
+ // @since 2.12
|
||
|
|
+ protected byte[] _finishChunkedBytes() throws IOException
|
||
|
|
+ {
|
||
|
|
// or, if not, chunked...
|
||
|
|
ByteArrayBuilder bb = _getByteArrayBuilder();
|
||
|
|
while (true) {
|
||
|
|
@@ -2262,14 +2290,17 @@ public final class CBORParser extends ParserMinimalBase
|
||
|
|
throw _constructError("Mismatched chunk in chunked content: expected "+CBORConstants.MAJOR_TYPE_BYTES
|
||
|
|
+" but encountered "+type);
|
||
|
|
}
|
||
|
|
- len = _decodeExplicitLength(ch & 0x1F);
|
||
|
|
+ int len = _decodeExplicitLength(ch & 0x1F);
|
||
|
|
if (len < 0) {
|
||
|
|
throw _constructError("Illegal chunked-length indicator within chunked-length value (type "+CBORConstants.MAJOR_TYPE_BYTES+")");
|
||
|
|
}
|
||
|
|
+ final int chunkLen = len;
|
||
|
|
while (len > 0) {
|
||
|
|
int avail = _inputEnd - _inputPtr;
|
||
|
|
if (_inputPtr >= _inputEnd) {
|
||
|
|
- loadMoreGuaranteed();
|
||
|
|
+ if (!loadMore()) {
|
||
|
|
+ _reportIncompleteBinaryRead(chunkLen, chunkLen-len);
|
||
|
|
+ }
|
||
|
|
avail = _inputEnd - _inputPtr;
|
||
|
|
}
|
||
|
|
int count = Math.min(avail, len);
|
||
|
|
@@ -2280,7 +2311,33 @@ public final class CBORParser extends ParserMinimalBase
|
||
|
|
}
|
||
|
|
return bb.toByteArray();
|
||
|
|
}
|
||
|
|
-
|
||
|
|
+
|
||
|
|
+ // @since 2.12
|
||
|
|
+ protected byte[] _finishLongContiguousBytes(final int expLen) throws IOException
|
||
|
|
+ {
|
||
|
|
+ int left = expLen;
|
||
|
|
+
|
||
|
|
+ // 04-Dec-2020, tatu: Let's NOT use recycled instance since we have much
|
||
|
|
+ // longer content and there is likely less benefit of trying to recycle
|
||
|
|
+ // segments
|
||
|
|
+ try (final ByteArrayBuilder bb = new ByteArrayBuilder(LONGEST_NON_CHUNKED_BINARY >> 1)) {
|
||
|
|
+ while (left > 0) {
|
||
|
|
+ int avail = _inputEnd - _inputPtr;
|
||
|
|
+ if (avail <= 0) {
|
||
|
|
+ if (!loadMore()) {
|
||
|
|
+ _reportIncompleteBinaryRead(expLen, expLen-left);
|
||
|
|
+ }
|
||
|
|
+ avail = _inputEnd - _inputPtr;
|
||
|
|
+ }
|
||
|
|
+ int count = Math.min(avail, left);
|
||
|
|
+ bb.write(_inputBuffer, _inputPtr, count);
|
||
|
|
+ _inputPtr += count;
|
||
|
|
+ left -= count;
|
||
|
|
+ }
|
||
|
|
+ return bb.toByteArray();
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
protected final JsonToken _decodeFieldName() throws IOException
|
||
|
|
{
|
||
|
|
if (_inputPtr >= _inputEnd) {
|
||
|
|
@@ -2429,9 +2486,8 @@ public final class CBORParser extends ParserMinimalBase
|
||
|
|
} else if (type == CBORConstants.MAJOR_TYPE_INT_NEG) {
|
||
|
|
name = _numberToName(ch, true);
|
||
|
|
} else if (type == CBORConstants.MAJOR_TYPE_BYTES) {
|
||
|
|
- /* 08-Sep-2014, tatu: As per [Issue#5], there are codecs
|
||
|
|
- * (f.ex. Perl module "CBOR::XS") that use Binary data...
|
||
|
|
- */
|
||
|
|
+ // 08-Sep-2014, tatu: As per [Issue#5], there are codecs
|
||
|
|
+ // (f.ex. Perl module "CBOR::XS") that use Binary data...
|
||
|
|
final int blen = _decodeExplicitLength(ch & 0x1F);
|
||
|
|
byte[] b = _finishBytes(blen);
|
||
|
|
// TODO: Optimize, if this becomes commonly used & bottleneck; we have
|
||
|
|
@@ -2984,7 +3040,7 @@ public final class CBORParser extends ParserMinimalBase
|
||
|
|
/**********************************************************
|
||
|
|
*/
|
||
|
|
|
||
|
|
- protected final boolean loadMore() throws IOException
|
||
|
|
+ protected boolean loadMore() throws IOException
|
||
|
|
{
|
||
|
|
if (_inputStream != null) {
|
||
|
|
_currInputProcessed += _inputEnd;
|
||
|
|
@@ -3005,7 +3061,7 @@ public final class CBORParser extends ParserMinimalBase
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
- protected final void loadMoreGuaranteed() throws IOException {
|
||
|
|
+ protected void loadMoreGuaranteed() throws IOException {
|
||
|
|
if (!loadMore()) { _reportInvalidEOF(); }
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -3129,6 +3185,13 @@ public final class CBORParser extends ParserMinimalBase
|
||
|
|
_reportInvalidOther(mask);
|
||
|
|
}
|
||
|
|
|
||
|
|
+ // @since 2.12
|
||
|
|
+ protected void _reportIncompleteBinaryRead(int expLen, int actLen) throws IOException
|
||
|
|
+ {
|
||
|
|
+ _reportInvalidEOF(String.format(" for Binary value: expected %d bytes, only found %d",
|
||
|
|
+ expLen, actLen), _currToken);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
/*
|
||
|
|
/**********************************************************
|
||
|
|
/* Internal methods, other
|
||
|
|
--
|
||
|
|
2.23.0
|
||
|
|
|