@@ -125,7 +125,7 @@ CALL_XML_HANDLER_SETTER(const struct HandlerInfo *handler_info,
125125}
126126
127127static int
128- set_error_code (PyObject *err, enum XML_Error code)
128+ set_xml_error_attr_code (PyObject *err, enum XML_Error code)
129129{
130130 PyObject *v = PyLong_FromLong((long)code);
131131 int ok = v != NULL && PyObject_SetAttr(err, &_Py_ID(code), v) != -1;
@@ -137,7 +137,7 @@ set_error_code(PyObject *err, enum XML_Error code)
137137 * false on an exception.
138138 */
139139static int
140- set_error_location (PyObject *err, const char *name, XML_Size value)
140+ set_xml_error_attr_location (PyObject *err, const char *name, XML_Size value)
141141{
142142 PyObject *v = PyLong_FromSize_t((size_t)value);
143143 int ok = v != NULL && PyObject_SetAttrString(err, name, v) != -1;
@@ -146,42 +146,32 @@ set_error_location(PyObject *err, const char *name, XML_Size value)
146146}
147147
148148
149- static PyObject *
150- format_xml_error(enum XML_Error code, XML_Size lineno, XML_Size column)
151- {
152- const char *errmsg = XML_ErrorString(code);
153- PyUnicodeWriter *writer = PyUnicodeWriter_Create(strlen(errmsg) + 1);
154- if (writer == NULL) {
155- return NULL;
156- }
157- if (PyUnicodeWriter_Format(writer,
158- "%s: line %zu, column %zu",
159- errmsg, (size_t)lineno, (size_t)column) < 0)
160- {
161- PyUnicodeWriter_Discard(writer);
162- return NULL;
163- }
164- return PyUnicodeWriter_Finish(writer);
165- }
166-
167149static PyObject *
168150set_xml_error(pyexpat_state *state,
169151 enum XML_Error code, XML_Size lineno, XML_Size column,
170152 const char *errmsg)
171153{
172- PyObject *arg = errmsg == NULL
173- ? format_xml_error(code, lineno, column)
174- : PyUnicode_FromStringAndSize(errmsg, strlen(errmsg));
154+ PyObject *arg;
155+ if (errmsg == NULL) {
156+ arg = PyUnicode_FromFormat(
157+ "%s: line %zu, column %zu",
158+ XML_ErrorString(code),
159+ (size_t)lineno, (size_t)column
160+ );
161+ }
162+ else {
163+ arg = PyUnicode_FromStringAndSize(errmsg, strlen(errmsg));
164+ }
175165 if (arg == NULL) {
176166 return NULL;
177167 }
178168 PyObject *res = PyObject_CallOneArg(state->error, arg);
179169 Py_DECREF(arg);
180170 if (
181171 res != NULL
182- && set_error_code (res, code)
183- && set_error_location (res, "lineno", lineno)
184- && set_error_location (res, "offset", column)
172+ && set_xml_error_attr_code (res, code)
173+ && set_xml_error_attr_location (res, "lineno", lineno)
174+ && set_xml_error_attr_location (res, "offset", column)
185175 ) {
186176 PyErr_SetObject(state->error, res);
187177 }
@@ -1184,6 +1174,50 @@ pyexpat_xmlparser_UseForeignDTD_impl(xmlparseobject *self, PyTypeObject *cls,
11841174}
11851175#endif
11861176
1177+ #if XML_COMBINED_VERSION >= 20702
1178+ static PyObject *
1179+ set_activation_threshold(xmlparseobject *self,
1180+ PyTypeObject *cls,
1181+ unsigned long long threshold,
1182+ XML_Bool (*setter)(XML_Parser, unsigned long long))
1183+ {
1184+ assert(self->itself != NULL);
1185+ if (setter(self->itself, threshold) == XML_TRUE) {
1186+ Py_RETURN_NONE;
1187+ }
1188+ // The setter fails if self->itself is NULL (which is not possible here)
1189+ // or is a non-root parser, which currently only happens for parsers
1190+ // created by ExternalEntityParserCreate().
1191+ pyexpat_state *state = PyType_GetModuleState(cls);
1192+ return set_invalid_arg(state, self, "parser must be a root parser");
1193+ }
1194+
1195+ static PyObject *
1196+ set_maximum_amplification(xmlparseobject *self,
1197+ PyTypeObject *cls,
1198+ float max_factor,
1199+ XML_Bool (*setter)(XML_Parser, float))
1200+ {
1201+ assert(self->itself != NULL);
1202+ if (setter(self->itself, max_factor) == XML_TRUE) {
1203+ Py_RETURN_NONE;
1204+ }
1205+ // The setter fails if self->itself is NULL (which is not possible here),
1206+ // is a non-root parser, which currently only happens for parsers created
1207+ // by ExternalEntityParserCreate(), or if 'max_factor' is NaN or < 1.0.
1208+ pyexpat_state *state = PyType_GetModuleState(cls);
1209+ // Note: Expat has no API to determine whether a parser is a root parser,
1210+ // and since the Expat functions for defining the various maximum allowed
1211+ // amplifcation factors fail when a bad parser or an out-of-range factor
1212+ // is given without specifying which check failed, we check whether the
1213+ // factor is out-of-range to improve the error message. See also gh-90949.
1214+ const char *message = (isnan(max_factor) || max_factor < 1.0f)
1215+ ? "'max_factor' must be at least 1.0"
1216+ : "parser must be a root parser";
1217+ return set_invalid_arg(state, self, message);
1218+ }
1219+ #endif
1220+
11871221#if XML_COMBINED_VERSION >= 20702
11881222/*[clinic input]
11891223@permit_long_summary
@@ -1205,15 +1239,10 @@ pyexpat_xmlparser_SetAllocTrackerActivationThreshold_impl(xmlparseobject *self,
12051239 unsigned long long threshold)
12061240/*[clinic end generated code: output=bed7e93207ba08c5 input=54182cd71ad69978]*/
12071241{
1208- assert(self->itself != NULL);
1209- if (XML_SetAllocTrackerActivationThreshold(self->itself, threshold) == XML_TRUE) {
1210- Py_RETURN_NONE;
1211- }
1212- // XML_SetAllocTrackerActivationThreshold() can only fail if self->itself
1213- // is not a root parser (currently, this is equivalent to be created
1214- // by ExternalEntityParserCreate()).
1215- pyexpat_state *state = PyType_GetModuleState(cls);
1216- return set_invalid_arg(state, self, "parser must be a root parser");
1242+ return set_activation_threshold(
1243+ self, cls, threshold,
1244+ XML_SetAllocTrackerActivationThreshold
1245+ );
12171246}
12181247#endif
12191248
@@ -1248,24 +1277,10 @@ pyexpat_xmlparser_SetAllocTrackerMaximumAmplification_impl(xmlparseobject *self,
12481277 float max_factor)
12491278/*[clinic end generated code: output=6e44bd48c9b112a0 input=3544abf9dd7ae055]*/
12501279{
1251- assert(self->itself != NULL);
1252- if (XML_SetAllocTrackerMaximumAmplification(self->itself, max_factor) == XML_TRUE) {
1253- Py_RETURN_NONE;
1254- }
1255- // XML_SetAllocTrackerMaximumAmplification() can fail if self->itself
1256- // is not a root parser (currently, this is equivalent to be created
1257- // by ExternalEntityParserCreate()) or if 'max_factor' is NaN or < 1.0.
1258- //
1259- // Expat does not provide a way to determine whether a parser is a root
1260- // or not, nor does it provide a way to distinguish between failures in
1261- // XML_SetAllocTrackerMaximumAmplification() (see gh-90949), we manually
1262- // detect the factor out-of-range issue here so that users have a better
1263- // error message.
1264- pyexpat_state *state = PyType_GetModuleState(cls);
1265- const char *message = (isnan(max_factor) || max_factor < 1.0f)
1266- ? "'max_factor' must be at least 1.0"
1267- : "parser must be a root parser";
1268- return set_invalid_arg(state, self, message);
1280+ return set_maximum_amplification(
1281+ self, cls, max_factor,
1282+ XML_SetAllocTrackerMaximumAmplification
1283+ );
12691284}
12701285#endif
12711286
0 commit comments