Skip to content

Commit df434f0

Browse files
committed
Fix crash after indirect modification of string by user error handler
Fixes oss-fuzz #39346
1 parent c103619 commit df434f0

File tree

2 files changed

+36
-3
lines changed

2 files changed

+36
-3
lines changed

Zend/tests/str_offset_005.phpt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
string offset 005 indirect string modification by error handler
3+
--FILE--
4+
<?php
5+
set_error_handler(function(){$GLOBALS['a']=8;});
6+
$a='a';
7+
var_dump($a[$b]);
8+
var_dump($a);
9+
?>
10+
--EXPECT--
11+
string(1) "a"
12+
int(8)

Zend/zend_execute.c

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2361,6 +2361,7 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
23612361
}
23622362
}
23632363
if (!is_list && EXPECTED(Z_TYPE_P(container) == IS_STRING)) {
2364+
zend_string *str = Z_STR_P(container);
23642365
zend_long offset;
23652366

23662367
try_string_offset:
@@ -2386,13 +2387,33 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
23862387
return;
23872388
}
23882389
case IS_UNDEF:
2390+
/* The string may be destroyed while throwing the notice.
2391+
* Temporarily increase the refcount to detect this situation. */
2392+
if (!(GC_FLAGS(str) & IS_ARRAY_IMMUTABLE)) {
2393+
GC_ADDREF(str);
2394+
}
23892395
ZVAL_UNDEFINED_OP2();
2396+
if (!(GC_FLAGS(str) & IS_ARRAY_IMMUTABLE) && GC_DELREF(str) == 0) {
2397+
zend_string_release_ex(str, 0);
2398+
ZVAL_NULL(result);
2399+
return;
2400+
}
23902401
case IS_DOUBLE:
23912402
case IS_NULL:
23922403
case IS_FALSE:
23932404
case IS_TRUE:
23942405
if (type != BP_VAR_IS) {
2406+
/* The string may be destroyed while throwing the notice.
2407+
* Temporarily increase the refcount to detect this situation. */
2408+
if (!(GC_FLAGS(str) & IS_ARRAY_IMMUTABLE)) {
2409+
GC_ADDREF(str);
2410+
}
23952411
zend_error(E_WARNING, "String offset cast occurred");
2412+
if (!(GC_FLAGS(str) & IS_ARRAY_IMMUTABLE) && GC_DELREF(str) == 0) {
2413+
zend_string_release_ex(str, 0);
2414+
ZVAL_NULL(result);
2415+
return;
2416+
}
23962417
}
23972418
break;
23982419
case IS_REFERENCE:
@@ -2410,7 +2431,7 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
24102431
}
24112432
out:
24122433

2413-
if (UNEXPECTED(Z_STRLEN_P(container) < ((offset < 0) ? -(size_t)offset : ((size_t)offset + 1)))) {
2434+
if (UNEXPECTED(ZSTR_LEN(str) < ((offset < 0) ? -(size_t)offset : ((size_t)offset + 1)))) {
24142435
if (type != BP_VAR_IS) {
24152436
zend_error(E_WARNING, "Uninitialized string offset " ZEND_LONG_FMT, offset);
24162437
ZVAL_EMPTY_STRING(result);
@@ -2422,8 +2443,8 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
24222443
zend_long real_offset;
24232444

24242445
real_offset = (UNEXPECTED(offset < 0)) /* Handle negative offset */
2425-
? (zend_long)Z_STRLEN_P(container) + offset : offset;
2426-
c = (zend_uchar)Z_STRVAL_P(container)[real_offset];
2446+
? (zend_long)ZSTR_LEN(str) + offset : offset;
2447+
c = (zend_uchar)ZSTR_VAL(str)[real_offset];
24272448

24282449
ZVAL_CHAR(result, c);
24292450
}

0 commit comments

Comments
 (0)