Skip to content

Commit 1bdd095

Browse files
committed
Saner numeric string handling for string offsets
1 parent 802e5ef commit 1bdd095

18 files changed

+143
-84
lines changed

Zend/tests/bug24773.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Bug #24773 (unset() of integers treated as arrays causes a crash)
66
unset($array["lvl1"]["lvl2"]["b"]);
77
?>
88
--EXPECTF--
9-
Fatal error: Uncaught Error: Cannot use string offset as an array in %s:%d
9+
Fatal error: Uncaught TypeError: Illegal offset type in %s:%d
1010
Stack trace:
1111
#0 {main}
1212
thrown in %s on line %d

Zend/tests/bug31098.phpt

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,28 @@ var_dump(isset($a['b']));
1717

1818
$simpleString = "Bogus String Text";
1919
echo isset($simpleString->wrong)?"bug\n":"ok\n";
20-
echo isset($simpleString["wrong"])?"bug\n":"ok\n";
20+
try {
21+
echo isset($simpleString["wrong"])?"bug\n":"ok\n";
22+
} catch (\TypeError $e) {
23+
echo $e->getMessage() . \PHP_EOL;
24+
}
2125
echo isset($simpleString[-20])?"bug\n":"ok\n";
2226
echo isset($simpleString[0])?"ok\n":"bug\n";
2327
echo isset($simpleString["0"])?"ok\n":"bug\n";
2428
echo isset($simpleString["16"])?"ok\n":"bug\n";
2529
echo isset($simpleString["17"])?"bug\n":"ok\n";
2630
echo $simpleString->wrong === null?"ok\n":"bug\n";
27-
echo $simpleString["wrong"] === "B"?"ok\n":"bug\n";
31+
try {
32+
echo $simpleString["wrong"] === "B"?"ok\n":"bug\n";
33+
} catch (\TypeError $e) {
34+
echo $e->getMessage() . \PHP_EOL;
35+
}
2836
echo $simpleString["0"] === "B"?"ok\n":"bug\n";
29-
$simpleString["wrong"] = "f";
37+
try {
38+
$simpleString["wrong"] = "f";
39+
} catch (\TypeError $e) {
40+
echo $e->getMessage() . \PHP_EOL;
41+
}
3042
echo $simpleString["0"] === "f"?"ok\n":"bug\n";
3143
?>
3244
--EXPECTF--
@@ -46,10 +58,7 @@ ok
4658

4759
Warning: Attempt to read property "wrong" on string in %s on line %d
4860
ok
49-
50-
Warning: Illegal string offset "wrong" in %s on line %d
51-
ok
61+
Illegal offset type
5262
ok
53-
54-
Warning: Illegal string offset "wrong" in %s on line %d
63+
Illegal offset type
5564
ok

Zend/tests/bug53432.phpt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ var_dump($str[-1] = 'a');
1616
var_dump($str);
1717

1818
$str = '';
19-
var_dump($str['foo'] = 'a');
19+
try {
20+
var_dump($str['foo'] = 'a');
21+
} catch (\TypeError $e) {
22+
echo $e->getMessage() . \PHP_EOL;
23+
}
2024
var_dump($str);
2125

2226
$str = '';
@@ -53,9 +57,7 @@ string(6) " a"
5357
Warning: Illegal string offset -1 in %s on line %d
5458
NULL
5559
string(0) ""
56-
57-
Warning: Illegal string offset "foo" in %s on line %d
58-
string(1) "a"
60+
Illegal offset type
5961
string(1) "a"
6062
Error: [] operator not supported for strings
6163
string(0) ""

Zend/tests/bug64578.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ Bug #64578 (debug_backtrace in set_error_handler corrupts zend heap: segfault)
55

66
set_error_handler(function($no, $err) { var_dump($err); });
77

8-
function x($s) { $s['a'] = 1; };
8+
function x($s) { $s['2a'] = 1; };
99
$y = '1';
1010
x($y);
1111
print_r($y);
1212
--EXPECT--
13-
string(25) "Illegal string offset "a""
13+
string(26) "Illegal string offset "2a""
1414
1

Zend/tests/bug73792.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ Bug #73792 (invalid foreach loop hangs script)
44
<?php
55
$a = 'aaa';
66

