Skip to content

Commit b02da6c

Browse files
committed
Add warnings when doing something dodgy with range()
1 parent 03fda30 commit b02da6c

File tree

6 files changed

+173
-112
lines changed

6 files changed

+173
-112
lines changed

ext/standard/array.c

Lines changed: 124 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2667,6 +2667,60 @@ PHP_FUNCTION(array_fill_keys)
26672667
zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
26682668
} while (0)
26692669

2670+
static uint8_t php_range_process_input(const zval *input, uint32_t arg_num, zend_long /* restrict */ *lval, double /* restrict */ *dval)
2671+
{
2672+
switch (Z_TYPE_P(input)) {
2673+
case IS_LONG:
2674+
*lval = Z_LVAL_P(input);
2675+
*dval = (double) Z_LVAL_P(input);
2676+
return IS_LONG;
2677+
case IS_DOUBLE:
2678+
*dval = Z_DVAL_P(input);
2679+
check_dval_value:
2680+
if (zend_isinf(*dval)) {
2681+
zend_argument_value_error(arg_num, "must be a finite number, INF provided");
2682+
return 0;
2683+
}
2684+
if (zend_isnan(*dval)) {
2685+
zend_argument_value_error(arg_num, "must be a finite number, NAN provided");
2686+
return 0;
2687+
}
2688+
return IS_DOUBLE;
2689+
case IS_STRING: {
2690+
if (Z_STRLEN_P(input) == 0) {
2691+
const char *arg_name = get_active_function_arg_name(arg_num);
2692+
php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must not be empty, casted to 0", arg_num, arg_name);
2693+
if (UNEXPECTED(EG(exception))) {
2694+
return 0;
2695+
}
2696+
*lval = 0;
2697+
*dval = 0.0;
2698+
return IS_LONG;
2699+
}
2700+
uint8_t type = is_numeric_str_function(Z_STR_P(input), lval, dval);
2701+
if (type == IS_DOUBLE) {
2702+
goto check_dval_value;
2703+
}
2704+
if (type == IS_LONG) {
2705+
*dval = (double) *lval;
2706+
return IS_LONG;
2707+
}
2708+
if (Z_STRLEN_P(input) != 1) {
2709+
const char *arg_name = get_active_function_arg_name(arg_num);
2710+
php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must be a single byte, subsequent bytes are ignored", arg_num, arg_name);
2711+
if (UNEXPECTED(EG(exception))) {
2712+
return 0;
2713+
}
2714+
}
2715+
/* Set fall back values to 0 in case the other argument is not a string */
2716+
*lval = 0;
2717+
*dval = 0.0;
2718+
return IS_STRING;
2719+
}
2720+
EMPTY_SWITCH_DEFAULT_CASE();
2721+
}
2722+
}
2723+
26702724
/* {{{ Create an array containing the range of integers or characters from low to high (inclusive) */
26712725
PHP_FUNCTION(range)
26722726
{
@@ -2717,22 +2771,54 @@ PHP_FUNCTION(range)
27172771
}
27182772
}
27192773

