blob: d113e82694e90c0448deddb02bc11855aa7fe7ce [file] [edit]
diff -urN Python-3.11.15/Include/pyexpat.h Python-3.11.15-modified/Include/pyexpat.h
--- Python-3.11.15/Include/pyexpat.h 2026-03-03 00:52:57.000000000 +0000
+++ Python-3.11.15-modified/Include/pyexpat.h 2026-06-12 08:52:32.735201343 +0000
@@ -57,6 +57,9 @@
XML_Parser parser, unsigned long long activationThresholdBytes);
XML_Bool (*SetAllocTrackerMaximumAmplification)(
XML_Parser parser, float maxAmplificationFactor);
+ /* might be NULL for expat < 2.8.0 */
+ XML_Bool (*SetHashSalt16Bytes)(
+ XML_Parser parser, const uint8_t entropy[16]);
/* always add new stuff to the end! */
};
diff -urN Python-3.11.15/Include/pyhash.h Python-3.11.15-modified/Include/pyhash.h
--- Python-3.11.15/Include/pyhash.h 2026-03-03 00:52:57.000000000 +0000
+++ Python-3.11.15-modified/Include/pyhash.h 2026-06-12 08:52:26.391396981 +0000
@@ -39,14 +39,14 @@
* pppppppp ssssssss ........ fnv -- two Py_hash_t
* k0k0k0k0 k1k1k1k1 ........ siphash -- two uint64_t
* ........ ........ ssssssss djbx33a -- 16 bytes padding + one Py_hash_t
- * ........ ........ eeeeeeee pyexpat XML hash salt
+ * eeeeeeee eeeeeeee eeeeeeee pyexpat XML hash salt
*
* memory layout on 32 bit systems
* cccccccc cccccccc cccccccc uc
* ppppssss ........ ........ fnv -- two Py_hash_t
* k0k0k0k0 k1k1k1k1 ........ siphash -- two uint64_t (*)
* ........ ........ ssss.... djbx33a -- 16 bytes padding + one Py_hash_t
- * ........ ........ eeee.... pyexpat XML hash salt
+ * eeeeeeee eeeeeeee eeee.... pyexpat XML hash salt
*
* (*) The siphash member may not be available on 32 bit platforms without
* an unsigned int64 data type.
@@ -71,7 +71,9 @@
Py_hash_t suffix;
} djbx33a;
struct {
- unsigned char padding[16];
+ /* 16 bytes for XML_SetHashSalt16Bytes */
+ uint8_t hashsalt16[16];
+ /* 4/8 bytes for legacy XML_SetHashSalt */
Py_hash_t hashsalt;
} expat;
} _Py_HashSecret_t;
diff -urN Python-3.11.15/Misc/NEWS.d/next/Security/2026-04-26-19-30-45.gh-issue-149018.a9SqWb.rst Python-3.11.15-modified/Misc/NEWS.d/next/Security/2026-04-26-19-30-45.gh-issue-149018.a9SqWb.rst
--- Python-3.11.15/Misc/NEWS.d/next/Security/2026-04-26-19-30-45.gh-issue-149018.a9SqWb.rst 1970-01-01 00:00:00.000000000 +0000
+++ Python-3.11.15-modified/Misc/NEWS.d/next/Security/2026-04-26-19-30-45.gh-issue-149018.a9SqWb.rst 2026-06-12 08:52:26.391681801 +0000
@@ -0,0 +1,3 @@
+Improved protection against XML hash-flooding attacks in
+:mod:`xml.parsers.expat` and :mod:`xml.etree.ElementTree` when Python is
+compiled with libExpat 2.8.0 or later.
diff -urN Python-3.11.15/Modules/_elementtree.c Python-3.11.15-modified/Modules/_elementtree.c
--- Python-3.11.15/Modules/_elementtree.c 2026-03-03 00:52:57.000000000 +0000
+++ Python-3.11.15-modified/Modules/_elementtree.c 2026-06-12 08:52:26.392007562 +0000
@@ -3656,7 +3656,12 @@
return -1;
}
/* expat < 2.1.0 has no XML_SetHashSalt() */
- if (EXPAT(SetHashSalt) != NULL) {
+ // Prefer 16-byte entropy, only expat >= 2.8.0. See gh-149018
+ if (EXPAT(SetHashSalt16Bytes) != NULL) {
+ EXPAT(SetHashSalt16Bytes)(self->parser,
+ _Py_HashSecret.expat.hashsalt16);
+ }
+ else if (EXPAT(SetHashSalt) != NULL) {
EXPAT(SetHashSalt)(self->parser,
(unsigned long)_Py_HashSecret.expat.hashsalt);
}
diff -urN Python-3.11.15/Modules/pyexpat.c Python-3.11.15-modified/Modules/pyexpat.c
--- Python-3.11.15/Modules/pyexpat.c 2026-03-03 00:52:57.000000000 +0000
+++ Python-3.11.15-modified/Modules/pyexpat.c 2026-06-12 08:52:26.392547680 +0000
@@ -1378,7 +1378,10 @@
Py_DECREF(self);
return NULL;
}
-#if XML_COMBINED_VERSION >= 20100
+#if XML_COMBINED_VERSION >= 20800
+ /* This feature was added upstream in libexpat 2.8.0. */
+ XML_SetHashSalt16Bytes(self->itself, _Py_HashSecret.expat.hashsalt16);
+#elif XML_COMBINED_VERSION >= 20100
/* This feature was added upstream in libexpat 2.1.0. */
XML_SetHashSalt(self->itself,
(unsigned long)_Py_HashSecret.expat.hashsalt);
@@ -2241,6 +2244,11 @@
capi.SetAllocTrackerActivationThreshold = NULL;
capi.SetAllocTrackerMaximumAmplification = NULL;
#endif
+#if XML_COMBINED_VERSION >= 20800
+ capi.SetHashSalt16Bytes = XML_SetHashSalt16Bytes;
+#else
+ capi.SetHashSalt16Bytes = NULL;
+#endif
/* export using capsule */
PyObject *capi_object = PyCapsule_New(&capi, PyExpat_CAPSULE_NAME, NULL);