Bug 1374012 - Update to Expat 2.2.1. Part 2a: Better hashing. r=erahm
authorPeter Van der Beken <peterv@propagandism.org>
Thu, 27 Dec 2018 15:13:11 +0000
changeset 509076 e63ef8bbd6a150adaa9bf185dbd5c843c68d0af2
parent 509075 31402242ed1cd666cd3ac887f9b7fa8d0478e52d
child 509077 4b02b28ff6fb8692a6dbf8c2f5815144867c269d
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerserahm
bugs1374012
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1374012 - Update to Expat 2.2.1. Part 2a: Better hashing. r=erahm Differential Revision: https://phabricator.services.mozilla.com/D14440
parser/expat/lib/expat.h
parser/expat/lib/siphash.h
parser/expat/lib/xmlparse.c
--- a/parser/expat/lib/expat.h
+++ b/parser/expat/lib/expat.h
@@ -878,16 +878,26 @@ enum XML_ParamEntityParsing {
    entities is requested; otherwise it will return non-zero.
    Note: If XML_SetParamEntityParsing is called after XML_Parse or
       XML_ParseBuffer, then it has no effect and will always return 0.
 */
 XMLPARSEAPI(int)
 XML_SetParamEntityParsing(XML_Parser parser,
                           enum XML_ParamEntityParsing parsing);
 
+/* Sets the hash salt to use for internal hash calculations.
+   Helps in preventing DoS attacks based on predicting hash
+   function behavior. This must be called before parsing is started.
+   Returns 1 if successful, 0 when called after parsing has started.
+   Note: If parser == NULL, the function will do nothing and return 0.
+*/
+XMLPARSEAPI(int)
+XML_SetHashSalt(XML_Parser parser,
+                unsigned long hash_salt);
+
 /* If XML_Parse or XML_ParseBuffer have returned XML_STATUS_ERROR, then
    XML_GetErrorCode returns information about the error.
 */
 XMLPARSEAPI(enum XML_Error)
 XML_GetErrorCode(XML_Parser parser);
 
 /* These functions return information about the current parse
    location.  They may be called from any callback called to report
new file mode 100644
--- /dev/null
+++ b/parser/expat/lib/siphash.h
@@ -0,0 +1,344 @@
+/* ==========================================================================
+ * siphash.h - SipHash-2-4 in a single header file
+ * --------------------------------------------------------------------------
+ * Derived by William Ahern from the reference implementation[1] published[2]
+ * by Jean-Philippe Aumasson and Daniel J. Berstein. Licensed in kind.
+ * by Jean-Philippe Aumasson and Daniel J. Berstein.
+ * Minimal changes by Sebastian Pipping on top, details below.
+ * Licensed under the CC0 Public Domain Dedication license.
+ *
+ * 1. https://www.131002.net/siphash/siphash24.c
+ * 2. https://www.131002.net/siphash/
+ * --------------------------------------------------------------------------
+ * HISTORY:
+ *
+ * 2017-06-10  (Sebastian Pipping)
+ *   - Clarify license note in the header
+ *   - Address C89 issues:
+ *     - Stop using inline keyword (and let compiler decide)
+ *     - Turn integer suffix ULL to UL
+ *     - Replace _Bool by int
+ *     - Turn macro siphash24 into a function
+ *     - Address invalid conversion (void pointer) by explicit cast
+ *   - Always expose sip24_valid (for self-tests)
+ *
+ * 2012-11-04 - Born.  (William Ahern)
+ * --------------------------------------------------------------------------
+ * USAGE:
+ *
+ * SipHash-2-4 takes as input two 64-bit words as the key, some number of
+ * message bytes, and outputs a 64-bit word as the message digest. This
+ * implementation employs two data structures: a struct sipkey for
+ * representing the key, and a struct siphash for representing the hash
+ * state.
+ *
+ * For converting a 16-byte unsigned char array to a key, use either the
+ * macro sip_keyof or the routine sip_tokey. The former instantiates a
+ * compound literal key, while the latter requires a key object as a
+ * parameter.
+ *
+ * 	unsigned char secret[16];
+ * 	arc4random_buf(secret, sizeof secret);
+ * 	struct sipkey *key = sip_keyof(secret);
+ *
+ * For hashing a message, use either the convenience macro siphash24 or the
+ * routines sip24_init, sip24_update, and sip24_final.
+ *
+ * 	struct siphash state;
+ * 	void *msg;
+ * 	size_t len;
+ * 	uint64_t hash;
+ *
+ * 	sip24_init(&state, key);
+ * 	sip24_update(&state, msg, len);
+ * 	hash = sip24_final(&state);
+ *
+ * or
+ *
+ * 	hash = siphash24(msg, len, key);
+ *
+ * To convert the 64-bit hash value to a canonical 8-byte little-endian
+ * binary representation, use either the macro sip_binof or the routine
+ * sip_tobin. The former instantiates and returns a compound literal array,
+ * while the latter requires an array object as a parameter.
+ * --------------------------------------------------------------------------
+ * NOTES:
+ *
+ * o Neither sip_keyof, sip_binof, nor siphash24 will work with compilers
+ *   lacking compound literal support. Instead, you must use the lower-level
+ *   interfaces which take as parameters the temporary state objects.
+ *
+ * o Uppercase macros may evaluate parameters more than once. Lowercase
+ *   macros should not exhibit any such side effects.
+ * ==========================================================================
+ */
+#ifndef SIPHASH_H
+#define SIPHASH_H
+
+#include <stddef.h> /* size_t */
+#include <stdint.h> /* uint64_t uint32_t uint8_t */
+
+
+#define SIP_ROTL(x, b) (uint64_t)(((x) << (b)) | ( (x) >> (64 - (b))))
+
+#define SIP_U32TO8_LE(p, v) \
+	(p)[0] = (uint8_t)((v) >>  0); (p)[1] = (uint8_t)((v) >>  8); \
+	(p)[2] = (uint8_t)((v) >> 16); (p)[3] = (uint8_t)((v) >> 24);
+
+#define SIP_U64TO8_LE(p, v) \
+	SIP_U32TO8_LE((p) + 0, (uint32_t)((v) >>  0)); \
+	SIP_U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
+
+#define SIP_U8TO64_LE(p) \
+	(((uint64_t)((p)[0]) <<  0) | \
+	 ((uint64_t)((p)[1]) <<  8) | \
+	 ((uint64_t)((p)[2]) << 16) | \
+	 ((uint64_t)((p)[3]) << 24) | \
+	 ((uint64_t)((p)[4]) << 32) | \
+	 ((uint64_t)((p)[5]) << 40) | \
+	 ((uint64_t)((p)[6]) << 48) | \
+	 ((uint64_t)((p)[7]) << 56))
+
+
+#define SIPHASH_INITIALIZER { 0, 0, 0, 0, { 0 }, 0, 0 }
+
+struct siphash {
+	uint64_t v0, v1, v2, v3;
+
+	unsigned char buf[8], *p;
+	uint64_t c;
+}; /* struct siphash */
+
+
+#define SIP_KEYLEN 16
+
+struct sipkey {
+	uint64_t k[2];
+}; /* struct sipkey */
+
+#define sip_keyof(k) sip_tokey(&(struct sipkey){ { 0 } }, (k))
+
+static struct sipkey *sip_tokey(struct sipkey *key, const void *src) {
+	key->k[0] = SIP_U8TO64_LE((const unsigned char *)src);
+	key->k[1] = SIP_U8TO64_LE((const unsigned char *)src + 8);
+	return key;
+} /* sip_tokey() */
+
+
+#define sip_binof(v) sip_tobin((unsigned char[8]){ 0 }, (v))
+
+static void *sip_tobin(void *dst, uint64_t u64) {
+	SIP_U64TO8_LE((unsigned char *)dst, u64);
+	return dst;
+} /* sip_tobin() */
+
+
+static void sip_round(struct siphash *H, const int rounds) {
+	int i;
+
+	for (i = 0; i < rounds; i++) {
+		H->v0 += H->v1;
+		H->v1 = SIP_ROTL(H->v1, 13);
+		H->v1 ^= H->v0;
+		H->v0 = SIP_ROTL(H->v0, 32);
+
+		H->v2 += H->v3;
+		H->v3 = SIP_ROTL(H->v3, 16);
+		H->v3 ^= H->v2;
+
+		H->v0 += H->v3;
+		H->v3 = SIP_ROTL(H->v3, 21);
+		H->v3 ^= H->v0;
+
+		H->v2 += H->v1;
+		H->v1 = SIP_ROTL(H->v1, 17);
+		H->v1 ^= H->v2;
+		H->v2 = SIP_ROTL(H->v2, 32);
+	}
+} /* sip_round() */
+
+
+static struct siphash *sip24_init(struct siphash *H, const struct sipkey *key) {
+	H->v0 = 0x736f6d6570736575UL ^ key->k[0];
+	H->v1 = 0x646f72616e646f6dUL ^ key->k[1];
+	H->v2 = 0x6c7967656e657261UL ^ key->k[0];
+	H->v3 = 0x7465646279746573UL ^ key->k[1];
+
+	H->p = H->buf;
+	H->c = 0;
+
+	return H;
+} /* sip24_init() */
+
+
+#define sip_endof(a) (&(a)[sizeof (a) / sizeof *(a)])
+
+static struct siphash *sip24_update(struct siphash *H, const void *src, size_t len) {
+	const unsigned char *p = (const unsigned char *)src, *pe = p + len;
+	uint64_t m;
+
+	do {
+		while (p < pe && H->p < sip_endof(H->buf))
+			*H->p++ = *p++;
+
+		if (H->p < sip_endof(H->buf))
+			break;
+
+		m = SIP_U8TO64_LE(H->buf);
+		H->v3 ^= m;
+		sip_round(H, 2);
+		H->v0 ^= m;
+
+		H->p = H->buf;
+		H->c += 8;
+	} while (p < pe);
+
+	return H;
+} /* sip24_update() */
+
+
+static uint64_t sip24_final(struct siphash *H) {
+	char left = H->p - H->buf;
+	uint64_t b = (H->c + left) << 56;
+
+	switch (left) {
+	case 7: b |= (uint64_t)H->buf[6] << 48;
+	case 6: b |= (uint64_t)H->buf[5] << 40;
+	case 5: b |= (uint64_t)H->buf[4] << 32;
+	case 4: b |= (uint64_t)H->buf[3] << 24;
+	case 3: b |= (uint64_t)H->buf[2] << 16;
+	case 2: b |= (uint64_t)H->buf[1] << 8;
+	case 1: b |= (uint64_t)H->buf[0] << 0;
+	case 0: break;
+	}
+
+	H->v3 ^= b;
+	sip_round(H, 2);
+	H->v0 ^= b;
+	H->v2 ^= 0xff;
+	sip_round(H, 4);
+
+	return H->v0 ^ H->v1 ^ H->v2  ^ H->v3;
+} /* sip24_final() */
+
+
+static uint64_t siphash24(const void *src, size_t len, const struct sipkey *key) {
+	struct siphash state = SIPHASH_INITIALIZER;
+	return sip24_final(sip24_update(sip24_init(&state, key), src, len));
+} /* siphash24() */
+
+
+/*
+ * SipHash-2-4 output with
+ * k = 00 01 02 ...
+ * and
+ * in = (empty string)
+ * in = 00 (1 byte)
+ * in = 00 01 (2 bytes)
+ * in = 00 01 02 (3 bytes)
+ * ...
+ * in = 00 01 02 ... 3e (63 bytes)
+ */
+static int sip24_valid(void) {
+	static const unsigned char vectors[64][8] = {
+		{ 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, },
+		{ 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, },
+		{ 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, },
+		{ 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, },
+		{ 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, },
+		{ 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, },
+		{ 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, },
+		{ 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, },
+		{ 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, },
+		{ 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, },
+		{ 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, },
+		{ 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, },
+		{ 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, },
+		{ 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, },
+		{ 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, },
+		{ 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, },
+		{ 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, },
+		{ 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, },
+		{ 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, },
+		{ 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, },
+		{ 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, },
+		{ 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, },
+		{ 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, },
+		{ 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, },
+		{ 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, },
+		{ 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, },
+		{ 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, },
+		{ 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, },
+		{ 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, },
+		{ 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, },
+		{ 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, },
+		{ 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, },
+		{ 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, },
+		{ 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, },
+		{ 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, },
+		{ 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, },
+		{ 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, },
+		{ 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, },
+		{ 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, },
+		{ 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, },
+		{ 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, },
+		{ 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, },
+		{ 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, },
+		{ 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, },
+		{ 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, },
+		{ 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, },
+		{ 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, },
+		{ 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, },
+		{ 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, },
+		{ 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, },
+		{ 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, },
+		{ 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, },
+		{ 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, },
+		{ 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, },
+		{ 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, },
+		{ 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, },
+		{ 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, },
+		{ 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, },
+		{ 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, },
+		{ 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, },
+		{ 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, },
+		{ 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, },
+		{ 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, },
+		{ 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, }
+	};
+	unsigned char in[64];
+	struct sipkey k;
+	size_t i;
+
+	sip_tokey(&k, "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017");
+
+	for (i = 0; i < sizeof in; ++i) {
+		in[i] = i;
+
+		if (siphash24(in, i, &k) != SIP_U8TO64_LE(vectors[i]))
+			return 0;
+	}
+
+	return 1;
+} /* sip24_valid() */
+
+
+#if SIPHASH_MAIN
+
+#include <stdio.h>
+
+int main(void) {
+	int ok = sip24_valid();
+
+	if (ok)
+		puts("OK");
+	else
+		puts("FAIL");
+
+	return !ok;
+} /* main() */
+
+#endif /* SIPHASH_MAIN */
+
+
+#endif /* SIPHASH_H */
--- a/parser/expat/lib/xmlparse.c
+++ b/parser/expat/lib/xmlparse.c
@@ -1,30 +1,43 @@
 /* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
    See the file COPYING for copying permission.
 */
 
+#define _GNU_SOURCE                     /* syscall prototype */
+
 #include <stddef.h>
 #include <string.h>                     /* memset(), memcpy() */
 #include <assert.h>
 #include <limits.h>                     /* UINT_MAX */
+#include <stdio.h>                      /* fprintf */
+#include <stdlib.h>                     /* getenv */
+
+#ifdef _WIN32
+#define getpid GetCurrentProcessId
+#else
+#include <sys/time.h>                   /* gettimeofday() */
+#include <sys/types.h>                  /* getpid() */
+#include <unistd.h>                     /* getpid() */
+#endif
 
 #define XML_BUILDING_EXPAT 1
 
 #ifdef COMPILED_FROM_DSP
 #include "winconfig.h"
 #elif defined(MACOS_CLASSIC)
 #include "macconfig.h"
 #elif defined(__amigaos4__)
 #include "amigaconfig.h"
 #elif defined(HAVE_EXPAT_CONFIG_H)
 #include <expat_config.h>
 #endif /* ndef COMPILED_FROM_DSP */
 
 #include "expat.h"
+#include "siphash.h"
 
 #ifdef XML_UNICODE
 #define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX
 #define XmlConvert XmlUtf16Convert
 #define XmlGetInternalEncoding XmlGetUtf16InternalEncoding
 #define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS
 #define XmlEncode XmlUtf16Encode
 
@@ -100,27 +113,21 @@ typedef struct {
 typedef struct {
   NAMED **v;
   unsigned char power;
   size_t size;
   size_t used;
   const XML_Memory_Handling_Suite *mem;
 } HASH_TABLE;
 
-/* Basic character hash algorithm, taken from Python's string hash:
-   h = h * 1000003 ^ character, the constant being a prime number.
-
-*/
-#ifdef XML_UNICODE
-#define CHAR_HASH(h, c) \
-  (((h) * 0xF4243) ^ (unsigned short)(c))
-#else
-#define CHAR_HASH(h, c) \
-  (((h) * 0xF4243) ^ (unsigned char)(c))
-#endif
+static size_t
+keylen(KEY s);
+
+static void
+copy_salt_to_sipkey(XML_Parser parser, struct sipkey * key);
 
 /* For probing (after a collision) we need a step size relative prime
    to the hash table size, which is a power of 2. We use double-hashing,
    since we can calculate a second hash value cheaply by taking those bits
    of the first hash value that were discarded (masked out) when the table
    index was calculated: index = hash & mask, where mask = table->size - 1.
    We limit the maximum step size to table->size / 4 (mask >> 2) and make
    it odd, since odd numbers are always relative prime to a power of 2.
@@ -395,22 +402,23 @@ static void FASTCALL normalizePublicId(X
 static DTD * dtdCreate(const XML_Memory_Handling_Suite *ms);
 /* BEGIN MOZILLA CHANGE (unused API) */
 /* do not call if parentParser != NULL */
 //static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms);
 /* END MOZILLA CHANGE */
 static void
 dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms);
 static int
-dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms);
+dtdCopy(XML_Parser oldParser,
+        DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms);
 static int
