@@ -63,6 +63,10 @@ private Feature(boolean defaultState) {
63
63
private final static double MATH_POW_2_10 = Math .pow (2 , 10 );
64
64
private final static double MATH_POW_2_NEG14 = Math .pow (2 , -14 );
65
65
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
+
66
70
/*
67
71
/**********************************************************
68
72
/* Configuration
@@ -1706,13 +1710,15 @@ public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IO
1706
1710
}
1707
1711
}
1708
1712
1709
- private int _readAndWriteBytes (OutputStream out , int total ) throws IOException
1713
+ private int _readAndWriteBytes (OutputStream out , final int total ) throws IOException
1710
1714
{
1711
1715
int left = total ;
1712
1716
while (left > 0 ) {
1713
1717
int avail = _inputEnd - _inputPtr ;
1714
1718
if (_inputPtr >= _inputEnd ) {
1715
- loadMoreGuaranteed ();
1719
+ if (!loadMore ()) {
1720
+ _reportIncompleteBinaryRead (total , total -left );
1721
+ }
1716
1722
avail = _inputEnd - _inputPtr ;
1717
1723
}
1718
1724
int count = Math .min (avail , left );
@@ -2425,33 +2431,55 @@ private final int _nextChunkedByte2() throws IOException
2425
2431
// either way, got it now
2426
2432
return _inputBuffer [_inputPtr ++];
2427
2433
}
2428
-
2434
+
2435
+ /**
2436
+ * Helper called to complete reading of binary data ("byte string") in
2437
+ * case contents are needed.
2438
+ */
2429
2439
@ SuppressWarnings ("resource" )
2430
2440
protected byte [] _finishBytes (int len ) throws IOException
2431
2441
{
2442
+ // Chunked?
2432
2443
// First, simple: non-chunked
2433
- if (len > = 0 ) {
2444
+ if (len < = 0 ) {
2434
2445
if (len == 0 ) {
2435
2446
return NO_BYTES ;
2436
2447
}
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 );
2440
2461
}
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 );
2452
2476
}
2453
2477
}
2478
+ }
2454
2479
2480
+ // @since 2.12
2481
+ protected byte [] _finishChunkedBytes () throws IOException
2482
+ {
2455
2483
// or, if not, chunked...
2456
2484
ByteArrayBuilder bb = _getByteArrayBuilder ();
2457
2485
while (true ) {
@@ -2468,14 +2496,17 @@ protected byte[] _finishBytes(int len) throws IOException
2468
2496
throw _constructError ("Mismatched chunk in chunked content: expected " +CBORConstants .MAJOR_TYPE_BYTES
2469
2497
+" but encountered " +type );
2470
2498
}
2471
- len = _decodeExplicitLength (ch & 0x1F );
2499
+ int len = _decodeExplicitLength (ch & 0x1F );
2472
2500
if (len < 0 ) {
2473
2501
throw _constructError ("Illegal chunked-length indicator within chunked-length value (type " +CBORConstants .MAJOR_TYPE_BYTES +")" );
2474
2502
}
2503
+ final int chunkLen = len ;
2475
2504
while (len > 0 ) {
2476
2505
int avail = _inputEnd - _inputPtr ;
2477
2506
if (_inputPtr >= _inputEnd ) {
2478
- loadMoreGuaranteed ();
2507
+ if (!loadMore ()) {
2508
+ _reportIncompleteBinaryRead (chunkLen , chunkLen -len );
2509
+ }
2479
2510
avail = _inputEnd - _inputPtr ;
2480
2511
}
2481
2512
int count = Math .min (avail , len );
@@ -2486,7 +2517,33 @@ protected byte[] _finishBytes(int len) throws IOException
2486
2517
}
2487
2518
return bb .toByteArray ();
2488
2519
}
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
+
2490
2547
protected final JsonToken _decodeFieldName () throws IOException
2491
2548
{
2492
2549
if (_inputPtr >= _inputEnd ) {
@@ -2635,9 +2692,8 @@ protected final void _decodeNonStringName(int ch) throws IOException
2635
2692
} else if (type == CBORConstants .MAJOR_TYPE_INT_NEG ) {
2636
2693
name = _numberToName (ch , true );
2637
2694
} 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...
2641
2697
final int blen = _decodeExplicitLength (ch & 0x1F );
2642
2698
byte [] b = _finishBytes (blen );
2643
2699
// TODO: Optimize, if this becomes commonly used & bottleneck; we have
@@ -3204,7 +3260,7 @@ private final int _decodeChunkedUTF8_4(int c) throws IOException
3204
3260
/**********************************************************
3205
3261
*/
3206
3262
3207
- protected final boolean loadMore () throws IOException
3263
+ protected boolean loadMore () throws IOException
3208
3264
{
3209
3265
if (_inputStream != null ) {
3210
3266
_currInputProcessed += _inputEnd ;
@@ -3225,7 +3281,7 @@ protected final boolean loadMore() throws IOException
3225
3281
return false ;
3226
3282
}
3227
3283
3228
- protected final void loadMoreGuaranteed () throws IOException {
3284
+ protected void loadMoreGuaranteed () throws IOException {
3229
3285
if (!loadMore ()) { _reportInvalidEOF (); }
3230
3286
}
3231
3287
@@ -3351,6 +3407,13 @@ protected void _reportInvalidOther(int mask, int ptr) throws JsonParseException
3351
3407
_reportInvalidOther (mask );
3352
3408
}
3353
3409
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
+
3354
3417
/*
3355
3418
/**********************************************************
3356
3419
/* Internal methods, other
0 commit comments