@@ -63,6 +63,10 @@ private Feature(boolean defaultState) {
6363 private final static double MATH_POW_2_10 = Math .pow (2 , 10 );
6464 private final static double MATH_POW_2_NEG14 = Math .pow (2 , -14 );
6565
66+ // 2.11.4: [dataformats-binary#186] Avoid OOME/DoS for bigger binary;
67+ // read only up to 250k
68+ protected final static int LONGEST_NON_CHUNKED_BINARY = 250_000 ;
69+
6670 /*
6771 /**********************************************************
6872 /* Configuration
@@ -1706,13 +1710,15 @@ public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IO
17061710 }
17071711 }
17081712
1709- private int _readAndWriteBytes (OutputStream out , int total ) throws IOException
1713+ private int _readAndWriteBytes (OutputStream out , final int total ) throws IOException
17101714 {
17111715 int left = total ;
17121716 while (left > 0 ) {
17131717 int avail = _inputEnd - _inputPtr ;
17141718 if (_inputPtr >= _inputEnd ) {
1715- loadMoreGuaranteed ();
1719+ if (!loadMore ()) {
1720+ _reportIncompleteBinaryRead (total , total -left );
1721+ }
17161722 avail = _inputEnd - _inputPtr ;
17171723 }
17181724 int count = Math .min (avail , left );
@@ -2425,33 +2431,55 @@ private final int _nextChunkedByte2() throws IOException
24252431 // either way, got it now
24262432 return _inputBuffer [_inputPtr ++];
24272433 }
2428-
2434+
2435+ /**
2436+ * Helper called to complete reading of binary data ("byte string") in
2437+ * case contents are needed.
2438+ */
24292439 @ SuppressWarnings ("resource" )
24302440 protected byte [] _finishBytes (int len ) throws IOException
24312441 {
2442+ // Chunked?
24322443 // First, simple: non-chunked
2433- if (len > = 0 ) {
2444+ if (len < = 0 ) {
24342445 if (len == 0 ) {
24352446 return NO_BYTES ;
24362447 }
2437- byte [] b = new byte [len ];
2438- if (_inputPtr >= _inputEnd ) {
2439- loadMoreGuaranteed ();
2448+ return _finishChunkedBytes ();
2449+ }
2450+ // Non-chunked, contiguous
2451+ if (len > LONGEST_NON_CHUNKED_BINARY ) {
2452+ // [dataformats-binary#186]: avoid immediate allocation for longest
2453+ return _finishLongContiguousBytes (len );
2454+ }
2455+
2456+ final byte [] b = new byte [len ];
2457+ final int expLen = len ;
2458+ if (_inputPtr >= _inputEnd ) {
2459+ if (!loadMore ()) {
2460+ _reportIncompleteBinaryRead (expLen , 0 );
24402461 }
2441- int ptr = 0 ;
2442- while (true ) {
2443- int toAdd = Math .min (len , _inputEnd - _inputPtr );
2444- System .arraycopy (_inputBuffer , _inputPtr , b , ptr , toAdd );
2445- _inputPtr += toAdd ;
2446- ptr += toAdd ;
2447- len -= toAdd ;
2448- if (len <= 0 ) {
2449- return b ;
2450- }
2451- loadMoreGuaranteed ();
2462+ }
2463+
2464+ int ptr = 0 ;
2465+ while (true ) {
2466+ int toAdd = Math .min (len , _inputEnd - _inputPtr );
2467+ System .arraycopy (_inputBuffer , _inputPtr , b , ptr , toAdd );
2468+ _inputPtr += toAdd ;
2469+ ptr += toAdd ;
2470+ len -= toAdd ;
2471+ if (len <= 0 ) {
2472+ return b ;
2473+ }
2474+ if (!loadMore ()) {
2475+ _reportIncompleteBinaryRead (expLen , ptr );
24522476 }
24532477 }
2478+ }
24542479
2480+ // @since 2.12
2481+ protected byte [] _finishChunkedBytes () throws IOException
2482+ {
24552483 // or, if not, chunked...
24562484 ByteArrayBuilder bb = _getByteArrayBuilder ();
24572485 while (true ) {
@@ -2468,14 +2496,17 @@ protected byte[] _finishBytes(int len) throws IOException
24682496 throw _constructError ("Mismatched chunk in chunked content: expected " +CBORConstants .MAJOR_TYPE_BYTES
24692497 +" but encountered " +type );
24702498 }
2471- len = _decodeExplicitLength (ch & 0x1F );
2499+ int len = _decodeExplicitLength (ch & 0x1F );
24722500 if (len < 0 ) {
24732501 throw _constructError ("Illegal chunked-length indicator within chunked-length value (type " +CBORConstants .MAJOR_TYPE_BYTES +")" );
24742502 }
2503+ final int chunkLen = len ;
24752504 while (len > 0 ) {
24762505 int avail = _inputEnd - _inputPtr ;
24772506 if (_inputPtr >= _inputEnd ) {
2478- loadMoreGuaranteed ();
2507+ if (!loadMore ()) {
2508+ _reportIncompleteBinaryRead (chunkLen , chunkLen -len );
2509+ }
24792510 avail = _inputEnd - _inputPtr ;
24802511 }
24812512 int count = Math .min (avail , len );
@@ -2486,7 +2517,33 @@ protected byte[] _finishBytes(int len) throws IOException
24862517 }
24872518 return bb .toByteArray ();
24882519 }
2489-
2520+
2521+ // @since 2.12
2522+ protected byte [] _finishLongContiguousBytes (final int expLen ) throws IOException
2523+ {
2524+ int left = expLen ;
2525+
2526+ // 04-Dec-2020, tatu: Let's NOT use recycled instance since we have much
2527+ // longer content and there is likely less benefit of trying to recycle
2528+ // segments
2529+ try (final ByteArrayBuilder bb = new ByteArrayBuilder (LONGEST_NON_CHUNKED_BINARY >> 1 )) {
2530+ while (left > 0 ) {
2531+ int avail = _inputEnd - _inputPtr ;
2532+ if (avail <= 0 ) {
2533+ if (!loadMore ()) {
2534+ _reportIncompleteBinaryRead (expLen , expLen -left );
2535+ }
2536+ avail = _inputEnd - _inputPtr ;
2537+ }
2538+ int count = Math .min (avail , left );
2539+ bb .write (_inputBuffer , _inputPtr , count );
2540+ _inputPtr += count ;
2541+ left -= count ;
2542+ }
2543+ return bb .toByteArray ();
2544+ }
2545+ }
2546+
24902547 protected final JsonToken _decodeFieldName () throws IOException
24912548 {
24922549 if (_inputPtr >= _inputEnd ) {
@@ -2635,9 +2692,8 @@ protected final void _decodeNonStringName(int ch) throws IOException
26352692 } else if (type == CBORConstants .MAJOR_TYPE_INT_NEG ) {
26362693 name = _numberToName (ch , true );
26372694 } else if (type == CBORConstants .MAJOR_TYPE_BYTES ) {
2638- /* 08-Sep-2014, tatu: As per [Issue#5], there are codecs
2639- * (f.ex. Perl module "CBOR::XS") that use Binary data...
2640- */
2695+ // 08-Sep-2014, tatu: As per [Issue#5], there are codecs
2696+ // (f.ex. Perl module "CBOR::XS") that use Binary data...
26412697 final int blen = _decodeExplicitLength (ch & 0x1F );
26422698 byte [] b = _finishBytes (blen );
26432699 // TODO: Optimize, if this becomes commonly used & bottleneck; we have
@@ -3204,7 +3260,7 @@ private final int _decodeChunkedUTF8_4(int c) throws IOException
32043260 /**********************************************************
32053261 */
32063262
3207- protected final boolean loadMore () throws IOException
3263+ protected boolean loadMore () throws IOException
32083264 {
32093265 if (_inputStream != null ) {
32103266 _currInputProcessed += _inputEnd ;
@@ -3225,7 +3281,7 @@ protected final boolean loadMore() throws IOException
32253281 return false ;
32263282 }
32273283
3228- protected final void loadMoreGuaranteed () throws IOException {
3284+ protected void loadMoreGuaranteed () throws IOException {
32293285 if (!loadMore ()) { _reportInvalidEOF (); }
32303286 }
32313287
@@ -3351,6 +3407,13 @@ protected void _reportInvalidOther(int mask, int ptr) throws JsonParseException
33513407 _reportInvalidOther (mask );
33523408 }
33533409
3410+ // @since 2.12
3411+ protected void _reportIncompleteBinaryRead (int expLen , int actLen ) throws IOException
3412+ {
3413+ _reportInvalidEOF (String .format (" for Binary value: expected %d bytes, only found %d" ,
3414+ expLen , actLen ), _currToken );
3415+ }
3416+
33543417 /*
33553418 /**********************************************************
33563419 /* Internal methods, other
0 commit comments