-copyEntityTable(HASH_TABLE *, STRING_POOL *, const HASH_TABLE *);
-
+copyEntityTable(XML_Parser oldParser,
+                HASH_TABLE *, STRING_POOL *, const HASH_TABLE *);
 static NAMED *
-lookup(HASH_TABLE *table, KEY name, size_t createSize);
+lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize);
 static void FASTCALL
 hashTableInit(HASH_TABLE *, const XML_Memory_Handling_Suite *ms);
 /* BEGIN MOZILLA CHANGE (unused API) */
 //static void FASTCALL hashTableClear(HASH_TABLE *);
 /* END MOZILLA CHANGE */
 static void FASTCALL hashTableDestroy(HASH_TABLE *);
 static void FASTCALL
 hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *);
@@ -435,16 +443,19 @@ static const XML_Char * FASTCALL
 poolAppendString(STRING_POOL *pool, const XML_Char *s);
 
 static int FASTCALL nextScaffoldPart(XML_Parser parser);
 static XML_Content * build_model(XML_Parser parser);
 static ELEMENT_TYPE *
 getElementType(XML_Parser parser, const ENCODING *enc,
                const char *ptr, const char *end);
 
+static unsigned long generate_hash_secret_salt(XML_Parser parser);
+static XML_Bool startParsing(XML_Parser parser);
+
 static XML_Parser
 parserCreate(const XML_Char *encodingName,
              const XML_Memory_Handling_Suite *memsuite,
              const XML_Char *nameSep,
              DTD *dtd);
 static void
 parserInit(XML_Parser parser, const XML_Char *encodingName);
 
