Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Include/internal/pycore_pyhash.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ _Py_HashPointerRaw(const void *ptr)
* 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.
Expand All @@ -58,7 +58,9 @@ typedef union {
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;
Expand Down
3 changes: 3 additions & 0 deletions Include/pyexpat.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ struct PyExpat_CAPI
XML_Parser parser, unsigned long long activationThresholdBytes);
XML_Bool (*SetBillionLaughsAttackProtectionMaximumAmplification)(
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! */
};

Original file line number Diff line number Diff line change
@@ -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.
8 changes: 6 additions & 2 deletions Modules/_elementtree.c
Original file line number Diff line number Diff line change
Expand Up @@ -3735,8 +3735,12 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *target,
PyErr_NoMemory();
return -1;
}
/* expat < 2.1.0 has no XML_SetHashSalt() */
if (EXPAT(st, SetHashSalt) != NULL) {
// Prefer 16-byte entropy, only expat >= 2.8.0. See gh-149018
if (EXPAT(st, SetHashSalt16Bytes) != NULL) {
EXPAT(st, SetHashSalt16Bytes)(self->parser,
_Py_HashSecret.expat.hashsalt16);
}
else if (EXPAT(st, SetHashSalt) != NULL) {
EXPAT(st, SetHashSalt)(self->parser,
(unsigned long)_Py_HashSecret.expat.hashsalt);
}
Expand Down
10 changes: 9 additions & 1 deletion Modules/pyexpat.c
Original file line number Diff line number Diff line change
Expand Up @@ -1533,7 +1533,10 @@ newxmlparseobject(pyexpat_state *state, const char *encoding,
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);
Expand Down Expand Up @@ -2427,6 +2430,11 @@ pyexpat_exec(PyObject *mod)
#else
capi->SetHashSalt = NULL;
#endif
#if XML_COMBINED_VERSION >= 20800
capi->SetHashSalt16Bytes = XML_SetHashSalt16Bytes;
#else
capi->SetHashSalt16Bytes = NULL;
#endif
#if XML_COMBINED_VERSION >= 20600
capi->SetReparseDeferralEnabled = XML_SetReparseDeferralEnabled;
#else
Expand Down
Loading