Skip to content

Commit d94be24

Browse files
committed
Fix GH-16326: Memory management is broken for bad dictionaries
We must not `efree()` `zend_string`s, since they may have a refcount greater than one, and may even be interned. We also must not confuse `zend_string *` with `zend_string **`. And we should play it safe by using `safe_emalloc()` to avoid theoretical integer overflows. We also simplify a bit, according to suggestions of @TimWolla. Closes GH-16335.
1 parent b6ca871 commit d94be24

File tree

3 files changed

+43
-19
lines changed

3 files changed

+43
-19
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ PHP NEWS
4646
. Fixed bug GH-16292 (Segmentation fault in ext/xmlreader/php_xmlreader.c).
4747
(nielsdos)
4848

49+
- Zlib:
50+
. Fixed bug GH-16326 (Memory management is broken for bad dictionaries.)
51+
(cmb)
52+
4953
24 Oct 2024, PHP 8.2.25
5054

5155
- Calendar:

ext/zlib/tests/gh16326.phpt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
GH-16326 (Memory management is broken for bad dictionaries)
3+
--EXTENSIONS--
4+
zlib
5+
--FILE--
6+
<?php
7+
try {
8+
deflate_init(ZLIB_ENCODING_DEFLATE, ["dictionary" => [" ", ""]]);
9+
} catch (ValueError $ex) {
10+
echo $ex->getMessage(), "\n";
11+
}
12+
try {
13+
deflate_init(ZLIB_ENCODING_DEFLATE, ["dictionary" => ["hello", "wor\0ld"]]);
14+
} catch (ValueError $ex) {
15+
echo $ex->getMessage(), "\n";
16+
}
17+
try {
18+
deflate_init(ZLIB_ENCODING_DEFLATE, ["dictionary" => [" ", new stdClass]]);
19+
} catch (Error $ex) {
20+
echo $ex->getMessage(), "\n";
21+
}
22+
?>
23+
--EXPECT--
24+
deflate_init(): Argument #2 ($options) must not contain empty strings
25+
deflate_init(): Argument #2 ($options) must not contain strings with null bytes
26+
Object of class stdClass could not be converted to string

ext/zlib/zlib.c

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -807,35 +807,29 @@ static bool zlib_create_dictionary_string(HashTable *options, char **dict, size_
807807
if (zend_hash_num_elements(dictionary) > 0) {
808808
char *dictptr;
809809
zval *cur;
810-
zend_string **strings = emalloc(sizeof(zend_string *) * zend_hash_num_elements(dictionary));
810+
zend_string **strings = safe_emalloc(zend_hash_num_elements(dictionary), sizeof(zend_string *), 0);
811811
zend_string **end, **ptr = strings - 1;
812812

813813
ZEND_HASH_FOREACH_VAL(dictionary, cur) {
814-
size_t i;
815-
816814
*++ptr = zval_get_string(cur);
817-
if (!*ptr || ZSTR_LEN(*ptr) == 0 || EG(exception)) {
818-
if (*ptr) {
819-
efree(*ptr);
820-
}
821-
while (--ptr >= strings) {
822-
efree(ptr);
823-
}
815+
ZEND_ASSERT(*ptr);
816+
if (ZSTR_LEN(*ptr) == 0 || EG(exception)) {
817+
do {
818+
zend_string_release(*ptr);
819+
} while (--ptr >= strings);
824820
efree(strings);
825821
if (!EG(exception)) {
826822
zend_argument_value_error(2, "must not contain empty strings");
827823
}
828824
return 0;
829825
}
830-
for (i = 0; i < ZSTR_LEN(*ptr); i++) {
831-
if (ZSTR_VAL(*ptr)[i] == 0) {
832-
do {
833-
efree(ptr);
834-
} while (--ptr >= strings);
835-
efree(strings);
836-
zend_argument_value_error(2, "must not contain strings with null bytes");
837-
return 0;
838-
}
826+
if (zend_str_has_nul_byte(*ptr)) {
827+
do {
828+
zend_string_release(*ptr);
829+
} while (--ptr >= strings);
830+
efree(strings);
831+
zend_argument_value_error(2, "must not contain strings with null bytes");
832+
return 0;
839833
}
840834

841835
*dictlen += ZSTR_LEN(*ptr) + 1;

0 commit comments

Comments
 (0)