Skip to content

Commit 6fb6188

Browse files
MaxKellermannshivammathur
authored andcommitted
Replace memcmp() with zend_string functions (php#8216)
* ext/oci8: use zend_string_equals() Eliminate duplicate code. * main/php_variables: use zend_string_equals_literal() Eliminate duplicate code. * Zend/zend_string: add zend_string_equals_cstr() Allows eliminating duplicate code. * Zend, ext/{opcache,standard}, main/output: use zend_string_equals_cstr() Eliminate duplicate code. * Zend/zend_string: add zend_string_starts_with() * ext/{opcache,phar,spl,standard}: use zend_string_starts_with() This adds missing length checks to several callers, e.g. in cache_script_in_shared_memory(). This is important when the zend_string is shorter than the string parameter, when memcmp() happens to check backwards; this can result in an out-of-bounds memory access.
1 parent 742b7d8 commit 6fb6188

File tree

15 files changed

+46
-48
lines changed

15 files changed

+46
-48
lines changed

Zend/zend_attributes.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,8 @@ static zend_attribute *get_attribute_str(HashTable *attributes, const char *str,
9494
zend_attribute *attr;
9595

9696
ZEND_HASH_FOREACH_PTR(attributes, attr) {
97-
if (attr->offset == offset && ZSTR_LEN(attr->lcname) == len) {
98-
if (0 == memcmp(ZSTR_VAL(attr->lcname), str, len)) {
99-
return attr;
100-
}
97+
if (attr->offset == offset && zend_string_equals_cstr(attr->lcname, str, len)) {
98+
return attr;
10199
}
102100
} ZEND_HASH_FOREACH_END();
103101
}

Zend/zend_compile.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,7 @@ static zend_always_inline bool zend_is_confusable_type(const zend_string *name,
266266
/* Intentionally using case-sensitive comparison here, because "integer" is likely intended
267267
* as a scalar type, while "Integer" is likely a class type. */
268268
for (; info->name; ++info) {
269-
if (ZSTR_LEN(name) == info->name_len
270-
&& memcmp(ZSTR_VAL(name), info->name, info->name_len) == 0
271-
) {
269+
if (zend_string_equals_cstr(name, info->name, info->name_len)) {
272270
*correct_name = info->correct_name;
273271
return 1;
274272
}
@@ -3487,7 +3485,7 @@ static uint32_t zend_get_arg_num(zend_function *fn, zend_string *arg_name) {
34873485
for (uint32_t i = 0; i < fn->common.num_args; i++) {
34883486
zend_internal_arg_info *arg_info = &fn->internal_function.arg_info[i];
34893487
size_t len = strlen(arg_info->name);
3490-
if (len == ZSTR_LEN(arg_name) && !memcmp(arg_info->name, ZSTR_VAL(arg_name), len)) {
3488+
if (zend_string_equals_cstr(arg_name, arg_info->name, len)) {
34913489
return i + 1;
34923490
}
34933491
}

Zend/zend_execute.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4969,7 +4969,7 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name(
49694969
for (uint32_t i = 0; i < num_args; i++) {
49704970
zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[i];
49714971
size_t len = strlen(arg_info->name);
4972-
if (len == ZSTR_LEN(arg_name) && !memcmp(arg_info->name, ZSTR_VAL(arg_name), len)) {
4972+
if (zend_string_equals_cstr(arg_name, arg_info->name, len)) {
49734973
*cache_slot = fbc;
49744974
*(uintptr_t *)(cache_slot + 1) = i;
49754975
return i;

Zend/zend_execute_API.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1840,8 +1840,7 @@ ZEND_API zend_result zend_set_local_var_str(const char *name, size_t len, zval *
18401840

18411841
do {
18421842
if (ZSTR_H(*str) == h &&
1843-
ZSTR_LEN(*str) == len &&
1844-
memcmp(ZSTR_VAL(*str), name, len) == 0) {
1843+
zend_string_equals_cstr(*str, name, len)) {
18451844
zval *var = EX_VAR_NUM(str - op_array->vars);
18461845
zval_ptr_dtor(var);
18471846
ZVAL_COPY_VALUE(var, value);

Zend/zend_hash.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -704,8 +704,7 @@ static zend_always_inline Bucket *zend_hash_str_find_bucket(const HashTable *ht,
704704
p = HT_HASH_TO_BUCKET_EX(arData, idx);
705705
if ((p->h == h)
706706
&& p->key
707-
&& (ZSTR_LEN(p->key) == len)
708-
&& !memcmp(ZSTR_VAL(p->key), str, len)) {
707+
&& zend_string_equals_cstr(p->key, str, len)) {
709708
return p;
710709
}
711710
idx = Z_NEXT(p->val);
@@ -1500,8 +1499,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_hash_str_del_ind(HashTable *ht, const ch
15001499
p = HT_HASH_TO_BUCKET(ht, idx);
15011500
if ((p->h == h)
15021501
&& p->key
1503-
&& (ZSTR_LEN(p->key) == len)
1504-
&& !memcmp(ZSTR_VAL(p->key), str, len)) {
1502+
&& zend_string_equals_cstr(p->key, str, len)) {
15051503
if (Z_TYPE(p->val) == IS_INDIRECT) {
15061504
zval *data = Z_INDIRECT(p->val);
15071505

@@ -1544,8 +1542,9 @@ ZEND_API zend_result ZEND_FASTCALL zend_hash_str_del(HashTable *ht, const char *
15441542
p = HT_HASH_TO_BUCKET(ht, idx);
15451543
if ((p->h == h)
15461544
&& p->key
1547-
&& (ZSTR_LEN(p->key) == len)
1548-
&& !memcmp(ZSTR_VAL(p->key), str, len)) {
1545+
&& zend_string_equals_cstr(p->key, str, len)) {
1546+
zend_string_release(p->key);
1547+
p->key = NULL;
15491548
_zend_hash_del_el_ex(ht, idx, p, prev);
15501549
return SUCCESS;
15511550
}

Zend/zend_string.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,8 @@ static zend_always_inline zend_string *zend_interned_string_ht_lookup_ex(zend_ul
135135
idx = HT_HASH(interned_strings, nIndex);
136136
while (idx != HT_INVALID_IDX) {
137137
p = HT_HASH_TO_BUCKET(interned_strings, idx);
138-
if ((p->h == h) && (ZSTR_LEN(p->key) == size)) {
139-
if (!memcmp(ZSTR_VAL(p->key), str, size)) {
140-
return p->key;
141-
}
138+
if ((p->h == h) && zend_string_equals_cstr(p->key, str, size)) {
139+
return p->key;
142140
}
143141
idx = Z_NEXT(p->val);
144142
}

Zend/zend_string.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,11 @@ static zend_always_inline void zend_string_release_ex(zend_string *s, bool persi
341341
}
342342
}
343343

344+
static zend_always_inline bool zend_string_equals_cstr(const zend_string *s1, const char *s2, size_t s2_length)
345+
{
346+
return ZSTR_LEN(s1) == s2_length && !memcmp(ZSTR_VAL(s1), s2, s2_length);
347+
}
348+
344349
#if defined(__GNUC__) && (defined(__i386__) || (defined(__x86_64__) && !defined(__ILP32__)))
345350
BEGIN_EXTERN_C()
346351
ZEND_API bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2);
@@ -369,7 +374,20 @@ static zend_always_inline bool zend_string_equals(zend_string *s1, zend_string *
369374
(ZSTR_LEN(str) == sizeof(c) - 1 && !zend_binary_strcasecmp(ZSTR_VAL(str), ZSTR_LEN(str), (c), sizeof(c) - 1))
370375

371376
#define zend_string_equals_literal(str, literal) \
372-
(ZSTR_LEN(str) == sizeof(literal)-1 && !memcmp(ZSTR_VAL(str), literal, sizeof(literal) - 1))
377+
zend_string_equals_cstr(str, literal, strlen(literal))
378+
379+
static zend_always_inline bool zend_string_starts_with_cstr(const zend_string *str, const char *prefix, size_t prefix_length)
380+
{
381+
return ZSTR_LEN(str) >= prefix_length && !memcmp(ZSTR_VAL(str), prefix, prefix_length);
382+
}
383+
384+
static zend_always_inline bool zend_string_starts_with(const zend_string *str, const zend_string *prefix)
385+
{
386+
return zend_string_starts_with_cstr(str, ZSTR_VAL(prefix), ZSTR_LEN(prefix));
387+
}
388+
389+
#define zend_string_starts_with_literal(str, prefix) \
390+
zend_string_starts_with_cstr(str, prefix, strlen(prefix))
373391

374392
/*
375393
* DJBX33A (Daniel J. Bernstein, Times 33 with Addition)

ext/oci8/oci8.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,9 +1123,7 @@ php_oci_connection *php_oci_do_connect_ex(char *username, int username_len, char
11231123
}
11241124

11251125
if ((tmp_val != NULL) && (tmp != NULL) &&
1126-
(ZSTR_LEN(tmp->hash_key) == ZSTR_LEN(hashed_details.s)) &&
1127-
(memcmp(ZSTR_VAL(tmp->hash_key), ZSTR_VAL(hashed_details.s),
1128-
ZSTR_LEN(tmp->hash_key)) == 0)) {
1126+
zend_string_equals(tmp->hash_key, hashed_details.s)) {
11291127
connection = tmp;
11301128
GC_ADDREF(connection->id);
11311129
}
@@ -2176,8 +2174,7 @@ static php_oci_spool *php_oci_get_spool(char *username, int username_len, char *
21762174
}
21772175
zend_register_persistent_resource_ex(session_pool->spool_hash_key, session_pool, le_psessionpool);
21782176
} else if (spool_out_le->type == le_psessionpool &&
2179-
ZSTR_LEN(((php_oci_spool *)(spool_out_le->ptr))->spool_hash_key) == ZSTR_LEN(spool_hashed_details.s) &&
2180-
memcmp(ZSTR_VAL(((php_oci_spool *)(spool_out_le->ptr))->spool_hash_key), ZSTR_VAL(spool_hashed_details.s), ZSTR_LEN(spool_hashed_details.s)) == 0) {
2177+
zend_string_equals(((php_oci_spool *)(spool_out_le->ptr))->spool_hash_key, spool_hashed_details.s)) {
21812178
/* retrieve the cached session pool */
21822179
session_pool = (php_oci_spool *)(spool_out_le->ptr);
21832180
}

ext/opcache/ZendAccelerator.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -560,10 +560,8 @@ static zend_always_inline zend_string *accel_find_interned_string_ex(zend_ulong
560560
if (EXPECTED(pos != STRTAB_INVALID_POS)) {
561561
do {
562562
s = STRTAB_POS_TO_STR(&ZCSG(interned_strings), pos);
563-
if (EXPECTED(ZSTR_H(s) == h) && EXPECTED(ZSTR_LEN(s) == size)) {
564-
if (!memcmp(ZSTR_VAL(s), str, size)) {
565-
return s;
566-
}
563+
if (EXPECTED(ZSTR_H(s) == h) && zend_string_equals_cstr(s, str, size)) {
564+
return s;
567565
}
568566
pos = STRTAB_COLLISION(s);
569567
} while (pos != STRTAB_INVALID_POS);
@@ -1637,7 +1635,7 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr
16371635
zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", ZSTR_VAL(new_persistent_script->script.filename));
16381636
if (key &&
16391637
/* key may contain non-persistent PHAR aliases (see issues #115 and #149) */
1640-
memcmp(ZSTR_VAL(key), "phar://", sizeof("phar://") - 1) != 0 &&
1638+
!zend_string_starts_with_literal(key, "phar://") &&
16411639
!zend_string_equals(new_persistent_script->script.filename, key)) {
16421640
/* link key to the same persistent script in hash table */
16431641
zend_string *new_key = accel_new_interned_key(key);

ext/phar/stream.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -912,8 +912,7 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from
912912

913913
ZEND_HASH_FOREACH_BUCKET(&phar->virtual_dirs, b) {
914914
str_key = b->key;
915-
if (ZSTR_LEN(str_key) >= from_len &&
916-
memcmp(ZSTR_VAL(str_key), ZSTR_VAL(resource_from->path)+1, from_len) == 0 &&
915+
if (zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource_from->path)+1, from_len) &&
917916
(ZSTR_LEN(str_key) == from_len || IS_SLASH(ZSTR_VAL(str_key)[from_len]))) {
918917

919918
new_str_key = zend_string_alloc(ZSTR_LEN(str_key) + to_len - from_len, 0);
@@ -930,8 +929,7 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from
930929

931930
ZEND_HASH_FOREACH_BUCKET(&phar->mounted_dirs, b) {
932931
str_key = b->key;
933-
if (ZSTR_LEN(str_key) >= from_len &&
934-
memcmp(ZSTR_VAL(str_key), ZSTR_VAL(resource_from->path)+1, from_len) == 0 &&
932+
if (zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource_from->path)+1, from_len) &&
935933
(ZSTR_LEN(str_key) == from_len || IS_SLASH(ZSTR_VAL(str_key)[from_len]))) {
936934

937935
new_str_key = zend_string_alloc(ZSTR_LEN(str_key) + to_len - from_len, 0);

0 commit comments

Comments
 (0)