diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2021-01-15 17:28:37 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2021-01-15 17:29:33 +0100 |
commit | cebdad4b533c350ee59e9437a85ed65096a128f0 (patch) | |
tree | 6f030e0c3290e60745fd15393810bf9227287a6a | |
parent | 141c4be70a97ea9dd99b3a80543098c677d12c1e (diff) | |
download | php-git-cebdad4b533c350ee59e9437a85ed65096a128f0.tar.gz |
Protect against buffer overflow in xxhash unserialization
We need to make sure that memsize is < 32 bytes.
Fixes oss-fuzz #29538.
-rw-r--r-- | ext/hash/hash_xxhash.c | 37 | ||||
-rw-r--r-- | ext/hash/tests/xxhash_unserialize_memsize.phpt | 27 |
2 files changed, 62 insertions, 2 deletions
diff --git a/ext/hash/hash_xxhash.c b/ext/hash/hash_xxhash.c index 28d9d11af7..3edcdfc963 100644 --- a/ext/hash/hash_xxhash.c +++ b/ext/hash/hash_xxhash.c @@ -17,6 +17,11 @@ #include "php_hash.h" #include "php_hash_xxhash.h" +static int php_hash_xxh32_unserialize( + php_hashcontext_object *hash, zend_long magic, const zval *zv); +static int php_hash_xxh64_unserialize( + php_hashcontext_object *hash, zend_long magic, const zval *zv); + const php_hash_ops php_hash_xxh32_ops = { "xxh32", (php_hash_init_func_t) PHP_XXH32Init, @@ -24,7 +29,7 @@ const php_hash_ops php_hash_xxh32_ops = { (php_hash_final_func_t) PHP_XXH32Final, (php_hash_copy_func_t) PHP_XXH32Copy, php_hash_serialize, - php_hash_unserialize, + php_hash_xxh32_unserialize, PHP_XXH32_SPEC, 4, 4, @@ -72,6 +77,20 @@ PHP_HASH_API int PHP_XXH32Copy(const php_hash_ops *ops, PHP_XXH32_CTX *orig_cont return SUCCESS; } +static int php_hash_xxh32_unserialize( + php_hashcontext_object *hash, zend_long magic, const zval *zv) +{ + PHP_XXH32_CTX *ctx = (PHP_XXH32_CTX *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC + && (r = php_hash_unserialize_spec(hash, zv, PHP_XXH32_SPEC)) == SUCCESS + && ctx->s.memsize < 32) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} + const php_hash_ops php_hash_xxh64_ops = { "xxh64", (php_hash_init_func_t) PHP_XXH64Init, @@ -79,7 +98,7 @@ const php_hash_ops php_hash_xxh64_ops = { (php_hash_final_func_t) PHP_XXH64Final, (php_hash_copy_func_t) PHP_XXH64Copy, php_hash_serialize, - php_hash_unserialize, + php_hash_xxh64_unserialize, PHP_XXH64_SPEC, 8, 8, @@ -188,6 +207,20 @@ PHP_HASH_API int PHP_XXH3_64_Copy(const php_hash_ops *ops, PHP_XXH3_64_CTX *orig return SUCCESS; } +static int php_hash_xxh64_unserialize( + php_hashcontext_object *hash, zend_long magic, const zval *zv) +{ + PHP_XXH64_CTX *ctx = (PHP_XXH64_CTX *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC + && (r = php_hash_unserialize_spec(hash, zv, PHP_XXH64_SPEC)) == SUCCESS + && ctx->s.memsize < 32) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} + const php_hash_ops php_hash_xxh3_128_ops = { "xxh128", (php_hash_init_func_t) PHP_XXH3_128_Init, diff --git a/ext/hash/tests/xxhash_unserialize_memsize.phpt b/ext/hash/tests/xxhash_unserialize_memsize.phpt new file mode 100644 index 0000000000..36750505c9 --- /dev/null +++ b/ext/hash/tests/xxhash_unserialize_memsize.phpt @@ -0,0 +1,27 @@ +--TEST-- +xxhash memsize must be in range when unserializing +--FILE-- +<?php +try { + $str = <<<'STR' + O:11:"HashContext":5:{i:0;s:5:"xxh32";i:1;i:0;i:2;a:12:{i:0;i:0;i:1;i:0;i:2;i:606290984;i:3;i:-2048144777;i:4;i:0;i:5;i:1640531535;i:6;i:0;i:7;i:0;i:8;i:0;i:9;i:0;i:10;i:80;i:11;i:0;}i:3;i:2;i:4;a:0:{}} + STR; + $hash = unserialize($str); + hash_update($hash, ''); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} + +try { + $str = <<<'STR' + O:11:"HashContext":5:{i:0;s:5:"xxh64";i:1;i:0;i:2;a:22:{i:0;i:0;i:1;i:0;i:2;i:6;i:3;i:2;i:4;i:8;i:5;i:9;i:6;i:0;i:7;i:0;i:8;i:1;i:9;i:5;i:10;i:0;i:11;i:0;i:12;i:0;i:13;i:0;i:14;i:0;i:15;i:0;i:16;i:0;i:17;i:0;i:18;i:70;i:19;i:0;i:20;i:0;i:21;i:0;}i:3;i:2;i:4;a:0:{}} + STR; + $hash = unserialize($str); + hash_update($hash, ''); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Incomplete or ill-formed serialization data ("xxh32" code -2000) +Incomplete or ill-formed serialization data ("xxh64" code -2000) |