Skip to content

Commit cebdad4

Browse files
committed
Protect against buffer overflow in xxhash unserialization
We need to make sure that memsize is < 32 bytes. Fixes oss-fuzz #29538.
1 parent 141c4be commit cebdad4

File tree

2 files changed

+62
-2
lines changed

2 files changed

+62
-2
lines changed

ext/hash/hash_xxhash.c

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,19 @@
1717
#include "php_hash.h"
1818
#include "php_hash_xxhash.h"
1919

20+
static int php_hash_xxh32_unserialize(
21+
php_hashcontext_object *hash, zend_long magic, const zval *zv);
22+
static int php_hash_xxh64_unserialize(
23+
php_hashcontext_object *hash, zend_long magic, const zval *zv);
24+
2025
const php_hash_ops php_hash_xxh32_ops = {
2126
"xxh32",
2227
(php_hash_init_func_t) PHP_XXH32Init,
2328
(php_hash_update_func_t) PHP_XXH32Update,
2429
(php_hash_final_func_t) PHP_XXH32Final,
2530
(php_hash_copy_func_t) PHP_XXH32Copy,
2631
php_hash_serialize,
27-
php_hash_unserialize,
32+
php_hash_xxh32_unserialize,
2833
PHP_XXH32_SPEC,
2934
4,
3035
4,
@@ -72,14 +77,28 @@ PHP_HASH_API int PHP_XXH32Copy(const php_hash_ops *ops, PHP_XXH32_CTX *orig_cont
7277
return SUCCESS;
7378
}
7479

80+
static int php_hash_xxh32_unserialize(
81+
php_hashcontext_object *hash, zend_long magic, const zval *zv)
82+
{
83+
PHP_XXH32_CTX *ctx = (PHP_XXH32_CTX *) hash->context;
84+
int r = FAILURE;
85+
if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
86+
&& (r = php_hash_unserialize_spec(hash, zv, PHP_XXH32_SPEC)) == SUCCESS
87+
&& ctx->s.memsize < 32) {
88+
return SUCCESS;
89+
} else {
90+
return r != SUCCESS ? r : -2000;
91+
}
92+
}
93+
7594
const php_hash_ops php_hash_xxh64_ops = {
7695
"xxh64",
7796
(php_hash_init_func_t) PHP_XXH64Init,
7897
(php_hash_update_func_t) PHP_XXH64Update,
7998
(php_hash_final_func_t) PHP_XXH64Final,
8099
(php_hash_copy_func_t) PHP_XXH64Copy,
81100
php_hash_serialize,
82-
php_hash_unserialize,
101+
php_hash_xxh64_unserialize,
83102
PHP_XXH64_SPEC,
84103
8,
85104
8,
@@ -188,6 +207,20 @@ PHP_HASH_API int PHP_XXH3_64_Copy(const php_hash_ops *ops, PHP_XXH3_64_CTX *orig
188207
return SUCCESS;
189208
}
190209

210+
static int php_hash_xxh64_unserialize(
211+
php_hashcontext_object *hash, zend_long magic, const zval *zv)
212+
{
213+
PHP_XXH64_CTX *ctx = (PHP_XXH64_CTX *) hash->context;
214+
int r = FAILURE;
215+
if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
216+
&& (r = php_hash_unserialize_spec(hash, zv, PHP_XXH64_SPEC)) == SUCCESS
217+
&& ctx->s.memsize < 32) {
218+
return SUCCESS;
219+
} else {
220+
return r != SUCCESS ? r : -2000;
221+
}
222+
}
223+
191224
const php_hash_ops php_hash_xxh3_128_ops = {
192225
"xxh128",
193226
(php_hash_init_func_t) PHP_XXH3_128_Init,
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
xxhash memsize must be in range when unserializing
3+
--FILE--
4+
<?php
5+
try {
6+
$str = <<<'STR'
7+
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:{}}
8+
STR;
9+
$hash = unserialize($str);
10+
hash_update($hash, '');
11+
} catch (Exception $e) {
12+
echo $e->getMessage(), "\n";
13+
}
14+
15+
try {
16+
$str = <<<'STR'
17+
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:{}}
18+
STR;
19+
$hash = unserialize($str);
20+
hash_update($hash, '');
21+
} catch (Exception $e) {
22+
echo $e->getMessage(), "\n";
23+
}
24+
?>
25+
--EXPECT--
26+
Incomplete or ill-formed serialization data ("xxh32" code -2000)
27+
Incomplete or ill-formed serialization data ("xxh64" code -2000)

0 commit comments

Comments
 (0)