7-
foreach ($a['bbb'] as &$value) {
7+
foreach ($a['2bbb'] as &$value) {
88
echo 'loop';
99
}
1010

1111
unset($value);
1212
echo 'done';
1313
?>
1414
--EXPECTF--
15-
Warning: Illegal string offset "bbb" in %s on line %d
15+
Warning: Illegal string offset "2bbb" in %s on line %d
1616

1717
Fatal error: Uncaught Error: Cannot iterate on string offsets by reference in %sbug73792.php:4
1818
Stack trace:

Zend/tests/bug76534.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ set_error_handler(function ($severity, $message, $file, $line) {
77
});
88

99
$x = "foo";
10-
$y = &$x["bar"];
10+
$y = &$x["2bar"];
1111
?>
1212
--EXPECTF--
13-
Fatal error: Uncaught Exception: Illegal string offset "bar" in %s:%d
13+
Fatal error: Uncaught Exception: Illegal string offset "2bar" in %s:%d
1414
Stack trace:
1515
#0 %sbug76534.php(%d): {closure}(2, 'Illegal string ...', '%s', %d)
1616
#1 {main}

Zend/tests/const_dereference_002.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ error_reporting(E_ALL);
66

77
var_dump("foobar"[3]);
88
var_dump("foobar"[2][0]);
9-
var_dump("foobar"["foo"]["bar"]);
9+
var_dump("foobar"["0foo"]["0bar"]);
1010
--EXPECTF--
1111
string(1) "b"
1212
string(1) "o"
1313

14-
Warning: Illegal string offset "foo" in %s on line %d
14+
Warning: Illegal string offset "0foo" in %s on line %d
1515

16-
Warning: Illegal string offset "bar" in %s on line %d
16+
Warning: Illegal string offset "0bar" in %s on line %d
1717
string(1) "f"

Zend/tests/indexing_001.phpt

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,13 @@ array(1) {
7575
}
7676
}
7777

78-
Warning: Illegal string offset "foo" in %s on line %d
79-
8078
Warning: Array to string conversion in %s on line %d
81-
82-
Warning: Only the first byte will be assigned to the string offset in %s on line %d
83-
string(1) "A"
84-
85-
Warning: Illegal string offset "foo" in %s on line %d
79+
Illegal offset type
80+
string(0) ""
8681

8782
Warning: Array to string conversion in %s on line %d
88-
89-
Warning: Only the first byte will be assigned to the string offset in %s on line %d
90-
string(1) "A"
83+
Illegal offset type
84+
string(1) " "
9185
Cannot use a scalar value as an array
9286
float(0.1)
9387
array(1) {

Zend/tests/offset_assign.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
--TEST--
2-
Crash on $x['x']['y'] += 1 when $x is string
2+
Crash on $x['2x']['y'] += 1 when $x is string
33
--FILE--
44
<?php
55
$x = "a";
6-
$x['x']['y'] += 1;
6+
$x['2x']['y'] += 1;
77

88
echo "Done\n";
99
?>
1010
--EXPECTF--
11-
Warning: Illegal string offset "x" in %s on line %d
11+
Warning: Illegal string offset "2x" in %s on line %d
1212

1313
Fatal error: Uncaught Error: Cannot use string offset as an array in %soffset_assign.php:%d
1414
Stack trace:

Zend/tests/offset_string.phpt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ $str = "Sitting on a corner all alone, staring from the bottom of his soul";
88
var_dump($str[1]);
99
var_dump($str[0.0836]);
1010
var_dump($str[NULL]);
11-
var_dump($str["run away"]);
11+
try {
12+
var_dump($str["run away"]);
13+
} catch (\TypeError $e) {
14+
echo $e->getMessage() . \PHP_EOL;
15+
}
1216
var_dump($str["13"]);
1317
var_dump($str["14.5"]);
1418
var_dump($str["15 and then some"]);
@@ -47,9 +51,7 @@ string(1) "S"
4751

4852
Warning: String offset cast occurred in %s on line %d
4953
string(1) "S"
50-
51-
Warning: Illegal string offset "run away" in %s on line %d
52-
string(1) "S"
54+
Illegal offset type
5355
string(1) "c"
5456

5557
Warning: Illegal string offset "14.5" in %s on line %d

Zend/zend_execute.c

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,13 +1328,24 @@ static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type
13281328
if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
13291329
switch(Z_TYPE_P(dim)) {
13301330
case IS_STRING:
1331-
if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, false)) {
1332-
break;
1331+
{
1332+
/* allow errors in string offset for BC reasons */
1333+
zend_uchar numeric_string_type = is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &offset, NULL, true);
1334+
if (IS_LONG == numeric_string_type) {
1335+
/* emit Illegal string warning on leading numerical string */
1336+
if (0 == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, false)
1337+
&& type != BP_VAR_UNSET) {
1338+
zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
1339+
}
1340+
return offset;
13331341
}
1334-
if (type != BP_VAR_UNSET) {
1342+
if (IS_DOUBLE == numeric_string_type) {
13351343
zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
1344+
break;
13361345
}
1346+
zend_illegal_offset();
13371347
break;
1348+
}
13381349
case IS_UNDEF:
13391350
ZVAL_UNDEFINED_OP2();
13401351
case IS_DOUBLE:
@@ -2313,17 +2324,28 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
23132324
try_string_offset:
23142325
if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
23152326
switch (Z_TYPE_P(dim)) {
2316-
/* case IS_LONG: */
23172327
case IS_STRING:
2318-
if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, false)) {
2328+
{
2329+
/* allow errors in string offset for BC reasons */
2330+
zend_uchar numeric_string_type = is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &offset, NULL, true);
2331+
if (IS_LONG == numeric_string_type) {
2332+
/* emit Illegal string warning on leading numerical string */
2333+
if (0 == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, false)) {
2334+
zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
2335+
}
2336+
goto out;
2337+
}
2338+
if (IS_DOUBLE == numeric_string_type) {
2339+
zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
23192340
break;
23202341
}
23212342
if (type == BP_VAR_IS) {
23222343
ZVAL_NULL(result);
23232344
return;
23242345
}
2325-
zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
2346+
zend_illegal_offset();
23262347
break;
2348+
}
23272349
case IS_UNDEF:
23282350
ZVAL_UNDEFINED_OP2();
23292351
case IS_DOUBLE:
@@ -2346,6 +2368,7 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
23462368
} else {
23472369
offset = Z_LVAL_P(dim);
23482370
}
2371+
out:
23492372

