Skip to content

Commit 356a031

Browse files
authored
gh-146563: add exception note for invalid Expat handler return values (#146565)
1 parent 4286227 commit 356a031

File tree

4 files changed

+57
-1
lines changed

4 files changed

+57
-1
lines changed

Include/internal/pycore_pyerrors.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ PyAPI_FUNC(PyObject*) _PyErr_FormatFromCause(
2929
...
3030
);
3131

32-
extern int _PyException_AddNote(
32+
// Export for 'pyexpat' shared extension.
33+
PyAPI_FUNC(int) _PyException_AddNote(
3334
PyObject *exc,
3435
PyObject *note);
3536

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.parsers.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: 25 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+
int rc = _PyException_AddNote(exc, note);
516+
Py_DECREF(note);
517+
if (rc < 0) {
518+
goto error;
519+
};
520+
goto done;
521+
522+
error:
523+
PyErr_Clear();
524+
done:
525+
PyErr_SetRaisedException(exc);
526+
}
527+
506528
#define RC_HANDLER(RETURN_TYPE, NAME, PARAMS, \
507529
INIT, PARSE_FORMAT, CONVERSION, \
508530
RETURN_VARIABLE, GETUSERDATA) \
@@ -536,6 +558,9 @@ my_ ## NAME ## Handler PARAMS { \
536558
} \
537559
CONVERSION \
538560
Py_DECREF(rv); \
561+
if (PyErr_Occurred()) { \
562+
invalid_expat_handler_rv(#NAME); \
563+
} \
539564
return RETURN_VARIABLE; \
540565
}
541566

0 commit comments

Comments
 (0)