Skip to content

Commit ce8c3be

Browse files
committed
gh-146563: add exception note for invalid Expat handler return values
1 parent 1efe441 commit ce8c3be

File tree

7 files changed

+63
-0
lines changed

7 files changed

+63
-0
lines changed

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ struct _Py_global_strings {
299299
STRUCT_FOR_ID(aclose)
300300
STRUCT_FOR_ID(add)
301301
STRUCT_FOR_ID(add_done_callback)
302+
STRUCT_FOR_ID(add_note)
302303
STRUCT_FOR_ID(adobe)
303304
STRUCT_FOR_ID(after_in_child)
304305
STRUCT_FOR_ID(after_in_parent)

Include/internal/pycore_runtime_init_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_pyexpat.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,34 @@ def _test_exception(self, have_source):
510510
self.assertIn('call_with_frame("StartElement"',
511511
entries[1].line)
512512

513+
def test_invalid_NotStandalone(self):
514+
parser = expat.ParserCreate()
515+
parser.NotStandaloneHandler = mock.Mock(return_value="bad value")
516+
parser.ElementDeclHandler = lambda _1, _2: None
517+
518+
payload = b"""\
519+
<!DOCTYPE quotations SYSTEM "quotations.dtd" [<!ELEMENT root ANY>]><root/>
520+
"""
521+
with self.assertRaises(TypeError) as cm:
522+
parser.Parse(payload, True)
523+
parser.NotStandaloneHandler.assert_called_once()
524+
525+
notes = ["invalid 'NotStandalone' event handler return value"]
526+
self.assertEqual(cm.exception.__notes__, notes)
527+
528+
def test_invalid_ExternalEntityRefHandler(self):
529+
parser = expat.ParserCreate()
530+
parser.UseForeignDTD()
531+
parser.SetParamEntityParsing(expat.XML_PARAM_ENTITY_PARSING_ALWAYS)
532+
parser.ExternalEntityRefHandler = mock.Mock(return_value=None)
533+
534+
with self.assertRaises(TypeError) as cm:
535+
parser.Parse(b"<?xml version='1.0'?><element/>", True)
536+
parser.ExternalEntityRefHandler.assert_called_once()
537+
538+
notes = ["invalid 'ExternalEntityRef' event handler return value"]
539+
self.assertEqual(cm.exception.__notes__, notes)
540+
513541

514542
# Test Current* members:
515543
class PositionTest(unittest.TestCase):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:mod:`xml.parser.expat`: add an exception note when a custom Expat handler
2+
return value cannot be properly interpreted. Patch by Bénédikt Tran.

Modules/pyexpat.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,28 @@ my_StartElementHandler(void *userData,
503503
}
504504
}
505505

506+
static inline void
507+
invalid_expat_handler_rv(const char *name)
508+
{
509+
PyObject *exc = PyErr_GetRaisedException();
510+
assert(exc != NULL);
511+
PyObject *note = PyUnicode_FromFormat("invalid '%s' event handler return value", name);
512+
if (note == NULL) {
513+
goto error;
514+
}
515+
PyObject *res = PyObject_CallMethodOneArg(exc, &_Py_ID(add_note), note);
516+
Py_DECREF(note);
517+
if (res == NULL) {
518+
goto error;
519+
}
520+
goto done;
521+
522+
error:
523+
PyErr_Clear();
524+
done:
525+
_PyErr_ChainExceptions1(exc);
526+
}
527+
506528
#define RC_HANDLER(RETURN_TYPE, NAME, PARAMS, \
507529
INIT, PARSE_FORMAT, CONVERSION, \
508530
RETURN_VARIABLE, GETUSERDATA) \
@@ -536,6 +558,10 @@ my_ ## NAME ## Handler PARAMS { \
536558
} \
537559
CONVERSION \
538560
Py_DECREF(rv); \
561+
if (PyErr_Occurred()) { \
562+
invalid_expat_handler_rv(#NAME); \
563+
return RETURN_VARIABLE; \
564+
} \
539565
return RETURN_VARIABLE; \
540566
}
541567

0 commit comments

Comments
 (0)