Skip to content

Commit 7bcc37e

Browse files
committed
Raise OverflowError for oversized pickle lengths
Extract the Python 3.14-compatible size-decoding error class and messages from #8954. Reject oversized advertised lengths before payload reads or allocation. Use bounded header-only regressions in the existing pickle snippet without changing canonical assertions. Assisted-by: OpenAI Codex:model identifier unavailable
1 parent 3af5f8a commit 7bcc37e

2 files changed

Lines changed: 33 additions & 6 deletions

File tree

‎crates/stdlib/src/pickle.rs‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -818,13 +818,18 @@ mod _pickle {
818818
.ok()
819819
.filter(|n| isize::try_from(*n).is_ok())
820820
.ok_or_else(|| {
821-
new_unpickling_error(
822-
vm,
821+
let message = if what == "FRAME" {
822+
format!(
823+
"FRAME length exceeds system's maximum of {} bytes",
824+
isize::MAX
825+
)
826+
} else {
823827
format!(
824828
"{what} exceeds system's maximum size of {} bytes",
825829
isize::MAX
826-
),
827-
)
830+
)
831+
};
832+
vm.new_overflow_error(message)
828833
})
829834
}
830835

@@ -1090,7 +1095,7 @@ mod _pickle {
10901095
}
10911096
Some(BINBYTES8) => {
10921097
let arr: [u8; 8] = st.read_n(8, vm)?.try_into().unwrap();
1093-
let n = read_size(&arr, vm, "BINBYTES8")?;
1098+
let n = read_size(&arr, vm, "BINBYTES")?;
10941099
let data = st.read_n(n, vm)?.to_vec();
10951100
stack.push(vm.ctx.new_bytes(data).into());
10961101
}
@@ -1129,7 +1134,7 @@ mod _pickle {
11291134
}
11301135
Some(BINUNICODE8) => {
11311136
let arr: [u8; 8] = st.read_n(8, vm)?.try_into().unwrap();
1132-
let n = read_size(&arr, vm, "BINUNICODE8")?;
1137+
let n = read_size(&arr, vm, "BINUNICODE")?;
11331138
let data = st.read_n(n, vm)?.to_vec();
11341139
stack.push(utf8_surrogatepass(&data, vm)?);
11351140
}

‎extra_tests/snippets/stdlib_pickle.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import _pickle
22
import io
3+
import sys
4+
5+
from testutils import assert_raises
36

47
# BINPUT leaves holes; MEMOIZE uses the number of present entries.
58
unpickler = _pickle.Unpickler(io.BytesIO(b"\x80\x04]q\x07\x94h\x01\x86."))
@@ -9,3 +12,22 @@
912
assert list(memo) == [1, 7]
1013
assert memo[1] is left
1114
assert memo[7] is left
15+
16+
# Oversized advertised lengths are rejected before trying to read or allocate.
17+
for opcode, name in (
18+
(b"\x8e", "BINBYTES"),
19+
(b"\x8d", "BINUNICODE"),
20+
(b"\x95", "FRAME"),
21+
):
22+
data = b"\x80\x04" + opcode + (sys.maxsize + 1).to_bytes(8, "little")
23+
for load in (
24+
_pickle.loads,
25+
lambda data: _pickle.Unpickler(io.BytesIO(data)).load(),
26+
):
27+
with assert_raises(OverflowError) as caught:
28+
load(data)
29+
if name == "FRAME":
30+
expected = f"FRAME length exceeds system's maximum of {sys.maxsize} bytes"
31+
else:
32+
expected = f"{name} exceeds system's maximum size of {sys.maxsize} bytes"
33+
assert str(caught.exception) == expected

0 commit comments

Comments
 (0)