Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Lib/test/test_decimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3963,15 +3963,21 @@ def test_flag_comparisons(self):
d.update(c.flags)
self.assertEqual(d, c.flags)
self.assertEqual(c.flags, d)
self.assertEqual(frozendict(d), c.flags)
self.assertEqual(c.flags, frozendict(d))

d[Inexact] = True
self.assertNotEqual(d, c.flags)
self.assertNotEqual(c.flags, d)
self.assertNotEqual(frozendict(d), c.flags)
self.assertNotEqual(c.flags, frozendict(d))

# Invalid SignalDict
d = {Inexact:False}
self.assertNotEqual(d, c.flags)
self.assertNotEqual(c.flags, d)
self.assertNotEqual(frozendict(d), c.flags)
self.assertNotEqual(c.flags, frozendict(d))

d = ["xyz"]
self.assertNotEqual(d, c.flags)
Expand Down
17 changes: 17 additions & 0 deletions Lib/test/test_source_encoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,23 @@ def test_issue7820(self):
# two bytes in common with the UTF-8 BOM
self.assertRaises(SyntaxError, eval, b'\xef\xbb\x20')

def test_truncated_utf8_at_eof(self):
# Regression test for https://issues.oss-fuzz.com/issues/451112368
# Truncated multi-byte UTF-8 sequences at end of input caused an
# out-of-bounds read in Parser/tokenizer/helpers.c:valid_utf8().
truncated = [
b'\xc2', # 2-byte lead, missing 1 continuation
b'\xdf', # 2-byte lead, missing 1 continuation
b'\xe0', # 3-byte lead, missing 2 continuations
b'\xe0\xa0', # 3-byte lead, missing 1 continuation
b'\xf0\x90', # 4-byte lead, missing 2 continuations
b'\xf0\x90\x80', # 4-byte lead, missing 1 continuation
b'\xf3', # 4-byte lead, missing 3 (the oss-fuzz reproducer)
]
for seq in truncated:
with self.subTest(seq=seq):
self.assertRaises(SyntaxError, compile, seq, '<test>', 'exec')

