CVE-2020-28491: Allocation of Resources Without Limits or Throttling (CWE-770)
value: This affects the package com.fasterxml.jackson.dataformat:jackson-dataformat-cbor from 0 and before 2.11.4, from 2.12.0-rc1 and before 2.12.1. Unchecked allocation of byte buffer can cause a java.lang.OutOfMemoryError exception.
CWE description: The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
CVSS v3 base score: 7.5
Published: 2021-02-18T16:15Z
Repository: https://github.com/FasterXML/jackson-dataformats-binary
Language: Java
Files changed: cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORGenerator.java, cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java, cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/BrokenLongBinary186Test.java, release-notes/CREDITS-2.x, release-notes/VERSION-2.x
Commit date: 2020-12-04 16:27:55 -0800
Commit message: Fix eager allocation aspect of #186

Diff: 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 275826c..880e585 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
@@ -196,11 +196,11 @@ public class CBORGenerator extends GeneratorBase
 
     protected int _elementCountsPtr;
 
     /**
      * 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 76e0fe6..06a69fa 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
@@ -61,10 +61,14 @@ public class CBORParser extends ParserMinimalBase
 
     // Constants for handling of 16-bit "mini-floats"
     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
     /**********************************************************
      */
@@ -1704,17 +1708,19 @@ public class CBORParser extends ParserMinimalBase
             }
             total += _readAndWriteBytes(out, len);
         }
     }
 
-    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);
             out.write(_inputBuffer, _inputPtr, count);
             _inputPtr += count;
@@ -2423,37 +2429,59 @@ public class CBORParser extends ParserMinimalBase
             _chunkEnd = _inputEnd;
         }
         // 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) {
             if (_inputPtr >= _inputEnd) {
                 loadMoreGuaranteed();
@@ -2466,29 +2494,58 @@ public class CBORParser extends ParserMinimalBase
             int type = (ch >> 5);
             if (type != CBORConstants.MAJOR_TYPE_BYTES) {
                 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);
                 bb.write(_inputBuffer, _inputPtr, count);
                 _inputPtr += count;
                 len -= count;
             }
         }
         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) {
             loadMoreGuaranteed();
         }
@@ -2633,13 +2690,12 @@ public class CBORParser extends ParserMinimalBase
         if (type == CBORConstants.MAJOR_TYPE_INT_POS) {
             name = _numberToName(ch, false);
         } 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
             //  more optimized UTF-8 codecs available.
             name = new String(b, UTF8);
@@ -3202,11 +3258,11 @@ public class CBORParser extends ParserMinimalBase
     /**********************************************************
     /* Low-level reading, other
     /**********************************************************
      */
 
-    protected final boolean loadMore() throws IOException
+    protected boolean loadMore() throws IOException
     {
         if (_inputStream != null) {
             _currInputProcessed += _inputEnd;
 
             int count = _inputStream.read(_inputBuffer, 0, _inputBuffer.length);
@@ -3223,11 +3279,11 @@ public class CBORParser extends ParserMinimalBase
             }
         }
         return false;
     }
 
