| 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); |