2720-
/* If the range is given as strings, generate an array of characters. */
2721-
if (Z_TYPE_P(user_start) == IS_STRING && Z_TYPE_P(user_end) == IS_STRING && Z_STRLEN_P(user_start) >= 1 && Z_STRLEN_P(user_end) >= 1) {
2722-
int type1, type2;
2723-
unsigned char low, high;
2774+
uint8_t start_type;
2775+
double start_double;
2776+
zend_long start_long;
2777+
uint8_t end_type;
2778+
double end_double;
2779+
zend_long end_long;
27242780

2725-
type1 = is_numeric_string(Z_STRVAL_P(user_start), Z_STRLEN_P(user_start), NULL, NULL, 0);
2726-
type2 = is_numeric_string(Z_STRVAL_P(user_end), Z_STRLEN_P(user_end), NULL, NULL, 0);
2781+
start_type = php_range_process_input(user_start, 1, &start_long, &start_double);
2782+
if (start_type == 0) {
2783+
RETURN_THROWS();
2784+
}
2785+
end_type = php_range_process_input(user_end, 2, &end_long, &end_double);
2786+
if (end_type == 0) {
2787+
RETURN_THROWS();
2788+
}
2789+
2790+
/* If the range is given as strings, generate an array of characters. */
2791+
if (start_type == IS_STRING || end_type == IS_STRING) {
2792+
if (UNEXPECTED(start_type != end_type)) {
2793+
if (start_type != IS_STRING) {
2794+
php_error_docref(NULL, E_WARNING, "Argument #1 ($start) must be a string if argument #2 ($end)"
2795+
" is a string, argument #2 ($end) converted to 0");
2796+
end_type = IS_LONG;
2797+
} else {
2798+
php_error_docref(NULL, E_WARNING, "Argument #2 ($end) must be a string if argument #1 ($start)"
2799+
" is a string, argument #1 ($start) converted to 0");
2800+
start_type = IS_LONG;
2801+
}
2802+
if (UNEXPECTED(EG(exception))) {
2803+
RETURN_THROWS();
2804+
}
2805+
goto handle_numeric_inputs;
2806+
}
27272807

2728-
if (type1 == IS_DOUBLE || type2 == IS_DOUBLE || is_step_double) {
2729-
goto double_str;
2730-
} else if (type1 == IS_LONG || type2 == IS_LONG) {
2731-
goto long_str;
2808+
if (is_step_double) {
2809+
php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be of type int when generating an array"
2810+
" of characters, inputs converted to 0");
2811+
if (UNEXPECTED(EG(exception))) {
2812+
RETURN_THROWS();
2813+
}
2814+
end_type = IS_LONG;
2815+
start_type = IS_LONG;
2816+
goto handle_numeric_inputs;
27322817
}
27332818

2734-
low = (unsigned char)Z_STRVAL_P(user_start)[0];
2735-
high = (unsigned char)Z_STRVAL_P(user_end)[0];
2819+
/* Generate array of characters */
2820+
unsigned char low = (unsigned char)Z_STRVAL_P(user_start)[0];
2821+
unsigned char high = (unsigned char)Z_STRVAL_P(user_end)[0];
27362822

27372823
if (low > high) { /* Negative Steps */
27382824
if (low - high < step) {
@@ -2772,93 +2858,84 @@ PHP_FUNCTION(range)
27722858
ZVAL_CHAR(&tmp, low);
27732859
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
27742860
}
2775-
} else if (Z_TYPE_P(user_start) == IS_DOUBLE || Z_TYPE_P(user_end) == IS_DOUBLE || is_step_double) {
2776-
double low, high, element;
2777-
uint32_t i, size;
2778-
double_str:
2779-
low = zval_get_double(user_start);
2780-
high = zval_get_double(user_end);
2861+
return;
2862+
}
27812863

2782-
if (zend_isinf(high) || zend_isinf(low)) {
2783-
zend_value_error("Invalid range supplied: start=%0.0f end=%0.0f", low, high);
2784-
RETURN_THROWS();
2785-
}
2864+
handle_numeric_inputs:
2865+
if (start_type == IS_DOUBLE || end_type == IS_DOUBLE || is_step_double) {
2866+
double element;
2867+
uint32_t i, size;
27862868

2787-
if (low > high) { /* Negative steps */
2788-
if (low - high < step_double) {
2869+
if (start_double > end_double) { /* Negative steps */
2870+
if (start_double - end_double < step_double) {
27892871
err = 1;
27902872
goto err;
27912873
}
27922874

2793-
RANGE_CHECK_DOUBLE_INIT_ARRAY(low, high, step_double);
2875+
RANGE_CHECK_DOUBLE_INIT_ARRAY(start_double, end_double, step_double);
27942876

27952877
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2796-
for (i = 0, element = low; i < size && element >= high; ++i, element = low - (i * step_double)) {
2878+
for (i = 0, element = start_double; i < size && element >= end_double; ++i, element = start_double - (i * step_double)) {
27972879
ZEND_HASH_FILL_SET_DOUBLE(element);
27982880
ZEND_HASH_FILL_NEXT();
27992881
}
28002882
} ZEND_HASH_FILL_END();
2801-
} else if (high > low) { /* Positive steps */
2802-
if (high - low < step_double) {
2883+
} else if (end_double > start_double) { /* Positive steps */
2884+
if (end_double - start_double < step_double) {
28032885
err = 1;
28042886
goto err;
28052887
}
28062888

2807-
RANGE_CHECK_DOUBLE_INIT_ARRAY(high, low, step_double);
2889+
RANGE_CHECK_DOUBLE_INIT_ARRAY(end_double, start_double, step_double);
28082890

28092891
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2810-
for (i = 0, element = low; i < size && element <= high; ++i, element = low + (i * step_double)) {
2892+
for (i = 0, element = start_double; i < size && element <= end_double; ++i, element = start_double + (i * step_double)) {
28112893
ZEND_HASH_FILL_SET_DOUBLE(element);
28122894
ZEND_HASH_FILL_NEXT();
28132895
}
28142896
} ZEND_HASH_FILL_END();
28152897
} else {
28162898
array_init(return_value);
2817-
ZVAL_DOUBLE(&tmp, low);
2899+
ZVAL_DOUBLE(&tmp, start_double);
28182900
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
28192901
}
28202902
} else {
2821-
zend_long low, high;
2903+
ZEND_ASSERT(start_type == IS_LONG && end_type == IS_LONG && !is_step_double);
28222904
/* unsigned_step is a zend_ulong so that comparisons to it don't overflow, i.e. low - high < lstep */
2823-
zend_ulong unsigned_step;
2905+
zend_ulong unsigned_step= (zend_ulong)step;
28242906
uint32_t i, size;
2825-
long_str:
2826-
low = zval_get_long(user_start);
2827-
high = zval_get_long(user_end);
2828-
2829-
unsigned_step = (zend_ulong)step;
28302907

2831-
if (low > high) { /* Negative steps */
2832-
if ((zend_ulong)low - high < unsigned_step) {
2908+
if (start_long > end_long) { /* Negative steps */
2909+
if ((zend_ulong)start_long - end_long < unsigned_step) {
28332910
err = 1;
28342911
goto err;
28352912
}
28362913

2837-
RANGE_CHECK_LONG_INIT_ARRAY(low, high, unsigned_step);
2914+
RANGE_CHECK_LONG_INIT_ARRAY(start_long, end_long, unsigned_step);
28382915

28392916
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
28402917
for (i = 0; i < size; ++i) {
2841-
ZEND_HASH_FILL_SET_LONG(low - (i * unsigned_step));
2918+
ZEND_HASH_FILL_SET_LONG(start_long - (i * unsigned_step));
28422919
ZEND_HASH_FILL_NEXT();
28432920
}
28442921
} ZEND_HASH_FILL_END();
2845-
} else if (high > low) { /* Positive steps */
2846-
if ((zend_ulong)high - low < unsigned_step) {
2922+
} else if (end_long > start_long) { /* Positive steps */
2923+
if ((zend_ulong)end_long - start_long < unsigned_step) {
28472924
err = 1;
28482925
goto err;
28492926
}
28502927

2851-
RANGE_CHECK_LONG_INIT_ARRAY(high, low, unsigned_step);
2928+
RANGE_CHECK_LONG_INIT_ARRAY(end_long, start_long, unsigned_step);
28522929

28532930
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
28542931
for (i = 0; i < size; ++i) {
2855-
ZEND_HASH_FILL_SET_LONG(low + (i * unsigned_step));
2932+
ZEND_HASH_FILL_SET_LONG(start_long + (i * unsigned_step));
28562933
ZEND_HASH_FILL_NEXT();
28572934
}
28582935
} ZEND_HASH_FILL_END();
28592936
} else {
28602937
array_init(return_value);
2861-
ZVAL_LONG(&tmp, low);
2938+
ZVAL_LONG(&tmp, start_long);
28622939
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
28632940
}
28642941
}

ext/standard/tests/array/range/bug32021.phpt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ $foo = range('', 'z');
66
var_dump($foo);
77
?>
88
ALIVE
9-
--EXPECT--
9+
--EXPECTF--
10+
Warning: range(): Argument #1 ($start) must not be empty, casted to 0 in %s on line %d
11+
12+
Warning: range(): Argument #1 ($start) must be a string if argument #2 ($end) is a string, argument #2 ($end) converted to 0 in %s on line %d
1013
array(1) {
1114
[0]=>
1215
int(0)

ext/standard/tests/array/range/range_bug70239_0.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ try {
99
}
1010
?>
1111
--EXPECT--
12-
Invalid range supplied: start=0 end=inf
12+
range(): Argument #2 ($end) must be a finite number, INF provided

ext/standard/tests/array/range/range_bug70239_1.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ try {
99
}
1010
?>
1111
--EXPECT--
12-
Invalid range supplied: start=inf end=inf
12+
range(): Argument #1 ($start) must be a finite number, INF provided

ext/standard/tests/array/range/range_inputs_float_NAN_values.phpt

Lines changed: 20 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ $fs = [$f1, $f2, $f3, 5.5];
1919
foreach ($fs as $s) {
2020
foreach ($fs as $e) {
2121
echo "range($s, $e);\n";
22-
var_dump( range($s, $e) );
22+
try {
23+
var_dump( range($s, $e) );
24+
} catch (\ValueError $e) {
25+
echo $e->getMessage(), PHP_EOL;
26+
}
2327
}
2428
}
2529

@@ -29,80 +33,35 @@ float(NAN)
2933
float(NAN)
3034
float(NAN)
3135
range(NAN, NAN);
32-
array(1) {
33-
[0]=>
34-
float(NAN)
35-
}
36+
range(): Argument #1 ($start) must be a finite number, NAN provided
3637
range(NAN, NAN);
37-
array(1) {
38-
[0]=>
39-
float(NAN)
40-
}
38+
range(): Argument #1 ($start) must be a finite number, NAN provided
4139
range(NAN, NAN);
42-
array(1) {
43-
[0]=>
44-
float(NAN)
45-
}
40+
range(): Argument #1 ($start) must be a finite number, NAN provided
4641
range(NAN, 5.5);
47-
array(1) {
48-
[0]=>
49-
float(NAN)
50-
}
42+
range(): Argument #1 ($start) must be a finite number, NAN provided
5143
range(NAN, NAN);
52-
array(1) {
53-
[0]=>
54-
float(NAN)
55-
}
44+
range(): Argument #1 ($start) must be a finite number, NAN provided
5645
range(NAN, NAN);
57-
array(1) {
58-
[0]=>
59-
float(NAN)
60-
}
46+
range(): Argument #1 ($start) must be a finite number, NAN provided
6147
range(NAN, NAN);
62-
array(1) {
63-
[0]=>
64-
float(NAN)
65-
}
48+
range(): Argument #1 ($start) must be a finite number, NAN provided
6649
range(NAN, 5.5);
67-
array(1) {
68-
[0]=>
69-
float(NAN)
70-
}
50+
range(): Argument #1 ($start) must be a finite number, NAN provided
7151
range(NAN, NAN);
72-
array(1) {
73-
[0]=>
74-
float(NAN)
75-
}
52+
range(): Argument #1 ($start) must be a finite number, NAN provided
7653
range(NAN, NAN);
77-
array(1) {
78-
[0]=>
79-
float(NAN)
80-
}
54+
range(): Argument #1 ($start) must be a finite number, NAN provided
8155
range(NAN, NAN);
82-
array(1) {
83-
[0]=>
84-
float(NAN)
85-
}
56+
range(): Argument #1 ($start) must be a finite number, NAN provided
8657
range(NAN, 5.5);
87-
array(1) {
88-
[0]=>
89-
float(NAN)
90-
}
58+
range(): Argument #1 ($start) must be a finite number, NAN provided
9159
range(5.5, NAN);
92-
array(1) {
93-
[0]=>
94-
float(5.5)
95-
}
60+
range(): Argument #2 ($end) must be a finite number, NAN provided
9661
range(5.5, NAN);
97-
array(1) {
98-
[0]=>
99-
float(5.5)
100-
}
62+
range(): Argument #2 ($end) must be a finite number, NAN provided
10163
range(5.5, NAN);
102-
array(1) {
103-
[0]=>
104-
float(5.5)
105-
}
64+
range(): Argument #2 ($end) must be a finite number, NAN provided
10665
range(5.5, 5.5);
10766
array(1) {
10867
[0]=>

0 commit comments

Comments
 (0)