-    protected final void loadMoreGuaranteed() throws IOException {
+    protected void loadMoreGuaranteed() throws IOException {
         if (!loadMore()) { _reportInvalidEOF(); }
     }
     
     /**
      * Helper method that will try to load at least specified number bytes in
@@ -3349,10 +3405,17 @@ public class CBORParser extends ParserMinimalBase
     protected void _reportInvalidOther(int mask, int ptr) throws JsonParseException {
         _inputPtr = ptr;
         _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
     /**********************************************************
      */
diff --git a/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/BrokenLongBinary186Test.java b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/BrokenLongBinary186Test.java
new file mode 100644
index 0000000..be489af
--- /dev/null
+++ b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/BrokenLongBinary186Test.java
@@ -0,0 +1,93 @@
+package com.fasterxml.jackson.dataformat.cbor;
+
+import java.io.ByteArrayOutputStream;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+// Mostly for [dataformats-binary#186]: corrupt encoding indicating humongous payload
+public class BrokenLongBinary186Test extends CBORTestBase
+{
+    private final ObjectMapper MAPPER = cborMapper();
+
+    /*
+    /**********************************************************************
+    /* First regular, read-it-all access, from non-chunked
+    /**********************************************************************
+     */
+
+    // [dataformats-binary#186]
+    public void testCorruptVeryLongBinary() throws Exception {
+        // Let's do about 2 GB to likely trigger failure
+        _testCorruptLong(1_999_999_999, 95000);
+    }
+
+    // [dataformats-binary#186]
+    public void testCorruptQuiteLongBinary() throws Exception {
+        // Value below limit for chunked handling
+        _testCorruptLong(CBORParser.LONGEST_NON_CHUNKED_BINARY >> 1, 37);
+    }
+
+    private void _testCorruptLong(int allegedLength, int actualIncluded) throws Exception
+    {
+        JsonParser p = MAPPER.createParser(_createBrokenDoc(allegedLength, actualIncluded));
+        assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
+        try {
+            p.getBinaryValue();
+            fail("Should fail");
+        } catch (JsonProcessingException e) {
+            verifyException(e, "Unexpected end-of-input for Binary value");
+            verifyException(e, "expected "+allegedLength+" bytes, only found "+actualIncluded);
+        }
+    }
+
+    /*
+    /**********************************************************************
+    /* And then "streaming" access
+    /**********************************************************************
+     */
+
+    // [dataformats-binary#186]
+    public void testQuiteLongStreaming() throws Exception
+    {
+        // Can try bit shorter here, like 500 megs
+        final int allegedLength = 500_000_000;
+        
+        JsonParser p = MAPPER.createParser(_createBrokenDoc(allegedLength, 72000));
+        assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
+        try {
+            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+            p.readBinaryValue(bytes);
+            fail("Should fail");
+        } catch (JsonProcessingException e) {
+            verifyException(e, "Unexpected end-of-input for Binary value");
+            verifyException(e, "expected "+allegedLength+" bytes, only found 72000");
+        }
+    }
+
+    private byte[] _createBrokenDoc(int allegedLength, int actualIncluded) throws Exception
+    {
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+
+        if (allegedLength > 0xFFFF) {
+            bytes.write((byte) (CBORConstants.PREFIX_TYPE_BYTES | CBORConstants.SUFFIX_UINT32_ELEMENTS));
+            bytes.write((byte) (allegedLength >> 24));
+            bytes.write((byte) (allegedLength >> 16));
+            bytes.write((byte) (allegedLength >> 8));
+            bytes.write((byte) allegedLength);
+        } else { // assume shorter
+            bytes.write((byte) (CBORConstants.PREFIX_TYPE_BYTES | CBORConstants.SUFFIX_UINT16_ELEMENTS));
+            bytes.write((byte) (allegedLength >> 8));
+            bytes.write((byte) allegedLength);
+        }
+        // but only include couple of bytes
+        for (int i = 0; i < actualIncluded; ++i) {
+            bytes.write((byte) i);
+        }
+        return bytes.toByteArray();
+    }
+
+}
diff --git a/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/failing/BrokenLongBinary186Test.java b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/failing/BrokenLongBinary186Test.java
deleted file mode 100644
index 3346b3c..0000000
--- a/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/failing/BrokenLongBinary186Test.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.fasterxml.jackson.dataformat.cbor.failing;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.JsonToken;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.dataformat.cbor.CBORConstants;
-import com.fasterxml.jackson.dataformat.cbor.CBORTestBase;
-
-// Mostly for [dataformats-binary#186]: corrupt encoding indicating humongous payload
-public class BrokenLongBinary186Test extends CBORTestBase
-{
-    private final ObjectMapper MAPPER = cborMapper();
-
-    // [dataformats-binary#186]
-    public void testCorruptVeryLongBinary() throws Exception
-    {
-        // Let's do 999,999,999 bytes to likely trigger failure
-        final int LONG_LEN = 999_999_999;
-        byte[] DOC = new byte[] {
-                        (byte) (CBORConstants.PREFIX_TYPE_BYTES | CBORConstants.SUFFIX_UINT32_ELEMENTS),
-                        (byte) (LONG_LEN >> 24),
-                        (byte) (LONG_LEN >> 16),
-                        (byte) (LONG_LEN >> 8),
-                        (byte) LONG_LEN,
-                        // but only include 2 bytes
-                        0, 0
-        };
-        JsonParser p = MAPPER.createParser(DOC);
-        assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
-        try {
-            p.getBinaryValue();
-            fail("Should fail");
-        } catch (JsonProcessingException e) {
-//            e.printStackTrace();
-
-            // 01-Dec-2020, tatu: Need to decide what kind of exception should be
-            //    produced...
-            verifyException(e, "foobar");
-        }
-    }
-}
diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x
index b802adc..790996c 100644
--- a/release-notes/CREDITS-2.x
+++ b/release-notes/CREDITS-2.x
@@ -114,12 +114,15 @@ Marcos Passos (marcospassos@github)
 John (iziamos@github)
 * Reported, suggested fix for #178: Fix issue wit input offsets when parsing CBOR from `InputStream`
  (2.10.0)
 
 Paul Adolph (padolph@github)
-* Reported #185: Internal parsing of tagged arrays can lead to stack overflow
+* Reported #185: (cbor) Internal parsing of tagged arrays can lead to stack overflow
  (2.10.1)
+* Reported #186: (cbor) Eager allocation of byte buffer can cause `java.lang.OutOfMemoryError`
+   exception
+ (2.11.4)
 
 Yanming Zhou (quaff@github)
 * Reported #188: Unexpected `MismatchedInputException` for `byte[]` value bound to `String`
   in collection/array
  (2.10.1)
diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x
index dce88e4..8325be1 100644
--- a/release-notes/VERSION-2.x
+++ b/release-notes/VERSION-2.x
@@ -6,13 +6,19 @@ Project: jackson-datatypes-binaryModules:
 
 ------------------------------------------------------------------------
 === Releases ===
 ------------------------------------------------------------------------
 
+2.11.4 (not yet released)
+
+#186: (cbor) Eager allocation of byte buffer can cause `java.lang.OutOfMemoryError`
+   exception
+ (reported by Paul A)
+
 2.11.3 (02-Oct-2020)
 
-#219: Cache record names to avoid hitting class loader
+#219: (avro) Cache record names to avoid hitting class loader
  (contributed by Marcos P)
 
 2.11.2 (02-Aug-2020)
 
 #216: (avro) Avro null deserialization

Vulnerable code:      * when writing defined-length Arrays, Objects; marker {@link #INDEFINITE_LENGTH}
    private int _readAndWriteBytes(OutputStream out, int total) throws IOException
                loadMoreGuaranteed();
    
        if (len >= 0) {
            byte[] b = new byte[len];
            if (_inputPtr >= _inputEnd) {
                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;
                }
                loadMoreGuaranteed();
            len = _decodeExplicitLength(ch & 0x1F);
                    loadMoreGuaranteed();
    
            /* 08-Sep-2014, tatu: As per [Issue#5], there are codecs
             *   (f.ex. Perl module "CBOR::XS") that use Binary data...
             */
    protected final boolean loadMore() throws IOException
    protected final void loadMoreGuaranteed() throws IOException {
package com.fasterxml.jackson.dataformat.cbor.failing;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.cbor.CBORConstants;
import com.fasterxml.jackson.dataformat.cbor.CBORTestBase;

// Mostly for [dataformats-binary#186]: corrupt encoding indicating humongous payload
public class BrokenLongBinary186Test extends CBORTestBase
{
    private final ObjectMapper MAPPER = cborMapper();

    // [dataformats-binary#186]
    public void testCorruptVeryLongBinary() throws Exception
    {
        // Let's do 999,999,999 bytes to likely trigger failure
        final int LONG_LEN = 999_999_999;
        byte[] DOC = new byte[] {
                        (byte) (CBORConstants.PREFIX_TYPE_BYTES | CBORConstants.SUFFIX_UINT32_ELEMENTS),
                        (byte) (LONG_LEN >> 24),
                        (byte) (LONG_LEN >> 16),
                        (byte) (LONG_LEN >> 8),
                        (byte) LONG_LEN,
                        // but only include 2 bytes
                        0, 0
        };
        JsonParser p = MAPPER.createParser(DOC);
        assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
        try {
            p.getBinaryValue();
            fail("Should fail");
        } catch (JsonProcessingException e) {
//            e.printStackTrace();

            // 01-Dec-2020, tatu: Need to decide what kind of exception should be
            //    produced...
            verifyException(e, "foobar");
        }
    }
}
* Reported #185: Internal parsing of tagged arrays can lead to stack overflow
#219: Cache record names to avoid hitting class loader
Fixed code:      * when writing defined-length Arrays, Objects; marker {code INDEFINITE_LENGTH}
    // 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;

    private int _readAndWriteBytes(OutputStream out, final int total) throws IOException
                if (!loadMore()) {
                    _reportIncompleteBinaryRead(total, total-left);
                }

    /**
     * Helper called to complete reading of binary data ("byte string") in
     * case contents are needed.
     */
        // Chunked?
        if (len <= 0) {
            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;
            }
            if (!loadMore()) {
                _reportIncompleteBinaryRead(expLen, ptr);
    }
    // @since 2.12
    protected byte[] _finishChunkedBytes() throws IOException
    {
            int len = _decodeExplicitLength(ch & 0x1F);
            final int chunkLen = len;
                    if (!loadMore()) {
                        _reportIncompleteBinaryRead(chunkLen, chunkLen-len);
                    }

    // @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();
        }
    }

            // 08-Sep-2014, tatu: As per [Issue#5], there are codecs
            //   (f.ex. Perl module "CBOR::XS") that use Binary data...
    protected boolean loadMore() throws IOException
    protected void loadMoreGuaranteed() throws IOException {
    // @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);
    }

package com.fasterxml.jackson.dataformat.cbor;

import java.io.ByteArrayOutputStream;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;

import com.fasterxml.jackson.databind.ObjectMapper;

// Mostly for [dataformats-binary#186]: corrupt encoding indicating humongous payload
public class BrokenLongBinary186Test extends CBORTestBase
{
    private final ObjectMapper MAPPER = cborMapper();

    /*
    /**********************************************************************
    /* First regular, read-it-all access, from non-chunked
    /**********************************************************************
     */

    // [dataformats-binary#186]
    public void testCorruptVeryLongBinary() throws Exception {
        // Let's do about 2 GB to likely trigger failure
        _testCorruptLong(1_999_999_999, 95000);
    }

    // [dataformats-binary#186]
    public void testCorruptQuiteLongBinary() throws Exception {
        // Value below limit for chunked handling
        _testCorruptLong(CBORParser.LONGEST_NON_CHUNKED_BINARY >> 1, 37);
    }

    private void _testCorruptLong(int allegedLength, int actualIncluded) throws Exception
    {
        JsonParser p = MAPPER.createParser(_createBrokenDoc(allegedLength, actualIncluded));
        assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
        try {
            p.getBinaryValue();
            fail("Should fail");
        } catch (JsonProcessingException e) {
            verifyException(e, "Unexpected end-of-input for Binary value");
            verifyException(e, "expected "+allegedLength+" bytes, only found "+actualIncluded);
        }
    }

    /*
    /**********************************************************************
    /* And then "streaming" access
    /**********************************************************************
     */

    // [dataformats-binary#186]
    public void testQuiteLongStreaming() throws Exception
    {
        // Can try bit shorter here, like 500 megs
        final int allegedLength = 500_000_000;
        
        JsonParser p = MAPPER.createParser(_createBrokenDoc(allegedLength, 72000));
        assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
        try {
            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            p.readBinaryValue(bytes);
            fail("Should fail");
        } catch (JsonProcessingException e) {
            verifyException(e, "Unexpected end-of-input for Binary value");
            verifyException(e, "expected "+allegedLength+" bytes, only found 72000");
        }
    }

    private byte[] _createBrokenDoc(int allegedLength, int actualIncluded) throws Exception
    {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();

        if (allegedLength > 0xFFFF) {
            bytes.write((byte) (CBORConstants.PREFIX_TYPE_BYTES | CBORConstants.SUFFIX_UINT32_ELEMENTS));
            bytes.write((byte) (allegedLength >> 24));
            bytes.write((byte) (allegedLength >> 16));
            bytes.write((byte) (allegedLength >> 8));
            bytes.write((byte) allegedLength);
        } else { // assume shorter
            bytes.write((byte) (CBORConstants.PREFIX_TYPE_BYTES | CBORConstants.SUFFIX_UINT16_ELEMENTS));
            bytes.write((byte) (allegedLength >> 8));
            bytes.write((byte) allegedLength);
        }
        // but only include couple of bytes
        for (int i = 0; i < actualIncluded; ++i) {
            bytes.write((byte) i);
        }
        return bytes.toByteArray();
    }

}
* Reported #185: (cbor) Internal parsing of tagged arrays can lead to stack overflow
* Reported #186: (cbor) Eager allocation of byte buffer can cause `java.lang.OutOfMemoryError`
   exception
 (2.11.4)
2.11.4 (not yet released)

#186: (cbor) Eager allocation of byte buffer can cause `java.lang.OutOfMemoryError`
   exception
 (reported by Paul A)

#219: (avro) Cache record names to avoid hitting class loader