Skip to content

Commit 4d974b1

Browse files
committed
ext/hash: Fix GH-16711: Segfault in mhash()
1 parent 5253647 commit 4d974b1

File tree

2 files changed

+153
-54
lines changed

2 files changed

+153
-54
lines changed

ext/hash/hash.c

Lines changed: 55 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ static struct mhash_bc_entry mhash_to_hash[MHASH_NUM_ALGOS] = {
9999
{"XXH3", "xxh3", 40},
100100
{"XXH128", "xxh128", 41},
101101
};
102+
static bool php_is_valid_mhash_algo(zend_long hash_algo) {
103+
return hash_algo >= 0 && hash_algo < MHASH_NUM_ALGOS && hash_algo != 4 && hash_algo != 6 && hash_algo != 26;
104+
}
105+
102106
#endif
103107

104108
/* Hash Registry Access */
@@ -1184,10 +1188,11 @@ static void mhash_init(INIT_FUNC_ARGS)
11841188
int algo_number = 0;
11851189

11861190
for (algo_number = 0; algo_number < MHASH_NUM_ALGOS; algo_number++) {
1187-
struct mhash_bc_entry algorithm = mhash_to_hash[algo_number];
1188-
if (algorithm.mhash_name == NULL) {
1191+
if (!php_is_valid_mhash_algo(algo_number)) {
11891192
continue;
11901193
}
1194+
struct mhash_bc_entry algorithm = mhash_to_hash[algo_number];
1195+
ZEND_ASSERT(algorithm.mhash_name != NULL);
11911196

11921197
len = slprintf(buf, 127, "MHASH_%s", algorithm.mhash_name);
11931198
zend_register_long_constant(buf, len, algorithm.value, CONST_CS | CONST_PERSISTENT, module_number);
@@ -1209,11 +1214,12 @@ PHP_FUNCTION(mhash)
12091214
}
12101215

12111216
/* need to convert the first parameter from int constant to string algorithm name */
1212-
if (algorithm >= 0 && algorithm < MHASH_NUM_ALGOS) {
1217+
if (php_is_valid_mhash_algo(algorithm)) {
12131218
struct mhash_bc_entry algorithm_lookup = mhash_to_hash[algorithm];
1214-
if (algorithm_lookup.hash_name) {
1215-
algo = zend_string_init(algorithm_lookup.hash_name, strlen(algorithm_lookup.hash_name), 0);
1216-
}
1219+
ZEND_ASSERT(algorithm_lookup.hash_name != NULL);
1220+
algo = zend_string_init(algorithm_lookup.hash_name, strlen(algorithm_lookup.hash_name), 0);
1221+
} else {
1222+
RETURN_FALSE;
12171223
}
12181224

12191225
if (key) {
@@ -1222,9 +1228,8 @@ PHP_FUNCTION(mhash)
12221228
php_hash_do_hash(return_value, algo, data, data_len, 1, 0, NULL);
12231229
}
12241230

1225-
if (algo) {
1226-
zend_string_release(algo);
1227-
}
1231+
ZEND_ASSERT(algo != NULL);
1232+
zend_string_release(algo);
12281233
}
12291234
/* }}} */
12301235

@@ -1237,11 +1242,10 @@ PHP_FUNCTION(mhash_get_hash_name)
12371242
RETURN_THROWS();
12381243
}
12391244

1240-
if (algorithm >= 0 && algorithm < MHASH_NUM_ALGOS) {
1245+
if (php_is_valid_mhash_algo(algorithm)) {
12411246
struct mhash_bc_entry algorithm_lookup = mhash_to_hash[algorithm];
1242-
if (algorithm_lookup.mhash_name) {
1243-
RETURN_STRING(algorithm_lookup.mhash_name);
1244-
}
1247+
ZEND_ASSERT(algorithm_lookup.mhash_name != NULL);
1248+
RETURN_STRING(algorithm_lookup.mhash_name);
12451249
}
12461250
RETURN_FALSE;
12471251
}
@@ -1267,14 +1271,12 @@ PHP_FUNCTION(mhash_get_block_size)
12671271
}
12681272
RETVAL_FALSE;
12691273

