]> granicus.if.org Git - php/commitdiff
Protect against buffer overflow in xxhash unserialization
authorNikita Popov <nikita.ppv@gmail.com>
Fri, 15 Jan 2021 16:28:37 +0000 (17:28 +0100)
committerNikita Popov <nikita.ppv@gmail.com>
Fri, 15 Jan 2021 16:29:33 +0000 (17:29 +0100)
We need to make sure that memsize is < 32 bytes.

Fixes oss-fuzz #29538.

ext/hash/hash_xxhash.c
ext/hash/tests/xxhash_unserialize_memsize.phpt [new file with mode: 0644]

index 28d9d11af717072149a2896f8b50c7a90a77d1e8..3edcdfc9636bf08471e23d0f0dea5a33324cf0d7 100644 (file)
 #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 (file)
index 0000000..3675050
--- /dev/null
@@ -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)