@@ -552,16 +563,17 @@ struct XML_ParserStruct {
   XML_Char m_namespaceSeparator;
   XML_Parser m_parentParser;
   XML_ParsingStatus m_parsingStatus;
 #ifdef XML_DTD
   XML_Bool m_isParamEntity;
   XML_Bool m_useForeignDTD;
   enum XML_ParamEntityParsing m_paramEntityParsing;
 #endif
+  unsigned long m_hash_secret_salt;
 /* BEGIN MOZILLA CHANGE (Report opening tag of mismatched closing tag) */
   const XML_Char* m_mismatch;
 /* END MOZILLA CHANGE */
 };
 
 #define MALLOC(s) (parser->m_mem.malloc_fcn((s)))
 #define REALLOC(p,s) (parser->m_mem.realloc_fcn((p),(s)))
 #define FREE(p) (parser->m_mem.free_fcn((p)))
@@ -662,55 +674,222 @@ struct XML_ParserStruct {
 #define parentParser (parser->m_parentParser)
 #define ps_parsing (parser->m_parsingStatus.parsing)
 #define ps_finalBuffer (parser->m_parsingStatus.finalBuffer)
 #ifdef XML_DTD
 #define isParamEntity (parser->m_isParamEntity)
 #define useForeignDTD (parser->m_useForeignDTD)
 #define paramEntityParsing (parser->m_paramEntityParsing)
 #endif /* XML_DTD */
+#define hash_secret_salt (parser->m_hash_secret_salt)
 /* BEGIN MOZILLA CHANGE (Report opening tag of mismatched closing tag) */
 #define mismatch (parser->m_mismatch)
 /* END MOZILLA CHANGE */
 
 /* BEGIN MOZILLA CHANGE (unused API) */
 #if 0
 XML_Parser XMLCALL
+XML_ParserCreate(const XML_Char *encodingName)
+{
+  return XML_ParserCreate_MM(encodingName, NULL, NULL);
+}
+
+XML_Parser XMLCALL
 XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep)
 {
   XML_Char tmp[2];
   *tmp = nsSep;
   return XML_ParserCreate_MM(encodingName, NULL, tmp);
 }
 #endif
 /* END MOZILLA CHANGE */
 
 static const XML_Char implicitContext[] = {
   'x', 'm', 'l', '=', 'h', 't', 't', 'p', ':', '/', '/',
   'w', 'w', 'w', '.', 'w', '3', '.', 'o', 'r', 'g', '/',
   'X', 'M', 'L', '/', '1', '9', '9', '8', '/',
   'n', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e', '\0'
 };
 