1270-
if (algorithm >= 0 && algorithm < MHASH_NUM_ALGOS) {
1274+
if (php_is_valid_mhash_algo(algorithm)) {
12711275
struct mhash_bc_entry algorithm_lookup = mhash_to_hash[algorithm];
1272-
if (algorithm_lookup.mhash_name) {
1273-
const php_hash_ops *ops = zend_hash_str_find_ptr(&php_hash_hashtable, algorithm_lookup.hash_name, strlen(algorithm_lookup.hash_name));
1274-
if (ops) {
1275-
RETVAL_LONG(ops->digest_size);
1276-
}
1277-
}
1276+
ZEND_ASSERT(algorithm_lookup.hash_name != NULL);
1277+
const php_hash_ops *ops = zend_hash_str_find_ptr(&php_hash_hashtable, algorithm_lookup.hash_name, strlen(algorithm_lookup.hash_name));
1278+
ZEND_ASSERT(ops != NULL);
1279+
RETVAL_LONG(ops->digest_size);
12781280
}
12791281
}
12801282
/* }}} */
@@ -1309,47 +1311,46 @@ PHP_FUNCTION(mhash_keygen_s2k)
13091311
salt_len = SALT_SIZE;
13101312

13111313
RETVAL_FALSE;
1312-
if (algorithm >= 0 && algorithm < MHASH_NUM_ALGOS) {
1314+
if (php_is_valid_mhash_algo(algorithm)) {
13131315
struct mhash_bc_entry algorithm_lookup = mhash_to_hash[algorithm];
1314-
if (algorithm_lookup.mhash_name) {
1315-
const php_hash_ops *ops = zend_hash_str_find_ptr(&php_hash_hashtable, algorithm_lookup.hash_name, strlen(algorithm_lookup.hash_name));
1316-
if (ops) {
1317-
unsigned char null = '\0';
1318-
void *context;
1319-
char *key, *digest;
1320-
int i = 0, j = 0;
1321-
size_t block_size = ops->digest_size;
1322-
size_t times = bytes / block_size;
1323-
1324-
if ((bytes % block_size) != 0) {
1325-
times++;
1326-
}
1327-
1328-
context = php_hash_alloc_context(ops);
1329-
ops->hash_init(context, NULL);
1316+
ZEND_ASSERT(algorithm_lookup.hash_name != NULL);
1317+
const php_hash_ops *ops = zend_hash_str_find_ptr(&php_hash_hashtable, algorithm_lookup.hash_name, strlen(algorithm_lookup.hash_name));
1318+
ZEND_ASSERT(ops != NULL);
1319+
1320+
unsigned char null = '\0';
1321+
void *context;
1322+
char *key, *digest;
1323+
int i = 0, j = 0;
1324+
size_t block_size = ops->digest_size;
1325+
size_t times = bytes / block_size;
1326+
1327+
if ((bytes % block_size) != 0) {
1328+
times++;
1329+
}
13301330

1331-
key = ecalloc(1, times * block_size);
1332-
digest = emalloc(ops->digest_size + 1);
1331+
context = php_hash_alloc_context(ops);
1332+
ops->hash_init(context, NULL);
13331333

1334-
for (i = 0; i < times; i++) {
1335-
ops->hash_init(context, NULL);
1334+
key = ecalloc(1, times * block_size);
1335+
digest = emalloc(ops->digest_size + 1);
13361336

1337-
for (j=0;j<i;j++) {
1338-
ops->hash_update(context, &null, 1);
1339-
}
1340-
ops->hash_update(context, (unsigned char *)padded_salt, salt_len);
1341-
ops->hash_update(context, (unsigned char *)password, password_len);
1342-
ops->hash_final((unsigned char *)digest, context);
1343-
memcpy( &key[i*block_size], digest, block_size);
1344-
}
1337+
for (i = 0; i < times; i++) {
1338+
ops->hash_init(context, NULL);
13451339

1346-
RETVAL_STRINGL(key, bytes);
1347-
ZEND_SECURE_ZERO(key, bytes);
1348-
efree(digest);
1349-
efree(context);
1350-
efree(key);
1340+
for (j=0;j<i;j++) {
1341+
ops->hash_update(context, &null, 1);
13511342
}
1343+
ops->hash_update(context, (unsigned char *)padded_salt, salt_len);
1344+
ops->hash_update(context, (unsigned char *)password, password_len);
1345+
ops->hash_final((unsigned char *)digest, context);
1346+
memcpy( &key[i*block_size], digest, block_size);
13521347
}
1348+
1349+
RETVAL_STRINGL(key, bytes);
1350+
ZEND_SECURE_ZERO(key, bytes);
1351+
efree(digest);
1352+
efree(context);
1353+
efree(key);
13531354
}
13541355
}
13551356
/* }}} */

ext/hash/tests/gh16711.phpt

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
--TEST--
2+
GH-16711: Segmentation fault in mhash()
3+
--SKIPIF--
4+
<?php if(!function_exists('mhash')) { die('skip mhash compatibility layer not available'); } ?>
5+
--FILE--
6+
<?php
7+
8+
$re = new ReflectionExtension("hash");
9+
var_dump($re->getConstants());
10+
11+
var_dump(mhash(133, 1086849124, 133));
12+
?>
13+
--EXPECTF--
14+
array(40) {
15+
["HASH_HMAC"]=>
16+
int(1)
17+
["MHASH_CRC32"]=>
18+
int(0)
19+
["MHASH_MD5"]=>
20+
int(1)
21+
["MHASH_SHA1"]=>
22+
int(2)
23+
["MHASH_HAVAL256"]=>
24+
int(3)
25+
["MHASH_RIPEMD160"]=>
26+
int(5)
27+
["MHASH_TIGER"]=>
28+
int(7)
29+
["MHASH_GOST"]=>
30+
int(8)
31+
["MHASH_CRC32B"]=>
32+
int(9)
33+
["MHASH_HAVAL224"]=>
34+
int(10)
35+
["MHASH_HAVAL192"]=>
36+
int(11)
37+
["MHASH_HAVAL160"]=>
38+
int(12)
39+
["MHASH_HAVAL128"]=>
40+
int(13)
41+
["MHASH_TIGER128"]=>
42+
int(14)
43+
["MHASH_TIGER160"]=>
44+
int(15)
45+
["MHASH_MD4"]=>
46+
int(16)
47+
["MHASH_SHA256"]=>
48+
int(17)
49+
["MHASH_ADLER32"]=>
50+
int(18)
51+
["MHASH_SHA224"]=>
52+
int(19)
53+
["MHASH_SHA512"]=>
54+
int(20)
55+
["MHASH_SHA384"]=>
56+
int(21)
57+
["MHASH_WHIRLPOOL"]=>
58+
int(22)
59+
["MHASH_RIPEMD128"]=>
60+
int(23)
61+
["MHASH_RIPEMD256"]=>
62+
int(24)
63+
["MHASH_RIPEMD320"]=>
64+
int(25)
65+
["MHASH_SNEFRU256"]=>
66+
int(27)
67+
["MHASH_MD2"]=>
68+
int(28)
69+
["MHASH_FNV132"]=>
70+
int(29)
71+
["MHASH_FNV1A32"]=>
72+
int(30)
73+
["MHASH_FNV164"]=>
74+
int(31)
75+
["MHASH_FNV1A64"]=>
76+
int(32)
77+
["MHASH_JOAAT"]=>
78+
int(33)
79+
["MHASH_CRC32C"]=>
80+
int(34)
81+
["MHASH_MURMUR3A"]=>
82+
int(35)
83+
["MHASH_MURMUR3C"]=>
84+
int(36)
85+
["MHASH_MURMUR3F"]=>
86+
int(37)
87+
["MHASH_XXH32"]=>
88+
int(38)
89+
["MHASH_XXH64"]=>
90+
int(39)
91+
["MHASH_XXH3"]=>
92+
int(40)
93+
["MHASH_XXH128"]=>
94+
int(41)
95+
}
96+
97+
Deprecated: Function mhash() is deprecated since 8.1 in %s on line %d
98+
bool(false)

0 commit comments

Comments
 (0)