23502373
if (UNEXPECTED(Z_STRLEN_P(container) < ((offset < 0) ? -(size_t)offset : ((size_t)offset + 1)))) {
23512374
if (type != BP_VAR_IS) {

ext/opcache/jit/zend_jit_helpers.c

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -677,13 +677,24 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_str_r_helper(zval *container, zval
677677
try_string_offset:
678678
if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
679679
switch (Z_TYPE_P(dim)) {
680-
/* case IS_LONG: */
681680
case IS_STRING:
682-
if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, false)) {
681+
{
682+
/* allow errors in string offset for BC reasons */
683+
zend_uchar numeric_string_type = is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &offset, NULL, true);
684+
if (IS_LONG == numeric_string_type) {
685+
/* emit Illegal string warning on leading numerical string */
686+
if (0 == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, false)) {
687+
zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
688+
}
689+
goto out;
690+
}
691+
if (IS_DOUBLE == numeric_string_type) {
692+
zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
683693
break;
684694
}
685-
zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
695+
zend_type_error("Illegal offset type");
686696
break;
697+
}
687698
case IS_UNDEF:
688699
zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
689700
case IS_DOUBLE:
@@ -704,6 +715,7 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_str_r_helper(zval *container, zval
704715
} else {
705716
offset = Z_LVAL_P(dim);
706717
}
718+
out:
707719

708720
if (UNEXPECTED(Z_STRLEN_P(container) < ((offset < 0) ? -(size_t)offset : ((size_t)offset + 1)))) {
709721
zend_error(E_WARNING, "Uninitialized string offset " ZEND_LONG_FMT, offset);
@@ -824,13 +836,24 @@ static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type)
824836
if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
825837
switch(Z_TYPE_P(dim)) {
826838
case IS_STRING:
827-
if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, false)) {
828-
break;
839+
{
840+
/* allow errors in string offset for BC reasons */
841+
zend_uchar numeric_string_type = is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &offset, NULL, true);
842+
if (IS_LONG == numeric_string_type) {
843+
/* emit Illegal string warning on leading numerical string */
844+
if (0 == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, false)
845+
&& type != BP_VAR_UNSET) {
846+
zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
847+
}
848+
return offset;
829849
}
830-
if (type != BP_VAR_UNSET) {
850+
if (IS_DOUBLE == numeric_string_type) {
831851
zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
852+
break;
832853
}
854+
zend_type_error("Illegal offset type");
833855
break;
856+
}
834857
case IS_UNDEF:
835858
zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
836859
case IS_DOUBLE:

ext/opcache/tests/jit/fetch_dim_r_003.phpt

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,18 @@ function foo() {
2020
var_dump($a[false]);
2121
var_dump($a[true]);
2222
var_dump($a[null]);
23-
var_dump($a["ab"]);
23+
try {
24+
var_dump($a["ab"]);
25+
} catch (\TypeError $e) {
26+
echo $e->getMessage() . \PHP_EOL;
27+
}
2428
$x = "a";
2529
$y = "b";
26-
var_dump($a[$x . $y]);
30+
try {
31+
var_dump($a[$x . $y]);
32+
} catch (\TypeError $e) {
33+
echo $e->getMessage() . \PHP_EOL;
34+
}
2735
var_dump($a["2x"]);
2836
$x = "2";
2937
$y = "x";
@@ -47,15 +55,11 @@ string(1) "B"
4755
4856
Warning: String offset cast occurred in %s on line %d
4957
string(1) "A"
58+
Illegal offset type
59+
Illegal offset type
5060

51-
Warning: Illegal string offset "ab" in %sfetch_dim_r_003.php on line 12
52-
string(1) "A"
53-
54-
Warning: Illegal string offset "ab" in %sfetch_dim_r_003.php on line 15
55-
string(1) "A"
56-
57-
Warning: Illegal string offset "2x" in %sfetch_dim_r_003.php on line 16
61+
Warning: Illegal string offset "2x" in %sfetch_dim_r_003.php on line 24
5862
string(1) "C"
5963

60-
Warning: Illegal string offset "2x" in %sfetch_dim_r_003.php on line 19
64+
Warning: Illegal string offset "2x" in %sfetch_dim_r_003.php on line 27
6165
string(1) "C"

0 commit comments

Comments
 (0)