35
35
36
36
#include <stdbool.h>
37
37
38
+ /* Though rare, it is possible to have 64-bit zend_longs and a 32-bit size_t. */
39
+ #define MAX_ZVAL_COUNT ((SIZE_MAX / sizeof(zval)) - 1)
40
+ #define MAX_VALID_OFFSET ((size_t)(MAX_ZVAL_COUNT > ZEND_LONG_MAX ? ZEND_LONG_MAX : MAX_ZVAL_COUNT))
41
+
38
42
/* Miscellaneous utility functions */
39
43
static inline zval * spl_zval_copy_range (const zval * original , size_t n ) {
40
44
const size_t bytes = n * sizeof (zval );
@@ -120,6 +124,15 @@ typedef struct _spl_vector_it {
120
124
121
125
static void spl_vector_raise_capacity (spl_vector * intern , const size_t new_capacity );
122
126
127
+ /*
128
+ * If a size this large is encountered, assume the allocation will likely fail or
129
+ * future changes to the capacity will overflow.
130
+ */
131
+ static ZEND_COLD void spl_error_noreturn_max_vector_capacity ()
132
+ {
133
+ zend_error_noreturn (E_ERROR , "exceeded max valid Vector capacity" );
134
+ }
135
+
123
136
static spl_vector * spl_vector_from_object (zend_object * obj )
124
137
{
125
138
return (spl_vector * )((char * )(obj ) - XtOffsetOf (spl_vector , std ));
@@ -166,7 +179,6 @@ static bool spl_vector_entries_uninitialized(const spl_vector_entries *array)
166
179
167
180
static void spl_vector_raise_capacity (spl_vector * intern , const size_t new_capacity ) {
168
181
ZEND_ASSERT (new_capacity > intern -> array .capacity );
169
- ZEND_ASSERT (new_capacity <= MAX_VALID_OFFSET + 1 );
170
182
if (intern -> array .capacity == 0 ) {
171
183
intern -> array .entries = safe_emalloc (new_capacity , sizeof (zval ), 0 );
172
184
} else {
@@ -205,82 +217,17 @@ static void spl_vector_entries_init_elems(spl_vector_entries *array, zend_long f
205
217
}
206
218
*/
207
219
208
- static inline void spl_vector_entries_set_empty_list (spl_vector_entries * array ) {
220
+ static zend_always_inline void spl_vector_entries_set_empty_list (spl_vector_entries * array ) {
209
221
array -> size = 0 ;
210
222
array -> capacity = 0 ;
211
223
array -> entries = (zval * )empty_entry_list ;
212
224
}
213
225
214
- static void spl_vector_entries_init_from_array (spl_vector_entries * array , zend_array * values , bool preserve_keys )
215
- {
216
- const uint32_t num_elements = zend_hash_num_elements (values );
217
- if (num_elements <= 0 ) {
218
- spl_vector_entries_set_empty_list (array );
219
- return ;
220
- }
221
-
222
- zval * val ;
223
- zval * entries ;
224
-
225
- array -> size = 0 ; /* reset size in case emalloc() fails */
226
- if (preserve_keys ) {
227
- zend_string * str_index ;
228
- zend_ulong num_index , max_index = 0 ;
229
-
230
- ZEND_HASH_FOREACH_KEY (values , num_index , str_index ) {
231
- if (UNEXPECTED (str_index != NULL || (zend_long )num_index < 0 )) {
232
- zend_throw_exception_ex (spl_ce_UnexpectedValueException , 0 , "array must contain only positive integer keys" );
233
- return ;
234
- }
235
-
236
- if (num_index > max_index ) {
237
- max_index = num_index ;
238
- }
239
- } ZEND_HASH_FOREACH_END ();
240
-
241
- if (UNEXPECTED (max_index > MAX_VALID_OFFSET )) {
242
- zend_error_noreturn ("exceeded max valid offset of Vector" );
243
- return ;
244
- }
245
- const zend_ulong size = max_index + 1 ;
246
- ZEND_ASSERT (size > 0 );
247
- array -> entries = entries = safe_emalloc (size , sizeof (zval ), 0 );
248
- array -> size = size ;
249
- array -> capacity = size ;
250
- /* Optimization: Don't need to null remaining elements if all elements from 0..num_elements-1 are set. */
251
- ZEND_ASSERT (size >= num_elements );
252
- if (size > num_elements ) {
253
- for (uint32_t i = 0 ; i < size ; i ++ ) {
254
- ZVAL_NULL (& entries [i ]);
255
- }
256
- }
257
-
258
- ZEND_HASH_FOREACH_KEY_VAL (values , num_index , str_index , val ) {
259
- ZEND_ASSERT (num_index < size );
260
- ZEND_ASSERT (!str_index );
261
- /* should happen for non-corrupt array inputs */
262
- ZEND_ASSERT (size == num_elements || Z_TYPE (entries [num_index ]) == IS_NULL );
263
- ZVAL_COPY_DEREF (& entries [num_index ], val );
264
- } ZEND_HASH_FOREACH_END ();
265
- return ;
266
- }
267
-
268
- size_t i = 0 ;
269
- array -> entries = entries = safe_emalloc (num_elements , sizeof (zval ), 0 );
270
- array -> size = num_elements ;
271
- array -> capacity = num_elements ;
272
- ZEND_HASH_FOREACH_VAL (values , val ) {
273
- ZEND_ASSERT (i < num_elements );
274
- ZVAL_COPY_DEREF (& entries [i ], val );
275
- i ++ ;
276
- } ZEND_HASH_FOREACH_END ();
277
- }
278
-
279
226
static void spl_vector_entries_init_from_traversable (spl_vector_entries * array , zend_object * obj )
280
227
{
281
228
zend_class_entry * ce = obj -> ce ;
282
229
zend_object_iterator * iter ;
283
- zend_long size = 0 , capacity = 0 ;
230
+ size_t size = 0 , capacity = 0 ;
284
231
array -> size = 0 ;
285
232
array -> entries = NULL ;
286
233
zval * entries = NULL ;
@@ -317,6 +264,7 @@ static void spl_vector_entries_init_from_traversable(spl_vector_entries *array,
317
264
if (size >= capacity ) {
318
265
/* Not using Countable::count(), that would potentially have side effects or throw UnsupportedOperationException or be slow to compute */
319
266
if (entries ) {
267
+ /* The safe_erealloc macro emits its own fatal error on integer overflow */
320
268
capacity *= 2 ;
321
269
entries = safe_erealloc (entries , capacity , sizeof (zval ), 0 );
322
270
} else {
@@ -520,7 +468,7 @@ PHP_METHOD(Vector, clear)
520
468
efree (old_entries );
521
469
}
522
470
523
- /* Set size of this vector */
471
+ /* Set size of this Vector */
524
472
PHP_METHOD (Vector , setSize )
525
473
{
526
474
zend_long size ;
@@ -535,7 +483,8 @@ PHP_METHOD(Vector, setSize)
535
483
if (size < 0 ) {
536
484
zend_argument_value_error (1 , "must be greater than or equal to 0" );
537
485
} else {
538
- zend_error_noreturn ("exceeded max valid Teds\\Vector offset" );
486
+ spl_error_noreturn_max_vector_capacity ();
487
+ ZEND_UNREACHABLE ();
539
488
}
540
489
RETURN_THROWS ();
541
490
}
@@ -593,12 +542,10 @@ PHP_METHOD(Vector, __construct)
593
542
{
594
543
zval * object = ZEND_THIS ;
595
544
zval * iterable = NULL ;
596
- bool preserve_keys = true;
597
545
598
- ZEND_PARSE_PARAMETERS_START (0 , 2 )
546
+ ZEND_PARSE_PARAMETERS_START (0 , 1 )
599
547
Z_PARAM_OPTIONAL
600
548
Z_PARAM_ITERABLE (iterable )
601
- Z_PARAM_BOOL (preserve_keys )
602
549
ZEND_PARSE_PARAMETERS_END ();
603
550
604
551
spl_vector * intern = Z_VECTOR_P (object );
@@ -608,17 +555,39 @@ PHP_METHOD(Vector, __construct)
608
555
/* called __construct() twice, bail out */
609
556
RETURN_THROWS ();
610
557
}
558
+ uint32_t num_elements ;
559
+ HashTable * values ;
611
560
if (!iterable ) {
561
+ set_empty_list :
612
562
spl_vector_entries_set_empty_list (& intern -> array );
613
563
return ;
614
564
}
615
565
616
566
switch (Z_TYPE_P (iterable )) {
617
567
case IS_ARRAY :
618
- spl_vector_entries_init_from_array (& intern -> array , Z_ARRVAL_P (iterable ), preserve_keys );
568
+ values = Z_ARRVAL_P (iterable );
569
+ num_elements = zend_hash_num_elements (values );
570
+ if (num_elements == 0 ) {
571
+ goto set_empty_list ;
572
+ }
573
+
574
+ zval * val ;
575
+ zval * entries ;
576
+ spl_vector_entries * array = & intern -> array ;
577
+
578
+ array -> size = 0 ; /* reset size in case emalloc() fails */
579
+ size_t i = 0 ;
580
+ array -> entries = entries = safe_emalloc (num_elements , sizeof (zval ), 0 );
581
+ array -> size = num_elements ;
582
+ array -> capacity = num_elements ;
583
+ ZEND_HASH_FOREACH_VAL (values , val ) {
584
+ ZEND_ASSERT (i < num_elements );
585
+ ZVAL_COPY_DEREF (& entries [i ], val );
586
+ i ++ ;
587
+ } ZEND_HASH_FOREACH_END ();
619
588
return ;
620
589
case IS_OBJECT :
621
- spl_vector_entries_init_from_traversable (& intern -> array , Z_OBJ_P (iterable ), preserve_keys );
590
+ spl_vector_entries_init_from_traversable (& intern -> array , Z_OBJ_P (iterable ));
622
591
return ;
623
592
EMPTY_SWITCH_DEFAULT_CASE ();
624
593
}
@@ -776,9 +745,7 @@ static void spl_vector_entries_init_from_array_values(spl_vector_entries *array,
776
745
{
777
746
size_t num_entries = zend_hash_num_elements (raw_data );
778
747
if (num_entries == 0 ) {
779
- array -> size = 0 ;
780
- array -> capacity = 0 ;
781
- array -> entries = NULL ;
748
+ spl_vector_entries_set_empty_list (array );
782
749
return ;
783
750
}
784
751
zval * entries = safe_emalloc (num_entries , sizeof (zval ), 0 );
@@ -1184,7 +1151,7 @@ static zend_always_inline void spl_vector_push(spl_vector *intern, zval *value)
1184
1151
1185
1152
if (old_size >= old_capacity ) {
1186
1153
ZEND_ASSERT (old_size == old_capacity );
1187
- spl_vector_raise_capacity (intern , old_size ? old_size * 2 : 4 );
1154
+ spl_vector_raise_capacity (intern , old_size > 2 ? old_size * 2 : 4 );
1188
1155
}
1189
1156
ZVAL_COPY (& intern -> array .entries [old_size ], value );
1190
1157
intern -> array .size ++ ;
@@ -1208,18 +1175,13 @@ PHP_METHOD(Vector, push)
1208
1175
const size_t new_size = old_size + argc ;
1209
1176
/* The compiler will type check but eliminate dead code on platforms where size_t is 32 bits (4 bytes) */
1210
1177
if (SIZEOF_SIZE_T < 8 && UNEXPECTED (new_size > MAX_VALID_OFFSET + 1 || new_size < old_size )) {
1211
- zend_throw_exception_ex ( spl_ce_UnexpectedValueException , 0 , "exceeded max valid offset" );
1212
- RETURN_THROWS ();
1178
+ spl_error_noreturn_max_vector_capacity ( );
1179
+ ZEND_UNREACHABLE ();
1213
1180
}
1214
1181
const size_t old_capacity = intern -> array .capacity ;
1215
1182
if (new_size > old_capacity ) {
1216
- size_t new_capacity = old_size ? old_size * 2 : 4 ;
1217
- if (UNEXPECTED (new_size > new_capacity )) {
1218
- new_capacity = new_size + (new_size >> 1 );
1219
- }
1220
- if (SIZEOF_SIZE_T < 8 && UNEXPECTED (new_capacity > MAX_VALID_OFFSET + 1 )) {
1221
- new_capacity = MAX_VALID_OFFSET + 1 ;
1222
- }
1183
+ const size_t new_capacity = new_size >= 3 ? (new_size - 1 ) * 2 : 4 ;
1184
+ ZEND_ASSERT (new_capacity >= new_size );
1223
1185
spl_vector_raise_capacity (intern , new_capacity );
1224
1186
}
1225
1187
zval * entries = intern -> array .entries ;
0 commit comments