Skip to content

Commit b52fc88

Browse files
committed
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.
1 parent ced3c5d commit b52fc88

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
@@ -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 & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ CALL_XML_HANDLER_SETTER(const struct HandlerInfo *handler_info,
123123
}
124124

125125
static int
126-
set_error_code(PyObject *err, enum XML_Error code)
126+
set_xml_error_attr_code(PyObject *err, enum XML_Error code)
127127
{
128128
PyObject *v = PyLong_FromLong((long)code);
129129
int ok = v != NULL && PyObject_SetAttr(err, &_Py_ID(code), v) != -1;
@@ -135,7 +135,7 @@ set_error_code(PyObject *err, enum XML_Error code)
135135
* false on an exception.
136136
*/
137137
static int
138-
set_error_location(PyObject *err, const char *name, XML_Size value)
138+
set_xml_error_attr_location(PyObject *err, const char *name, XML_Size value)
139139
{
140140
PyObject *v = PyLong_FromSize_t((size_t)value);
141141
int ok = v != NULL && PyObject_SetAttrString(err, name, v) != -1;
@@ -144,42 +144,32 @@ set_error_location(PyObject *err, const char *name, XML_Size value)
144144
}
145145

146146

147-
static PyObject *
148-
format_xml_error(enum XML_Error code, XML_Size lineno, XML_Size column)
149-
{
150-
const char *errmsg = XML_ErrorString(code);
151-
PyUnicodeWriter *writer = PyUnicodeWriter_Create(strlen(errmsg) + 1);
152-
if (writer == NULL) {
153-
return NULL;
154-
}
155-
if (PyUnicodeWriter_Format(writer,
156-
"%s: line %zu, column %zu",
157-
errmsg, (size_t)lineno, (size_t)column) < 0)
158-
{
159-
PyUnicodeWriter_Discard(writer);
160-
return NULL;
161-
}
162-
return PyUnicodeWriter_Finish(writer);
163-
}
164-
165147
static PyObject *
166148
set_xml_error(pyexpat_state *state,
167149
enum XML_Error code, XML_Size lineno, XML_Size column,
168150
const char *errmsg)
169151
{
170-
PyObject *arg = errmsg == NULL
171-
? format_xml_error(code, lineno, column)
172-
: PyUnicode_FromStringAndSize(errmsg, strlen(errmsg));
152+
PyObject *arg;
153+
if (errmsg == NULL) {
154+
arg = PyUnicode_FromFormat(
155+
"%s: line %zu, column %zu",
156+
XML_ErrorString(code),
157+
(size_t)lineno, (size_t)column
158+
);
159+
}
160+
else {
161+
arg = PyUnicode_FromStringAndSize(errmsg, strlen(errmsg));
162+
}
173163
if (arg == NULL) {
174164
return NULL;
175165
}
176166
PyObject *res = PyObject_CallOneArg(state->error, arg);
177167
Py_DECREF(arg);
178168
if (
179169
res != NULL
180-
&& set_error_code(res, code)
181-
&& set_error_location(res, "lineno", lineno)
182-
&& set_error_location(res, "offset", column)
170+
&& set_xml_error_attr_code(res, code)
171+
&& set_xml_error_attr_location(res, "lineno", lineno)
172+
&& set_xml_error_attr_location(res, "offset", column)
183173
) {
184174
PyErr_SetObject(state->error, res);
185175
}
@@ -1176,6 +1166,50 @@ pyexpat_xmlparser_UseForeignDTD_impl(xmlparseobject *self, PyTypeObject *cls,
11761166
}
11771167
#endif
11781168

1169+
#if XML_COMBINED_VERSION >= 20702
1170+
static PyObject *
1171+
set_activation_threshold(xmlparseobject *self,
1172+
PyTypeObject *cls,
1173+
unsigned long long threshold,
1174+
XML_Bool (*setter)(XML_Parser, unsigned long long))
1175+
{
1176+
assert(self->itself != NULL);
1177+
if (setter(self->itself, threshold) == XML_TRUE) {
1178+
Py_RETURN_NONE;
1179+
}
1180+
// The setter fails if self->itself is NULL (which is not possible here)
1181+
// or is a non-root parser, which currently only happens for parsers
1182+
// created by ExternalEntityParserCreate().
1183+
pyexpat_state *state = PyType_GetModuleState(cls);
1184+
return set_invalid_arg(state, self, "parser must be a root parser");
1185+
}
1186+
1187+
static PyObject *
1188+
set_maximum_amplification(xmlparseobject *self,
1189+
PyTypeObject *cls,
1190+
float max_factor,
1191+
XML_Bool (*setter)(XML_Parser, float))
1192+
{
1193+
assert(self->itself != NULL);
1194+
if (setter(self->itself, max_factor) == XML_TRUE) {
1195+
Py_RETURN_NONE;
1196+
}
1197+
// The setter fails if self->itself is NULL (which is not possible here),
1198+
// is a non-root parser, which currently only happens for parsers created
1199+
// by ExternalEntityParserCreate(), or if 'max_factor' is NaN or < 1.0.
1200+
pyexpat_state *state = PyType_GetModuleState(cls);
1201+
// Note: Expat has no API to determine whether a parser is a root parser,
1202+
// and since the Expat functions for defining the various maximum allowed
1203+
// amplifcation factors fail when a bad parser or an out-of-range factor
1204+
// is given without specifying which check failed, we check whether the
1205+
// factor is out-of-range to improve the error message. See also gh-90949.
1206+
const char *message = (isnan(max_factor) || max_factor < 1.0f)
1207+
? "'max_factor' must be at least 1.0"
1208+
: "parser must be a root parser";
1209+
return set_invalid_arg(state, self, message);
1210+
}
1211+
#endif
1212+
11791213
#if XML_COMBINED_VERSION >= 20702
11801214
/*[clinic input]
11811215
pyexpat.xmlparser.SetAllocTrackerActivationThreshold
@@ -1195,15 +1229,10 @@ pyexpat_xmlparser_SetAllocTrackerActivationThreshold_impl(xmlparseobject *self,
11951229
unsigned long long threshold)
11961230
/*[clinic end generated code: output=bed7e93207ba08c5 input=9c706b75c18e4ea1]*/
11971231
{
1198-
assert(self->itself != NULL);
1199-
if (XML_SetAllocTrackerActivationThreshold(self->itself, threshold) == XML_TRUE) {
1200-
Py_RETURN_NONE;
1201-
}
1202-
// XML_SetAllocTrackerActivationThreshold() can only fail if self->itself
1203-
// is not a root parser (currently, this is equivalent to be created
1204-
// by ExternalEntityParserCreate()).
1205-
pyexpat_state *state = PyType_GetModuleState(cls);
1206-
return set_invalid_arg(state, self, "parser must be a root parser");
1232+
return set_activation_threshold(
1233+
self, cls, threshold,
1234+
XML_SetAllocTrackerActivationThreshold
1235+
);
12071236
}
12081237
#endif
12091238

@@ -1236,24 +1265,10 @@ pyexpat_xmlparser_SetAllocTrackerMaximumAmplification_impl(xmlparseobject *self,
12361265
float max_factor)
12371266
/*[clinic end generated code: output=6e44bd48c9b112a0 input=918b9266b490a722]*/
12381267
{
1239-
assert(self->itself != NULL);
1240-
if (XML_SetAllocTrackerMaximumAmplification(self->itself, max_factor) == XML_TRUE) {
1241-
Py_RETURN_NONE;
1242-
}
1243-
// XML_SetAllocTrackerMaximumAmplification() can fail if self->itself
1244-
// is not a root parser (currently, this is equivalent to be created
1245-
// by ExternalEntityParserCreate()) or if 'max_factor' is NaN or < 1.0.
1246-
//
1247-
// Expat does not provide a way to determine whether a parser is a root
1248-
// or not, nor does it provide a way to distinguish between failures in
1249-
// XML_SetAllocTrackerMaximumAmplification() (see gh-90949), we manually
1250-
// detect the factor out-of-range issue here so that users have a better
1251-
// error message.
1252-
pyexpat_state *state = PyType_GetModuleState(cls);
1253-
const char *message = (isnan(max_factor) || max_factor < 1.0f)
1254-
? "'max_factor' must be at least 1.0"
1255-
: "parser must be a root parser";
1256-
return set_invalid_arg(state, self, message);
1268+
return set_maximum_amplification(
1269+
self, cls, max_factor,
1270+
XML_SetAllocTrackerMaximumAmplification
1271+
);
12571272
}
12581273
#endif
12591274

0 commit comments

Comments
 (0)