Skip to content

Commit 31170c4

Browse files
Merge branch 'main' into fix-compression-block-decompress-error-handling
2 parents 043fb4a + 3fc945d commit 31170c4

5 files changed

Lines changed: 30 additions & 4 deletions

File tree

Lib/test/test_decimal.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3963,15 +3963,21 @@ def test_flag_comparisons(self):
39633963
d.update(c.flags)
39643964
self.assertEqual(d, c.flags)
39653965
self.assertEqual(c.flags, d)
3966+
self.assertEqual(frozendict(d), c.flags)
3967+
self.assertEqual(c.flags, frozendict(d))
39663968

39673969
d[Inexact] = True
39683970
self.assertNotEqual(d, c.flags)
39693971
self.assertNotEqual(c.flags, d)
3972+
self.assertNotEqual(frozendict(d), c.flags)
3973+
self.assertNotEqual(c.flags, frozendict(d))
39703974

39713975
# Invalid SignalDict
39723976
d = {Inexact:False}
39733977
self.assertNotEqual(d, c.flags)
39743978
self.assertNotEqual(c.flags, d)
3979+
self.assertNotEqual(frozendict(d), c.flags)
3980+
self.assertNotEqual(c.flags, frozendict(d))
39753981

39763982
d = ["xyz"]
39773983
self.assertNotEqual(d, c.flags)

Lib/test/test_source_encoding.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,23 @@ def test_issue7820(self):
6565
# two bytes in common with the UTF-8 BOM
6666
self.assertRaises(SyntaxError, eval, b'\xef\xbb\x20')
6767

68+
def test_truncated_utf8_at_eof(self):
69+
# Regression test for https://issues.oss-fuzz.com/issues/451112368
70+
# Truncated multi-byte UTF-8 sequences at end of input caused an
71+
# out-of-bounds read in Parser/tokenizer/helpers.c:valid_utf8().
72+
truncated = [
73+
b'\xc2', # 2-byte lead, missing 1 continuation
74+
b'\xdf', # 2-byte lead, missing 1 continuation
75+
b'\xe0', # 3-byte lead, missing 2 continuations
76+
b'\xe0\xa0', # 3-byte lead, missing 1 continuation
77+
b'\xf0\x90', # 4-byte lead, missing 2 continuations
78+
b'\xf0\x90\x80', # 4-byte lead, missing 1 continuation
79+
b'\xf3', # 4-byte lead, missing 3 (the oss-fuzz reproducer)
80+
]
81+
for seq in truncated:
82+
with self.subTest(seq=seq):
83+
self.assertRaises(SyntaxError, compile, seq, '<test>', 'exec')
84+
6885
@support.requires_subprocess()
6986
def test_20731(self):
7087
sub = subprocess.Popen([sys.executable,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix heap buffer overflow in the parser found by OSS-Fuzz.

Modules/_decimal/_decimal.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ dict_as_flags(decimal_state *state, PyObject *val)
552552
uint32_t flags = 0;
553553
int x;
554554

555-
if (!PyDict_Check(val)) {
555+
if (!PyAnyDict_Check(val)) {
556556
PyErr_SetString(PyExc_TypeError,
557557
"argument must be a signal dict");
558558
return DEC_INVALID_SIGNALS;
@@ -802,7 +802,7 @@ signaldict_richcompare(PyObject *v, PyObject *w, int op)
802802
if (PyDecSignalDict_Check(state, w)) {
803803
res = (SdFlags(v)==SdFlags(w)) ^ (op==Py_NE) ? Py_True : Py_False;
804804
}
805-
else if (PyDict_Check(w)) {
805+
else if (PyAnyDict_Check(w)) {
806806
uint32_t flags = dict_as_flags(state, w);
807807
if (flags & DEC_ERRORS) {
808808
if (flags & DEC_INVALID_SIGNALS) {

Parser/tokenizer/helpers.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -494,9 +494,11 @@ valid_utf8(const unsigned char* s)
494494
return 0;
495495
}
496496
length = expected + 1;
497-
for (; expected; expected--)
498-
if (s[expected] < 0x80 || s[expected] >= 0xC0)
497+
for (int i = 1; i <= expected; i++) {
498+
if (s[i] < 0x80 || s[i] >= 0xC0) {
499499
return 0;
500+
}
501+
}
500502
return length;
501503
}
502504

0 commit comments

Comments
 (0)