Skip to content

Commit b235056

Browse files
picnixzhartwork
authored andcommitted
pythongh-90949: amend pythonGH-139234 in prevision of future mitigation API (python#139366)
Fix some typos left in f04bea4, and simplify some internal functions to ease maintenance of future mitigation APIs. (cherry picked from commit 68a1778)
1 parent 9dd807e commit b235056

File tree

3 files changed

+72
-47
lines changed

3 files changed

+72
-47
lines changed

Doc/library/pyexpat.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,8 @@ common XML vulnerabilities.
281281
.. note::
282282

283283
The maximum amplification factor is only considered if the threshold
284-
that can be adjusted :meth:`.SetAllocTrackerActivationThreshold` is
285-
exceeded.
284+
that can be adjusted by :meth:`.SetAllocTrackerActivationThreshold`
285+
is exceeded.
286286

287287
.. versionadded:: next
288288

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
Add :func:`~xml.parsers.expat.xmlparser.SetAllocTrackerActivationThreshold`
2-
and :func:`~xml.parsers.expat.xmlparser.SetAllocTrackerMaximumAmplification`
1+
Add :meth:`~xml.parsers.expat.xmlparser.SetAllocTrackerActivationThreshold`
2+
and :meth:`~xml.parsers.expat.xmlparser.SetAllocTrackerMaximumAmplification`
33
to :ref:`xmlparser <xmlparser-objects>` objects to prevent use of
44
disproportional amounts of dynamic memory from within an Expat parser.
55
Patch by Bénédikt Tran.

Modules/pyexpat.c

Lines changed: 68 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ struct HandlerInfo {
111111
static struct HandlerInfo handler_info[64];
112112

113113
static int
114-
set_error_code(PyObject *err, enum XML_Error code)
114+
set_xml_error_attr_code(PyObject *err, enum XML_Error code)
115115
{
116116
PyObject *v = PyLong_FromLong((long)code);
117117
int ok = v != NULL && PyObject_SetAttr(err, &_Py_ID(code), v) != -1;
@@ -123,7 +123,7 @@ set_error_code(PyObject *err, enum XML_Error code)
123123
* false on an exception.
124124
*/
125125
static int
126-
set_error_location(PyObject *err, const char *name, XML_Size value)
126+
set_xml_error_attr_location(PyObject *err, const char *name, XML_Size value)
127127
{
128128
PyObject *v = PyLong_FromSize_t((size_t)value);
129129
int ok = v != NULL && PyObject_SetAttrString(err, name, v) != -1;
@@ -132,32 +132,32 @@ set_error_location(PyObject *err, const char *name, XML_Size value)
132132
}
133133

134134

135-
static PyObject *
136-
format_xml_error(enum XML_Error code, XML_Size lineno, XML_Size column)
137-
{
138-
const char *errmsg = XML_ErrorString(code);
139-
return PyUnicode_FromFormat("%s: line %zu, column %zu",
140-
errmsg, (size_t)lineno, (size_t)column);
141-
}
142-
143135
static PyObject *
144136
set_xml_error(pyexpat_state *state,
145137
enum XML_Error code, XML_Size lineno, XML_Size column,
146138
const char *errmsg)
147139
{
148-
PyObject *arg = errmsg == NULL
149-
? format_xml_error(code, lineno, column)
150-
: PyUnicode_FromStringAndSize(errmsg, strlen(errmsg));
140+
PyObject *arg;
141+
if (errmsg == NULL) {
142+
arg = PyUnicode_FromFormat(
143+
"%s: line %zu, column %zu",
144+
XML_ErrorString(code),
145+
(size_t)lineno, (size_t)column
146+
);
147+
}
148+
else {
149+
arg = PyUnicode_FromStringAndSize(errmsg, strlen(errmsg));
150+
}
151151
if (arg == NULL) {
152152
return NULL;
153153
}
154154
PyObject *res = PyObject_CallOneArg(state->error, arg);
155155
Py_DECREF(arg);
156156
if (
157157
res != NULL
158-
&& set_error_code(res, code)
159-
&& set_error_location(res, "lineno", lineno)
160-
&& set_error_location(res, "offset", column)
158+
&& set_xml_error_attr_code(res, code)
159+
&& set_xml_error_attr_location(res, "lineno", lineno)
160+
&& set_xml_error_attr_location(res, "offset", column)
161161
) {
162162
PyErr_SetObject(state->error, res);
163163
}
@@ -1134,6 +1134,50 @@ pyexpat_xmlparser_UseForeignDTD_impl(xmlparseobject *self, PyTypeObject *cls,
11341134
}
11351135
#endif
11361136

1137+
#if XML_COMBINED_VERSION >= 20702
1138+
static PyObject *
1139+
set_activation_threshold(xmlparseobject *self,
1140+
PyTypeObject *cls,
1141+
unsigned long long threshold,
1142+
XML_Bool (*setter)(XML_Parser, unsigned long long))
1143+
{
1144+
assert(self->itself != NULL);
1145+
if (setter(self->itself, threshold) == XML_TRUE) {
1146+
Py_RETURN_NONE;
1147+
}
1148+
// The setter fails if self->itself is NULL (which is not possible here)
1149+
// or is a non-root parser, which currently only happens for parsers
1150+
// created by ExternalEntityParserCreate().
1151+
pyexpat_state *state = PyType_GetModuleState(cls);
1152+
return set_invalid_arg(state, self, "parser must be a root parser");
1153+
}
1154+
1155+
static PyObject *
1156+
set_maximum_amplification(xmlparseobject *self,
1157+
PyTypeObject *cls,
1158+
float max_factor,
1159+
XML_Bool (*setter)(XML_Parser, float))
1160+
{
1161+
assert(self->itself != NULL);
1162+
if (setter(self->itself, max_factor) == XML_TRUE) {
1163+
Py_RETURN_NONE;
1164+
}
1165+
// The setter fails if self->itself is NULL (which is not possible here),
1166+
// is a non-root parser, which currently only happens for parsers created
1167+
// by ExternalEntityParserCreate(), or if 'max_factor' is NaN or < 1.0.
1168+
pyexpat_state *state = PyType_GetModuleState(cls);
1169+
// Note: Expat has no API to determine whether a parser is a root parser,
1170+
// and since the Expat functions for defining the various maximum allowed
1171+
// amplifcation factors fail when a bad parser or an out-of-range factor
1172+
// is given without specifying which check failed, we check whether the
1173+
// factor is out-of-range to improve the error message. See also gh-90949.
1174+
const char *message = (isnan(max_factor) || max_factor < 1.0f)
1175+
? "'max_factor' must be at least 1.0"
1176+
: "parser must be a root parser";
1177+
return set_invalid_arg(state, self, message);
1178+
}
1179+
#endif
1180+
11371181
#if XML_COMBINED_VERSION >= 20702
11381182
/*[clinic input]
11391183
pyexpat.xmlparser.SetAllocTrackerActivationThreshold
@@ -1153,15 +1197,10 @@ pyexpat_xmlparser_SetAllocTrackerActivationThreshold_impl(xmlparseobject *self,
11531197
unsigned long long threshold)
11541198
/*[clinic end generated code: output=bed7e93207ba08c5 input=9c706b75c18e4ea1]*/
11551199
{
1156-
assert(self->itself != NULL);
1157-
if (XML_SetAllocTrackerActivationThreshold(self->itself, threshold) == XML_TRUE) {
1158-
Py_RETURN_NONE;
1159-
}
1160-
// XML_SetAllocTrackerActivationThreshold() can only fail if self->itself
1161-
// is not a root parser (currently, this is equivalent to be created
1162-
// by ExternalEntityParserCreate()).
1163-
pyexpat_state *state = PyType_GetModuleState(cls);
1164-
return set_invalid_arg(state, self, "parser must be a root parser");
1200+
return set_activation_threshold(
1201+
self, cls, threshold,
1202+
XML_SetAllocTrackerActivationThreshold
1203+
);
11651204
}
11661205
#endif
11671206

@@ -1194,24 +1233,10 @@ pyexpat_xmlparser_SetAllocTrackerMaximumAmplification_impl(xmlparseobject *self,
11941233
float max_factor)
11951234
/*[clinic end generated code: output=6e44bd48c9b112a0 input=918b9266b490a722]*/
11961235
{
1197-
assert(self->itself != NULL);
1198-
if (XML_SetAllocTrackerMaximumAmplification(self->itself, max_factor) == XML_TRUE) {
1199-
Py_RETURN_NONE;
1200-
}
1201-
// XML_SetAllocTrackerMaximumAmplification() can fail if self->itself
1202-
// is not a root parser (currently, this is equivalent to be created
1203-
// by ExternalEntityParserCreate()) or if 'max_factor' is NaN or < 1.0.
1204-
//
1205-
// Expat does not provide a way to determine whether a parser is a root
1206-
// or not, nor does it provide a way to distinguish between failures in
1207-
// XML_SetAllocTrackerMaximumAmplification() (see gh-90949), we manually
1208-
// detect the factor out-of-range issue here so that users have a better
1209-
// error message.
1210-
pyexpat_state *state = PyType_GetModuleState(cls);
1211-
const char *message = (isnan(max_factor) || max_factor < 1.0f)
1212-
? "'max_factor' must be at least 1.0"
1213-
: "parser must be a root parser";
1214-
return set_invalid_arg(state, self, message);
1236+
return set_maximum_amplification(
1237+
self, cls, max_factor,
1238+
XML_SetAllocTrackerMaximumAmplification
1239+
);
12151240
}
12161241
#endif
12171242

0 commit comments

Comments
 (0)