@@ -64,6 +64,10 @@ private Feature(boolean defaultState) {
6464 private final static double MATH_POW_2_10 = Math .pow (2 , 10 );
6565 private final static double MATH_POW_2_NEG14 = Math .pow (2 , -14 );
6666
67+ // 2.11.4: [dataformats-binary#186] Avoid OOME/DoS for bigger binary;
68+ // read only up to 250k
69+ protected final static int LONGEST_NON_CHUNKED_BINARY = 250_000 ;
70+
6771 /*
6872 /**********************************************************
6973 /* Configuration
@@ -1708,13 +1712,15 @@ public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IO
17081712 }
17091713 }
17101714
1711- private int _readAndWriteBytes (OutputStream out , int total ) throws IOException
1715+ private int _readAndWriteBytes (OutputStream out , final int total ) throws IOException
17121716 {
17131717 int left = total ;
17141718 while (left > 0 ) {
17151719 int avail = _inputEnd - _inputPtr ;
17161720 if (_inputPtr >= _inputEnd ) {
1717- loadMoreGuaranteed ();
1721+ if (!loadMore ()) {
1722+ _reportIncompleteBinaryRead (total , total -left );
1723+ }
17181724 avail = _inputEnd - _inputPtr ;
17191725 }
17201726 int count = Math .min (avail , left );
@@ -2432,33 +2438,55 @@ private final int _nextChunkedByte2() throws IOException
24322438 // either way, got it now
24332439 return _inputBuffer [_inputPtr ++];
24342440 }
2435-
2441+
2442+ /**
2443+ * Helper called to complete reading of binary data ("byte string") in
2444+ * case contents are needed.
2445+ */
24362446 @ SuppressWarnings ("resource" )
24372447 protected byte [] _finishBytes (int len ) throws IOException
24382448 {
2449+ // Chunked?
24392450 // First, simple: non-chunked
2440- if (len > = 0 ) {
2451+ if (len < = 0 ) {
24412452 if (len == 0 ) {
24422453 return NO_BYTES ;
24432454 }
2444- byte [] b = new byte [len ];
2445- if (_inputPtr >= _inputEnd ) {
2446- loadMoreGuaranteed ();
2455+ return _finishChunkedBytes ();
2456+ }
2457+ // Non-chunked, contiguous
2458+ if (len > LONGEST_NON_CHUNKED_BINARY ) {
2459+ // [dataformats-binary#186]: avoid immediate allocation for longest
2460+ return _finishLongContiguousBytes (len );
2461+ }
2462+
2463+ final byte [] b = new byte [len ];
2464+ final int expLen = len ;
2465+ if (_inputPtr >= _inputEnd ) {
2466+ if (!loadMore ()) {
2467+ _reportIncompleteBinaryRead (expLen , 0 );
24472468 }
2448- int ptr = 0 ;
2449- while (true ) {
2450- int toAdd = Math .min (len , _inputEnd - _inputPtr );
2451- System .arraycopy (_inputBuffer , _inputPtr , b , ptr , toAdd );
2452- _inputPtr += toAdd ;
2453- ptr += toAdd ;
2454- len -= toAdd ;
2455- if (len <= 0 ) {
2456- return b ;
2457- }
2458- loadMoreGuaranteed ();
2469+ }
2470+
2471+ int ptr = 0 ;
2472+ while (true ) {
2473+ int toAdd = Math .min (len , _inputEnd - _inputPtr );
2474+ System .arraycopy (_inputBuffer , _inputPtr , b , ptr , toAdd );
2475+ _inputPtr += toAdd ;
2476+ ptr += toAdd ;
2477+ len -= toAdd ;
2478+ if (len <= 0 ) {
2479+ return b ;
2480+ }
2481+ if (!loadMore ()) {
2482+ _reportIncompleteBinaryRead (expLen , ptr );
24592483 }
24602484 }
2485+ }
24612486
2487+ // @since 2.12
2488+ protected byte [] _finishChunkedBytes () throws IOException
2489+ {
24622490 // or, if not, chunked...
24632491 ByteArrayBuilder bb = _getByteArrayBuilder ();
24642492 while (true ) {
@@ -2475,14 +2503,17 @@ protected byte[] _finishBytes(int len) throws IOException
24752503 throw _constructError ("Mismatched chunk in chunked content: expected " +CBORConstants .MAJOR_TYPE_BYTES
24762504 +" but encountered " +type );
24772505 }
2478- len = _decodeExplicitLength (ch & 0x1F );
2506+ int len = _decodeExplicitLength (ch & 0x1F );
24792507 if (len < 0 ) {
24802508 throw _constructError ("Illegal chunked-length indicator within chunked-length value (type " +CBORConstants .MAJOR_TYPE_BYTES +")" );
24812509 }
2510+ final int chunkLen = len ;
24822511 while (len > 0 ) {
24832512 int avail = _inputEnd - _inputPtr ;
24842513 if (_inputPtr >= _inputEnd ) {
2485- loadMoreGuaranteed ();
2514+ if (!loadMore ()) {
2515+ _reportIncompleteBinaryRead (chunkLen , chunkLen -len );
2516+ }
24862517 avail = _inputEnd - _inputPtr ;
24872518 }
24882519 int count = Math .min (avail , len );
@@ -2493,7 +2524,33 @@ protected byte[] _finishBytes(int len) throws IOException
24932524 }
24942525 return bb .toByteArray ();
24952526 }
2496-
2527+
2528+ // @since 2.12
2529+ protected byte [] _finishLongContiguousBytes (final int expLen ) throws IOException
2530+ {
2531+ int left = expLen ;
2532+
2533+ // 04-Dec-2020, tatu: Let's NOT use recycled instance since we have much
2534+ // longer content and there is likely less benefit of trying to recycle
2535+ // segments
2536+ try (final ByteArrayBuilder bb = new ByteArrayBuilder (LONGEST_NON_CHUNKED_BINARY >> 1 )) {
2537+ while (left > 0 ) {
2538+ int avail = _inputEnd - _inputPtr ;
2539+ if (avail <= 0 ) {
2540+ if (!loadMore ()) {
2541+ _reportIncompleteBinaryRead (expLen , expLen -left );
2542+ }
2543+ avail = _inputEnd - _inputPtr ;
2544+ }
2545+ int count = Math .min (avail , left );
2546+ bb .write (_inputBuffer , _inputPtr , count );
2547+ _inputPtr += count ;
2548+ left -= count ;
2549+ }
2550+ return bb .toByteArray ();
2551+ }
2552+ }
2553+
24972554 protected final JsonToken _decodeFieldName () throws IOException
24982555 {
24992556 if (_inputPtr >= _inputEnd ) {
@@ -2642,9 +2699,8 @@ protected final void _decodeNonStringName(int ch) throws IOException
26422699 } else if (type == CBORConstants .MAJOR_TYPE_INT_NEG ) {
26432700 name = _numberToName (ch , true );
26442701 } else if (type == CBORConstants .MAJOR_TYPE_BYTES ) {
2645- /* 08-Sep-2014, tatu: As per [Issue#5], there are codecs
2646- * (f.ex. Perl module "CBOR::XS") that use Binary data...
2647- */
2702+ // 08-Sep-2014, tatu: As per [Issue#5], there are codecs
2703+ // (f.ex. Perl module "CBOR::XS") that use Binary data...
26482704 final int blen = _decodeExplicitLength (ch & 0x1F );
26492705 byte [] b = _finishBytes (blen );
26502706 // TODO: Optimize, if this becomes commonly used & bottleneck; we have
@@ -3249,7 +3305,7 @@ private final int _decodeChunkedUTF8_4(int c) throws IOException
32493305 /**********************************************************
32503306 */
32513307
3252- protected final boolean loadMore () throws IOException
3308+ protected boolean loadMore () throws IOException
32533309 {
32543310 if (_inputStream != null ) {
32553311 _currInputProcessed += _inputEnd ;
@@ -3270,7 +3326,7 @@ protected final boolean loadMore() throws IOException
32703326 return false ;
32713327 }
32723328
3273- protected final void loadMoreGuaranteed () throws IOException {
3329+ protected void loadMoreGuaranteed () throws IOException {
32743330 if (!loadMore ()) { _reportInvalidEOF (); }
32753331 }
32763332
@@ -3395,6 +3451,13 @@ protected void _reportInvalidOther(int mask, int ptr) throws JsonParseException
33953451 _reportInvalidOther (mask );
33963452 }
33973453
3454+ // @since 2.12
3455+ protected void _reportIncompleteBinaryRead (int expLen , int actLen ) throws IOException
3456+ {
3457+ _reportInvalidEOF (String .format (" for Binary value: expected %d bytes, only found %d" ,
3458+ expLen , actLen ), _currToken );
3459+ }
3460+
33983461 /*
33993462 /**********************************************************
34003463 /* Internal methods, other
0 commit comments