Skip to content

Commit a5d026a

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 aeefb63 commit a5d026a

File tree

3 files changed

+72
-57
lines changed

3 files changed

+72
-57
lines changed

Doc/library/pyexpat.rst

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

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

288288
.. versionadded:: next
289289

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 & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ struct HandlerInfo {
107107
static struct HandlerInfo handler_info[64];
108108

109109
static int
110-
set_error_code(PyObject *err, enum XML_Error code)
110+
set_xml_error_attr_code(PyObject *err, enum XML_Error code)
111111
{
112112
PyObject *v = PyLong_FromLong((long)code);
113113
int ok = v != NULL && PyObject_SetAttr(err, &_Py_ID(code), v) != -1;
@@ -119,7 +119,7 @@ set_error_code(PyObject *err, enum XML_Error code)
119119
* false on an exception.
120120
*/
121121
static int
122-
set_error_location(PyObject *err, const char *name, XML_Size value)
122+
set_xml_error_attr_location(PyObject *err, const char *name, XML_Size value)
123123
{
124124
PyObject *v = PyLong_FromSize_t((size_t)value);
125125
int ok = v != NULL && PyObject_SetAttrString(err, name, v) != -1;
@@ -128,42 +128,32 @@ set_error_location(PyObject *err, const char *name, XML_Size value)
128128
}
129129

130130

131-
static PyObject *
132-
format_xml_error(enum XML_Error code, XML_Size lineno, XML_Size column)
133-
{
134-
const char *errmsg = XML_ErrorString(code);
135-
PyUnicodeWriter *writer = PyUnicodeWriter_Create(strlen(errmsg) + 1);
136-
if (writer == NULL) {
137-
return NULL;
138-
}
139-
if (PyUnicodeWriter_Format(writer,
140-
"%s: line %zu, column %zu",
141-
errmsg, (size_t)lineno, (size_t)column) < 0)
142-
{
143-
PyUnicodeWriter_Discard(writer);
144-
return NULL;
145-
}
146-
return PyUnicodeWriter_Finish(writer);
147-
}
148-
149131
static PyObject *
150132
set_xml_error(pyexpat_state *state,
151133
enum XML_Error code, XML_Size lineno, XML_Size column,
152134
const char *errmsg)
153135
{
154-
PyObject *arg = errmsg == NULL
155-
? format_xml_error(code, lineno, column)
156-
: PyUnicode_FromStringAndSize(errmsg, strlen(errmsg));
136+
PyObject *arg;
137+
if (errmsg == NULL) {
138+
arg = PyUnicode_FromFormat(
139+
"%s: line %zu, column %zu",
140+
XML_ErrorString(code),
141+
(size_t)lineno, (size_t)column
142+
);
143+
}
144+
else {
145+
arg = PyUnicode_FromStringAndSize(errmsg, strlen(errmsg));
146+
}
157147
if (arg == NULL) {
158148
return NULL;
159149
}
160150
PyObject *res = PyObject_CallOneArg(state->error, arg);
161151
Py_DECREF(arg);
162152
if (
163153
res != NULL
164-
&& set_error_code(res, code)
165-
&& set_error_location(res, "lineno", lineno)
166-
&& set_error_location(res, "offset", column)
154+
&& set_xml_error_attr_code(res, code)
155+
&& set_xml_error_attr_location(res, "lineno", lineno)
156+
&& set_xml_error_attr_location(res, "offset", column)
167157
) {
168158
PyErr_SetObject(state->error, res);
169159
}
@@ -1147,6 +1137,50 @@ pyexpat_xmlparser_UseForeignDTD_impl(xmlparseobject *self, PyTypeObject *cls,
11471137
}
11481138
#endif
11491139

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

@@ -1207,24 +1236,10 @@ pyexpat_xmlparser_SetAllocTrackerMaximumAmplification_impl(xmlparseobject *self,
12071236
float max_factor)
12081237
/*[clinic end generated code: output=6e44bd48c9b112a0 input=918b9266b490a722]*/
12091238
{
1210-
assert(self->itself != NULL);
1211-
if (XML_SetAllocTrackerMaximumAmplification(self->itself, max_factor) == XML_TRUE) {
1212-
Py_RETURN_NONE;
1213-
}
1214-
// XML_SetAllocTrackerMaximumAmplification() can fail if self->itself
1215-
// is not a root parser (currently, this is equivalent to be created
1216-
// by ExternalEntityParserCreate()) or if 'max_factor' is NaN or < 1.0.
1217-
//
1218-
// Expat does not provide a way to determine whether a parser is a root
1219-
// or not, nor does it provide a way to distinguish between failures in
1220-
// XML_SetAllocTrackerMaximumAmplification() (see gh-90949), we manually
1221-
// detect the factor out-of-range issue here so that users have a better
1222-
// error message.
1223-
pyexpat_state *state = PyType_GetModuleState(cls);
1224-
const char *message = (isnan(max_factor) || max_factor < 1.0f)
1225-
? "'max_factor' must be at least 1.0"
1226-
: "parser must be a root parser";
1227-
return set_invalid_arg(state, self, message);
1239+
return set_maximum_amplification(
1240+
self, cls, max_factor,
1241+
XML_SetAllocTrackerMaximumAmplification
1242+
);
12281243
}
12291244
#endif
12301245

0 commit comments

Comments
 (0)