Skip to content

Commit

Permalink
Fixed C decoder failing to decode byte strings longer than 65536 bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
agronholm committed Feb 1, 2024
1 parent 8a6da70 commit 6d8e2a4
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 15 deletions.
2 changes: 2 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ This library adheres to `Semantic Versioning <http://semver.org/>`_.
**UNRELEASED**

- Fixed use-after-free in the decoder's C version when prematurely encountering the end of stream
- Fixed the C version of the decoder improperly raising ``CBORDecodeEOF`` when decoding a text
string longer than 65536 bytes

**5.6.0** (2024-01-17)

Expand Down
13 changes: 8 additions & 5 deletions source/decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -597,22 +597,22 @@ decode_definite_long_bytestring(CBORDecoderObject *self, Py_ssize_t length)
PyObject *buffer = NULL;
Py_ssize_t left = length;
while (left) {
Py_ssize_t chunk_length = length <= 65536 ? length : 65536;
Py_ssize_t chunk_length = left <= 65536 ? left : 65536;
PyObject *chunk = fp_read_object(self, chunk_length);
if (!chunk) {
break;
goto error;
}

if (!PyBytes_CheckExact(chunk)) {
Py_DECREF(chunk);
break;
goto error;
}

if (buffer) {
PyObject *new_buffer = PyByteArray_Concat(buffer, chunk);
Py_DECREF(chunk);
if (!new_buffer)
break;
goto error;

if (new_buffer != buffer) {
Py_DECREF(buffer);
Expand All @@ -622,7 +622,7 @@ decode_definite_long_bytestring(CBORDecoderObject *self, Py_ssize_t length)
buffer = PyByteArray_FromObject(chunk);
Py_DECREF(chunk);
if (!buffer)
break;
goto error;
}
left -= chunk_length;
}
Expand All @@ -638,6 +638,9 @@ decode_definite_long_bytestring(CBORDecoderObject *self, Py_ssize_t length)
}
}
return ret;
error:
Py_XDECREF(buffer);
return NULL;
}


Expand Down
13 changes: 3 additions & 10 deletions tests/test_decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
from uuid import UUID

import pytest
from hypothesis import example, given
from hypothesis.strategies import binary

from cbor2 import FrozenDict

Expand Down Expand Up @@ -215,21 +213,16 @@ def test_special(impl, special_values):
@pytest.mark.parametrize(
"payload, expected",
[
("40", b""),
("4401020304", b"\x01\x02\x03\x04"),
pytest.param("40", b"", id="blank"),
pytest.param("4401020304", b"\x01\x02\x03\x04", id="short"),
pytest.param("5a00011170" + "12" * 70000, b"\x12" * 70000, id="long"),
],
)
def test_binary(impl, payload, expected):
decoded = impl.loads(unhexlify(payload))
assert decoded == expected


@given(binary(min_size=2**6, max_size=2**20))
@example(b"\x12" * 65537) # anything over 2**16 fails in C
def test_binary_roundtrip(impl, expected):
assert expected == impl.loads(impl.dumps(expected)), "Binary string fails to round-trip"


@pytest.mark.parametrize(
"payload, expected",
[
Expand Down

0 comments on commit 6d8e2a4

Please sign in to comment.