+
+#if defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM)
+# include <errno.h>
+
+# if defined(HAVE_GETRANDOM)
+#  include <sys/random.h>    /* getrandom */
+# else
+#  include <unistd.h>        /* syscall */
+#  include <sys/syscall.h>   /* SYS_getrandom */
+# endif
+
+/* Obtain entropy on Linux 3.17+ */
+static int
+writeRandomBytes_getrandom(void * target, size_t count) {
+  int success = 0;  /* full count bytes written? */
+  size_t bytesWrittenTotal = 0;
+  const unsigned int getrandomFlags = 0;
+
+  do {
+    void * const currentTarget = (void*)((char*)target + bytesWrittenTotal);
+    const size_t bytesToWrite = count - bytesWrittenTotal;
+
+    const int bytesWrittenMore =
+#if defined(HAVE_GETRANDOM)
+        getrandom(currentTarget, bytesToWrite, getrandomFlags);
+#else
+        syscall(SYS_getrandom, currentTarget, bytesToWrite, getrandomFlags);
+#endif
+
+    if (bytesWrittenMore > 0) {
+      bytesWrittenTotal += bytesWrittenMore;
+      if (bytesWrittenTotal >= count)
+        success = 1;
+    }
+  } while (! success && (errno == EINTR || errno == EAGAIN));
+
+  return success;
+}
+
+#endif  /* defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) */
+
+
+#ifdef _WIN32
+
+typedef BOOLEAN (APIENTRY *RTLGENRANDOM_FUNC)(PVOID, ULONG);
+
+/* Obtain entropy on Windows XP / Windows Server 2003 and later.
+ * Hint on RtlGenRandom and the following article from libsodioum.
+ *
+ * Michael Howard: Cryptographically Secure Random number on Windows without using CryptoAPI
+ * https://blogs.msdn.microsoft.com/michael_howard/2005/01/14/cryptographically-secure-random-number-on-windows-without-using-cryptoapi/
+ */
+static int
+writeRandomBytes_RtlGenRandom(void * target, size_t count) {
+  int success = 0;  /* full count bytes written? */
+  const HMODULE advapi32 = LoadLibrary("ADVAPI32.DLL");
+
+  if (advapi32) {
+    const RTLGENRANDOM_FUNC RtlGenRandom
+        = (RTLGENRANDOM_FUNC)GetProcAddress(advapi32, "SystemFunction036");
+    if (RtlGenRandom) {
+      if (RtlGenRandom((PVOID)target, (ULONG)count) == TRUE) {
+        success = 1;
+      }
+    }
+    FreeLibrary(advapi32);
+  }
+
+  return success;
+}
+
+#endif /* _WIN32 */
+
+
+static unsigned long
+gather_time_entropy(void)
+{
+#ifdef _WIN32
+  FILETIME ft;
+  GetSystemTimeAsFileTime(&ft); /* never fails */
+  return ft.dwHighDateTime ^ ft.dwLowDateTime;
+#else
+  struct timeval tv;
+  int gettimeofday_res;
+
+  gettimeofday_res = gettimeofday(&tv, NULL);
+  assert (gettimeofday_res == 0);
+
+  /* Microseconds time is <20 bits entropy */
+  return tv.tv_usec;
+#endif
+}
+
+#if defined(HAVE_ARC4RANDOM_BUF) && defined(HAVE_LIBBSD)
+# include <bsd/stdlib.h>
+#endif
+
+static unsigned long
+ENTROPY_DEBUG(const char * label, unsigned long entropy) {
+/* BEGIN MOZILLA CHANGE (don't getenv every time we set up a hash) */
+#if 0
+  const char * const EXPAT_ENTROPY_DEBUG = getenv("EXPAT_ENTROPY_DEBUG");
+  if (EXPAT_ENTROPY_DEBUG && ! strcmp(EXPAT_ENTROPY_DEBUG, "1")) {
+    fprintf(stderr, "Entropy: %s --> 0x%0*lx (%lu bytes)\n",
+        label,
+        (int)sizeof(entropy) * 2, entropy,
+        (unsigned long)sizeof(entropy));
+  }
+#endif
+/* END MOZILLA CHANGE */
+  return entropy;
+}
+
+static unsigned long
+generate_hash_secret_salt(XML_Parser parser)
+{
+  unsigned long entropy;
+  (void)parser;
+#if defined(HAVE_ARC4RANDOM_BUF) || defined(__CloudABI__)
+  (void)gather_time_entropy;
+  arc4random_buf(&entropy, sizeof(entropy));
+  return ENTROPY_DEBUG("arc4random_buf", entropy);
+#else
+  /* Try high quality providers first .. */
+#ifdef _WIN32
+  if (writeRandomBytes_RtlGenRandom((void *)&entropy, sizeof(entropy))) {
+    return ENTROPY_DEBUG("RtlGenRandom", entropy);
+  }
+#elif defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM)
+  if (writeRandomBytes_getrandom((void *)&entropy, sizeof(entropy))) {
+    return ENTROPY_DEBUG("getrandom", entropy);
+  }
+#endif
+  /* .. and self-made low quality for backup: */
+
+  /* Process ID is 0 bits entropy if attacker has local access */
+  entropy = gather_time_entropy() ^ getpid();
+
+  /* Factors are 2^31-1 and 2^61-1 (Mersenne primes M31 and M61) */
+  if (sizeof(unsigned long) == 4) {
+    return ENTROPY_DEBUG("fallback(4)", entropy * 2147483647);
+  } else {
+    return ENTROPY_DEBUG("fallback(8)",
+        entropy * (unsigned long)2305843009213693951);
+  }
+#endif
+}
+
+static unsigned long
+get_hash_secret_salt(XML_Parser parser) {
+  if (parser->m_parentParser != NULL)
+    return get_hash_secret_salt(parser->m_parentParser);
+  return parser->m_hash_secret_salt;
+}
+
+static XML_Bool  /* only valid for root parser */
+startParsing(XML_Parser parser)
+{
+    /* hash functions must be initialized before setContext() is called */
+    if (hash_secret_salt == 0)
+      hash_secret_salt = generate_hash_secret_salt(parser);
+    if (ns) {
+      /* implicit context only set for root parser, since child
+         parsers (i.e. external entity parsers) will inherit it
+      */
+      return setContext(parser, implicitContext);
+    }
+    return XML_TRUE;
+}
+
 XML_Parser XMLCALL
 XML_ParserCreate_MM(const XML_Char *encodingName,
                     const XML_Memory_Handling_Suite *memsuite,
                     const XML_Char *nameSep)
 {
-  XML_Parser parser = parserCreate(encodingName, memsuite, nameSep, NULL);
-  if (parser != NULL && ns) {
-    /* implicit context only set for root parser, since child
-       parsers (i.e. external entity parsers) will inherit it
-    */
-    if (!setContext(parser, implicitContext)) {
-      XML_ParserFree(parser);
-      return NULL;
-    }
-  }
-  return parser;
+  return parserCreate(encodingName, memsuite, nameSep, NULL);
 }
 
 static XML_Parser
 parserCreate(const XML_Char *encodingName,
              const XML_Memory_Handling_Suite *memsuite,
              const XML_Char *nameSep,
              DTD *dtd)
 {
@@ -878,16 +1057,17 @@ parserInit(XML_Parser parser, const XML_
   unknownEncodingData = NULL;
   parentParser = NULL;
   ps_parsing = XML_INITIALIZED;
 #ifdef XML_DTD
   isParamEntity = XML_FALSE;
   useForeignDTD = XML_FALSE;
   paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
 #endif
+  hash_secret_salt = 0;
 }
 
 /* BEGIN MOZILLA CHANGE (unused API) */
 #if 0
 /* moves list of bindings to freeBindingList */
 static void FASTCALL
 moveToFreeBindingList(XML_Parser parser, BINDING *bindings)
 {
@@ -927,17 +1107,17 @@ XML_ParserReset(XML_Parser parser, const
   moveToFreeBindingList(parser, inheritedBindings);
   FREE(unknownEncodingMem);
   if (unknownEncodingRelease)
     unknownEncodingRelease(unknownEncodingData);
   poolClear(&tempPool);
   poolClear(&temp2Pool);
   parserInit(parser, encodingName);
   dtdReset(_dtd, &parser->m_mem);
-  return setContext(parser, implicitContext);
+  return XML_TRUE;
 }
 
 enum XML_Status XMLCALL
 XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName)
 {
   /* Block after XML_Parse()/XML_ParseBuffer() has been called.
      XXX There's no way for the caller to determine which of the
      XXX possible error cases caused the XML_STATUS_ERROR return.
@@ -998,16 +1178,22 @@ XML_ExternalEntityParserCreate(XML_Parse
   void *oldHandlerArg = handlerArg;
   XML_Bool oldDefaultExpandInternalEntities = defaultExpandInternalEntities;
   XML_Parser oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg;
 #ifdef XML_DTD
   enum XML_ParamEntityParsing oldParamEntityParsing = paramEntityParsing;
   int oldInEntityValue = prologState.inEntityValue;
 #endif
   XML_Bool oldns_triplets = ns_triplets;
+  /* Note that the new parser shares the same hash secret as the old
+     parser, so that dtdCopy and copyEntityTable can lookup values
+     from hash tables associated with either parser without us having
+     to worry which hash secrets each table has.
+  */
+  unsigned long oldhash_secret_salt = hash_secret_salt;
 
 #ifdef XML_DTD
   if (!context)
     newDtd = oldDtd;
 #endif /* XML_DTD */
 
   /* Note that the magical uses of the pre-processor to make field
      access look more like C++ require that `parser' be overwritten
@@ -1051,23 +1237,24 @@ XML_ExternalEntityParserCreate(XML_Parse
   if (oldUserData == oldHandlerArg)
     handlerArg = userData;
   else
     handlerArg = parser;
   if (oldExternalEntityRefHandlerArg != oldParser)
     externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg;
   defaultExpandInternalEntities = oldDefaultExpandInternalEntities;
   ns_triplets = oldns_triplets;
+  hash_secret_salt = oldhash_secret_salt;
   parentParser = oldParser;
 #ifdef XML_DTD
   paramEntityParsing = oldParamEntityParsing;
   prologState.inEntityValue = oldInEntityValue;
   if (context) {
 #endif /* XML_DTD */
-    if (!dtdCopy(_dtd, oldDtd, &parser->m_mem)
+    if (!dtdCopy(oldParser, _dtd, oldDtd, &parser->m_mem)
       || !setContext(parser, context)) {
       XML_ParserFree(parser);
       return NULL;
     }
     processor = externalEntityInitProcessor;
 #ifdef XML_DTD
   }
   else {
@@ -1467,26 +1654,46 @@ XML_SetParamEntityParsing(XML_Parser par
 #ifdef XML_DTD
   paramEntityParsing = peParsing;
   return 1;
 #else
   return peParsing == XML_PARAM_ENTITY_PARSING_NEVER;
 #endif
 }
 
+int XMLCALL
+XML_SetHashSalt(XML_Parser parser,
+                unsigned long hash_salt)
+{
+  if (parser == NULL)
+    return 0;
+  if (parser->m_parentParser)
+    return XML_SetHashSalt(parser->m_parentParser, hash_salt);
+  /* block after XML_Parse()/XML_ParseBuffer() has been called */
+  if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+    return 0;
+  hash_secret_salt = hash_salt;
+  return 1;
+}
+
 enum XML_Status XMLCALL
 XML_Parse(XML_Parser parser, const char *s, int len, int isFinal)
 {
   switch (ps_parsing) {
   case XML_SUSPENDED:
     errorCode = XML_ERROR_SUSPENDED;
     return XML_STATUS_ERROR;
   case XML_FINISHED:
     errorCode = XML_ERROR_FINISHED;
     return XML_STATUS_ERROR;
+  case XML_INITIALIZED:
+    if (parentParser == NULL && !startParsing(parser)) {
+      errorCode = XML_ERROR_NO_MEMORY;
+      return XML_STATUS_ERROR;
+    }
   default:
     ps_parsing = XML_PARSING;
   }
 
   if (len == 0) {
     ps_finalBuffer = (XML_Bool)isFinal;
     if (!isFinal)
       return XML_STATUS_OK;
@@ -1626,16 +1833,21 @@ XML_ParseBuffer(XML_Parser parser, int l
 
   switch (ps_parsing) {
   case XML_SUSPENDED:
     errorCode = XML_ERROR_SUSPENDED;
     return XML_STATUS_ERROR;
   case XML_FINISHED:
     errorCode = XML_ERROR_FINISHED;
     return XML_STATUS_ERROR;
+  case XML_INITIALIZED:
+    if (parentParser == NULL && !startParsing(parser)) {
+      errorCode = XML_ERROR_NO_MEMORY;
+      return XML_STATUS_ERROR;
+    }
   default:
     ps_parsing = XML_PARSING;
   }
 
   start = bufferPtr;
   positionPtr = start;
   bufferEnd += len;
   parseEndPtr = bufferEnd;
@@ -2300,17 +2512,17 @@ doContent(XML_Parser parser,
             reportDefault(parser, enc, s, next);
           break;
         }
         name = poolStoreString(&dtd->pool, enc,
                                 s + enc->minBytesPerChar,
                                 next - enc->minBytesPerChar);
         if (!name)
           return XML_ERROR_NO_MEMORY;
-        entity = (ENTITY *)lookup(&dtd->generalEntities, name, 0);
+        entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0);
         poolDiscard(&dtd->pool);
         /* First, determine if a check for an existing declaration is needed;
            if yes, check that the entity exists, and that it is internal,
            otherwise call the skipped entity or default handler.
         */
         if (!dtd->hasParamEntityRefs || dtd->standalone) {
           if (!entity)
             return XML_ERROR_UNDEFINED_ENTITY;
@@ -2719,22 +2931,22 @@ storeAtts(XML_Parser parser, const ENCOD
   int nPrefixes = 0;
 /* BEGIN MOZILLA CHANGE (Include xmlns attributes in attributes array) */
   int nXMLNSDeclarations = 0;
 /* END MOZILLA CHANGE */
   BINDING *binding;
   const XML_Char *localPart;
 
   /* lookup the element type name */
-  elementType = (ELEMENT_TYPE *)lookup(&dtd->elementTypes, tagNamePtr->str,0);
+  elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, tagNamePtr->str,0);
   if (!elementType) {
     const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str);
     if (!name)
       return XML_ERROR_NO_MEMORY;
-    elementType = (ELEMENT_TYPE *)lookup(&dtd->elementTypes, name,
+    elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name,
                                          sizeof(ELEMENT_TYPE));
     if (!elementType)
       return XML_ERROR_NO_MEMORY;
     if (ns && !setElementTypePrefix(parser, elementType))
       return XML_ERROR_NO_MEMORY;
   }
   nDefaultAtts = elementType->nDefaultAtts;
 
@@ -2919,41 +3131,52 @@ storeAtts(XML_Parser parser, const ENCOD
 /* END MOZILLA CHANGE */
 
     /* expand prefixed names and check for duplicates */
     for (; i < attIndex; i += 2) {
       const XML_Char *s = appAtts[i];
       if (s[-1] == 2) {  /* prefixed */
         ATTRIBUTE_ID *id;
         const BINDING *b;
-        unsigned long uriHash = 0;
+        unsigned long uriHash;
+        struct siphash sip_state;
+        struct sipkey sip_key;
+
+        copy_salt_to_sipkey(parser, &sip_key);
+        sip24_init(&sip_state, &sip_key);
+
         ((XML_Char *)s)[-1] = 0;  /* clear flag */
-        id = (ATTRIBUTE_ID *)lookup(&dtd->attributeIds, s, 0);
+        id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, s, 0);
         b = id->prefix->binding;
         if (!b)
           return XML_ERROR_UNBOUND_PREFIX;
 
         /* as we expand the name we also calculate its hash value */
         for (j = 0; j < b->uriLen; j++) {
           const XML_Char c = b->uri[j];
           if (!poolAppendChar(&tempPool, c))
             return XML_ERROR_NO_MEMORY;
-          uriHash = CHAR_HASH(uriHash, c);
         }
+
+        sip24_update(&sip_state, b->uri, b->uriLen * sizeof(XML_Char));
+
         while (*s++ != XML_T(':'))
           ;
+
+        sip24_update(&sip_state, s, keylen(s) * sizeof(XML_Char));
+
         do {  /* copies null terminator */
-          const XML_Char c = *s;
           if (!poolAppendChar(&tempPool, *s))
             return XML_ERROR_NO_MEMORY;
-          uriHash = CHAR_HASH(uriHash, c);
         } while (*s++);
 
+        uriHash = (unsigned long)sip24_final(&sip_state);
+
         { /* Check hash table for duplicate of expanded name (uriName).
-             Derived from code in lookup(HASH_TABLE *table, ...).
+             Derived from code in lookup(parser, HASH_TABLE *table, ...).
           */
           unsigned char step = 0;
           unsigned long mask = nsAttsSize - 1;
           j = uriHash & mask;  /* index into hash table */
           while (nsAtts[j].version == version) {
             /* for speed we compare stored hash values first */
             if (uriHash == nsAtts[j].hash) {
               const XML_Char *s1 = poolStart(&tempPool);
@@ -3931,17 +4154,18 @@ doProlog(XML_Parser parser,
         enc = encoding;
         handleDefault = XML_FALSE;
       }
       break;
 #endif /* XML_DTD */
     case XML_ROLE_DOCTYPE_PUBLIC_ID:
 #ifdef XML_DTD
       useForeignDTD = XML_FALSE;
-      declEntity = (ENTITY *)lookup(&dtd->paramEntities,
+      declEntity = (ENTITY *)lookup(parser,
+                                    &dtd->paramEntities,
                                     externalSubsetName,
                                     sizeof(ENTITY));
       if (!declEntity)
         return XML_ERROR_NO_MEMORY;
 #endif /* XML_DTD */
       dtd->hasParamEntityRefs = XML_TRUE;
       if (startDoctypeDeclHandler) {
         if (!XmlIsPublicId(enc, s, next, eventPP))
@@ -3986,17 +4210,18 @@ doProlog(XML_Parser parser,
          XML_ROLE_DOCTYPE_SYSTEM_ID, even if startDoctypeDeclHandler
          was not set, indicating an external subset
       */
 #ifdef XML_DTD
       if (doctypeSysid || useForeignDTD) {
         XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs;
         dtd->hasParamEntityRefs = XML_TRUE;
         if (paramEntityParsing && externalEntityRefHandler) {
-          ENTITY *entity = (ENTITY *)lookup(&dtd->paramEntities,
+          ENTITY *entity = (ENTITY *)lookup(parser,
+                                            &dtd->paramEntities,
                                             externalSubsetName,
                                             sizeof(ENTITY));
           if (!entity)
             return XML_ERROR_NO_MEMORY;
           if (useForeignDTD)
             entity->base = curBase;
           dtd->paramEntityRead = XML_FALSE;
           if (!externalEntityRefHandler(externalEntityRefHandlerArg,
@@ -4030,17 +4255,17 @@ doProlog(XML_Parser parser,
 #ifdef XML_DTD
       /* if there is no DOCTYPE declaration then now is the
          last chance to read the foreign DTD
       */
       if (useForeignDTD) {
         XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs;
         dtd->hasParamEntityRefs = XML_TRUE;
         if (paramEntityParsing && externalEntityRefHandler) {
-          ENTITY *entity = (ENTITY *)lookup(&dtd->paramEntities,
+          ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities,
                                             externalSubsetName,
                                             sizeof(ENTITY));
           if (!entity)
             return XML_ERROR_NO_MEMORY;
           entity->base = curBase;
           dtd->paramEntityRead = XML_FALSE;
           if (!externalEntityRefHandler(externalEntityRefHandlerArg,
                                         0,
@@ -4244,17 +4469,18 @@ doProlog(XML_Parser parser,
 #endif /* XML_DTD */
           && notStandaloneHandler
           && !notStandaloneHandler(handlerArg))
         return XML_ERROR_NOT_STANDALONE;
 #ifndef XML_DTD
       break;
 #else /* XML_DTD */
       if (!declEntity) {
-        declEntity = (ENTITY *)lookup(&dtd->paramEntities,
+        declEntity = (ENTITY *)lookup(parser,
+                                      &dtd->paramEntities,
                                       externalSubsetName,
                                       sizeof(ENTITY));
         if (!declEntity)
           return XML_ERROR_NO_MEMORY;
         declEntity->publicId = NULL;
       }
       /* fall through */
 #endif /* XML_DTD */
@@ -4319,17 +4545,17 @@ doProlog(XML_Parser parser,
         if (XmlPredefinedEntityName(enc, s, next)) {
           declEntity = NULL;
           break;
         }
         if (dtd->keepProcessing) {
           const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next);
           if (!name)
             return XML_ERROR_NO_MEMORY;
-          declEntity = (ENTITY *)lookup(&dtd->generalEntities, name,
+          declEntity = (ENTITY *)lookup(parser, &dtd->generalEntities, name,
                                         sizeof(ENTITY));
           if (!declEntity)
             return XML_ERROR_NO_MEMORY;
           if (declEntity->name != name) {
             poolDiscard(&dtd->pool);
             declEntity = NULL;
           }
           else {
@@ -4351,17 +4577,17 @@ doProlog(XML_Parser parser,
       }
       break;
     case XML_ROLE_PARAM_ENTITY_NAME:
 #ifdef XML_DTD
       if (dtd->keepProcessing) {
         const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next);
         if (!name)
           return XML_ERROR_NO_MEMORY;
-        declEntity = (ENTITY *)lookup(&dtd->paramEntities,
+        declEntity = (ENTITY *)lookup(parser, &dtd->paramEntities,
                                            name, sizeof(ENTITY));
         if (!declEntity)
           return XML_ERROR_NO_MEMORY;
         if (declEntity->name != name) {
           poolDiscard(&dtd->pool);
           declEntity = NULL;
         }
         else {
@@ -4533,17 +4759,17 @@ doProlog(XML_Parser parser,
       else {
         const XML_Char *name;
         ENTITY *entity;
         name = poolStoreString(&dtd->pool, enc,
                                 s + enc->minBytesPerChar,
                                 next - enc->minBytesPerChar);
         if (!name)
           return XML_ERROR_NO_MEMORY;
-        entity = (ENTITY *)lookup(&dtd->paramEntities, name, 0);
+        entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0);
         poolDiscard(&dtd->pool);
         /* first, determine if a check for an existing declaration is needed;
            if yes, check that the entity exists, and that it is internal,
            otherwise call the skipped entity handler
         */
         if (prologState.documentEntity &&
             (dtd->standalone
              ? !openInternalEntities
@@ -5085,17 +5311,17 @@ appendAttributeValue(XML_Parser parser, 
                 return XML_ERROR_NO_MEMORY;
           break;
         }
         name = poolStoreString(&temp2Pool, enc,
                                ptr + enc->minBytesPerChar,
                                next - enc->minBytesPerChar);
         if (!name)
           return XML_ERROR_NO_MEMORY;
-        entity = (ENTITY *)lookup(&dtd->generalEntities, name, 0);
+        entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0);
         poolDiscard(&temp2Pool);
         /* First, determine if a check for an existing declaration is needed;
            if yes, check that the entity exists, and that it is internal.
         */
         if (pool == &dtd->pool)  /* are we called from prolog? */
           checkEntityDecl =
 #ifdef XML_DTD
               prologState.documentEntity &&
@@ -5200,17 +5426,17 @@ storeEntityValue(XML_Parser parser,
         ENTITY *entity;
         name = poolStoreString(&tempPool, enc,
                                entityTextPtr + enc->minBytesPerChar,
                                next - enc->minBytesPerChar);
         if (!name) {
           result = XML_ERROR_NO_MEMORY;
           goto endEntityValue;
         }
-        entity = (ENTITY *)lookup(&dtd->paramEntities, name, 0);
+        entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0);
         poolDiscard(&tempPool);
         if (!entity) {
           /* not a well-formedness error - see XML 1.0: WFC Entity Declared */
           /* cannot report skipped entity here - see comments on
              skippedEntityHandler
           if (skippedEntityHandler)
             skippedEntityHandler(handlerArg, name, 0);
           */
@@ -5490,17 +5716,17 @@ setElementTypePrefix(XML_Parser parser, 
       PREFIX *prefix;
       const XML_Char *s;
       for (s = elementType->name; s != name; s++) {
         if (!poolAppendChar(&dtd->pool, *s))
           return 0;
       }
       if (!poolAppendChar(&dtd->pool, XML_T('\0')))
         return 0;
-      prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&dtd->pool),
+      prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool),
                                 sizeof(PREFIX));
       if (!prefix)
         return 0;
       if (prefix->name == poolStart(&dtd->pool))
         poolFinish(&dtd->pool);
       else
         poolDiscard(&dtd->pool);
       elementType->prefix = prefix;
@@ -5519,17 +5745,17 @@ getAttributeId(XML_Parser parser, const 
   const XML_Char *name;
   if (!poolAppendChar(&dtd->pool, XML_T('\0')))
     return NULL;
   name = poolStoreString(&dtd->pool, enc, start, end);
   if (!name)
     return NULL;
   /* skip quotation mark - its storage will be re-used (like in name[-1]) */
   ++name;
-  id = (ATTRIBUTE_ID *)lookup(&dtd->attributeIds, name, sizeof(ATTRIBUTE_ID));
+  id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, name, sizeof(ATTRIBUTE_ID));
   if (!id)
     return NULL;
   if (id->name != name)
     poolDiscard(&dtd->pool);
   else {
     poolFinish(&dtd->pool);
     if (!ns)
       ;
@@ -5537,32 +5763,32 @@ getAttributeId(XML_Parser parser, const 
         && name[1] == XML_T('m')
         && name[2] == XML_T('l')
         && name[3] == XML_T('n')
         && name[4] == XML_T('s')
         && (name[5] == XML_T('\0') || name[5] == XML_T(':'))) {
       if (name[5] == XML_T('\0'))
         id->prefix = &dtd->defaultPrefix;
       else
-        id->prefix = (PREFIX *)lookup(&dtd->prefixes, name + 6, sizeof(PREFIX));
+        id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, name + 6, sizeof(PREFIX));
       id->xmlns = XML_TRUE;
     }
     else {
       int i;
       for (i = 0; name[i]; i++) {
         /* attributes without prefix are *not* in the default namespace */
         if (name[i] == XML_T(':')) {
           int j;
           for (j = 0; j < i; j++) {
             if (!poolAppendChar(&dtd->pool, name[j]))
               return NULL;
           }
           if (!poolAppendChar(&dtd->pool, XML_T('\0')))
             return NULL;
-          id->prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&dtd->pool),
+          id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool),
                                         sizeof(PREFIX));
           if (id->prefix->name == poolStart(&dtd->pool))
             poolFinish(&dtd->pool);
           else
             poolDiscard(&dtd->pool);
           break;
         }
       }
@@ -5648,32 +5874,32 @@ setContext(XML_Parser parser, const XML_
   DTD * const dtd = _dtd;  /* save one level of indirection */
   const XML_Char *s = context;
 
   while (*context != XML_T('\0')) {
     if (*s == CONTEXT_SEP || *s == XML_T('\0')) {
       ENTITY *e;
       if (!poolAppendChar(&tempPool, XML_T('\0')))
         return XML_FALSE;
-      e = (ENTITY *)lookup(&dtd->generalEntities, poolStart(&tempPool), 0);
+      e = (ENTITY *)lookup(parser, &dtd->generalEntities, poolStart(&tempPool), 0);
       if (e)
         e->open = XML_TRUE;
       if (*s != XML_T('\0'))
         s++;
       context = s;
       poolDiscard(&tempPool);
     }
     else if (*s == XML_T('=')) {
       PREFIX *prefix;
       if (poolLength(&tempPool) == 0)
         prefix = &dtd->defaultPrefix;
       else {
         if (!poolAppendChar(&tempPool, XML_T('\0')))
           return XML_FALSE;
-        prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&tempPool),
+        prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&tempPool),
                                   sizeof(PREFIX));
         if (!prefix)
           return XML_FALSE;
         if (prefix->name == poolStart(&tempPool)) {
           prefix->name = poolCopyString(&dtd->pool, prefix->name);
           if (!prefix->name)
             return XML_FALSE;
         }
@@ -5831,32 +6057,32 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity,
   }
   ms->free_fcn(p);
 }
 
 /* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise.
    The new DTD has already been initialized.
 */
 static int
-dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms)
+dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms)
 {
   HASH_TABLE_ITER iter;
 
   /* Copy the prefix table. */
 
   hashTableIterInit(&iter, &(oldDtd->prefixes));
   for (;;) {
     const XML_Char *name;
     const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter);
     if (!oldP)
       break;
     name = poolCopyString(&(newDtd->pool), oldP->name);
     if (!name)
       return 0;
-    if (!lookup(&(newDtd->prefixes), name, sizeof(PREFIX)))
+    if (!lookup(oldParser, &(newDtd->prefixes), name, sizeof(PREFIX)))
       return 0;
   }
 
   hashTableIterInit(&iter, &(oldDtd->attributeIds));
 
   /* Copy the attribute id table. */
 
   for (;;) {
@@ -5868,27 +6094,27 @@ dtdCopy(DTD *newDtd, const DTD *oldDtd, 
       break;
     /* Remember to allocate the scratch byte before the name. */
     if (!poolAppendChar(&(newDtd->pool), XML_T('\0')))
       return 0;
     name = poolCopyString(&(newDtd->pool), oldA->name);
     if (!name)
       return 0;
     ++name;
-    newA = (ATTRIBUTE_ID *)lookup(&(newDtd->attributeIds), name,
+    newA = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds), name,
                                   sizeof(ATTRIBUTE_ID));
     if (!newA)
       return 0;
     newA->maybeTokenized = oldA->maybeTokenized;
     if (oldA->prefix) {
       newA->xmlns = oldA->xmlns;
       if (oldA->prefix == &oldDtd->defaultPrefix)
         newA->prefix = &newDtd->defaultPrefix;
       else
-        newA->prefix = (PREFIX *)lookup(&(newDtd->prefixes),
+        newA->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes),
                                         oldA->prefix->name, 0);
     }
   }
 
   /* Copy the element type table. */
 
   hashTableIterInit(&iter, &(oldDtd->elementTypes));
 
@@ -5897,58 +6123,60 @@ dtdCopy(DTD *newDtd, const DTD *oldDtd, 
     ELEMENT_TYPE *newE;
     const XML_Char *name;
     const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter);
     if (!oldE)
       break;
     name = poolCopyString(&(newDtd->pool), oldE->name);
     if (!name)
       return 0;
-    newE = (ELEMENT_TYPE *)lookup(&(newDtd->elementTypes), name,
+    newE = (ELEMENT_TYPE *)lookup(oldParser, &(newDtd->elementTypes), name,
                                   sizeof(ELEMENT_TYPE));
     if (!newE)
       return 0;
     if (oldE->nDefaultAtts) {
       newE->defaultAtts = (DEFAULT_ATTRIBUTE *)
           ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
       if (!newE->defaultAtts) {
         ms->free_fcn(newE);
         return 0;
       }
     }
     if (oldE->idAtt)
       newE->idAtt = (ATTRIBUTE_ID *)
-          lookup(&(newDtd->attributeIds), oldE->idAtt->name, 0);
+          lookup(oldParser, &(newDtd->attributeIds), oldE->idAtt->name, 0);
     newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts;
     if (oldE->prefix)
-      newE->prefix = (PREFIX *)lookup(&(newDtd->prefixes),
+      newE->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes),
                                       oldE->prefix->name, 0);
     for (i = 0; i < newE->nDefaultAtts; i++) {
       newE->defaultAtts[i].id = (ATTRIBUTE_ID *)
-          lookup(&(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0);
+          lookup(oldParser, &(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0);
       newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata;
       if (oldE->defaultAtts[i].value) {
         newE->defaultAtts[i].value
             = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value);
         if (!newE->defaultAtts[i].value)
           return 0;
       }
       else
         newE->defaultAtts[i].value = NULL;
     }
   }
 
   /* Copy the entity tables. */
-  if (!copyEntityTable(&(newDtd->generalEntities),
+  if (!copyEntityTable(oldParser,
+                       &(newDtd->generalEntities),
                        &(newDtd->pool),
                        &(oldDtd->generalEntities)))
       return 0;
 
 #ifdef XML_DTD
-  if (!copyEntityTable(&(newDtd->paramEntities),
+  if (!copyEntityTable(oldParser,
+                       &(newDtd->paramEntities),
                        &(newDtd->pool),
                        &(oldDtd->paramEntities)))
       return 0;
   newDtd->paramEntityRead = oldDtd->paramEntityRead;
 #endif /* XML_DTD */
 
   newDtd->keepProcessing = oldDtd->keepProcessing;
   newDtd->hasParamEntityRefs = oldDtd->hasParamEntityRefs;
@@ -5961,17 +6189,18 @@ dtdCopy(DTD *newDtd, const DTD *oldDtd, 
   newDtd->scaffSize = oldDtd->scaffSize;
   newDtd->scaffLevel = oldDtd->scaffLevel;
   newDtd->scaffIndex = oldDtd->scaffIndex;
 
   return 1;
 }  /* End dtdCopy */
 
 static int
-copyEntityTable(HASH_TABLE *newTable,
+copyEntityTable(XML_Parser oldParser,
+                HASH_TABLE *newTable,
                 STRING_POOL *newPool,
                 const HASH_TABLE *oldTable)
 {
   HASH_TABLE_ITER iter;
   const XML_Char *cachedOldBase = NULL;
   const XML_Char *cachedNewBase = NULL;
 
   hashTableIterInit(&iter, oldTable);
@@ -5980,17 +6209,17 @@ copyEntityTable(HASH_TABLE *newTable,
     ENTITY *newE;
     const XML_Char *name;
     const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter);
     if (!oldE)
       break;
     name = poolCopyString(newPool, oldE->name);
     if (!name)
       return 0;
-    newE = (ENTITY *)lookup(newTable, name, sizeof(ENTITY));
+    newE = (ENTITY *)lookup(oldParser, newTable, name, sizeof(ENTITY));
     if (!newE)
       return 0;
     if (oldE->systemId) {
       const XML_Char *tem = poolCopyString(newPool, oldE->systemId);
       if (!tem)
         return 0;
       newE->systemId = tem;
       if (oldE->base) {
@@ -6037,47 +6266,66 @@ static XML_Bool FASTCALL
 keyeq(KEY s1, KEY s2)
 {
   for (; *s1 == *s2; s1++, s2++)
     if (*s1 == 0)
       return XML_TRUE;
   return XML_FALSE;
 }
 
-static unsigned long FASTCALL
-hash(KEY s)
+static size_t
+keylen(KEY s)
+{
+  size_t len = 0;
+  for (; *s; s++, len++);
+  return len;
+}
+
+static void
+copy_salt_to_sipkey(XML_Parser parser, struct sipkey * key)
 {
-  unsigned long h = 0;
-  while (*s)
-    h = CHAR_HASH(h, *s++);
-  return h;
+  key->k[0] = 0;
+  key->k[1] = get_hash_secret_salt(parser);
+}
+
+static unsigned long FASTCALL
+hash(XML_Parser parser, KEY s)
+{
+  struct siphash state;
+  struct sipkey key;
+  (void)sip_tobin;
+  (void)sip24_valid;
+  copy_salt_to_sipkey(parser, &key);
+  sip24_init(&state, &key);
+  sip24_update(&state, s, keylen(s) * sizeof(XML_Char));
+  return (unsigned long)sip24_final(&state);
 }
 
 static NAMED *
-lookup(HASH_TABLE *table, KEY name, size_t createSize)
+lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize)
 {
   size_t i;
   if (table->size == 0) {
     size_t tsize;
     if (!createSize)
       return NULL;
     table->power = INIT_POWER;
     /* table->size is a power of 2 */
     table->size = (size_t)1 << INIT_POWER;
     tsize = table->size * sizeof(NAMED *);
     table->v = (NAMED **)table->mem->malloc_fcn(tsize);
     if (!table->v) {
       table->size = 0;
       return NULL;
     }
     memset(table->v, 0, tsize);
-    i = hash(name) & ((unsigned long)table->size - 1);
+    i = hash(parser, name) & ((unsigned long)table->size - 1);
   }
   else {
-    unsigned long h = hash(name);
+    unsigned long h = hash(parser, name);
     unsigned long mask = (unsigned long)table->size - 1;
     unsigned char step = 0;
     i = h & mask;
     while (table->v[i]) {
       if (keyeq(name, table->v[i]->name))
         return table->v[i];
       if (!step)
         step = PROBE_STEP(h, mask, table->power);
@@ -6093,17 +6341,17 @@ lookup(HASH_TABLE *table, KEY name, size
       unsigned long newMask = (unsigned long)newSize - 1;
       size_t tsize = newSize * sizeof(NAMED *);
       NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize);
       if (!newV)
         return NULL;
       memset(newV, 0, tsize);
       for (i = 0; i < table->size; i++)
         if (table->v[i]) {
-          unsigned long newHash = hash(table->v[i]->name);
+          unsigned long newHash = hash(parser, table->v[i]->name);
           size_t j = newHash & newMask;
           step = 0;
           while (newV[j]) {
             if (!step)
               step = PROBE_STEP(newHash, newMask, newPower);
             j < step ? (j += newSize - step) : (j -= step);
           }
           newV[j] = table->v[i];
@@ -6524,17 +6772,17 @@ getElementType(XML_Parser parser,
                const char *end)
 {
   DTD * const dtd = _dtd;  /* save one level of indirection */
   const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end);
   ELEMENT_TYPE *ret;
 
   if (!name)
     return NULL;
-  ret = (ELEMENT_TYPE *) lookup(&dtd->elementTypes, name, sizeof(ELEMENT_TYPE));
+  ret = (ELEMENT_TYPE *) lookup(parser, &dtd->elementTypes, name, sizeof(ELEMENT_TYPE));
   if (!ret)
     return NULL;
   if (ret->name != name)
     poolDiscard(&dtd->pool);
   else {
     poolFinish(&dtd->pool);
     if (!setElementTypePrefix(parser, ret))
       return NULL;