@support.requires_subprocess()
def test_20731(self):
sub = subprocess.Popen([sys.executable,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix heap buffer overflow in the parser found by OSS-Fuzz.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix crash in ``_remote_debugging`` that caused ``test_external_inspection`` to intermittently fail. Patch by Taegyun Kim.
4 changes: 2 additions & 2 deletions Modules/_decimal/_decimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ dict_as_flags(decimal_state *state, PyObject *val)
uint32_t flags = 0;
int x;

if (!PyDict_Check(val)) {
if (!PyAnyDict_Check(val)) {
PyErr_SetString(PyExc_TypeError,
"argument must be a signal dict");
return DEC_INVALID_SIGNALS;
Expand Down Expand Up @@ -802,7 +802,7 @@ signaldict_richcompare(PyObject *v, PyObject *w, int op)
if (PyDecSignalDict_Check(state, w)) {
res = (SdFlags(v)==SdFlags(w)) ^ (op==Py_NE) ? Py_True : Py_False;
}
else if (PyDict_Check(w)) {
else if (PyAnyDict_Check(w)) {
uint32_t flags = dict_as_flags(state, w);
if (flags & DEC_ERRORS) {
if (flags & DEC_INVALID_SIGNALS) {
Expand Down
12 changes: 8 additions & 4 deletions Modules/_remote_debugging/_remote_debugging.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ extern "C" {
#include "internal/pycore_interpframe.h" // FRAME_OWNED_BY_INTERPRETER
#include "internal/pycore_llist.h" // struct llist_node
#include "internal/pycore_long.h" // _PyLong_GetZero
#include "internal/pycore_pyerrors.h" // _PyErr_FormatFromCause
#include "internal/pycore_stackref.h" // Py_TAG_BITS
#include "../../Python/remote_debug.h"

Expand Down Expand Up @@ -173,10 +174,13 @@ typedef enum _WIN32_THREADSTATE {
#define THREAD_STATUS_HAS_EXCEPTION (1 << 4)

/* Exception cause macro */
#define set_exception_cause(unwinder, exc_type, message) \
if (unwinder->debug) { \
_set_debug_exception_cause(exc_type, message); \
}
#define set_exception_cause(unwinder, exc_type, message) \
do { \
assert(PyErr_Occurred() && "function returned -1 without setting exception"); \
if (unwinder->debug) { \
_set_debug_exception_cause(exc_type, message); \
} \
} while (0)

/* ============================================================================
* TYPE DEFINITIONS
Expand Down
1 change: 1 addition & 0 deletions Modules/_remote_debugging/asyncio.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ iterate_set_entries(

// Validate mask and num_els to prevent huge loop iterations from garbage data
if (mask < 0 || mask >= MAX_SET_TABLE_SIZE || num_els < 0 || num_els > mask + 1) {
PyErr_SetString(PyExc_RuntimeError, "Invalid set object (corrupted remote memory)");
set_exception_cause(unwinder, PyExc_RuntimeError,
"Invalid set object (corrupted remote memory)");
return -1;
Expand Down
3 changes: 3 additions & 0 deletions Modules/_remote_debugging/code_objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,9 @@ parse_code_object(RemoteUnwinderObject *unwinder,
if (tlbc_entry) {
// Validate index bounds (also catches negative values since tlbc_index is signed)
if (ctx->tlbc_index < 0 || ctx->tlbc_index >= tlbc_entry->tlbc_array_size) {
PyErr_Format(PyExc_RuntimeError,
"Invalid tlbc_index %d (array size %zd, corrupted remote memory)",
ctx->tlbc_index, tlbc_entry->tlbc_array_size);
set_exception_cause(unwinder, PyExc_RuntimeError,
"Invalid tlbc_index (corrupted remote memory)");
goto error;
Expand Down
3 changes: 3 additions & 0 deletions Modules/_remote_debugging/frames.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ process_single_stack_chunk(
// Size must be at least enough for the header and reasonably bounded
if (actual_size <= offsetof(_PyStackChunk, data) || actual_size > MAX_STACK_CHUNK_SIZE) {
PyMem_RawFree(this_chunk);
PyErr_Format(PyExc_RuntimeError,
"Invalid stack chunk size %zu (corrupted remote memory)", actual_size);
set_exception_cause(unwinder, PyExc_RuntimeError,
"Invalid stack chunk size (corrupted remote memory)");
return -1;
Expand Down Expand Up @@ -244,6 +246,7 @@ parse_frame_from_chunks(
) {
void *frame_ptr = find_frame_in_chunks(chunks, address);
if (!frame_ptr) {
PyErr_Format(PyExc_RuntimeError, "Frame at address 0x%lx not found in stack chunks", address);
set_exception_cause(unwinder, PyExc_RuntimeError, "Frame not found in stack chunks");
return -1;
}
Expand Down
3 changes: 3 additions & 0 deletions Modules/_remote_debugging/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,9 @@ _remote_debugging_RemoteUnwinder_get_stack_trace_impl(RemoteUnwinderObject *self
// Detect cycle: if current_tstate didn't advance, we have corrupted data
if (current_tstate == prev_tstate) {
Py_DECREF(interpreter_threads);
PyErr_Format(PyExc_RuntimeError,
"Thread list cycle detected at address 0x%lx (corrupted remote memory)",
current_tstate);
set_exception_cause(self, PyExc_RuntimeError,
"Thread list cycle detected (corrupted remote memory)");
Py_CLEAR(result);
Expand Down
6 changes: 4 additions & 2 deletions Parser/tokenizer/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -494,9 +494,11 @@ valid_utf8(const unsigned char* s)
return 0;
}
length = expected + 1;
for (; expected; expected--)
if (s[expected] < 0x80 || s[expected] >= 0xC0)
for (int i = 1; i <= expected; i++) {
if (s[i] < 0x80 || s[i] >= 0xC0) {
return 0;
}
}
return length;
}

Expand Down
3 changes: 2 additions & 1 deletion Python/remote_debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -1302,6 +1302,7 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t *handle,
if (entry->data == NULL) {
entry->data = PyMem_RawMalloc(page_size);
if (entry->data == NULL) {
PyErr_NoMemory();
_set_debug_exception_cause(PyExc_MemoryError,
"Cannot allocate %zu bytes for page cache entry "
"during read from PID %d at address 0x%lx",
Expand All @@ -1311,7 +1312,7 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t *handle,
}

if (_Py_RemoteDebug_ReadRemoteMemory(handle, page_base, page_size, entry->data) < 0) {
// Try to just copy the exact ammount as a fallback
// Try to just copy the exact amount as a fallback
PyErr_Clear();
goto fallback;
}
Expand Down
Loading