From f85093a8f1367e3a2f577da4f2657c420d0f74d5 Mon Sep 17 00:00:00 2001 From: Eddie Kohler Date: Sun, 21 Jun 2020 23:08:22 -0400 Subject: [PATCH 1/5] SHA-3 Keccak_Hash: Store Keccak_HashInstance in the main context. Previously, the Keccak_HashInstance was separately allocated. This could cause memory leaks on errors. For instance, in php_hash_do_hash_hmac, the following code cleans up after a file read error: if (n < 0) { efree(context); efree(K); zend_string_release(digest); RETURN_FALSE; } This does not call the context's hash_final operation, which was the only way to free the separately-allocated Keccak state. The simplest fix is simply to place the Keccak_HashInstance state inside the context object. Then it doesn't need to be freed. As a result, there is no need to call hash_final in the HashContext destructor: HashContexts cannot contain internally allocated resources. --- ext/hash/hash.c | 4 ---- ext/hash/hash_sha3.c | 22 ++++++---------------- ext/hash/php_hash_sha3.h | 2 +- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/ext/hash/hash.c b/ext/hash/hash.c index 31ead37163795..2d356b613398f 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -1125,11 +1125,7 @@ static zend_object* php_hashcontext_create(zend_class_entry *ce) { static void php_hashcontext_dtor(zend_object *obj) { php_hashcontext_object *hash = php_hashcontext_from_object(obj); - /* Just in case the algo has internally allocated resources */ if (hash->context) { - unsigned char *dummy = emalloc(hash->ops->digest_size); - hash->ops->hash_final(dummy, hash->context); - efree(dummy); efree(hash->context); hash->context = NULL; } diff --git a/ext/hash/hash_sha3.c b/ext/hash/hash_sha3.c index e576adfcbfb94..32621514a8f5e 100644 --- a/ext/hash/hash_sha3.c +++ b/ext/hash/hash_sha3.c @@ -240,38 +240,28 @@ const php_hash_ops php_hash_sha3_##bits##_ops = { \ // ========================================================================== -static int hash_sha3_copy(const void *ops, void *orig_context, void *dest_context) -{ - PHP_SHA3_CTX* orig = (PHP_SHA3_CTX*)orig_context; - PHP_SHA3_CTX* dest = (PHP_SHA3_CTX*)dest_context; - memcpy(dest->hashinstance, orig->hashinstance, sizeof(Keccak_HashInstance)); - return SUCCESS; -} - #define DECLARE_SHA3_OPS(bits) \ void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx) { \ - ctx->hashinstance = emalloc(sizeof(Keccak_HashInstance)); \ - Keccak_HashInitialize_SHA3_##bits((Keccak_HashInstance *)ctx->hashinstance); \ + ZEND_ASSERT(sizeof(Keccak_HashInstance) <= sizeof(PHP_SHA3_##bits##_CTX)); \ + Keccak_HashInitialize_SHA3_##bits((Keccak_HashInstance *)ctx); \ } \ void PHP_SHA3##bits##Update(PHP_SHA3_##bits##_CTX* ctx, \ const unsigned char* input, \ size_t inputLen) { \ - Keccak_HashUpdate((Keccak_HashInstance *)ctx->hashinstance, input, inputLen * 8); \ + Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \ } \ void PHP_SHA3##bits##Final(unsigned char* digest, \ PHP_SHA3_##bits##_CTX* ctx) { \ - Keccak_HashFinal((Keccak_HashInstance *)ctx->hashinstance, digest); \ - efree(ctx->hashinstance); \ - ctx->hashinstance = NULL; \ + Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \ } \ const php_hash_ops php_hash_sha3_##bits##_ops = { \ (php_hash_init_func_t) PHP_SHA3##bits##Init, \ (php_hash_update_func_t) PHP_SHA3##bits##Update, \ (php_hash_final_func_t) PHP_SHA3##bits##Final, \ - hash_sha3_copy, \ + php_hash_copy, \ bits >> 3, \ (1600 - (2 * bits)) >> 3, \ - sizeof(PHP_SHA3_##bits##_CTX), \ + sizeof(PHP_SHA3_CTX), \ 1 \ } diff --git a/ext/hash/php_hash_sha3.h b/ext/hash/php_hash_sha3.h index 256da7bda6187..bb392c0ab0943 100644 --- a/ext/hash/php_hash_sha3.h +++ b/ext/hash/php_hash_sha3.h @@ -24,7 +24,7 @@ typedef struct { unsigned char state[200]; // 5 * 5 * sizeof(uint64) uint32_t pos; #else - void *hashinstance; + unsigned char state[224]; // this must fit a Keccak_HashInstance #endif } PHP_SHA3_CTX; From c8a5402d5a5f75a1a5fe3dcac163d7e1d53f3bfa Mon Sep 17 00:00:00 2001 From: Eddie Kohler Date: Sat, 27 Jun 2020 09:01:06 -0400 Subject: [PATCH 2/5] Move __alignof__ support check into main configure.ac. --- configure.ac | 14 ++++++++++++++ ext/standard/config.m4 | 17 ----------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/configure.ac b/configure.ac index df64a61015fb1..2516677328653 100644 --- a/configure.ac +++ b/configure.ac @@ -491,6 +491,20 @@ PHP_CHECK_BUILTIN_CPU_INIT dnl Check __builtin_cpu_supports PHP_CHECK_BUILTIN_CPU_SUPPORTS +dnl Check for __alignof__ support in the compiler +AC_CACHE_CHECK(whether the compiler supports __alignof__, ac_cv_alignof_exists,[ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +]],[[ + int align = __alignof__(int); +]])],[ + ac_cv_alignof_exists=yes +],[ + ac_cv_alignof_exists=no +])]) +if test "$ac_cv_alignof_exists" = "yes"; then + AC_DEFINE([HAVE_ALIGNOF], 1, [whether the compiler supports __alignof__]) +fi + dnl Check for structure members. AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[#include ]) AC_CHECK_MEMBERS([struct stat.st_blksize, struct stat.st_rdev]) diff --git a/ext/standard/config.m4 b/ext/standard/config.m4 index 5081a6b677f22..04b9d0aea1d6a 100644 --- a/ext/standard/config.m4 +++ b/ext/standard/config.m4 @@ -260,23 +260,6 @@ dnl If one of them is missing, use our own implementation, portable code is then dnl dnl TODO This is currently always enabled if test "$ac_cv_crypt_blowfish" = "no" || test "$ac_cv_crypt_des" = "no" || test "$ac_cv_crypt_ext_des" = "no" || test "$ac_cv_crypt_md5" = "no" || test "$ac_cv_crypt_sha512" = "no" || test "$ac_cv_crypt_sha256" = "no" || test "$ac_cv_func_crypt_r" != "yes" || true; then - - dnl - dnl Check for __alignof__ support in the compiler - dnl - AC_CACHE_CHECK(whether the compiler supports __alignof__, ac_cv_alignof_exists,[ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - ]],[[ - int align = __alignof__(int); - ]])],[ - ac_cv_alignof_exists=yes - ],[ - ac_cv_alignof_exists=no - ])]) - if test "$ac_cv_alignof_exists" = "yes"; then - AC_DEFINE([HAVE_ALIGNOF], 1, [whether the compiler supports __alignof__]) - fi - AC_DEFINE_UNQUOTED(PHP_USE_PHP_CRYPT_R, 1, [Whether PHP has to use its own crypt_r for blowfish, des, ext des and md5]) PHP_ADD_SOURCES(PHP_EXT_DIR(standard), crypt_freesec.c crypt_blowfish.c crypt_sha512.c crypt_sha256.c php_crypt_r.c) From 7103c56195a36160f88ba1b7fda4ea1c8f7e3ee4 Mon Sep 17 00:00:00 2001 From: Eddie Kohler Date: Fri, 26 Jun 2020 21:12:51 -0700 Subject: [PATCH 3/5] Mark Keccak functions that contain expected misaligned accesses. To avoid undefined behavior warnings for those accesses. --- ext/hash/sha3/generic32lc/KeccakP-1600-inplace32BI.c | 12 ++++++++++++ ext/hash/sha3/generic64lc/KeccakP-1600-opt64.c | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/ext/hash/sha3/generic32lc/KeccakP-1600-inplace32BI.c b/ext/hash/sha3/generic32lc/KeccakP-1600-inplace32BI.c index b56629122027e..74ade78eb54d1 100644 --- a/ext/hash/sha3/generic32lc/KeccakP-1600-inplace32BI.c +++ b/ext/hash/sha3/generic32lc/KeccakP-1600-inplace32BI.c @@ -17,6 +17,14 @@ and related or neighboring rights to the source code in this file. #include "brg_endian.h" #include "KeccakP-1600-SnP.h" #include "SnP-Relaned.h" +#ifdef __has_feature +# if __has_feature(undefined_behavior_sanitizer) +# define ALLOW_MISALIGNED_ACCESS __attribute__((no_sanitize("alignment"))) +# endif +#endif +#ifndef ALLOW_MISALIGNED_ACCESS +# define ALLOW_MISALIGNED_ACCESS +#endif typedef unsigned char UINT8; typedef unsigned int UINT32; @@ -162,6 +170,7 @@ void KeccakP1600_AddBytesInLane(void *state, unsigned int lanePosition, const un /* ---------------------------------------------------------------- */ +ALLOW_MISALIGNED_ACCESS void KeccakP1600_AddLanes(void *state, const unsigned char *data, unsigned int laneCount) { #if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) @@ -217,6 +226,7 @@ void KeccakP1600_OverwriteBytesInLane(void *state, unsigned int lanePosition, co /* ---------------------------------------------------------------- */ +ALLOW_MISALIGNED_ACCESS void KeccakP1600_OverwriteLanes(void *state, const unsigned char *data, unsigned int laneCount) { #if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) @@ -304,6 +314,7 @@ void KeccakP1600_ExtractBytesInLane(const void *state, unsigned int lanePosition /* ---------------------------------------------------------------- */ +ALLOW_MISALIGNED_ACCESS void KeccakP1600_ExtractLanes(const void *state, unsigned char *data, unsigned int laneCount) { #if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) @@ -378,6 +389,7 @@ void KeccakP1600_ExtractAndAddBytesInLane(const void *state, unsigned int lanePo /* ---------------------------------------------------------------- */ +ALLOW_MISALIGNED_ACCESS void KeccakP1600_ExtractAndAddLanes(const void *state, const unsigned char *input, unsigned char *output, unsigned int laneCount) { #if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) diff --git a/ext/hash/sha3/generic64lc/KeccakP-1600-opt64.c b/ext/hash/sha3/generic64lc/KeccakP-1600-opt64.c index 40853ff997933..fa708ad833fba 100644 --- a/ext/hash/sha3/generic64lc/KeccakP-1600-opt64.c +++ b/ext/hash/sha3/generic64lc/KeccakP-1600-opt64.c @@ -17,6 +17,14 @@ and related or neighboring rights to the source code in this file. #include #include "brg_endian.h" #include "KeccakP-1600-opt64-config.h" +#ifdef __has_feature +# if __has_feature(undefined_behavior_sanitizer) +# define ALLOW_MISALIGNED_ACCESS __attribute__((no_sanitize("alignment"))) +# endif +#endif +#ifndef ALLOW_MISALIGNED_ACCESS +# define ALLOW_MISALIGNED_ACCESS +#endif typedef unsigned char UINT8; typedef unsigned long long int UINT64; @@ -114,6 +122,7 @@ void KeccakP1600_AddBytesInLane(void *state, unsigned int lanePosition, const un /* ---------------------------------------------------------------- */ +ALLOW_MISALIGNED_ACCESS void KeccakP1600_AddLanes(void *state, const unsigned char *data, unsigned int laneCount) { #if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) @@ -462,6 +471,7 @@ void KeccakP1600_ExtractAndAddBytes(const void *state, const unsigned char *inpu /* ---------------------------------------------------------------- */ +ALLOW_MISALIGNED_ACCESS size_t KeccakF1600_FastLoop_Absorb(void *state, unsigned int laneCount, const unsigned char *data, size_t dataByteLen) { size_t originalDataByteLen = dataByteLen; From df8ff457215c8e39428db41bcd6f45f6694a2f03 Mon Sep 17 00:00:00 2001 From: Eddie Kohler Date: Mon, 8 Jun 2020 08:29:42 -0400 Subject: [PATCH 4/5] Make HashContexts serializable. * Modify php_hash_ops to contain the algorithm name and serialize and unserialize methods. * Implement __serialize and __unserialize magic methods on HashContext. Note that serialized HashContexts are not necessarily portable between PHP versions or from architecture to architecture. (Most are, though Keccak and slow SHA3s are not.) An exception is thrown when an unsupported serialization is attempted. Because of security concerns, HASH_HMAC contexts are not currently serializable; attempting to serialize one throws an exception. Serialization exposes the state of HashContext memory, so ensure that memory is zeroed before use by allocating it with a new php_hash_alloc_context function. Performance impact is negligible. Some hash internal states have logical pointers into a buffer, or sponge, that absorbs input provided in bytes rather than chunks. The unserialize functions for these hash functions must validate that the logical pointers are all within bounds, lest future hash operations cause out-of-bounds memory accesses. * Adler32, CRC32, FNV, joaat: simple state, no buffer positions * Gost, MD2, SHA3, Snefru, Tiger, Whirlpool: buffer positions must be validated * MD4, MD5, SHA1, SHA2, haval, ripemd: buffer positions encoded bitwise, forced to within bounds on use; no need to validate --- ext/hash/hash.c | 386 ++++++++++++++++++++++++- ext/hash/hash.stub.php | 4 + ext/hash/hash_adler32.c | 4 + ext/hash/hash_arginfo.h | 10 + ext/hash/hash_crc32.c | 12 + ext/hash/hash_fnv.c | 28 +- ext/hash/hash_gost.c | 25 +- ext/hash/hash_haval.c | 6 +- ext/hash/hash_joaat.c | 6 +- ext/hash/hash_md.c | 33 ++- ext/hash/hash_ripemd.c | 24 +- ext/hash/hash_sha.c | 42 ++- ext/hash/hash_sha3.c | 78 +++++ ext/hash/hash_snefru.c | 19 +- ext/hash/hash_tiger.c | 19 +- ext/hash/hash_whirlpool.c | 22 +- ext/hash/php_hash.h | 23 +- ext/hash/php_hash_adler32.h | 1 + ext/hash/php_hash_crc32.h | 1 + ext/hash/php_hash_fnv.h | 2 + ext/hash/php_hash_gost.h | 1 + ext/hash/php_hash_haval.h | 1 + ext/hash/php_hash_joaat.h | 1 + ext/hash/php_hash_md.h | 4 +- ext/hash/php_hash_ripemd.h | 4 + ext/hash/php_hash_sha.h | 4 + ext/hash/php_hash_sha3.h | 3 + ext/hash/php_hash_snefru.h | 1 + ext/hash/php_hash_tiger.h | 3 +- ext/hash/php_hash_whirlpool.h | 1 + ext/hash/sha3/generic32lc/KeccakHash.h | 2 + ext/hash/sha3/generic64lc/KeccakHash.h | 2 + ext/hash/tests/hash_serialize_001.phpt | 361 +++++++++++++++++++++++ ext/hash/tests/hash_serialize_002.phpt | 116 ++++++++ ext/hash/tests/hash_serialize_003.phpt | 261 +++++++++++++++++ ext/hash/tests/hash_serialize_004.phpt | 45 +++ ext/hash/tests/serialize-context.phpt | 13 - ext/standard/md5.h | 1 + ext/standard/sha1.h | 1 + 39 files changed, 1517 insertions(+), 53 deletions(-) create mode 100644 ext/hash/tests/hash_serialize_001.phpt create mode 100644 ext/hash/tests/hash_serialize_002.phpt create mode 100644 ext/hash/tests/hash_serialize_003.phpt create mode 100644 ext/hash/tests/hash_serialize_004.phpt delete mode 100644 ext/hash/tests/serialize-context.phpt diff --git a/ext/hash/hash.c b/ext/hash/hash.c index 2d356b613398f..469d91e34bfc2 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -23,12 +23,24 @@ #include "php_hash.h" #include "ext/standard/info.h" #include "ext/standard/file.h" +#include "ext/standard/php_var.h" +#include "ext/spl/spl_exceptions.h" #include "zend_interfaces.h" #include "zend_exceptions.h" +#include "zend_smart_str.h" #include "hash_arginfo.h" +#ifdef PHP_WIN32 +# define __alignof__ __alignof +#else +# ifndef HAVE_ALIGNOF +# include +# define __alignof__(type) offsetof (struct { char c; type member;}, member) +# endif +#endif + HashTable php_hash_hashtable; zend_class_entry *php_hashcontext_ce; static zend_object_handlers php_hashcontext_handlers; @@ -111,6 +123,226 @@ PHP_HASH_API int php_hash_copy(const void *ops, void *orig_context, void *dest_c } /* }}} */ + +static inline size_t align_to(size_t pos, size_t alignment) { + size_t offset = pos & (alignment - 1); + return pos + (offset ? alignment - offset : 0); +} + +static size_t parse_serialize_spec( + const char **specp, size_t *pos, size_t *sz, size_t *max_alignment) { + size_t count, alignment; + const char *spec = *specp; + /* parse size */ + if (*spec == 's' || *spec == 'S') { + *sz = 2; + alignment = __alignof__(uint16_t); /* usually 2 */ + } else if (*spec == 'l' || *spec == 'L') { + *sz = 4; + alignment = __alignof__(uint32_t); /* usually 4 */ + } else if (*spec == 'q' || *spec == 'Q') { + *sz = 8; + alignment = __alignof__(uint64_t); /* usually 8 */ + } else if (*spec == 'i' || *spec == 'I') { + *sz = sizeof(int); + alignment = __alignof__(int); /* usually 4 */ + } else { + ZEND_ASSERT(*spec == 'b' || *spec == 'B'); + *sz = 1; + alignment = 1; + } + /* process alignment */ + *pos = align_to(*pos, alignment); + *max_alignment = *max_alignment < alignment ? alignment : *max_alignment; + /* parse count */ + ++spec; + if (isdigit((unsigned char) *spec)) { + count = 0; + while (isdigit((unsigned char) *spec)) { + count = 10 * count + *spec - '0'; + ++spec; + } + } else { + count = 1; + } + *specp = spec; + return count; +} + +static uint64_t one_from_buffer(size_t sz, const unsigned char *buf) { + if (sz == 2) { + const uint16_t *x = (const uint16_t *) buf; + return *x; + } else if (sz == 4) { + const uint32_t *x = (const uint32_t *) buf; + return *x; + } else if (sz == 8) { + const uint64_t *x = (const uint64_t *) buf; + return *x; + } else { + ZEND_ASSERT(sz == 1); + return *buf; + } +} + +static void one_to_buffer(size_t sz, unsigned char *buf, uint64_t val) { + if (sz == 2) { + uint16_t *x = (uint16_t *) buf; + *x = val; + } else if (sz == 4) { + uint32_t *x = (uint32_t *) buf; + *x = val; + } else if (sz == 8) { + uint64_t *x = (uint64_t *) buf; + *x = val; + } else { + ZEND_ASSERT(sz == 1); + *buf = val; + } +} + +/* Serialize a hash context according to a `spec` string. + Spec contents: + b[COUNT] -- serialize COUNT bytes + s[COUNT] -- serialize COUNT 16-bit integers + l[COUNT] -- serialize COUNT 32-bit integers + q[COUNT] -- serialize COUNT 64-bit integers + i[COUNT] -- serialize COUNT `int`s + B[COUNT] -- skip COUNT bytes + S[COUNT], L[COUNT], etc. -- uppercase versions skip instead of read + . (must be last character) -- assert that the hash context has exactly + this size + Example: "llllllb64l16." is the spec for an MD5 context: 6 32-bit + integers, followed by 64 bytes, then 16 32-bit integers, and that's + exactly the size of the context. + + The serialization result is an array. Each integer is serialized as a + 32-bit integer, except that a run of 2 or more bytes is encoded as a + string, and each 64-bit integer is serialized as two 32-bit integers, least + significant bits first. This allows 32-bit and 64-bit architectures to + interchange serialized HashContexts. */ + +PHP_HASH_API int php_hash_serialize_spec(const php_hashcontext_object *hash, zval *zv, const char *spec) /* {{{ */ +{ + size_t pos = 0, max_alignment = 1; + unsigned char *buf = (unsigned char *) hash->context; + zval tmp; + array_init(zv); + while (*spec != '\0' && *spec != '.') { + char spec_ch = *spec; + size_t sz, count = parse_serialize_spec(&spec, &pos, &sz, &max_alignment); + if (pos + count * sz > hash->ops->context_size) { + return FAILURE; + } + if (isupper((unsigned char) spec_ch)) { + pos += count * sz; + } else if (sz == 1 && count > 1) { + ZVAL_STRINGL(&tmp, (char *) buf + pos, count); + zend_hash_next_index_insert(Z_ARRVAL_P(zv), &tmp); + pos += count; + } else { + while (count > 0) { + uint64_t val = one_from_buffer(sz, buf + pos); + pos += sz; + ZVAL_LONG(&tmp, (int32_t) val); + zend_hash_next_index_insert(Z_ARRVAL_P(zv), &tmp); + if (sz == 8) { + ZVAL_LONG(&tmp, (int32_t) (val >> 32)); + zend_hash_next_index_insert(Z_ARRVAL_P(zv), &tmp); + } + --count; + } + } + } + if (*spec == '.' && align_to(pos, max_alignment) != hash->ops->context_size) { + return FAILURE; + } + return SUCCESS; +} +/* }}} */ + +/* Unserialize a hash context serialized by `php_hash_serialize_spec` with `spec`. + Returns SUCCESS on success and a negative error code on failure. + Codes: FAILURE (-1) == generic failure + -999 == spec wrong size for context + -1000 - POS == problem at byte offset POS */ + +PHP_HASH_API int php_hash_unserialize_spec(php_hashcontext_object *hash, const zval *zv, const char *spec) /* {{{ */ +{ + size_t pos = 0, max_alignment = 1, j = 0; + unsigned char *buf = (unsigned char *) hash->context; + zval *elt; + if (Z_TYPE_P(zv) != IS_ARRAY) { + return FAILURE; + } + while (*spec != '\0' && *spec != '.') { + char spec_ch = *spec; + size_t sz, count = parse_serialize_spec(&spec, &pos, &sz, &max_alignment); + if (pos + count * sz > hash->ops->context_size) { + return -999; + } + if (isupper((unsigned char) spec_ch)) { + pos += count * sz; + } else if (sz == 1 && count > 1) { + elt = zend_hash_index_find(Z_ARRVAL_P(zv), j); + if (!elt || Z_TYPE_P(elt) != IS_STRING || Z_STRLEN_P(elt) != count) { + return -1000 - pos; + } + ++j; + memcpy(buf + pos, Z_STRVAL_P(elt), count); + pos += count; + } else { + while (count > 0) { + uint64_t val; + elt = zend_hash_index_find(Z_ARRVAL_P(zv), j); + if (!elt || Z_TYPE_P(elt) != IS_LONG) { + return -1000 - pos; + } + ++j; + val = (uint32_t) Z_LVAL_P(elt); + if (sz == 8) { + elt = zend_hash_index_find(Z_ARRVAL_P(zv), j); + if (!elt || Z_TYPE_P(elt) != IS_LONG) { + return -1000 - pos; + } + ++j; + val += ((uint64_t) Z_LVAL_P(elt)) << 32; + } + one_to_buffer(sz, buf + pos, val); + pos += sz; + --count; + } + } + } + if (*spec == '.' && align_to(pos, max_alignment) != hash->ops->context_size) { + return -999; + } + return SUCCESS; +} +/* }}} */ + +PHP_HASH_API int php_hash_serialize(const php_hashcontext_object *hash, zend_long *magic, zval *zv) /* {{{ */ +{ + if (hash->ops->serialize_spec) { + *magic = PHP_HASH_SERIALIZE_MAGIC_SPEC; + return php_hash_serialize_spec(hash, zv, hash->ops->serialize_spec); + } else { + return FAILURE; + } +} +/* }}} */ + +PHP_HASH_API int php_hash_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) /* {{{ */ +{ + if (hash->ops->serialize_spec + && magic == PHP_HASH_SERIALIZE_MAGIC_SPEC) { + return php_hash_unserialize_spec(hash, zv, hash->ops->serialize_spec); + } else { + return FAILURE; + } +} +/* }}} */ + /* Userspace */ static void php_hash_do_hash(INTERNAL_FUNCTION_PARAMETERS, int isfilename, zend_bool raw_output_default) /* {{{ */ @@ -147,7 +379,7 @@ static void php_hash_do_hash(INTERNAL_FUNCTION_PARAMETERS, int isfilename, zend_ } } - context = emalloc(ops->context_size); + context = php_hash_alloc_context(ops); ops->hash_init(context); if (isfilename) { @@ -271,7 +503,7 @@ static void php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAMETERS, int isfilename, } } - context = emalloc(ops->context_size); + context = php_hash_alloc_context(ops); K = emalloc(ops->block_size); digest = zend_string_alloc(ops->digest_size, 0); @@ -375,7 +607,7 @@ PHP_FUNCTION(hash_init) object_init_ex(return_value, php_hashcontext_ce); hash = php_hashcontext_from_object(Z_OBJ_P(return_value)); - context = emalloc(ops->context_size); + context = php_hash_alloc_context(ops); ops->hash_init(context); hash->ops = ops; @@ -664,7 +896,7 @@ PHP_FUNCTION(hash_hkdf) RETURN_THROWS(); } - context = emalloc(ops->context_size); + context = php_hash_alloc_context(ops); // Extract ops->hash_init(context); @@ -759,7 +991,7 @@ PHP_FUNCTION(hash_pbkdf2) RETURN_THROWS(); } - context = emalloc(ops->context_size); + context = php_hash_alloc_context(ops); ops->hash_init(context); K1 = emalloc(ops->block_size); @@ -1075,7 +1307,7 @@ PHP_FUNCTION(mhash_keygen_s2k) times++; } - context = emalloc(ops->context_size); + context = php_hash_alloc_context(ops); ops->hash_init(context); key = ecalloc(1, times * block_size); @@ -1148,7 +1380,7 @@ static zend_object *php_hashcontext_clone(zend_object *zobj) { newobj->ops = oldobj->ops; newobj->options = oldobj->options; - newobj->context = emalloc(newobj->ops->context_size); + newobj->context = php_hash_alloc_context(newobj->ops); newobj->ops->hash_init(newobj->context); if (SUCCESS != newobj->ops->hash_copy(newobj->ops, oldobj->context, newobj->context)) { @@ -1166,6 +1398,144 @@ static zend_object *php_hashcontext_clone(zend_object *zobj) { } /* }}} */ +/* Serialization format: 5-element array + Index 0: hash algorithm (string) + Index 1: options (long, 0) + Index 2: hash-determined serialization of context state (usually array) + Index 3: magic number defining layout of context state (long, usually 2) + Index 4: properties (array) + + HashContext serializations are not necessarily portable between architectures or + PHP versions. If the format of a serialized hash context changes, that should + be reflected in either a different value of `magic` or a different format of + the serialized context state. Most context states are unparsed and parsed using + a spec string, such as "llb128.", using the format defined by + `php_hash_serialize_spec`/`php_hash_unserialize_spec`. Some hash algorithms must + also check the unserialized state for validity, to ensure that using an + unserialized context is safe from memory errors. + + Currently HASH_HMAC contexts cannot be serialized, because serializing them + would require serializing the HMAC key in plaintext. */ + +/* {{{ proto array HashContext::__serialize() + Serialize the object */ +PHP_METHOD(HashContext, __serialize) +{ + zval *object = ZEND_THIS; + php_hashcontext_object *hash = php_hashcontext_from_object(Z_OBJ_P(object)); + zend_long magic = 0; + zval tmp; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + array_init(return_value); + + if (!hash->ops->hash_serialize) { + goto serialize_failure; + } else if (hash->options & PHP_HASH_HMAC) { + zend_throw_exception(NULL, "HashContext with HASH_HMAC option cannot be serialized", 0); + RETURN_THROWS(); + } + + ZVAL_STRING(&tmp, hash->ops->algo); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp); + + ZVAL_LONG(&tmp, hash->options); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp); + + if (hash->ops->hash_serialize(hash, &magic, &tmp) != SUCCESS) { + goto serialize_failure; + } + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp); + + ZVAL_LONG(&tmp, magic); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp); + + /* members */ + ZVAL_ARR(&tmp, zend_std_get_properties(&hash->std)); + Z_TRY_ADDREF(tmp); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp); + + return; + +serialize_failure: + zend_throw_exception_ex(NULL, 0, "HashContext for algorithm \"%s\" cannot be serialized", hash->ops->algo); + RETURN_THROWS(); +} +/* }}} */ + +/* {{{ proto void HashContext::__unserialize(array serialized) + * unserialize the object + */ +PHP_METHOD(HashContext, __unserialize) +{ + zval *object = ZEND_THIS; + php_hashcontext_object *hash = php_hashcontext_from_object(Z_OBJ_P(object)); + HashTable *data; + zval *algo_zv, *magic_zv, *options_zv, *hash_zv, *members_zv; + zend_long magic, options; + int unserialize_result; + const php_hash_ops *ops; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", &data) == FAILURE) { + RETURN_THROWS(); + } + + if (hash->context) { + zend_throw_exception(NULL, "HashContext::__unserialize called on initialized object", 0); + RETURN_THROWS(); + } + + algo_zv = zend_hash_index_find(data, 0); + options_zv = zend_hash_index_find(data, 1); + hash_zv = zend_hash_index_find(data, 2); + magic_zv = zend_hash_index_find(data, 3); + members_zv = zend_hash_index_find(data, 4); + + if (!algo_zv || Z_TYPE_P(algo_zv) != IS_STRING + || !magic_zv || Z_TYPE_P(magic_zv) != IS_LONG + || !options_zv || Z_TYPE_P(options_zv) != IS_LONG + || !hash_zv + || !members_zv || Z_TYPE_P(members_zv) != IS_ARRAY) { + zend_throw_exception(NULL, "Incomplete or ill-formed serialization data", 0); + RETURN_THROWS(); + } + + magic = Z_LVAL_P(magic_zv); + options = Z_LVAL_P(options_zv); + if (options & PHP_HASH_HMAC) { + zend_throw_exception(NULL, "HashContext with HASH_HMAC option cannot be serialized", 0); + RETURN_THROWS(); + } + + ops = php_hash_fetch_ops(Z_STR_P(algo_zv)); + if (!ops) { + zend_throw_exception(NULL, "Unknown hash algorithm", 0); + RETURN_THROWS(); + } else if (!ops->hash_unserialize) { + zend_throw_exception_ex(NULL, 0, "Hash algorithm \"%s\" cannot be unserialized", ops->algo); + RETURN_THROWS(); + } + + hash->ops = ops; + hash->context = php_hash_alloc_context(ops); + ops->hash_init(hash->context); + hash->options = options; + + unserialize_result = ops->hash_unserialize(hash, magic, hash_zv); + if (unserialize_result != SUCCESS) { + zend_throw_exception_ex(NULL, 0, "Incomplete or ill-formed serialization data (\"%s\" code %d)", ops->algo, unserialize_result); + /* free context */ + php_hashcontext_dtor(Z_OBJ_P(object)); + RETURN_THROWS(); + } + + object_properties_load(&hash->std, Z_ARRVAL_P(members_zv)); +} +/* }}} */ + /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(hash) @@ -1237,8 +1607,6 @@ PHP_MINIT_FUNCTION(hash) php_hashcontext_ce = zend_register_internal_class(&ce); php_hashcontext_ce->ce_flags |= ZEND_ACC_FINAL; php_hashcontext_ce->create_object = php_hashcontext_create; - php_hashcontext_ce->serialize = zend_class_serialize_deny; - php_hashcontext_ce->unserialize = zend_class_unserialize_deny; memcpy(&php_hashcontext_handlers, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/hash/hash.stub.php b/ext/hash/hash.stub.php index 32465415fcbff..f8788ed61ed51 100644 --- a/ext/hash/hash.stub.php +++ b/ext/hash/hash.stub.php @@ -53,4 +53,8 @@ function mhash(int $hash, string $data, string $key = UNKNOWN): string|false {} final class HashContext { private function __construct() {} + + public function __serialize(): array {} + + public function __unserialize(array $serialized): void {} } diff --git a/ext/hash/hash_adler32.c b/ext/hash/hash_adler32.c index 8d2d2fcb9365c..d45012f8e6d51 100644 --- a/ext/hash/hash_adler32.c +++ b/ext/hash/hash_adler32.c @@ -59,10 +59,14 @@ PHP_HASH_API int PHP_ADLER32Copy(const php_hash_ops *ops, PHP_ADLER32_CTX *orig_ } const php_hash_ops php_hash_adler32_ops = { + "adler32", (php_hash_init_func_t) PHP_ADLER32Init, (php_hash_update_func_t) PHP_ADLER32Update, (php_hash_final_func_t) PHP_ADLER32Final, (php_hash_copy_func_t) PHP_ADLER32Copy, + php_hash_serialize, + php_hash_unserialize, + PHP_ADLER32_SPEC, 4, /* what to say here? */ 4, sizeof(PHP_ADLER32_CTX), diff --git a/ext/hash/hash_arginfo.h b/ext/hash/hash_arginfo.h index c09fd12001627..7ba3fca9d7226 100644 --- a/ext/hash/hash_arginfo.h +++ b/ext/hash/hash_arginfo.h @@ -118,6 +118,12 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_HashContext___construct, 0, 0, 0) ZEND_END_ARG_INFO() +#define arginfo_class_HashContext___serialize arginfo_hash_algos + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_HashContext___unserialize, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, serialized, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + ZEND_FUNCTION(hash); ZEND_FUNCTION(hash_file); @@ -150,6 +156,8 @@ ZEND_FUNCTION(mhash_count); ZEND_FUNCTION(mhash); #endif ZEND_METHOD(HashContext, __construct); +ZEND_METHOD(HashContext, __serialize); +ZEND_METHOD(HashContext, __unserialize); static const zend_function_entry ext_functions[] = { @@ -189,5 +197,7 @@ static const zend_function_entry ext_functions[] = { static const zend_function_entry class_HashContext_methods[] = { ZEND_ME(HashContext, __construct, arginfo_class_HashContext___construct, ZEND_ACC_PRIVATE) + ZEND_ME(HashContext, __serialize, arginfo_class_HashContext___serialize, ZEND_ACC_PUBLIC) + ZEND_ME(HashContext, __unserialize, arginfo_class_HashContext___unserialize, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/ext/hash/hash_crc32.c b/ext/hash/hash_crc32.c index 52c67717ffbde..de270522d7e27 100644 --- a/ext/hash/hash_crc32.c +++ b/ext/hash/hash_crc32.c @@ -78,10 +78,14 @@ PHP_HASH_API int PHP_CRC32Copy(const php_hash_ops *ops, PHP_CRC32_CTX *orig_cont } const php_hash_ops php_hash_crc32_ops = { + "crc32", (php_hash_init_func_t) PHP_CRC32Init, (php_hash_update_func_t) PHP_CRC32Update, (php_hash_final_func_t) PHP_CRC32LEFinal, (php_hash_copy_func_t) PHP_CRC32Copy, + php_hash_serialize, + php_hash_unserialize, + PHP_CRC32_SPEC, 4, /* what to say here? */ 4, sizeof(PHP_CRC32_CTX), @@ -89,10 +93,14 @@ const php_hash_ops php_hash_crc32_ops = { }; const php_hash_ops php_hash_crc32b_ops = { + "crc32b", (php_hash_init_func_t) PHP_CRC32Init, (php_hash_update_func_t) PHP_CRC32BUpdate, (php_hash_final_func_t) PHP_CRC32BEFinal, (php_hash_copy_func_t) PHP_CRC32Copy, + php_hash_serialize, + php_hash_unserialize, + PHP_CRC32_SPEC, 4, /* what to say here? */ 4, sizeof(PHP_CRC32_CTX), @@ -100,10 +108,14 @@ const php_hash_ops php_hash_crc32b_ops = { }; const php_hash_ops php_hash_crc32c_ops = { + "crc32c", (php_hash_init_func_t) PHP_CRC32Init, (php_hash_update_func_t) PHP_CRC32CUpdate, (php_hash_final_func_t) PHP_CRC32BEFinal, (php_hash_copy_func_t) PHP_CRC32Copy, + php_hash_serialize, + php_hash_unserialize, + PHP_CRC32_SPEC, 4, /* what to say here? */ 4, sizeof(PHP_CRC32_CTX), diff --git a/ext/hash/hash_fnv.c b/ext/hash/hash_fnv.c index 976c53b527288..2ee81b9c8601b 100644 --- a/ext/hash/hash_fnv.c +++ b/ext/hash/hash_fnv.c @@ -21,10 +21,14 @@ #include "php_hash_fnv.h" const php_hash_ops php_hash_fnv132_ops = { + "fnv132", (php_hash_init_func_t) PHP_FNV132Init, (php_hash_update_func_t) PHP_FNV132Update, (php_hash_final_func_t) PHP_FNV132Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_FNV132_SPEC, 4, 4, sizeof(PHP_FNV132_CTX), @@ -32,10 +36,14 @@ const php_hash_ops php_hash_fnv132_ops = { }; const php_hash_ops php_hash_fnv1a32_ops = { + "fnv1a32", (php_hash_init_func_t) PHP_FNV132Init, (php_hash_update_func_t) PHP_FNV1a32Update, - (php_hash_final_func_t) PHP_FNV132Final, - (php_hash_copy_func_t) php_hash_copy, + (php_hash_final_func_t) PHP_FNV132Final, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_FNV132_SPEC, 4, 4, sizeof(PHP_FNV132_CTX), @@ -43,10 +51,14 @@ const php_hash_ops php_hash_fnv1a32_ops = { }; const php_hash_ops php_hash_fnv164_ops = { + "fnv164", (php_hash_init_func_t) PHP_FNV164Init, (php_hash_update_func_t) PHP_FNV164Update, (php_hash_final_func_t) PHP_FNV164Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_FNV164_SPEC, 8, 4, sizeof(PHP_FNV164_CTX), @@ -54,10 +66,14 @@ const php_hash_ops php_hash_fnv164_ops = { }; const php_hash_ops php_hash_fnv1a64_ops = { + "fnv1a64", (php_hash_init_func_t) PHP_FNV164Init, (php_hash_update_func_t) PHP_FNV1a64Update, - (php_hash_final_func_t) PHP_FNV164Final, - (php_hash_copy_func_t) php_hash_copy, + (php_hash_final_func_t) PHP_FNV164Final, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_FNV164_SPEC, 8, 4, sizeof(PHP_FNV164_CTX), diff --git a/ext/hash/hash_gost.c b/ext/hash/hash_gost.c index a9e689de16ce2..46ea032c32e30 100644 --- a/ext/hash/hash_gost.c +++ b/ext/hash/hash_gost.c @@ -304,11 +304,28 @@ PHP_HASH_API void PHP_GOSTFinal(unsigned char digest[32], PHP_GOST_CTX *context) ZEND_SECURE_ZERO(context, sizeof(*context)); } +static int php_gost_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) +{ + PHP_GOST_CTX *ctx = (PHP_GOST_CTX *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC + && (r = php_hash_unserialize_spec(hash, zv, PHP_GOST_SPEC)) == SUCCESS + && ctx->length < sizeof(ctx->buffer)) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} + const php_hash_ops php_hash_gost_ops = { + "gost", (php_hash_init_func_t) PHP_GOSTInit, (php_hash_update_func_t) PHP_GOSTUpdate, (php_hash_final_func_t) PHP_GOSTFinal, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_gost_unserialize, + PHP_GOST_SPEC, 32, 32, sizeof(PHP_GOST_CTX), @@ -316,10 +333,14 @@ const php_hash_ops php_hash_gost_ops = { }; const php_hash_ops php_hash_gost_crypto_ops = { + "gost-crypto", (php_hash_init_func_t) PHP_GOSTInitCrypto, (php_hash_update_func_t) PHP_GOSTUpdate, (php_hash_final_func_t) PHP_GOSTFinal, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_gost_unserialize, + PHP_GOST_SPEC, 32, 32, sizeof(PHP_GOST_CTX), diff --git a/ext/hash/hash_haval.c b/ext/hash/hash_haval.c index 62f63cbee1534..34210a8992aae 100644 --- a/ext/hash/hash_haval.c +++ b/ext/hash/hash_haval.c @@ -247,10 +247,14 @@ static void PHP_5HAVALTransform(uint32_t state[8], const unsigned char block[128 #define PHP_HASH_HAVAL_INIT(p,b) \ const php_hash_ops php_hash_##p##haval##b##_ops = { \ + "haval" #b "," #p, \ (php_hash_init_func_t) PHP_##p##HAVAL##b##Init, \ (php_hash_update_func_t) PHP_HAVALUpdate, \ (php_hash_final_func_t) PHP_HAVAL##b##Final, \ - (php_hash_copy_func_t) php_hash_copy, \ + php_hash_copy, \ + php_hash_serialize, \ + php_hash_unserialize, \ + PHP_HAVAL_SPEC, \ ((b) / 8), 128, sizeof(PHP_HAVAL_CTX), 1 }; \ PHP_HASH_API void PHP_##p##HAVAL##b##Init(PHP_HAVAL_CTX *context) \ { int i; context->count[0] = context->count[1] = 0; \ diff --git a/ext/hash/hash_joaat.c b/ext/hash/hash_joaat.c index 10c3ca2748f5d..5d5a2e53b9b39 100644 --- a/ext/hash/hash_joaat.c +++ b/ext/hash/hash_joaat.c @@ -22,10 +22,14 @@ #include "php_hash_joaat.h" const php_hash_ops php_hash_joaat_ops = { + "joaat", (php_hash_init_func_t) PHP_JOAATInit, (php_hash_update_func_t) PHP_JOAATUpdate, (php_hash_final_func_t) PHP_JOAATFinal, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_JOAAT_SPEC, 4, 4, sizeof(PHP_JOAAT_CTX), diff --git a/ext/hash/hash_md.c b/ext/hash/hash_md.c index e5cbb49b01302..94fafbbf798a8 100644 --- a/ext/hash/hash_md.c +++ b/ext/hash/hash_md.c @@ -18,10 +18,14 @@ #include "php_hash_md.h" const php_hash_ops php_hash_md5_ops = { + "md5", (php_hash_init_func_t) PHP_MD5Init, (php_hash_update_func_t) PHP_MD5Update, (php_hash_final_func_t) PHP_MD5Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_MD5_SPEC, 16, 64, sizeof(PHP_MD5_CTX), @@ -29,21 +33,31 @@ const php_hash_ops php_hash_md5_ops = { }; const php_hash_ops php_hash_md4_ops = { + "md4", (php_hash_init_func_t) PHP_MD4Init, (php_hash_update_func_t) PHP_MD4Update, (php_hash_final_func_t) PHP_MD4Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_MD4_SPEC, 16, 64, sizeof(PHP_MD4_CTX), 1 }; +static int php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv); + const php_hash_ops php_hash_md2_ops = { + "md2", (php_hash_init_func_t) PHP_MD2Init, (php_hash_update_func_t) PHP_MD2Update, (php_hash_final_func_t) PHP_MD2Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_md2_unserialize, + PHP_MD2_SPEC, 16, 16, sizeof(PHP_MD2_CTX), @@ -340,3 +354,16 @@ PHP_HASH_API void PHP_MD2Final(unsigned char output[16], PHP_MD2_CTX *context) memcpy(output, context->state, 16); } + +static int php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) +{ + PHP_MD2_CTX *ctx = (PHP_MD2_CTX *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC + && (r = php_hash_unserialize_spec(hash, zv, PHP_MD2_SPEC)) == SUCCESS + && (unsigned char) ctx->in_buffer < sizeof(ctx->buffer)) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} diff --git a/ext/hash/hash_ripemd.c b/ext/hash/hash_ripemd.c index 84a29f64868c8..db1d1dc02b9e2 100644 --- a/ext/hash/hash_ripemd.c +++ b/ext/hash/hash_ripemd.c @@ -22,10 +22,14 @@ #include "php_hash_ripemd.h" const php_hash_ops php_hash_ripemd128_ops = { + "ripemd128", (php_hash_init_func_t) PHP_RIPEMD128Init, (php_hash_update_func_t) PHP_RIPEMD128Update, (php_hash_final_func_t) PHP_RIPEMD128Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_RIPEMD128_SPEC, 16, 64, sizeof(PHP_RIPEMD128_CTX), @@ -33,10 +37,14 @@ const php_hash_ops php_hash_ripemd128_ops = { }; const php_hash_ops php_hash_ripemd160_ops = { + "ripemd160", (php_hash_init_func_t) PHP_RIPEMD160Init, (php_hash_update_func_t) PHP_RIPEMD160Update, (php_hash_final_func_t) PHP_RIPEMD160Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_RIPEMD160_SPEC, 20, 64, sizeof(PHP_RIPEMD160_CTX), @@ -44,10 +52,14 @@ const php_hash_ops php_hash_ripemd160_ops = { }; const php_hash_ops php_hash_ripemd256_ops = { + "ripemd256", (php_hash_init_func_t) PHP_RIPEMD256Init, (php_hash_update_func_t) PHP_RIPEMD256Update, (php_hash_final_func_t) PHP_RIPEMD256Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_RIPEMD256_SPEC, 32, 64, sizeof(PHP_RIPEMD256_CTX), @@ -55,10 +67,14 @@ const php_hash_ops php_hash_ripemd256_ops = { }; const php_hash_ops php_hash_ripemd320_ops = { + "ripemd320", (php_hash_init_func_t) PHP_RIPEMD320Init, (php_hash_update_func_t) PHP_RIPEMD320Update, (php_hash_final_func_t) PHP_RIPEMD320Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_RIPEMD320_SPEC, 40, 64, sizeof(PHP_RIPEMD320_CTX), diff --git a/ext/hash/hash_sha.c b/ext/hash/hash_sha.c index 30ab0d22adf7d..4ea5b768d703c 100644 --- a/ext/hash/hash_sha.c +++ b/ext/hash/hash_sha.c @@ -63,10 +63,14 @@ static void SHADecode32(uint32_t *output, const unsigned char *input, unsigned i /* }}} */ const php_hash_ops php_hash_sha1_ops = { + "sha1", (php_hash_init_func_t) PHP_SHA1Init, (php_hash_update_func_t) PHP_SHA1Update, (php_hash_final_func_t) PHP_SHA1Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_SHA1_SPEC, 20, 64, sizeof(PHP_SHA1_CTX), @@ -76,10 +80,14 @@ const php_hash_ops php_hash_sha1_ops = { /* sha224/sha256 */ const php_hash_ops php_hash_sha256_ops = { + "sha256", (php_hash_init_func_t) PHP_SHA256Init, (php_hash_update_func_t) PHP_SHA256Update, (php_hash_final_func_t) PHP_SHA256Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_SHA256_SPEC, 32, 64, sizeof(PHP_SHA256_CTX), @@ -87,10 +95,14 @@ const php_hash_ops php_hash_sha256_ops = { }; const php_hash_ops php_hash_sha224_ops = { + "sha224", (php_hash_init_func_t) PHP_SHA224Init, (php_hash_update_func_t) PHP_SHA224Update, (php_hash_final_func_t) PHP_SHA224Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_SHA224_SPEC, 28, 64, sizeof(PHP_SHA224_CTX), @@ -578,10 +590,14 @@ PHP_HASH_API void PHP_SHA384Final(unsigned char digest[48], PHP_SHA384_CTX * con /* }}} */ const php_hash_ops php_hash_sha384_ops = { + "sha384", (php_hash_init_func_t) PHP_SHA384Init, (php_hash_update_func_t) PHP_SHA384Update, (php_hash_final_func_t) PHP_SHA384Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_SHA384_SPEC, 48, 128, sizeof(PHP_SHA384_CTX), @@ -751,10 +767,14 @@ PHP_HASH_API void PHP_SHA512_224Final(unsigned char digest[28], PHP_SHA512_CTX * /* }}} */ const php_hash_ops php_hash_sha512_ops = { + "sha512", (php_hash_init_func_t) PHP_SHA512Init, (php_hash_update_func_t) PHP_SHA512Update, (php_hash_final_func_t) PHP_SHA512Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_SHA512_SPEC, 64, 128, sizeof(PHP_SHA512_CTX), @@ -762,10 +782,14 @@ const php_hash_ops php_hash_sha512_ops = { }; const php_hash_ops php_hash_sha512_256_ops = { + "sha512/256", (php_hash_init_func_t) PHP_SHA512_256Init, (php_hash_update_func_t) PHP_SHA512_256Update, (php_hash_final_func_t) PHP_SHA512_256Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_SHA512_SPEC, 32, 128, sizeof(PHP_SHA512_CTX), @@ -773,10 +797,14 @@ const php_hash_ops php_hash_sha512_256_ops = { }; const php_hash_ops php_hash_sha512_224_ops = { + "sha512/224", (php_hash_init_func_t) PHP_SHA512_224Init, (php_hash_update_func_t) PHP_SHA512_224Update, (php_hash_final_func_t) PHP_SHA512_224Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_SHA512_SPEC, 28, 128, sizeof(PHP_SHA512_CTX), diff --git a/ext/hash/hash_sha3.c b/ext/hash/hash_sha3.c index 32621514a8f5e..52bd495f9df60 100644 --- a/ext/hash/hash_sha3.c +++ b/ext/hash/hash_sha3.c @@ -201,6 +201,22 @@ static void PHP_SHA3_Final(unsigned char* digest, ZEND_SECURE_ZERO(ctx, sizeof(PHP_SHA3_CTX)); } +static int php_sha3_unserialize(php_hashcontext_object *hash, + zend_long magic, + const zval *zv, + size_t block_size) +{ + PHP_SHA3_CTX *ctx = (PHP_SHA3_CTX *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC + && (r = php_hash_unserialize_spec(hash, zv, PHP_SHA3_SPEC)) == SUCCESS + && ctx->pos < block_size) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} + // ========================================================================== #define DECLARE_SHA3_OPS(bits) \ @@ -219,11 +235,20 @@ void PHP_SHA3##bits##Final(unsigned char* digest, \ (1600 - (2 * bits)) >> 3, \ bits >> 3); \ } \ +static int php_sha3##bits##_unserialize(php_hashcontext_object *hash, \ + zend_long magic, \ + const zval *zv) { \ + return php_sha3_unserialize(hash, magic, zv, (1600 - (2 * bits)) >> 3); \ +} \ const php_hash_ops php_hash_sha3_##bits##_ops = { \ + "sha3-" #bits, \ (php_hash_init_func_t) PHP_SHA3##bits##Init, \ (php_hash_update_func_t) PHP_SHA3##bits##Update, \ (php_hash_final_func_t) PHP_SHA3##bits##Final, \ php_hash_copy, \ + php_hash_serialize, \ + php_sha3##bits##_unserialize, \ + PHP_SHA3_SPEC, \ bits >> 3, \ (1600 - (2 * bits)) >> 3, \ sizeof(PHP_SHA3_##bits##_CTX), \ @@ -237,6 +262,55 @@ const php_hash_ops php_hash_sha3_##bits##_ops = { \ #define SUCCESS SHA3_SUCCESS /* Avoid conflict between KeccacHash.h and zend_types.h */ #include "KeccakHash.h" +/* KECCAK SERIALIZATION + + Keccak_HashInstance consists of: + KeccakWidth1600_SpongeInstance { + unsigned char state[200]; + unsigned int rate; -- fixed for digest size + unsigned int byteIOIndex; -- in range [0, rate/8) + int squeezing; -- 0 normally, 1 only during finalize + } sponge; + unsigned int fixedOutputLength; -- fixed for digest size + unsigned char delimitedSuffix; -- fixed for digest size + + NB If the external sha3/ library is updated, the serialization code + may need to be updated. + + The simpler SHA3 code's serialization states are not interchangeable with + Keccak. Furthermore, the Keccak sponge state is sensitive to architecture + -- 32-bit and 64-bit implementations produce different states. It does not + appear that the state is sensitive to endianness. */ + +#if Keccak_HashInstance_ImplType == 64 +/* corresponds to sha3/generic64lc */ +# define PHP_HASH_SERIALIZE_MAGIC_KECCAK 100 +#elif Keccak_HashInstance_ImplType == 32 +/* corresponds to sha3/generic32lc */ +# define PHP_HASH_SERIALIZE_MAGIC_KECCAK 101 +#else +# error "Unknown Keccak_HashInstance_ImplType" +#endif +#define PHP_KECCAK_SPEC "b200IiIIB" + +static int php_keccak_serialize(const php_hashcontext_object *hash, zend_long *magic, zval *zv) +{ + *magic = PHP_HASH_SERIALIZE_MAGIC_KECCAK; + return php_hash_serialize_spec(hash, zv, PHP_KECCAK_SPEC); +} + +static int php_keccak_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) +{ + Keccak_HashInstance *ctx = (Keccak_HashInstance *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_KECCAK + && (r = php_hash_unserialize_spec(hash, zv, PHP_KECCAK_SPEC)) == SUCCESS + && ctx->sponge.byteIOIndex < ctx->sponge.rate / 8) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} // ========================================================================== @@ -255,10 +329,14 @@ void PHP_SHA3##bits##Final(unsigned char* digest, \ Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \ } \ const php_hash_ops php_hash_sha3_##bits##_ops = { \ + "sha3-" #bits, \ (php_hash_init_func_t) PHP_SHA3##bits##Init, \ (php_hash_update_func_t) PHP_SHA3##bits##Update, \ (php_hash_final_func_t) PHP_SHA3##bits##Final, \ php_hash_copy, \ + php_keccak_serialize, \ + php_keccak_unserialize, \ + PHP_KECCAK_SPEC, \ bits >> 3, \ (1600 - (2 * bits)) >> 3, \ sizeof(PHP_SHA3_CTX), \ diff --git a/ext/hash/hash_snefru.c b/ext/hash/hash_snefru.c index fae767f2feec3..292bfef2cb8da 100644 --- a/ext/hash/hash_snefru.c +++ b/ext/hash/hash_snefru.c @@ -189,11 +189,28 @@ PHP_HASH_API void PHP_SNEFRUFinal(unsigned char digest[32], PHP_SNEFRU_CTX *cont ZEND_SECURE_ZERO(context, sizeof(*context)); } +static int php_snefru_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) +{ + PHP_SNEFRU_CTX *ctx = (PHP_SNEFRU_CTX *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC + && (r = php_hash_unserialize_spec(hash, zv, PHP_SNEFRU_SPEC)) == SUCCESS + && ctx->length < sizeof(ctx->buffer)) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} + const php_hash_ops php_hash_snefru_ops = { + "snefru", (php_hash_init_func_t) PHP_SNEFRUInit, (php_hash_update_func_t) PHP_SNEFRUUpdate, (php_hash_final_func_t) PHP_SNEFRUFinal, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_snefru_unserialize, + PHP_SNEFRU_SPEC, 32, 32, sizeof(PHP_SNEFRU_CTX), diff --git a/ext/hash/hash_tiger.c b/ext/hash/hash_tiger.c index 399e95af0e3bc..8e0f365dc49eb 100644 --- a/ext/hash/hash_tiger.c +++ b/ext/hash/hash_tiger.c @@ -239,12 +239,29 @@ PHP_HASH_API void PHP_TIGER192Final(unsigned char digest[24], PHP_TIGER_CTX *con ZEND_SECURE_ZERO(context, sizeof(*context)); } +static int php_tiger_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) +{ + PHP_TIGER_CTX *ctx = (PHP_TIGER_CTX *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC + && (r = php_hash_unserialize_spec(hash, zv, PHP_TIGER_SPEC)) == SUCCESS + && ctx->length < sizeof(ctx->buffer)) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} + #define PHP_HASH_TIGER_OPS(p, b) \ const php_hash_ops php_hash_##p##tiger##b##_ops = { \ + "tiger" #b "," #p, \ (php_hash_init_func_t) PHP_##p##TIGERInit, \ (php_hash_update_func_t) PHP_TIGERUpdate, \ (php_hash_final_func_t) PHP_TIGER##b##Final, \ - (php_hash_copy_func_t) php_hash_copy, \ + php_hash_copy, \ + php_hash_serialize, \ + php_tiger_unserialize, \ + PHP_TIGER_SPEC, \ b/8, \ 64, \ sizeof(PHP_TIGER_CTX), \ diff --git a/ext/hash/hash_whirlpool.c b/ext/hash/hash_whirlpool.c index 5be7f80eea072..8d5cbb77370a8 100644 --- a/ext/hash/hash_whirlpool.c +++ b/ext/hash/hash_whirlpool.c @@ -429,11 +429,31 @@ PHP_HASH_API void PHP_WHIRLPOOLFinal(unsigned char digest[64], PHP_WHIRLPOOL_CTX ZEND_SECURE_ZERO(context, sizeof(*context)); } +static int php_whirlpool_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) +{ + PHP_WHIRLPOOL_CTX *ctx = (PHP_WHIRLPOOL_CTX *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC + && (r = php_hash_unserialize_spec(hash, zv, PHP_WHIRLPOOL_SPEC)) == SUCCESS + && ctx->buffer.pos >= 0 + && ctx->buffer.pos < (int) sizeof(ctx->buffer.data) + && ctx->buffer.bits >= ctx->buffer.pos * 8 + && ctx->buffer.bits < ctx->buffer.pos * 8 + 8) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} + const php_hash_ops php_hash_whirlpool_ops = { + "whirlpool", (php_hash_init_func_t) PHP_WHIRLPOOLInit, (php_hash_update_func_t) PHP_WHIRLPOOLUpdate, (php_hash_final_func_t) PHP_WHIRLPOOLFinal, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_whirlpool_unserialize, + PHP_WHIRLPOOL_SPEC, 64, 64, sizeof(PHP_WHIRLPOOL_CTX), diff --git a/ext/hash/php_hash.h b/ext/hash/php_hash.h index 79bbc066d6c83..33cc6a473a7a1 100644 --- a/ext/hash/php_hash.h +++ b/ext/hash/php_hash.h @@ -25,18 +25,28 @@ #define PHP_HASH_HMAC 0x0001 +#define PHP_HASH_SERIALIZE_MAGIC_SPEC 2 + #define L64 INT64_C +typedef struct _php_hashcontext_object php_hashcontext_object; + typedef void (*php_hash_init_func_t)(void *context); typedef void (*php_hash_update_func_t)(void *context, const unsigned char *buf, size_t count); typedef void (*php_hash_final_func_t)(unsigned char *digest, void *context); typedef int (*php_hash_copy_func_t)(const void *ops, void *orig_context, void *dest_context); +typedef int (*php_hash_serialize_func_t)(const php_hashcontext_object *hash, zend_long *magic, zval *zv); +typedef int (*php_hash_unserialize_func_t)(php_hashcontext_object *hash, zend_long magic, const zval *zv); typedef struct _php_hash_ops { + const char *algo; php_hash_init_func_t hash_init; php_hash_update_func_t hash_update; php_hash_final_func_t hash_final; php_hash_copy_func_t hash_copy; + php_hash_serialize_func_t hash_serialize; + php_hash_unserialize_func_t hash_unserialize; + const char *serialize_spec; size_t digest_size; size_t block_size; @@ -44,7 +54,7 @@ typedef struct _php_hash_ops { unsigned is_crypto: 1; } php_hash_ops; -typedef struct _php_hashcontext_object { +struct _php_hashcontext_object { const php_hash_ops *ops; void *context; @@ -52,7 +62,7 @@ typedef struct _php_hashcontext_object { unsigned char *key; zend_object std; -} php_hashcontext_object; +}; static inline php_hashcontext_object *php_hashcontext_from_object(zend_object *obj) { return ((php_hashcontext_object*)(obj + 1)) - 1; @@ -131,6 +141,15 @@ extern PHP_HASH_API zend_class_entry *php_hashcontext_ce; PHP_HASH_API const php_hash_ops *php_hash_fetch_ops(zend_string *algo); PHP_HASH_API void php_hash_register_algo(const char *algo, const php_hash_ops *ops); PHP_HASH_API int php_hash_copy(const void *ops, void *orig_context, void *dest_context); +PHP_HASH_API int php_hash_serialize(const php_hashcontext_object *context, zend_long *magic, zval *zv); +PHP_HASH_API int php_hash_unserialize(php_hashcontext_object *context, zend_long magic, const zval *zv); +PHP_HASH_API int php_hash_serialize_spec(const php_hashcontext_object *context, zval *zv, const char *spec); +PHP_HASH_API int php_hash_unserialize_spec(php_hashcontext_object *hash, const zval *zv, const char *spec); + +static inline void *php_hash_alloc_context(const php_hash_ops *ops) { + /* Zero out context memory so serialization doesn't expose internals */ + return ecalloc(1, ops->context_size); +} static inline void php_hash_bin2hex(char *out, const unsigned char *in, size_t in_len) { diff --git a/ext/hash/php_hash_adler32.h b/ext/hash/php_hash_adler32.h index 184091c97122b..049f16b28e956 100644 --- a/ext/hash/php_hash_adler32.h +++ b/ext/hash/php_hash_adler32.h @@ -22,6 +22,7 @@ typedef struct { uint32_t state; } PHP_ADLER32_CTX; +#define PHP_ADLER32_SPEC "l." PHP_HASH_API void PHP_ADLER32Init(PHP_ADLER32_CTX *context); PHP_HASH_API void PHP_ADLER32Update(PHP_ADLER32_CTX *context, const unsigned char *input, size_t len); diff --git a/ext/hash/php_hash_crc32.h b/ext/hash/php_hash_crc32.h index 346011e4176ad..4c1b0fedc9159 100644 --- a/ext/hash/php_hash_crc32.h +++ b/ext/hash/php_hash_crc32.h @@ -22,6 +22,7 @@ typedef struct { uint32_t state; } PHP_CRC32_CTX; +#define PHP_CRC32_SPEC "l." PHP_HASH_API void PHP_CRC32Init(PHP_CRC32_CTX *context); PHP_HASH_API void PHP_CRC32Update(PHP_CRC32_CTX *context, const unsigned char *input, size_t len); diff --git a/ext/hash/php_hash_fnv.h b/ext/hash/php_hash_fnv.h index 1cb8c78d295d4..6728b2e90205e 100644 --- a/ext/hash/php_hash_fnv.h +++ b/ext/hash/php_hash_fnv.h @@ -44,10 +44,12 @@ enum php_fnv_type { typedef struct { uint32_t state; } PHP_FNV132_CTX; +#define PHP_FNV132_SPEC "l." typedef struct { uint64_t state; } PHP_FNV164_CTX; +#define PHP_FNV164_SPEC "q." PHP_HASH_API void PHP_FNV132Init(PHP_FNV132_CTX *context); diff --git a/ext/hash/php_hash_gost.h b/ext/hash/php_hash_gost.h index 987eb655cb30a..eb9441faa66a4 100644 --- a/ext/hash/php_hash_gost.h +++ b/ext/hash/php_hash_gost.h @@ -27,6 +27,7 @@ typedef struct { unsigned char buffer[32]; const uint32_t (*tables)[4][256]; } PHP_GOST_CTX; +#define PHP_GOST_SPEC "l16l2bb32" PHP_HASH_API void PHP_GOSTInit(PHP_GOST_CTX *); PHP_HASH_API void PHP_GOSTUpdate(PHP_GOST_CTX *, const unsigned char *, size_t); diff --git a/ext/hash/php_hash_haval.h b/ext/hash/php_hash_haval.h index 8eaaf2d2b74f5..43802b41834fa 100644 --- a/ext/hash/php_hash_haval.h +++ b/ext/hash/php_hash_haval.h @@ -28,6 +28,7 @@ typedef struct { short output; void (*Transform)(uint32_t state[8], const unsigned char block[128]); } PHP_HAVAL_CTX; +#define PHP_HAVAL_SPEC "l8l2b128" #define PHP_HASH_HAVAL_INIT_DECL(p,b) PHP_HASH_API void PHP_##p##HAVAL##b##Init(PHP_HAVAL_CTX *); \ PHP_HASH_API void PHP_HAVAL##b##Final(unsigned char*, PHP_HAVAL_CTX *); diff --git a/ext/hash/php_hash_joaat.h b/ext/hash/php_hash_joaat.h index ede108f1bb23b..b0df8a67b38ae 100644 --- a/ext/hash/php_hash_joaat.h +++ b/ext/hash/php_hash_joaat.h @@ -20,6 +20,7 @@ typedef struct { uint32_t state; } PHP_JOAAT_CTX; +#define PHP_JOAAT_SPEC "l." PHP_HASH_API void PHP_JOAATInit(PHP_JOAAT_CTX *context); PHP_HASH_API void PHP_JOAATUpdate(PHP_JOAAT_CTX *context, const unsigned char *input, size_t inputLen); diff --git a/ext/hash/php_hash_md.h b/ext/hash/php_hash_md.h index aed999261e933..2a677fe54d662 100644 --- a/ext/hash/php_hash_md.h +++ b/ext/hash/php_hash_md.h @@ -26,6 +26,7 @@ typedef struct { uint32_t count[2]; unsigned char buffer[64]; } PHP_MD4_CTX; +#define PHP_MD4_SPEC "l4l2b64." PHP_HASH_API void PHP_MD4Init(PHP_MD4_CTX *); PHP_HASH_API void PHP_MD4Update(PHP_MD4_CTX *context, const unsigned char *, size_t); @@ -36,8 +37,9 @@ typedef struct { unsigned char state[48]; unsigned char checksum[16]; unsigned char buffer[16]; - char in_buffer; + unsigned char in_buffer; } PHP_MD2_CTX; +#define PHP_MD2_SPEC "b48b16b16b." PHP_HASH_API void PHP_MD2Init(PHP_MD2_CTX *context); PHP_HASH_API void PHP_MD2Update(PHP_MD2_CTX *context, const unsigned char *, size_t); diff --git a/ext/hash/php_hash_ripemd.h b/ext/hash/php_hash_ripemd.h index 131dd0ad8f0a1..dd9913b85b72a 100644 --- a/ext/hash/php_hash_ripemd.h +++ b/ext/hash/php_hash_ripemd.h @@ -24,24 +24,28 @@ typedef struct { uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } PHP_RIPEMD128_CTX; +#define PHP_RIPEMD128_SPEC "l4l2b64." typedef struct { uint32_t state[5]; /* state (ABCD) */ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } PHP_RIPEMD160_CTX; +#define PHP_RIPEMD160_SPEC "l5l2b64." typedef struct { uint32_t state[8]; /* state (ABCD) */ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } PHP_RIPEMD256_CTX; +#define PHP_RIPEMD256_SPEC "l8l2b64." typedef struct { uint32_t state[10]; /* state (ABCD) */ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } PHP_RIPEMD320_CTX; +#define PHP_RIPEMD320_SPEC "l10l2b64." PHP_HASH_API void PHP_RIPEMD128Init(PHP_RIPEMD128_CTX *); PHP_HASH_API void PHP_RIPEMD128Update(PHP_RIPEMD128_CTX *, const unsigned char *, size_t); diff --git a/ext/hash/php_hash_sha.h b/ext/hash/php_hash_sha.h index 95471e7398b13..16da9273636a7 100644 --- a/ext/hash/php_hash_sha.h +++ b/ext/hash/php_hash_sha.h @@ -27,6 +27,7 @@ typedef struct { uint32_t count[2]; /* number of bits, modulo 2^64 */ unsigned char buffer[64]; /* input buffer */ } PHP_SHA224_CTX; +#define PHP_SHA224_SPEC "l8l2b64." PHP_HASH_API void PHP_SHA224Init(PHP_SHA224_CTX *); PHP_HASH_API void PHP_SHA224Update(PHP_SHA224_CTX *, const unsigned char *, size_t); @@ -38,6 +39,7 @@ typedef struct { uint32_t count[2]; /* number of bits, modulo 2^64 */ unsigned char buffer[64]; /* input buffer */ } PHP_SHA256_CTX; +#define PHP_SHA256_SPEC "l8l2b64." PHP_HASH_API void PHP_SHA256Init(PHP_SHA256_CTX *); PHP_HASH_API void PHP_SHA256Update(PHP_SHA256_CTX *, const unsigned char *, size_t); @@ -49,6 +51,7 @@ typedef struct { uint64_t count[2]; /* number of bits, modulo 2^128 */ unsigned char buffer[128]; /* input buffer */ } PHP_SHA384_CTX; +#define PHP_SHA384_SPEC "q8q2b128." PHP_HASH_API void PHP_SHA384Init(PHP_SHA384_CTX *); PHP_HASH_API void PHP_SHA384Update(PHP_SHA384_CTX *, const unsigned char *, size_t); @@ -60,6 +63,7 @@ typedef struct { uint64_t count[2]; /* number of bits, modulo 2^128 */ unsigned char buffer[128]; /* input buffer */ } PHP_SHA512_CTX; +#define PHP_SHA512_SPEC "q8q2b128." PHP_HASH_API void PHP_SHA512Init(PHP_SHA512_CTX *); PHP_HASH_API void PHP_SHA512Update(PHP_SHA512_CTX *, const unsigned char *, size_t); diff --git a/ext/hash/php_hash_sha3.h b/ext/hash/php_hash_sha3.h index bb392c0ab0943..f75191f339fc6 100644 --- a/ext/hash/php_hash_sha3.h +++ b/ext/hash/php_hash_sha3.h @@ -27,6 +27,9 @@ typedef struct { unsigned char state[224]; // this must fit a Keccak_HashInstance #endif } PHP_SHA3_CTX; +#ifdef HAVE_SLOW_HASH3 +#define PHP_SHA3_SPEC "b200l." +#endif typedef PHP_SHA3_CTX PHP_SHA3_224_CTX; typedef PHP_SHA3_CTX PHP_SHA3_256_CTX; diff --git a/ext/hash/php_hash_snefru.h b/ext/hash/php_hash_snefru.h index 25ba1183e3bcf..0f339e93090ab 100644 --- a/ext/hash/php_hash_snefru.h +++ b/ext/hash/php_hash_snefru.h @@ -30,6 +30,7 @@ typedef struct { unsigned char length; unsigned char buffer[32]; } PHP_SNEFRU_CTX; +#define PHP_SNEFRU_SPEC "l16l2bb32" PHP_HASH_API void PHP_SNEFRUInit(PHP_SNEFRU_CTX *); PHP_HASH_API void PHP_SNEFRUUpdate(PHP_SNEFRU_CTX *, const unsigned char *, size_t); diff --git a/ext/hash/php_hash_tiger.h b/ext/hash/php_hash_tiger.h index 4476639040a6e..d30276ddeaa29 100644 --- a/ext/hash/php_hash_tiger.h +++ b/ext/hash/php_hash_tiger.h @@ -22,9 +22,10 @@ typedef struct { uint64_t state[3]; uint64_t passed; unsigned char buffer[64]; + uint32_t length; unsigned int passes:1; - size_t length; } PHP_TIGER_CTX; +#define PHP_TIGER_SPEC "q3qb64l" PHP_HASH_API void PHP_3TIGERInit(PHP_TIGER_CTX *context); PHP_HASH_API void PHP_4TIGERInit(PHP_TIGER_CTX *context); diff --git a/ext/hash/php_hash_whirlpool.h b/ext/hash/php_hash_whirlpool.h index f96da414a5315..fbd5948a4817b 100644 --- a/ext/hash/php_hash_whirlpool.h +++ b/ext/hash/php_hash_whirlpool.h @@ -27,6 +27,7 @@ typedef struct { unsigned char data[64]; } buffer; } PHP_WHIRLPOOL_CTX; +#define PHP_WHIRLPOOL_SPEC "q8b32iib64." PHP_HASH_API void PHP_WHIRLPOOLInit(PHP_WHIRLPOOL_CTX *); PHP_HASH_API void PHP_WHIRLPOOLUpdate(PHP_WHIRLPOOL_CTX *, const unsigned char *, size_t); diff --git a/ext/hash/sha3/generic32lc/KeccakHash.h b/ext/hash/sha3/generic32lc/KeccakHash.h index ec35d3dab2524..d53ac1c587aec 100644 --- a/ext/hash/sha3/generic32lc/KeccakHash.h +++ b/ext/hash/sha3/generic32lc/KeccakHash.h @@ -108,6 +108,8 @@ HashReturn Keccak_HashFinal(Keccak_HashInstance *hashInstance, BitSequence *hash */ HashReturn Keccak_HashSqueeze(Keccak_HashInstance *hashInstance, BitSequence *data, DataLength databitlen); +#define Keccak_HashInstance_ImplType 32 + #endif #endif diff --git a/ext/hash/sha3/generic64lc/KeccakHash.h b/ext/hash/sha3/generic64lc/KeccakHash.h index ec35d3dab2524..6afe6c8c9a85a 100644 --- a/ext/hash/sha3/generic64lc/KeccakHash.h +++ b/ext/hash/sha3/generic64lc/KeccakHash.h @@ -108,6 +108,8 @@ HashReturn Keccak_HashFinal(Keccak_HashInstance *hashInstance, BitSequence *hash */ HashReturn Keccak_HashSqueeze(Keccak_HashInstance *hashInstance, BitSequence *data, DataLength databitlen); +#define Keccak_HashInstance_ImplType 64 + #endif #endif diff --git a/ext/hash/tests/hash_serialize_001.phpt b/ext/hash/tests/hash_serialize_001.phpt new file mode 100644 index 0000000000000..d515d2c5c55cd --- /dev/null +++ b/ext/hash/tests/hash_serialize_001.phpt @@ -0,0 +1,361 @@ +--TEST-- +Hash: serialize()/unserialize() +--FILE-- + +--EXPECT-- +string(3) "md2" +string(32) "d5ac4ffd08f6a57b9bd402b8068392ff" +string(32) "d5ac4ffd08f6a57b9bd402b8068392ff" +string(3) "md4" +string(32) "302c45586b53a984bd3a1237cb81c15f" +string(32) "302c45586b53a984bd3a1237cb81c15f" +string(3) "md5" +string(32) "e35759f6ea35db254e415b5332269435" +string(32) "e35759f6ea35db254e415b5332269435" +string(4) "sha1" +string(40) "29f62a228f726cd728efa7a0ac6a2aba318baf15" +string(40) "29f62a228f726cd728efa7a0ac6a2aba318baf15" +string(6) "sha224" +string(56) "51fd0aa76a00b4a86103895cad5c7c2651ec7da9f4fc1e50c43ede29" +string(56) "51fd0aa76a00b4a86103895cad5c7c2651ec7da9f4fc1e50c43ede29" +string(6) "sha256" +string(64) "d3a13cf52af8e9390caed78b77b6b1e06e102204e3555d111dfd149bc5d54dba" +string(64) "d3a13cf52af8e9390caed78b77b6b1e06e102204e3555d111dfd149bc5d54dba" +string(6) "sha384" +string(96) "6950d861ace4102b803ab8b3779d2f471968233010d2608974ab89804cef6f76162b4433d6e554e11e40a7cdcf510ea3" +string(96) "6950d861ace4102b803ab8b3779d2f471968233010d2608974ab89804cef6f76162b4433d6e554e11e40a7cdcf510ea3" +string(10) "sha512/224" +string(56) "a2573d0e3f6c3e2d174c935a35a8ea31032f04e9e83499ac3ceda568" +string(56) "a2573d0e3f6c3e2d174c935a35a8ea31032f04e9e83499ac3ceda568" +string(10) "sha512/256" +string(64) "fddacab80b3a610ba024c9d75a5fe0cafe5ae7c789f829b3c5fbea8ef11ccc1a" +string(64) "fddacab80b3a610ba024c9d75a5fe0cafe5ae7c789f829b3c5fbea8ef11ccc1a" +string(6) "sha512" +string(128) "caced3db8e9e3a5543d5b933bcbe9e7834e6667545c3f5d4087b58ec8d78b4c8a4a5500c9b88f65f7368810ba9905e51f1cff3b25a5dccf76634108fb4e7ce13" +string(128) "caced3db8e9e3a5543d5b933bcbe9e7834e6667545c3f5d4087b58ec8d78b4c8a4a5500c9b88f65f7368810ba9905e51f1cff3b25a5dccf76634108fb4e7ce13" +string(8) "sha3-224" +string(56) "7e1126cffee98e5c4b0e9dd5c6efabd5c9356d668e9a2d3cfab724d4" +string(56) "7e1126cffee98e5c4b0e9dd5c6efabd5c9356d668e9a2d3cfab724d4" +string(8) "sha3-256" +string(64) "834abfed9197af09cbe66b7748c65a050a3755ef7a556d6764eb6eabc93b4c7a" +string(64) "834abfed9197af09cbe66b7748c65a050a3755ef7a556d6764eb6eabc93b4c7a" +string(8) "sha3-384" +string(96) "c9016992586f7a8663c5379ed892349c1140ad258f7c44ee82f61f0b8cb75c675012ea94dc1314e06699be2d1465f67b" +string(96) "c9016992586f7a8663c5379ed892349c1140ad258f7c44ee82f61f0b8cb75c675012ea94dc1314e06699be2d1465f67b" +string(8) "sha3-512" +string(128) "5f85341bc9c6621406bf1841c4ce01727ea8759fdf2927106c3e70a75ad9fffd095b87f995aeee844e1a2c287e1195ce809b9bdb1c31258f7fc098175b6de0b4" +string(128) "5f85341bc9c6621406bf1841c4ce01727ea8759fdf2927106c3e70a75ad9fffd095b87f995aeee844e1a2c287e1195ce809b9bdb1c31258f7fc098175b6de0b4" +string(9) "ripemd128" +string(32) "5f1bc5f5aeaf747574dd34a6535cd94a" +string(32) "5f1bc5f5aeaf747574dd34a6535cd94a" +string(9) "ripemd160" +string(40) "02a2a535ee10404c6b5cf9acb178a04fbed67269" +string(40) "02a2a535ee10404c6b5cf9acb178a04fbed67269" +string(9) "ripemd256" +string(64) "547d2ed85ca0a0e3208b5ecf4fc6a7fc1e64db8ff13493e4beaf11e4d71648e2" +string(64) "547d2ed85ca0a0e3208b5ecf4fc6a7fc1e64db8ff13493e4beaf11e4d71648e2" +string(9) "ripemd320" +string(80) "785a7df56858f550966cddfd59ce14b13bf4b18e7892c4c1ad91bf23bf67639bd2c96749ba29cfa6" +string(80) "785a7df56858f550966cddfd59ce14b13bf4b18e7892c4c1ad91bf23bf67639bd2c96749ba29cfa6" +string(9) "whirlpool" +string(128) "6e60597340640e621e25f975cef2b000b0c4c09a7af7d240a52d193002b0a8426fa7da7acc5b37ed9608016d4f396db834a0ea2f2c35f900461c9ac7e5604082" +string(128) "6e60597340640e621e25f975cef2b000b0c4c09a7af7d240a52d193002b0a8426fa7da7acc5b37ed9608016d4f396db834a0ea2f2c35f900461c9ac7e5604082" +string(10) "tiger128,3" +string(32) "8d68e78bc5e62ba925a67aa48595cfc6" +string(32) "8d68e78bc5e62ba925a67aa48595cfc6" +string(10) "tiger160,3" +string(40) "8d68e78bc5e62ba925a67aa48595cfc62cd1e5e0" +string(40) "8d68e78bc5e62ba925a67aa48595cfc62cd1e5e0" +string(10) "tiger192,3" +string(48) "8d68e78bc5e62ba925a67aa48595cfc62cd1e5e08224fc35" +string(48) "8d68e78bc5e62ba925a67aa48595cfc62cd1e5e08224fc35" +string(10) "tiger128,4" +string(32) "a26ca3f58e74fb32ee44b099cb1b5122" +string(32) "a26ca3f58e74fb32ee44b099cb1b5122" +string(10) "tiger160,4" +string(40) "a26ca3f58e74fb32ee44b099cb1b512203375900" +string(40) "a26ca3f58e74fb32ee44b099cb1b512203375900" +string(10) "tiger192,4" +string(48) "a26ca3f58e74fb32ee44b099cb1b512203375900f30b741d" +string(48) "a26ca3f58e74fb32ee44b099cb1b512203375900f30b741d" +string(6) "snefru" +string(64) "fbe88daa74c89b9e29468fa3cd3a657d31845e21bb58dd3f8d806f5179a85c26" +string(64) "fbe88daa74c89b9e29468fa3cd3a657d31845e21bb58dd3f8d806f5179a85c26" +string(9) "snefru256" +string(64) "fbe88daa74c89b9e29468fa3cd3a657d31845e21bb58dd3f8d806f5179a85c26" +string(64) "fbe88daa74c89b9e29468fa3cd3a657d31845e21bb58dd3f8d806f5179a85c26" +string(4) "gost" +string(64) "5820c7c4a0650587538b30ef4099f2b5993069758d5c847a552e6ef7360766a5" +string(64) "5820c7c4a0650587538b30ef4099f2b5993069758d5c847a552e6ef7360766a5" +string(11) "gost-crypto" +string(64) "f7c4e35548d66aabe2b106f20515d289fde90969225d3d7b83f6dd12d694f043" +string(64) "f7c4e35548d66aabe2b106f20515d289fde90969225d3d7b83f6dd12d694f043" +string(7) "adler32" +string(8) "6f7c0928" +string(8) "6f7c0928" +string(5) "crc32" +string(8) "e5cfc160" +string(8) "e5cfc160" +string(6) "crc32b" +string(8) "69147a4e" +string(8) "69147a4e" +string(6) "crc32c" +string(8) "5e405e93" +string(8) "5e405e93" +string(6) "fnv132" +string(8) "98139504" +string(8) "98139504" +string(7) "fnv1a32" +string(8) "aae4e042" +string(8) "aae4e042" +string(6) "fnv164" +string(16) "14522659f8138684" +string(16) "14522659f8138684" +string(7) "fnv1a64" +string(16) "bebc746a33b6ab62" +string(16) "bebc746a33b6ab62" +string(5) "joaat" +string(8) "aaebf370" +string(8) "aaebf370" +string(10) "haval128,3" +string(32) "86362472c8895e68e223ef8b3711d8d9" +string(32) "86362472c8895e68e223ef8b3711d8d9" +string(10) "haval160,3" +string(40) "fabdf6905f3ba18a3c93d6a16b91e31f7222a7a4" +string(40) "fabdf6905f3ba18a3c93d6a16b91e31f7222a7a4" +string(10) "haval192,3" +string(48) "e05d0ff5723028bd5494f32c0c2494cd0b9ccf7540af7b47" +string(48) "e05d0ff5723028bd5494f32c0c2494cd0b9ccf7540af7b47" +string(10) "haval224,3" +string(56) "56b196289d8de8a22296588cf90e5b09cb6fa1b01ce8e92bca40cae2" +string(56) "56b196289d8de8a22296588cf90e5b09cb6fa1b01ce8e92bca40cae2" +string(10) "haval256,3" +string(64) "ff4d7ab0fac2ca437b945461f9b62fd16e71e9103524d5d140445a00e3d49239" +string(64) "ff4d7ab0fac2ca437b945461f9b62fd16e71e9103524d5d140445a00e3d49239" +string(10) "haval128,4" +string(32) "ee44418e0195a0c4a35d112722919a9c" +string(32) "ee44418e0195a0c4a35d112722919a9c" +string(10) "haval160,4" +string(40) "f320cce982d5201a1ccacc1c5ff835a258a97eb1" +string(40) "f320cce982d5201a1ccacc1c5ff835a258a97eb1" +string(10) "haval192,4" +string(48) "a96600107463e8e97a7fe6f260d9bf4f4587a281caafa6db" +string(48) "a96600107463e8e97a7fe6f260d9bf4f4587a281caafa6db" +string(10) "haval224,4" +string(56) "7147c9e1c1e67b942da3229f59a1ab18f121f5d7f5765ca88bc9f200" +string(56) "7147c9e1c1e67b942da3229f59a1ab18f121f5d7f5765ca88bc9f200" +string(10) "haval256,4" +string(64) "82fec42679ed5a77a841962827b88a9cddf7d677736e50bc81f1a14b99f06061" +string(64) "82fec42679ed5a77a841962827b88a9cddf7d677736e50bc81f1a14b99f06061" +string(10) "haval128,5" +string(32) "8d0b157828328ae7d34d60b4b60c1dab" +string(32) "8d0b157828328ae7d34d60b4b60c1dab" +string(10) "haval160,5" +string(40) "54dab5e10dc41503f9b8aa32ffe3bab7cf1da8a3" +string(40) "54dab5e10dc41503f9b8aa32ffe3bab7cf1da8a3" +string(10) "haval192,5" +string(48) "7d91265a1b27698279d8d95a5ee0a20014528070bf6415e7" +string(48) "7d91265a1b27698279d8d95a5ee0a20014528070bf6415e7" +string(10) "haval224,5" +string(56) "7772b2e22f2a3bce917e08cf57ebece46bb33168619a776c6f2f7234" +string(56) "7772b2e22f2a3bce917e08cf57ebece46bb33168619a776c6f2f7234" +string(10) "haval256,5" +string(64) "438a602cb1a761f7bd0a633b7bd8b3ccd0577b524d05174ca1ae1f559b9a2c2a" +string(64) "438a602cb1a761f7bd0a633b7bd8b3ccd0577b524d05174ca1ae1f559b9a2c2a" +string(3) "md2" +string(32) "5c36f61062d091a8324991132c5e8dbd" +string(32) "5c36f61062d091a8324991132c5e8dbd" +string(3) "md4" +string(32) "1d4196526aada3506efb4c7425651584" +string(32) "1d4196526aada3506efb4c7425651584" +string(3) "md5" +string(32) "f255c114bd6ce94aad092b5141c00d46" +string(32) "f255c114bd6ce94aad092b5141c00d46" +string(4) "sha1" +string(40) "a273396f056554dcd491b5dea1e7baa3b89b802b" +string(40) "a273396f056554dcd491b5dea1e7baa3b89b802b" +string(6) "sha224" +string(56) "1aee028400c56ceb5539625dc2f395abf491409336ca0f3e177a50e2" +string(56) "1aee028400c56ceb5539625dc2f395abf491409336ca0f3e177a50e2" +string(6) "sha256" +string(64) "268e7f4cf88504a53fd77136c4c4748169f46ff7150b376569ada9c374836944" +string(64) "268e7f4cf88504a53fd77136c4c4748169f46ff7150b376569ada9c374836944" +string(6) "sha384" +string(96) "0d44981d04bb11b1ef75d5c2932bd0aa2785e7bc454daac954d77e2ca10047879b58997533fc99650b20049c6cb9a6cc" +string(96) "0d44981d04bb11b1ef75d5c2932bd0aa2785e7bc454daac954d77e2ca10047879b58997533fc99650b20049c6cb9a6cc" +string(10) "sha512/224" +string(56) "cbc2bbf0028ed803af785b0f264962c84ec48d8ee0908322ef995ddb" +string(56) "cbc2bbf0028ed803af785b0f264962c84ec48d8ee0908322ef995ddb" +string(10) "sha512/256" +string(64) "2cec704878ffa7128e0c4a61eef87d1f3c823184d364dfa3fed73beb00499b00" +string(64) "2cec704878ffa7128e0c4a61eef87d1f3c823184d364dfa3fed73beb00499b00" +string(6) "sha512" +string(128) "28d7c721433782a880f840af0c3f3ea2cad4ef55de2114dda9d504cedeb110e1cf2519c49e4b5da3da4484bb6ba4fd1621ceadc6408f4410b2ebe9d83a4202c2" +string(128) "28d7c721433782a880f840af0c3f3ea2cad4ef55de2114dda9d504cedeb110e1cf2519c49e4b5da3da4484bb6ba4fd1621ceadc6408f4410b2ebe9d83a4202c2" +string(8) "sha3-224" +string(56) "9a21a5464794c2c9784df50cf89cf72234e11941bddaee93f912753e" +string(56) "9a21a5464794c2c9784df50cf89cf72234e11941bddaee93f912753e" +string(8) "sha3-256" +string(64) "57aa7a90f29b5ab66592760592780da247fd39b4c911773687450f9df8cc8ed0" +string(64) "57aa7a90f29b5ab66592760592780da247fd39b4c911773687450f9df8cc8ed0" +string(8) "sha3-384" +string(96) "5d6d7e42b241288bc707b74c50f90a37d69a4afa854ca72021a22cb379356e53b6233aea1be2f33d393d6effa9b5e36c" +string(96) "5d6d7e42b241288bc707b74c50f90a37d69a4afa854ca72021a22cb379356e53b6233aea1be2f33d393d6effa9b5e36c" +string(8) "sha3-512" +string(128) "9b88c689bc13a36e6983b32e8ee9464d63b619f246ca451d1fe2a6c9670f01e71d0c8eb245f3204d27d27c056f2a0fef76a1e3bc30fb74cccbc984dbd4883ae6" +string(128) "9b88c689bc13a36e6983b32e8ee9464d63b619f246ca451d1fe2a6c9670f01e71d0c8eb245f3204d27d27c056f2a0fef76a1e3bc30fb74cccbc984dbd4883ae6" +string(9) "ripemd128" +string(32) "f95f5e22b8875ee0c48219ae97f0674b" +string(32) "f95f5e22b8875ee0c48219ae97f0674b" +string(9) "ripemd160" +string(40) "900d615c1abe714e340f4ecd6a3d65599fd30ff4" +string(40) "900d615c1abe714e340f4ecd6a3d65599fd30ff4" +string(9) "ripemd256" +string(64) "b9799db40d1af5614118c329169cdcd2c718db6af03bf945ea7f7ba72b8e14f4" +string(64) "b9799db40d1af5614118c329169cdcd2c718db6af03bf945ea7f7ba72b8e14f4" +string(9) "ripemd320" +string(80) "d6d12c1fca7a9c4a59c1be4f40188e92a746a035219e0a6ca1ee53b36a8282527187f7dffaa57ecc" +string(80) "d6d12c1fca7a9c4a59c1be4f40188e92a746a035219e0a6ca1ee53b36a8282527187f7dffaa57ecc" +string(9) "whirlpool" +string(128) "e8c6a921e7d8eac2fd21d4df6054bb27a02321b2beb5b01b6f88c40706164e64d67ec97519bf76c8af8df896745478b78d42a0159f1a0db16777771fd9d420dc" +string(128) "e8c6a921e7d8eac2fd21d4df6054bb27a02321b2beb5b01b6f88c40706164e64d67ec97519bf76c8af8df896745478b78d42a0159f1a0db16777771fd9d420dc" +string(10) "tiger128,3" +string(32) "a99d2c0348d480dc0f3c35852926e0f1" +string(32) "a99d2c0348d480dc0f3c35852926e0f1" +string(10) "tiger160,3" +string(40) "a99d2c0348d480dc0f3c35852926e0f1e1825c16" +string(40) "a99d2c0348d480dc0f3c35852926e0f1e1825c16" +string(10) "tiger192,3" +string(48) "a99d2c0348d480dc0f3c35852926e0f1e1825c1651957ee3" +string(48) "a99d2c0348d480dc0f3c35852926e0f1e1825c1651957ee3" +string(10) "tiger128,4" +string(32) "66e2c0322421c4e5a9208e6aeed481e5" +string(32) "66e2c0322421c4e5a9208e6aeed481e5" +string(10) "tiger160,4" +string(40) "66e2c0322421c4e5a9208e6aeed481e5c4b00448" +string(40) "66e2c0322421c4e5a9208e6aeed481e5c4b00448" +string(10) "tiger192,4" +string(48) "66e2c0322421c4e5a9208e6aeed481e5c4b00448e344d9d0" +string(48) "66e2c0322421c4e5a9208e6aeed481e5c4b00448e344d9d0" +string(6) "snefru" +string(64) "614ca924864fa0e8fa309aa0944e047d5edbfd4964a35858f4d8ec66a0fb88b0" +string(64) "614ca924864fa0e8fa309aa0944e047d5edbfd4964a35858f4d8ec66a0fb88b0" +string(9) "snefru256" +string(64) "614ca924864fa0e8fa309aa0944e047d5edbfd4964a35858f4d8ec66a0fb88b0" +string(64) "614ca924864fa0e8fa309aa0944e047d5edbfd4964a35858f4d8ec66a0fb88b0" +string(4) "gost" +string(64) "a00961e371287c71c527a41c14564f13b6ed12ac7cd9d5f5dfb3542a25e28d3b" +string(64) "a00961e371287c71c527a41c14564f13b6ed12ac7cd9d5f5dfb3542a25e28d3b" +string(11) "gost-crypto" +string(64) "68ca9aea6729dc07d995fbe071a4b5c6490bb27fc4dc65ec0e96200d5e082996" +string(64) "68ca9aea6729dc07d995fbe071a4b5c6490bb27fc4dc65ec0e96200d5e082996" +string(7) "adler32" +string(8) "d9141747" +string(8) "d9141747" +string(5) "crc32" +string(8) "59f8d3d2" +string(8) "59f8d3d2" +string(6) "crc32b" +string(8) "3ee63999" +string(8) "3ee63999" +string(6) "crc32c" +string(8) "516ad412" +string(8) "516ad412" +string(6) "fnv132" +string(8) "59ad036f" +string(8) "59ad036f" +string(7) "fnv1a32" +string(8) "fadc2cef" +string(8) "fadc2cef" +string(6) "fnv164" +string(16) "5e8c64fba6a5ffcf" +string(16) "5e8c64fba6a5ffcf" +string(7) "fnv1a64" +string(16) "893899e4415a920f" +string(16) "893899e4415a920f" +string(5) "joaat" +string(8) "836fb0e5" +string(8) "836fb0e5" +string(10) "haval128,3" +string(32) "ebeeeb05c18af1e53d2d127b561d5e0d" +string(32) "ebeeeb05c18af1e53d2d127b561d5e0d" +string(10) "haval160,3" +string(40) "f1a2c9604fb40899ad502abe0dfcec65115c8a9a" +string(40) "f1a2c9604fb40899ad502abe0dfcec65115c8a9a" +string(10) "haval192,3" +string(48) "d3a7315773a326678208650ed02510ed96cd488d74cd5231" +string(48) "d3a7315773a326678208650ed02510ed96cd488d74cd5231" +string(10) "haval224,3" +string(56) "6d7132fabc83c9ab7913748b79ecf10e25409569d3ed144177f46731" +string(56) "6d7132fabc83c9ab7913748b79ecf10e25409569d3ed144177f46731" +string(10) "haval256,3" +string(64) "7a469868ad4b92891a3a44524c58a2b8d0f3bebb92b4cf47d19bc6aba973eb95" +string(64) "7a469868ad4b92891a3a44524c58a2b8d0f3bebb92b4cf47d19bc6aba973eb95" +string(10) "haval128,4" +string(32) "6ecddb39615f43fd211839287ff38461" +string(32) "6ecddb39615f43fd211839287ff38461" +string(10) "haval160,4" +string(40) "bcd2e7821723ac22e122b8b7cbbd2daaa9a862df" +string(40) "bcd2e7821723ac22e122b8b7cbbd2daaa9a862df" +string(10) "haval192,4" +string(48) "ae74619a88dcec1fbecde28e27f009a65ecc12170824d2cd" +string(48) "ae74619a88dcec1fbecde28e27f009a65ecc12170824d2cd" +string(10) "haval224,4" +string(56) "fdaba6563f1334d40de24e311f14b324577f97c3b78b9439c408cdca" +string(56) "fdaba6563f1334d40de24e311f14b324577f97c3b78b9439c408cdca" +string(10) "haval256,4" +string(64) "289a2ba4820218bdb25a6534fbdf693f9de101362584fdd41e32244c719caa37" +string(64) "289a2ba4820218bdb25a6534fbdf693f9de101362584fdd41e32244c719caa37" +string(10) "haval128,5" +string(32) "ffa7993a4e183b245263fb1f63e27343" +string(32) "ffa7993a4e183b245263fb1f63e27343" +string(10) "haval160,5" +string(40) "375ee5ab3a9bd07a1dbe5d071e07b2afb3165e3b" +string(40) "375ee5ab3a9bd07a1dbe5d071e07b2afb3165e3b" +string(10) "haval192,5" +string(48) "c650585f93c6e041e835caedc621f8c42d8bc6829fb76789" +string(48) "c650585f93c6e041e835caedc621f8c42d8bc6829fb76789" +string(10) "haval224,5" +string(56) "bc674d465a822817d939f19b38edde083fe5668759836c203c56e3e4" +string(56) "bc674d465a822817d939f19b38edde083fe5668759836c203c56e3e4" +string(10) "haval256,5" +string(64) "da70ad9bd09ed7c9675329ea2b5279d57761807c7aeac6340d94b5d494809457" +string(64) "da70ad9bd09ed7c9675329ea2b5279d57761807c7aeac6340d94b5d494809457" +Done diff --git a/ext/hash/tests/hash_serialize_002.phpt b/ext/hash/tests/hash_serialize_002.phpt new file mode 100644 index 0000000000000..b6b4ccfb5eab7 --- /dev/null +++ b/ext/hash/tests/hash_serialize_002.phpt @@ -0,0 +1,116 @@ +--TEST-- +Hash: serialize()/unserialize() with HASH_HMAC +--FILE-- +getMessage(), "\n"; + } +} + +echo "Done\n"; +?> +--EXPECT-- +string(3) "md2" +HashContext with HASH_HMAC option cannot be serialized +string(3) "md4" +HashContext with HASH_HMAC option cannot be serialized +string(3) "md5" +HashContext with HASH_HMAC option cannot be serialized +string(4) "sha1" +HashContext with HASH_HMAC option cannot be serialized +string(6) "sha224" +HashContext with HASH_HMAC option cannot be serialized +string(6) "sha256" +HashContext with HASH_HMAC option cannot be serialized +string(6) "sha384" +HashContext with HASH_HMAC option cannot be serialized +string(10) "sha512/224" +HashContext with HASH_HMAC option cannot be serialized +string(10) "sha512/256" +HashContext with HASH_HMAC option cannot be serialized +string(6) "sha512" +HashContext with HASH_HMAC option cannot be serialized +string(8) "sha3-224" +HashContext with HASH_HMAC option cannot be serialized +string(8) "sha3-256" +HashContext with HASH_HMAC option cannot be serialized +string(8) "sha3-384" +HashContext with HASH_HMAC option cannot be serialized +string(8) "sha3-512" +HashContext with HASH_HMAC option cannot be serialized +string(9) "ripemd128" +HashContext with HASH_HMAC option cannot be serialized +string(9) "ripemd160" +HashContext with HASH_HMAC option cannot be serialized +string(9) "ripemd256" +HashContext with HASH_HMAC option cannot be serialized +string(9) "ripemd320" +HashContext with HASH_HMAC option cannot be serialized +string(9) "whirlpool" +HashContext with HASH_HMAC option cannot be serialized +string(10) "tiger128,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "tiger160,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "tiger192,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "tiger128,4" +HashContext with HASH_HMAC option cannot be serialized +string(10) "tiger160,4" +HashContext with HASH_HMAC option cannot be serialized +string(10) "tiger192,4" +HashContext with HASH_HMAC option cannot be serialized +string(6) "snefru" +HashContext with HASH_HMAC option cannot be serialized +string(9) "snefru256" +HashContext with HASH_HMAC option cannot be serialized +string(4) "gost" +HashContext with HASH_HMAC option cannot be serialized +string(11) "gost-crypto" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval128,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval160,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval192,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval224,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval256,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval128,4" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval160,4" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval192,4" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval224,4" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval256,4" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval128,5" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval160,5" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval192,5" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval224,5" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval256,5" +HashContext with HASH_HMAC option cannot be serialized +Done diff --git a/ext/hash/tests/hash_serialize_003.phpt b/ext/hash/tests/hash_serialize_003.phpt new file mode 100644 index 0000000000000..a687c2aeffbdf --- /dev/null +++ b/ext/hash/tests/hash_serialize_003.phpt @@ -0,0 +1,261 @@ +--TEST-- +Hash: serialization formats +--FILE-- +getMessage(), "\n", $e->getTraceAsString(); + } +} + +foreach ($serializations as $slist) { + $algo = $slist[0]; + $hash = hash($algo, "I can't remember anythingCan’t tell if this is true or dream"); + + $ctx = hash_init($algo); + hash_update($ctx, "I can't remember anything"); + $serial = base64_encode(serialize($ctx)); + if (!in_array($serial, $slist)) { + echo "$algo: unexpected serialization $serial\n"; + } + + test_serialization($serial, $hash, $algo); +} + +echo "Done\n"; +?> +--EXPECT-- +Done diff --git a/ext/hash/tests/hash_serialize_004.phpt b/ext/hash/tests/hash_serialize_004.phpt new file mode 100644 index 0000000000000..ee5c08f973e25 --- /dev/null +++ b/ext/hash/tests/hash_serialize_004.phpt @@ -0,0 +1,45 @@ +--TEST-- +Hash: serialization errors +--FILE-- +__unserialize($ctx->__serialize()); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} + +// bad formats +foreach ([ + "TzoxMToiSGFzaENvbnRleHQiOjA6e30=", // no contents + "TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtpOjE7aToxO2k6MDtpOjI7YTo4OntpOjA7aToxNzMyNTg0MTkzO2k6MTtpOi0yNzE3MzM4Nzk7aToyO2k6LTE3MzI1ODQxOTQ7aTozO2k6MjcxNzMzODc4O2k6NDtpOi0xMDA5NTg5Nzc2O2k6NTtpOjIwMDtpOjY7aTowO2k6NztzOjY0OiJJIGNhbid0IHJlbWVtYmVyIGFueXRoaW5nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIjt9aTozO2k6MjtpOjQ7YTowOnt9fQ==", // algorithm is int + "TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjQ6InNoYTEiO2k6MTtzOjA6IiI7aToyO2E6ODp7aTowO2k6MTczMjU4NDE5MztpOjE7aTotMjcxNzMzODc5O2k6MjtpOi0xNzMyNTg0MTk0O2k6MztpOjI3MTczMzg3ODtpOjQ7aTotMTAwOTU4OTc3NjtpOjU7aToyMDA7aTo2O2k6MDtpOjc7czo2NDoiSSBjYW4ndCByZW1lbWJlciBhbnl0aGluZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACI7fWk6MztpOjI7aTo0O2E6MDp7fX0=", // flags are string + "TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjQ6InNoYTEiO2k6MTtpOjE7aToyO2E6ODp7aTowO2k6MTczMjU4NDE5MztpOjE7aTotMjcxNzMzODc5O2k6MjtpOi0xNzMyNTg0MTk0O2k6MztpOjI3MTczMzg3ODtpOjQ7aTotMTAwOTU4OTc3NjtpOjU7aToyMDA7aTo2O2k6MDtpOjc7czo2NDoiSSBjYW4ndCByZW1lbWJlciBhbnl0aGluZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACI7fWk6MztpOjI7aTo0O2E6MDp7fX0=", // flags indicate HASH_HMAC + "TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjQ6InNoYTEiO2k6MTtpOjI7aToyO3M6MTA6ImFiY2RlZmdoaWoiO2k6MztpOjI7aTo0O2E6MDp7fX0=", // serialization format wrong + "TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjQ6InNoYTEiO2k6MTtpOjA7aToyO2E6ODp7aTowO2k6MTczMjU4NDE5MztpOjE7aTotMjcxNzMzODc5O2k6MjtpOi0xNzMyNTg0MTk0O2k6MztpOjI3MTczMzg3ODtpOjQ7aTotMTAwOTU4OTc3NjtpOjU7aToyMDA7aTo2O3M6MDoiIjtpOjc7czo2NDoiSSBjYW4ndCByZW1lbWJlciBhbnl0aGluZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACI7fWk6MztpOjI7aTo0O2E6MDp7fX0=", // serialization internals wrong + "TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjQ6InNoYTEiO2k6MTtpOjA7aToyO2E6ODp7aTowO2k6MTczMjU4NDE5MztpOjE7aTotMjcxNzMzODc5O2k6MjtpOi0xNzMyNTg0MTk0O2k6MztpOjI3MTczMzg3ODtpOjQ7aTotMTAwOTU4OTc3NjtpOjU7aToyMDA7aTo2O2k6MDtpOjc7czo2NDoiSSBjYW4ndCByZW1lbWJlciBhbnl0aGluZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACI7fWk6MztpOjA7aTo0O2E6MDp7fX0=", // bad magic number + "TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjQ6Inh4eDEiO2k6MTtpOjA7aToyO2E6ODp7aTowO2k6MTczMjU4NDE5MztpOjE7aTotMjcxNzMzODc5O2k6MjtpOi0xNzMyNTg0MTk0O2k6MztpOjI3MTczMzg3ODtpOjQ7aTotMTAwOTU4OTc3NjtpOjU7aToyMDA7aTo2O2k6MDtpOjc7czo2NDoiSSBjYW4ndCByZW1lbWJlciBhbnl0aGluZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACI7fWk6MztpOjI7aTo0O2E6MDp7fX0=" // bad algorithm +] as $serial) { + try { + $ctx = unserialize(base64_decode($serial)); + echo "Unexpected success\n"; + } catch (Exception $e) { + echo $e->getMessage(), "\n"; + } +} + +echo "Done\n"; +?> +--EXPECT-- +HashContext::__unserialize called on initialized object +Incomplete or ill-formed serialization data +Incomplete or ill-formed serialization data +Incomplete or ill-formed serialization data +HashContext with HASH_HMAC option cannot be serialized +Incomplete or ill-formed serialization data ("sha1" code -1) +Incomplete or ill-formed serialization data ("sha1" code -1024) +Incomplete or ill-formed serialization data ("sha1" code -1) +Unknown hash algorithm +Done diff --git a/ext/hash/tests/serialize-context.phpt b/ext/hash/tests/serialize-context.phpt deleted file mode 100644 index 32aa0ab806072..0000000000000 --- a/ext/hash/tests/serialize-context.phpt +++ /dev/null @@ -1,13 +0,0 @@ ---TEST-- -Hash: Context serialization ---FILE-- -getMessage()}\n"; -} ---EXPECT-- -Exception: Serialization of 'HashContext' is not allowed diff --git a/ext/standard/md5.h b/ext/standard/md5.h index 15417990d7bf7..ac60d7fca4cda 100644 --- a/ext/standard/md5.h +++ b/ext/standard/md5.h @@ -40,6 +40,7 @@ typedef struct { unsigned char buffer[64]; uint32_t block[16]; } PHP_MD5_CTX; +#define PHP_MD5_SPEC "llllllb64l16." PHPAPI void PHP_MD5Init(PHP_MD5_CTX *ctx); PHPAPI void PHP_MD5Update(PHP_MD5_CTX *ctx, const void *data, size_t size); diff --git a/ext/standard/sha1.h b/ext/standard/sha1.h index c904969dd36fe..ef98ecc29cc89 100644 --- a/ext/standard/sha1.h +++ b/ext/standard/sha1.h @@ -25,6 +25,7 @@ typedef struct { uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } PHP_SHA1_CTX; +#define PHP_SHA1_SPEC "l5l2b64." PHPAPI void PHP_SHA1Init(PHP_SHA1_CTX *); PHPAPI void PHP_SHA1Update(PHP_SHA1_CTX *, const unsigned char *, size_t); From 417414b0345722c99da158b7bbe8e406659f28e2 Mon Sep 17 00:00:00 2001 From: Eddie Kohler Date: Fri, 26 Jun 2020 21:16:43 -0700 Subject: [PATCH 5/5] Add unserializehash fuzzer. Unlike the straight unserialize fuzzer, this runs only on HashContexts, and it does an update and finalize on the contexts it creates. With @nikic. --- sapi/fuzzer/Makefile.frag | 3 + sapi/fuzzer/README.md | 9 ++ sapi/fuzzer/config.m4 | 1 + sapi/fuzzer/fuzzer-unserializehash.c | 104 ++++++++++++++++++ .../generate_unserializehash_corpus.php | 10 ++ 5 files changed, 127 insertions(+) create mode 100644 sapi/fuzzer/fuzzer-unserializehash.c create mode 100644 sapi/fuzzer/generate_unserializehash_corpus.php diff --git a/sapi/fuzzer/Makefile.frag b/sapi/fuzzer/Makefile.frag index 57952a8ff4752..75a387d3d4ee0 100644 --- a/sapi/fuzzer/Makefile.frag +++ b/sapi/fuzzer/Makefile.frag @@ -8,6 +8,9 @@ $(SAPI_FUZZER_PATH)/php-fuzz-parser: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_F $(SAPI_FUZZER_PATH)/php-fuzz-unserialize: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_UNSERIALIZE_OBJS) $(FUZZER_BUILD) $(PHP_FUZZER_UNSERIALIZE_OBJS) -o $@ +$(SAPI_FUZZER_PATH)/php-fuzz-unserializehash: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_UNSERIALIZEHASH_OBJS) + $(FUZZER_BUILD) $(PHP_FUZZER_UNSERIALIZEHASH_OBJS) -o $@ + $(SAPI_FUZZER_PATH)/php-fuzz-json: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_JSON_OBJS) $(FUZZER_BUILD) $(PHP_FUZZER_JSON_OBJS) -o $@ diff --git a/sapi/fuzzer/README.md b/sapi/fuzzer/README.md index 0645a04bc7152..710a10f053a60 100644 --- a/sapi/fuzzer/README.md +++ b/sapi/fuzzer/README.md @@ -22,6 +22,7 @@ When running `make` it creates these binaries in `sapi/fuzzer/`: * `php-fuzz-parser`: Fuzzing language parser and compiler * `php-fuzz-unserialize`: Fuzzing unserialize() function +* `php-fuzz-unserializehash`: Fuzzing unserialize() for HashContext objects * `php-fuzz-json`: Fuzzing JSON parser (requires --enable-json) * `php-fuzz-exif`: Fuzzing `exif_read_data()` function (requires --enable-exif) * `php-fuzz-mbstring`: fuzzing `mb_ereg[i]()` (requires --enable-mbstring) @@ -41,6 +42,14 @@ cp -r sapi/fuzzer/corpus/unserialize ./my-unserialize-corpus sapi/fuzzer/php-fuzz-unserialize -dict=$PWD/sapi/fuzzer/dict/unserialize ./my-unserialize-corpus ``` +For the unserializehash fuzzer, generate a corpus of initial hash serializations: + +```sh +sapi/cli/php sapi/fuzzer/generate_unserializehash_corpus.php +cp -r sapi/fuzzer/corpus/unserializehash ./my-unserialize-corpus +sapi/fuzzer/php-fuzz-unserializehash ./my-unserialize-corpus +``` + For the parser fuzzer, a corpus may be generated from Zend test files: ```sh diff --git a/sapi/fuzzer/config.m4 b/sapi/fuzzer/config.m4 index 8e15a274ec1c5..a148985df9365 100644 --- a/sapi/fuzzer/config.m4 +++ b/sapi/fuzzer/config.m4 @@ -76,6 +76,7 @@ if test "$PHP_FUZZER" != "no"; then PHP_FUZZER_TARGET([parser], PHP_FUZZER_PARSER_OBJS) PHP_FUZZER_TARGET([unserialize], PHP_FUZZER_UNSERIALIZE_OBJS) + PHP_FUZZER_TARGET([unserializehash], PHP_FUZZER_UNSERIALIZEHASH_OBJS) PHP_FUZZER_TARGET([json], PHP_FUZZER_JSON_OBJS) if test -n "$enable_exif" && test "$enable_exif" != "no"; then diff --git a/sapi/fuzzer/fuzzer-unserializehash.c b/sapi/fuzzer/fuzzer-unserializehash.c new file mode 100644 index 0000000000000..9fbc10e6a89ad --- /dev/null +++ b/sapi/fuzzer/fuzzer-unserializehash.c @@ -0,0 +1,104 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + */ + + +#include "fuzzer.h" + +#include "Zend/zend.h" +#include "main/php_config.h" +#include "main/php_main.h" + +#include +#include +#include + +#include "fuzzer-sapi.h" + +#include "ext/standard/php_var.h" + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t FullSize) { + zend_execute_data execute_data; + zend_function func; + const uint8_t *Start = memchr(Data, '|', FullSize); + if (!Start) { + return 0; + } + ++Start; + + size_t Size = (Data + FullSize) - Start; + unsigned char *orig_data = malloc(Size+1); + memcpy(orig_data, Start, Size); + orig_data[Size] = '\0'; + + if (fuzzer_request_startup()==FAILURE) { + return 0; + } + + /* Set up a dummy stack frame so that exceptions may be thrown. */ + { + memset(&execute_data, 0, sizeof(zend_execute_data)); + memset(&func, 0, sizeof(zend_function)); + + func.type = ZEND_INTERNAL_FUNCTION; + func.common.function_name = ZSTR_EMPTY_ALLOC(); + execute_data.func = &func; + EG(current_execute_data) = &execute_data; + } + + { + const unsigned char *data = orig_data; + zval result; + ZVAL_UNDEF(&result); + + php_unserialize_data_t var_hash; + PHP_VAR_UNSERIALIZE_INIT(var_hash); + php_var_unserialize(&result, (const unsigned char **) &data, data + Size, &var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + + if (Z_TYPE(result) == IS_OBJECT + && zend_string_equals_literal(Z_OBJCE(result)->name, "HashContext")) { + zval args[2]; + ZVAL_COPY_VALUE(&args[0], &result); + ZVAL_STRINGL(&args[1], (char *) Data, (Start - Data) - 1); + fuzzer_call_php_func_zval("hash_update", 2, args); + zval_ptr_dtor(&args[1]); + fuzzer_call_php_func_zval("hash_final", 1, args); + } + + zval_ptr_dtor(&result); + + /* Destroy any thrown exception. */ + if (EG(exception)) { + zend_object_release(EG(exception)); + EG(exception) = NULL; + } + } + + /* Unserialize may create circular structure. Make sure we free them. + * Two calls are performed to handle objects with destructors. */ + zend_gc_collect_cycles(); + zend_gc_collect_cycles(); + php_request_shutdown(NULL); + + free(orig_data); + + return 0; +} + +int LLVMFuzzerInitialize(int *argc, char ***argv) { + fuzzer_init_php(); + + /* fuzzer_shutdown_php(); */ + return 0; +} diff --git a/sapi/fuzzer/generate_unserializehash_corpus.php b/sapi/fuzzer/generate_unserializehash_corpus.php new file mode 100644 index 0000000000000..04c6ea1428753 --- /dev/null +++ b/sapi/fuzzer/generate_unserializehash_corpus.php @@ -0,0 +1,10 @@ +