Skip to content

Commit 2809328

Browse files
authored
Consistently raise ValueError on corrupted cache data and test more (#20153)
Test random and arbitrary cache data. Deserialization should fail in a predictable manner.
1 parent 630a143 commit 2809328

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed

mypyc/lib-rt/librt_internal.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,11 @@ read_str_internal(PyObject *data) {
319319
CPyTagged tagged_size = _read_short_int(data, first);
320320
if (tagged_size == CPY_INT_TAG)
321321
return NULL;
322+
if ((Py_ssize_t)tagged_size < 0) {
323+
// Fail fast for invalid/tampered data.
324+
PyErr_SetString(PyExc_ValueError, "invalid str size");
325+
return NULL;
326+
}
322327
Py_ssize_t size = tagged_size >> 1;
323328
// Read string content.
324329
char *buf = ((BufferObject *)data)->buf;
@@ -437,6 +442,11 @@ read_bytes_internal(PyObject *data) {
437442
CPyTagged tagged_size = _read_short_int(data, first);
438443
if (tagged_size == CPY_INT_TAG)
439444
return NULL;
445+
if ((Py_ssize_t)tagged_size < 0) {
446+
// Fail fast for invalid/tampered data.
447+
PyErr_SetString(PyExc_ValueError, "invalid bytes size");
448+
return NULL;
449+
}
440450
Py_ssize_t size = tagged_size >> 1;
441451
// Read bytes content.
442452
char *buf = ((BufferObject *)data)->buf;
@@ -601,6 +611,10 @@ read_int_internal(PyObject *data) {
601611
Py_ssize_t size_and_sign = _read_short_int(data, first);
602612
if (size_and_sign == CPY_INT_TAG)
603613
return CPY_INT_TAG;
614+
if ((Py_ssize_t)size_and_sign < 0) {
615+
PyErr_SetString(PyExc_ValueError, "invalid int data");
616+
return CPY_INT_TAG;
617+
}
604618
bool sign = (size_and_sign >> 1) & 1;
605619
Py_ssize_t size = size_and_sign >> 2;
606620

mypyc/test-data/run-classes.test

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5359,3 +5359,71 @@ def test_deletable_attr() -> None:
53595359
assert i.del_counter == 1
53605360

53615361
test_deletable_attr()
5362+
5363+
[case testBufferCorruptedData_librt_internal]
5364+
from librt.internal import (
5365+
Buffer, read_bool, read_str, read_float, read_int, read_tag, read_bytes
5366+
)
5367+
from random import randbytes
5368+
5369+
def check(data: bytes) -> None:
5370+
b = Buffer(data)
5371+
try:
5372+
while True:
5373+
read_bool(b)
5374+
except ValueError:
5375+
pass
5376+
b = Buffer(data)
5377+
read_tag(b) # Always succeeds
5378+
try:
5379+
while True:
5380+
read_int(b)
5381+
except ValueError:
5382+
pass
5383+
b = Buffer(data)
5384+
try:
5385+
while True:
5386+
read_str(b)
5387+
except ValueError:
5388+
pass
5389+
b = Buffer(data)
5390+
try:
5391+
while True:
5392+
read_bytes(b)
5393+
except ValueError:
5394+
pass
5395+
b = Buffer(data)
5396+
try:
5397+
while True:
5398+
read_float(b)
5399+
except ValueError:
5400+
pass
5401+
5402+
import time
5403+
5404+
def test_read_corrupted_data() -> None:
5405+
# Test various deterministic byte sequences (1 to 4 bytes).
5406+
t0 = time.time()
5407+
for a in range(256):
5408+
check(bytes([a]))
5409+
for a in range(256):
5410+
for b in range(256):
5411+
check(bytes([a, b]))
5412+
for a in range(32):
5413+
for b in range(48):
5414+
for c in range(48):
5415+
check(bytes([a, b, c]))
5416+
for a in range(32):
5417+
for b in (0, 5, 17, 34):
5418+
for c in (0, 5, 17, 34):
5419+
for d in (0, 5, 17, 34):
5420+
check(bytes([a, b, c, d]))
5421+
# Also test some random data.
5422+
for i in range(20000):
5423+
data = randbytes(16)
5424+
try:
5425+
check(data)
5426+
except BaseException as e:
5427+
print("RANDOMIZED TEST FAILURE -- please open an issue with the following context:")
5428+
print(">>>", e, data)
5429+
raise

0 commit comments

Comments
 (0)