Skip to content

Commit 52300dd

Browse files
committed
JSONDecodeError on buffer allocation failure
1 parent 61ab0e3 commit 52300dd

File tree

3 files changed

+40
-1
lines changed

3 files changed

+40
-1
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,9 @@ which the standard library allows, but is not valid JSON.
616616
It raises `JSONDecodeError` if a combination of array or object recurses
617617
1024 levels deep.
618618

619+
It raises `JSONDecodeError` if unable to allocate a buffer large enough
620+
to parse the document.
621+
619622
`JSONDecodeError` is a subclass of `json.JSONDecodeError` and `ValueError`.
620623
This is for compatibility with the standard library.
621624

src/deserialize/backend/yyjson.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const MINIMUM_BUFFER_CAPACITY: usize = 4096;
5050

5151
fn buffer_capacity_to_allocate(len: usize) -> usize {
5252
// The max memory size is (json_size / 2 * 16 * 1.5 + padding).
53-
(((len / 2) * 32) + 256 + (MINIMUM_BUFFER_CAPACITY - 1)) & !(MINIMUM_BUFFER_CAPACITY - 1)
53+
(((len / 2) * 24) + 256 + (MINIMUM_BUFFER_CAPACITY - 1)) & !(MINIMUM_BUFFER_CAPACITY - 1)
5454
}
5555

5656
fn unsafe_yyjson_is_ctn(val: *mut yyjson_val) -> bool {
@@ -73,6 +73,13 @@ pub(crate) fn deserialize(
7373
assume!(!data.is_empty());
7474
let buffer_capacity = buffer_capacity_to_allocate(data.len());
7575
let buffer_ptr = ffi!(PyMem_Malloc(buffer_capacity));
76+
if unlikely!(buffer_ptr.is_null()) {
77+
return Err(DeserializeError::from_yyjson(
78+
Cow::Borrowed("Not enough memory to allocate buffer for parsing"),
79+
0,
80+
data,
81+
));
82+
}
7683
let mut alloc = crate::ffi::yyjson::yyjson_alc {
7784
malloc: None,
7885
realloc: None,

test/test_buffer.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
2+
3+
import os
4+
5+
import pytest
6+
7+
import orjson
8+
9+
ORJSON_RUNNER_MEMORY_GIB = os.getenv("ORJSON_RUNNER_MEMORY_GIB", "")
10+
11+
12+
@pytest.mark.skipif(
13+
not ORJSON_RUNNER_MEMORY_GIB, reason="ORJSON_RUNNER_MEMORY_GIB not defined"
14+
)
15+
def test_memory_loads():
16+
buffer_factor = 12
17+
segment = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
18+
size = (
19+
(int(ORJSON_RUNNER_MEMORY_GIB) * 1024 * 1024 * 1024)
20+
// buffer_factor
21+
// len(segment)
22+
)
23+
doc = "".join((segment for _ in range(0, size)))
24+
with pytest.raises(orjson.JSONDecodeError) as exc_info:
25+
_ = orjson.loads(doc)
26+
assert (
27+
str(exc_info.value)
28+
== "Not enough memory to allocate buffer for parsing: line 1 column 1 (char 0)"
29+
)

0 commit comments

Comments
 (0)