1 /*
2 +----------------------------------------------------------------------+
3 | See COPYING file for further copyright information |
4 +----------------------------------------------------------------------+
5 | Author: Oleg Grenrus <oleg.grenrus@dynamoid.com> |
6 | See CREDITS for contributors |
7 +----------------------------------------------------------------------+
8 */
9
10 #ifdef HAVE_CONFIG_H
11 #include "config.h"
12 #endif
13
14 #ifdef PHP_WIN32
15 # include "ig_win32.h"
16 #endif
17
18 #include "php.h"
19 #include "php_ini.h"
20 #include "Zend/zend_alloc.h"
21 #include "Zend/zend_exceptions.h"
22 #include "Zend/zend_interfaces.h"
23 #include "ext/standard/info.h"
24 #include "ext/standard/php_var.h"
25
26 #if PHP_VERSION_ID >= 80100
27 #include "Zend/zend_enum.h"
28 #endif
29
30 #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION)
31 # include "ext/session/php_session.h"
32 #endif
33
34 #include "ext/standard/php_incomplete_class.h"
35
36 #if PHP_VERSION_ID < 70400
37 #define zend_get_properties_for(struc, purpose) Z_OBJPROP_P((struc))
38
39 #define zend_release_properties(ht) do {} while (0)
40 #endif
41
42 #if PHP_VERSION_ID < 70300
43 #define zend_string_efree(s) zend_string_release((s))
44 #define GC_ADDREF(p) (++GC_REFCOUNT((p)))
45 #endif
46
47 #if defined(HAVE_APCU_SUPPORT)
48 # include "ext/apcu/apc_serializer.h"
49 #endif /* HAVE_APCU_SUPPORT */
50
51 #include "php_igbinary.h"
52
53 #include "igbinary.h"
54 #include "igbinary_macros.h"
55
56 #include <assert.h>
57 #include <ctype.h>
58
59 #ifndef PHP_WIN32
60 # include <inttypes.h>
61 # include <stdbool.h>
62 # include <stdint.h>
63 #endif
64
65 #include <stddef.h>
66 #include "hash.h"
67 #include "hash_ptr.h"
68 #include "zend_alloc.h"
69 #include "igbinary_zend_hash.h"
70
71 #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION)
72 /** Session serializer function prototypes. */
73 PS_SERIALIZER_FUNCS(igbinary);
74 #endif /* HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) */
75
76 #if defined(HAVE_APCU_SUPPORT)
77 /** Apc serializer function prototypes */
78 static int APC_SERIALIZER_NAME(igbinary) (APC_SERIALIZER_ARGS);
79 static int APC_UNSERIALIZER_NAME(igbinary) (APC_UNSERIALIZER_ARGS);
80 #endif
81
HASH_OF_OBJECT(zval * p)82 static inline HashTable *HASH_OF_OBJECT(zval *p) {
83 ZEND_ASSERT(Z_TYPE_P(p) == IS_OBJECT);
84 return Z_OBJ_HT_P(p)->get_properties(
85 #if PHP_VERSION_ID >= 80000
86 Z_OBJ_P(p)
87 #else
88 p
89 #endif
90 );
91 }
92
93 #if PHP_VERSION_ID < 70300
94 #define zend_string_release_ex(s, persistent) zend_string_release((s))
95
zval_ptr_dtor_str(zval * zval_ptr)96 static zend_always_inline void zval_ptr_dtor_str(zval *zval_ptr)
97 {
98 if (Z_REFCOUNTED_P(zval_ptr) && !Z_DELREF_P(zval_ptr)) {
99 ZEND_ASSERT(Z_TYPE_P(zval_ptr) == IS_STRING);
100 ZEND_ASSERT(!ZSTR_IS_INTERNED(Z_STR_P(zval_ptr)));
101 ZEND_ASSERT(!(GC_FLAGS(Z_STR_P(zval_ptr)) & IS_STR_PERSISTENT));
102 efree(Z_STR_P(zval_ptr));
103 }
104 }
105 #endif
106
107 #define RETURN_1_IF_NON_ZERO(cmd) \
108 if (UNEXPECTED((cmd) != 0)) { \
109 return 1; \
110 }
111
112 #ifdef ZEND_ACC_NOT_SERIALIZABLE
113 # define IGBINARY_IS_NOT_SERIALIZABLE(ce) UNEXPECTED((ce)->ce_flags & (ZEND_ACC_NOT_SERIALIZABLE | ZEND_ACC_ANON_CLASS))
114 # define IGBINARY_IS_NOT_UNSERIALIZABLE(ce) IGBINARY_IS_NOT_SERIALIZABLE(ce)
115 #elif PHP_VERSION_ID >= 70400
116 # define IGBINARY_IS_NOT_SERIALIZABLE(ce) UNEXPECTED((ce)->serialize == zend_class_serialize_deny)
117 # define IGBINARY_IS_NOT_UNSERIALIZABLE(ce) UNEXPECTED((ce)->unserialize == zend_class_unserialize_deny)
118 #else
119 // Because '__serialize' is not available prior to 7.4, this check is redundant.
120 # define IGBINARY_IS_NOT_SERIALIZABLE(ce) (0)
121 # define IGBINARY_IS_NOT_UNSERIALIZABLE(ce) (0)
122 #endif
123
124
125 /* {{{ Types */
126 enum igbinary_type {
127 /* 00 */ igbinary_type_null, /**< Null. */
128
129 /* 01 */ igbinary_type_ref8, /**< Array reference. */
130 /* 02 */ igbinary_type_ref16, /**< Array reference. */
131 /* 03 */ igbinary_type_ref32, /**< Array reference. */
132
133 /* 04 */ igbinary_type_bool_false, /**< Boolean true. */
134 /* 05 */ igbinary_type_bool_true, /**< Boolean false. */
135
136 /* 06 */ igbinary_type_long8p, /**< Long 8bit positive. */
137 /* 07 */ igbinary_type_long8n, /**< Long 8bit negative. */
138 /* 08 */ igbinary_type_long16p, /**< Long 16bit positive. */
139 /* 09 */ igbinary_type_long16n, /**< Long 16bit negative. */
140 /* 0a */ igbinary_type_long32p, /**< Long 32bit positive. */
141 /* 0b */ igbinary_type_long32n, /**< Long 32bit negative. */
142
143 /* 0c */ igbinary_type_double, /**< Double. */
144
145 /* 0d */ igbinary_type_string_empty, /**< Empty string. */
146
147 /* 0e */ igbinary_type_string_id8, /**< String id. */
148 /* 0f */ igbinary_type_string_id16, /**< String id. */
149 /* 10 */ igbinary_type_string_id32, /**< String id. */
150
151 /* 11 */ igbinary_type_string8, /**< String. */
152 /* 12 */ igbinary_type_string16, /**< String. */
153 /* 13 */ igbinary_type_string32, /**< String. */
154
155 /* 14 */ igbinary_type_array8, /**< Array. */
156 /* 15 */ igbinary_type_array16, /**< Array. */
157 /* 16 */ igbinary_type_array32, /**< Array. */
158
159 /* 17 */ igbinary_type_object8, /**< Object. */
160 /* 18 */ igbinary_type_object16, /**< Object. */
161 /* 19 */ igbinary_type_object32, /**< Object. */
162
163 /* 1a */ igbinary_type_object_id8, /**< Object class name string id. */
164 /* 1b */ igbinary_type_object_id16, /**< Object class name string id. */
165 /* 1c */ igbinary_type_object_id32, /**< Object class name string id. */
166
167 /* 1d */ igbinary_type_object_ser8, /**< Object serialized data. */
168 /* 1e */ igbinary_type_object_ser16, /**< Object serialized data. */
169 /* 1f */ igbinary_type_object_ser32, /**< Object serialized data. */
170
171 /* 20 */ igbinary_type_long64p, /**< Long 64bit positive. */
172 /* 21 */ igbinary_type_long64n, /**< Long 64bit negative. */
173
174 /* 22 */ igbinary_type_objref8, /**< Object reference. */
175 /* 23 */ igbinary_type_objref16, /**< Object reference. */
176 /* 24 */ igbinary_type_objref32, /**< Object reference. */
177
178 /* 25 */ igbinary_type_ref, /**< Simple reference */
179 /* 26 */ igbinary_type_string64, /**< String larger than 4GB (originally, php strings had a limit of 32-bit lengths). */
180 /* 27 */ igbinary_type_enum_case, /**< PHP 8.1 Enum case. */
181 };
182
183 /* Defers calls to zval_ptr_dtor for values that are refcounted. */
184 struct deferred_dtor_tracker {
185 zval *zvals; /**< refcounted objects and arrays to call dtor on after unserializing. See i_zval_ptr_dtor */
186 size_t count; /**< count of refcounted in array for calls to dtor */
187 size_t capacity; /**< capacity of refcounted in array for calls to dtor */
188 };
189
190 /** Serializer data.
191 * @author Oleg Grenrus <oleg.grenrus@dynamoid.com>
192 */
193 struct igbinary_serialize_data {
194 uint8_t *buffer; /**< Buffer. */
195 size_t buffer_size; /**< Buffer size. */
196 size_t buffer_capacity; /**< Buffer capacity. */
197 bool scalar; /**< Serializing scalar. */
198 bool compact_strings; /**< Check for duplicate strings. */
199 struct hash_si strings; /**< Hash of already serialized strings. */
200 struct hash_si_ptr references; /**< Hash of already serialized potential references. (non-NULL uintptr_t => int32_t) */
201 uint32_t references_id; /**< Number of things that the unserializer might think are references. >= length of references */
202 uint32_t string_count; /**< Serialized string count, used for back referencing */
203
204 struct deferred_dtor_tracker deferred_dtor_tracker; /**< refcounted objects and arrays to call dtor on after serializing. See i_zval_ptr_dtor */
205 };
206
207 /*
208 Object {
209 reference {scalar, object, array, null} (convert to reference, share reference in zval_ref)
210 object {} (convert to zend_object, share zend_object* in reference)
211 array {} (convert to zend_array, share zend_array* in reference)
212 empty array {} (use ZVAL_EMPTY_ARRAY to create zvals)
213 }
214 */
215 enum zval_ref_type {
216 IG_REF_IS_REFERENCE, // Points to zend_reference
217 IG_REF_IS_OBJECT, // Points to zend_object
218 IG_REF_IS_ARRAY, // Points to zend_array
219 #if PHP_VERSION_ID >= 70300
220 IG_REF_IS_EMPTY_ARRAY, // Use the macro ZVAL_EMPTY_ARRAY to create a pointer to the empty array with the correct type info flags.
221 #endif
222 };
223
224 struct igbinary_value_ref {
225 // We reuse temporary values for object properties that are references or arrays.
226 union {
227 zend_reference *reference;
228 zend_object *object;
229 zend_array *array;
230 } reference;
231 enum zval_ref_type type;
232 };
233
234 struct deferred_unserialize_call {
235 zval param; /* The array parameter passed to the __unserialize call */
236 zend_object *object; /* The object which has a deferred call to __unserialize that is going to get called. */
237 };
238
239 struct deferred_call {
240 union {
241 zend_object *wakeup;
242 #if PHP_VERSION_ID >= 70400
243 /* Currently, zvals are safe to relocate */
244 struct deferred_unserialize_call unserialize;
245 #endif
246 } data;
247 #if PHP_VERSION_ID >= 70400
248 zend_bool is_unserialize;
249 #endif
250 };
251 /** Unserializer data.
252 * @author Oleg Grenrus <oleg.grenrus@dynamoid.com>
253 */
254 struct igbinary_unserialize_data {
255 const uint8_t *buffer; /**< Buffer with bytes to unserialize. */
256 const uint8_t *buffer_end; /**< Buffer size. */
257 const uint8_t *buffer_ptr; /**< Current read offset. */
258
259 zend_string **strings; /**< Unserialized strings. */
260 size_t strings_count; /**< Unserialized string count. */
261 size_t strings_capacity; /**< Unserialized string array capacity. */
262
263 struct igbinary_value_ref *references; /**< Unserialized Arrays/Objects/References */
264 size_t references_count; /**< Unserialized array/objects count. */
265 size_t references_capacity; /**< Unserialized array/object array capacity. */
266
267 struct deferred_call *deferred; /**< objects&data for calls to __unserialize/__wakeup */
268 size_t deferred_count; /**< count of objects in array for calls to __unserialize/__wakeup */
269 size_t deferred_capacity; /**< capacity of objects in array for calls to __unserialize/__wakeup */
270 zend_bool deferred_finished; /**< whether the deferred calls were performed */
271 struct deferred_dtor_tracker deferred_dtor_tracker; /**< refcounted objects and arrays to call dtor on after unserializing. See i_zval_ptr_dtor */
272 };
273
274 #define IGB_REF_VAL_2(igsd, n) ((igsd)->references[(n)])
275 #define IGB_NEEDS_MORE_DATA(igsd, n) UNEXPECTED((size_t)((igsd)->buffer_end - (igsd)->buffer_ptr) < (n))
276 #define IGB_REMAINING_BYTES(igsd) ((unsigned int)((igsd)->buffer_end - (igsd)->buffer_ptr))
277 #define IGB_BUFFER_OFFSET(igsd) ((unsigned int)((igsd)->buffer_ptr - (igsd)->buffer))
278
279 #define WANT_CLEAR (0)
280 #define WANT_REF (1 << 1)
281
282 /* }}} */
283 /* {{{ Serializing functions prototypes */
284 inline static int igbinary_serialize_data_init(struct igbinary_serialize_data *igsd, bool scalar);
285 inline static void igbinary_serialize_data_deinit(struct igbinary_serialize_data *igsd);
286
287 inline static void igbinary_serialize_header(struct igbinary_serialize_data *igsd);
288
289 inline static int igbinary_serialize8(struct igbinary_serialize_data *igsd, uint8_t i);
290 inline static int igbinary_serialize16(struct igbinary_serialize_data *igsd, uint16_t i);
291 inline static int igbinary_serialize32(struct igbinary_serialize_data *igsd, uint32_t i);
292 inline static int igbinary_serialize64(struct igbinary_serialize_data *igsd, uint64_t i);
293
294 inline static int igbinary_serialize_null(struct igbinary_serialize_data *igsd);
295 inline static int igbinary_serialize_bool(struct igbinary_serialize_data *igsd, int b);
296 inline static int igbinary_serialize_long(struct igbinary_serialize_data *igsd, zend_long l);
297 inline static int igbinary_serialize_double(struct igbinary_serialize_data *igsd, double d);
298 inline static int igbinary_serialize_string(struct igbinary_serialize_data *igsd, zend_string *s);
299 inline static int igbinary_serialize_chararray(struct igbinary_serialize_data *igsd, const char *s, size_t len);
300
301 inline static int igbinary_serialize_array(struct igbinary_serialize_data *igsd, zval *z, bool object, bool incomplete_class, bool serialize_props);
302 inline static int igbinary_serialize_array_ref(struct igbinary_serialize_data *igsd, zval *z, bool object);
303 inline static int igbinary_serialize_array_sleep(struct igbinary_serialize_data *igsd, zval *z, HashTable *ht, zend_class_entry *ce);
304 inline static int igbinary_serialize_object_name(struct igbinary_serialize_data *igsd, zend_string *name);
305 inline static int igbinary_serialize_object(struct igbinary_serialize_data *igsd, zval *z);
306
307 static int igbinary_serialize_zval(struct igbinary_serialize_data *igsd, zval *z);
308 /* }}} */
309 /* {{{ Unserializing functions prototypes */
310 inline static int igbinary_unserialize_data_init(struct igbinary_unserialize_data *igsd);
311 inline static void igbinary_unserialize_data_deinit(struct igbinary_unserialize_data *igsd);
312
313 inline static int igbinary_unserialize_header(struct igbinary_unserialize_data *igsd);
314
315 inline static uint8_t igbinary_unserialize8(struct igbinary_unserialize_data *igsd);
316 inline static uint16_t igbinary_unserialize16(struct igbinary_unserialize_data *igsd);
317 inline static uint32_t igbinary_unserialize32(struct igbinary_unserialize_data *igsd);
318 inline static uint64_t igbinary_unserialize64(struct igbinary_unserialize_data *igsd);
319
320 inline static int igbinary_unserialize_long(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zend_long *ret);
321 inline static int igbinary_unserialize_double(struct igbinary_unserialize_data *igsd, double *ret);
322 inline static zend_string *igbinary_unserialize_string(struct igbinary_unserialize_data *igsd, enum igbinary_type t);
323 inline static zend_string *igbinary_unserialize_chararray(struct igbinary_unserialize_data *igsd, enum igbinary_type t);
324
325 inline static int igbinary_unserialize_array(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags, zend_bool create_ref);
326 inline static int igbinary_unserialize_object(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags);
327 static int igbinary_unserialize_object_ser(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, zend_class_entry *ce);
328 inline static int igbinary_unserialize_ref(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags);
329
330 inline static int igbinary_unserialize_zval(struct igbinary_unserialize_data *igsd, zval *const z, int flags);
331 /* }}} */
332 /* {{{ arginfo */
333 ZEND_BEGIN_ARG_INFO_EX(arginfo_igbinary_serialize, 0, 0, 1)
334 ZEND_ARG_INFO(0, value)
335 ZEND_END_ARG_INFO()
336
337 ZEND_BEGIN_ARG_INFO_EX(arginfo_igbinary_unserialize, 0, 0, 1)
338 ZEND_ARG_INFO(0, str)
339 ZEND_END_ARG_INFO()
340 /* }}} */
341 /* {{{ igbinary_functions[] */
342 /** Exported php functions. */
343 zend_function_entry igbinary_functions[] = {
344 PHP_FE(igbinary_serialize, arginfo_igbinary_serialize)
345 PHP_FE(igbinary_unserialize, arginfo_igbinary_unserialize)
346 PHP_FE_END
347 };
348 /* }}} */
349
350 /* {{{ igbinary dependencies */
351 static const zend_module_dep igbinary_module_deps[] = {
352 ZEND_MOD_REQUIRED("standard")
353 #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION)
354 ZEND_MOD_REQUIRED("session")
355 #endif
356 #if defined(HAVE_APCU_SUPPORT)
357 ZEND_MOD_OPTIONAL("apcu")
358 #endif
359 ZEND_MOD_END
360 };
361 /* }}} */
362
363 /* {{{ igbinary_module_entry */
364 zend_module_entry igbinary_module_entry = {
365 #if ZEND_MODULE_API_NO >= 20050922
366 STANDARD_MODULE_HEADER_EX, NULL,
367 igbinary_module_deps,
368 #elif ZEND_MODULE_API_NO >= 20010901
369 STANDARD_MODULE_HEADER,
370 #endif
371 "igbinary",
372 igbinary_functions,
373 PHP_MINIT(igbinary),
374 PHP_MSHUTDOWN(igbinary),
375 NULL,
376 NULL,
377 PHP_MINFO(igbinary),
378 #if ZEND_MODULE_API_NO >= 20010901
379 PHP_IGBINARY_VERSION, /* Replace with version number for your extension */
380 #endif
381 STANDARD_MODULE_PROPERTIES
382 };
383 /* }}} */
384
385 ZEND_DECLARE_MODULE_GLOBALS(igbinary)
386
387 /* {{{ ZEND_GET_MODULE */
388 #ifdef COMPILE_DL_IGBINARY
ZEND_GET_MODULE(igbinary)389 ZEND_GET_MODULE(igbinary)
390 #endif
391 /* }}} */
392
393 /* {{{ INI entries */
394 PHP_INI_BEGIN()
395 STD_PHP_INI_BOOLEAN("igbinary.compact_strings", "1", PHP_INI_ALL, OnUpdateBool, compact_strings, zend_igbinary_globals, igbinary_globals)
396 PHP_INI_END()
397 /* }}} */
398
399 /* {{{ php_igbinary_init_globals */
400 static void php_igbinary_init_globals(zend_igbinary_globals *igbinary_globals) {
401 igbinary_globals->compact_strings = 1;
402 }
403 /* }}} */
404
405 /* {{{ PHP_MINIT_FUNCTION */
406 /**
407 * The module init function.
408 * This allocates the persistent resources of this PHP module.
409 */
PHP_MINIT_FUNCTION(igbinary)410 PHP_MINIT_FUNCTION(igbinary) {
411 (void)type;
412 (void)module_number;
413 ZEND_INIT_MODULE_GLOBALS(igbinary, php_igbinary_init_globals, NULL);
414
415 #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION)
416 php_session_register_serializer("igbinary",
417 PS_SERIALIZER_ENCODE_NAME(igbinary),
418 PS_SERIALIZER_DECODE_NAME(igbinary));
419 #endif
420
421 #if defined(HAVE_APCU_SUPPORT)
422 apc_register_serializer("igbinary",
423 APC_SERIALIZER_NAME(igbinary),
424 APC_UNSERIALIZER_NAME(igbinary),
425 NULL);
426 #endif
427
428 REGISTER_INI_ENTRIES();
429
430 return SUCCESS;
431 }
432 /* }}} */
433 /* {{{ PHP_MSHUTDOWN_FUNCTION */
434 /**
435 * The module shutdown function.
436 * This cleans up all persistent resources of this PHP module.
437 */
PHP_MSHUTDOWN_FUNCTION(igbinary)438 PHP_MSHUTDOWN_FUNCTION(igbinary) {
439 (void)type;
440 (void)module_number;
441
442 #ifdef ZTS
443 ts_free_id(igbinary_globals_id);
444 #endif
445
446 /*
447 * Clean up ini entries.
448 * Aside: It seems like the php_session_register_serializer unserializes itself, since MSHUTDOWN in ext/wddx/wddx.c doesn't exist?
449 */
450 UNREGISTER_INI_ENTRIES();
451
452 return SUCCESS;
453 }
454 /* }}} */
455 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(igbinary)456 PHP_MINFO_FUNCTION(igbinary) {
457 (void)zend_module;
458 php_info_print_table_start();
459 php_info_print_table_row(2, "igbinary support", "enabled");
460 php_info_print_table_row(2, "igbinary version", PHP_IGBINARY_VERSION);
461 #if defined(HAVE_APCU_SUPPORT)
462 php_info_print_table_row(2, "igbinary APCu serializer ABI", APC_SERIALIZER_ABI);
463 #else
464 php_info_print_table_row(2, "igbinary APCu serializer ABI", "no");
465 #endif
466 #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION)
467 php_info_print_table_row(2, "igbinary session support", "yes");
468 #else
469 php_info_print_table_row(2, "igbinary session support", "no");
470 #endif
471 php_info_print_table_end();
472
473 DISPLAY_INI_ENTRIES();
474 }
475 /* }}} */
476
477 /* {{{ igsd management */
478 /* Append to list of references to take out later. Returns SIZE_MAX on allocation error. */
igsd_append_ref(struct igbinary_unserialize_data * igsd,struct igbinary_value_ref v)479 static inline size_t igsd_append_ref(struct igbinary_unserialize_data *igsd, struct igbinary_value_ref v)
480 {
481 size_t ref_n;
482 if (igsd->references_count + 1 >= igsd->references_capacity) {
483 igsd->references_capacity *= 2;
484
485 struct igbinary_value_ref *new_references = erealloc(igsd->references, sizeof(igsd->references[0]) * igsd->references_capacity);
486 if (UNEXPECTED(new_references == NULL)) {
487 return SIZE_MAX;
488 }
489 igsd->references = new_references;
490 }
491
492 ref_n = igsd->references_count++;
493 IGB_REF_VAL_2(igsd, ref_n) = v;
494 return ref_n;
495 }
496
igsd_ensure_defer_capacity(struct igbinary_unserialize_data * igsd)497 static inline int igsd_ensure_defer_capacity(struct igbinary_unserialize_data *igsd) {
498 if (igsd->deferred_count >= igsd->deferred_capacity) {
499 if (igsd->deferred_capacity == 0) {
500 igsd->deferred_capacity = 2;
501 igsd->deferred = emalloc(sizeof(igsd->deferred[0]) * igsd->deferred_capacity);
502 } else {
503 igsd->deferred_capacity *= 2;
504 struct deferred_call *old_deferred = igsd->deferred;
505 igsd->deferred = erealloc(old_deferred, sizeof(igsd->deferred[0]) * igsd->deferred_capacity);
506 if (UNEXPECTED(igsd->deferred == NULL)) {
507 igsd->deferred = old_deferred;
508 return 1;
509 }
510 }
511 }
512 return 0;
513 }
514
igsd_defer_wakeup(struct igbinary_unserialize_data * igsd,zend_object * object)515 static inline int igsd_defer_wakeup(struct igbinary_unserialize_data *igsd, zend_object *object) {
516 // TODO: This won't be properly garbage collected if there is an OOM error, but would php terminate instead?
517 RETURN_1_IF_NON_ZERO(igsd_ensure_defer_capacity(igsd));
518
519 struct deferred_call *c = &igsd->deferred[igsd->deferred_count++];
520 c->data.wakeup = object;
521 #if PHP_VERSION_ID >= 70400
522 c->is_unserialize = 0;
523 #endif
524 return 0;
525 }
526
527 /* igsd_defer_unserialize {{{ */
528 #if PHP_VERSION_ID >= 70400
igsd_defer_unserialize(struct igbinary_unserialize_data * igsd,zend_object * object,zval param)529 static inline int igsd_defer_unserialize(struct igbinary_unserialize_data *igsd, zend_object *object, zval param) {
530 RETURN_1_IF_NON_ZERO(igsd_ensure_defer_capacity(igsd));
531
532 struct deferred_call *c = &igsd->deferred[igsd->deferred_count++];
533 struct deferred_unserialize_call* call = &c->data.unserialize;
534 call->object = object;
535 ZEND_ASSERT(Z_TYPE(param) == IS_ARRAY);
536 call->param = param;
537 c->is_unserialize = 1;
538 return 0;
539 }
540 #endif
541 /* }}} */
542
543 /* {{{ igbinary_finish_deferred_calls
544 * After all object instances were unserialized, perform the deferred calls to __wakeup() on all of the objects implementing that method.
545 */
igbinary_finish_deferred_calls(struct igbinary_unserialize_data * igsd)546 static int igbinary_finish_deferred_calls(struct igbinary_unserialize_data *igsd) {
547 #if PHP_VERSION_ID >= 70400 && PHP_VERSION_ID < 80000
548 zval unserialize_name;
549 #endif
550 zval wakeup_name;
551 size_t i;
552 struct deferred_call *deferred_arr;
553 size_t deferred_count = igsd->deferred_count;
554 zend_bool delayed_call_failed = 0;
555 igsd->deferred_finished = 1;
556 if (deferred_count == 0) { /* nothing to do */
557 return 0;
558 }
559 deferred_arr = igsd->deferred;
560 #if PHP_VERSION_ID >= 70400 && PHP_VERSION_ID < 80000
561 ZVAL_STRINGL(&unserialize_name, "__unserialize", sizeof("__unserialize") - 1);
562 #endif
563 ZVAL_STRINGL(&wakeup_name, "__wakeup", sizeof("__wakeup") - 1);
564 for (i = 0; i < deferred_count; i++) {
565 struct deferred_call *deferred = &deferred_arr[i];
566 #if PHP_VERSION_ID >= 70400
567 if (deferred->is_unserialize) {
568 struct deferred_unserialize_call *unserialize_call = &deferred->data.unserialize;
569 zend_object *const obj = unserialize_call->object;
570 ZEND_ASSERT(Z_TYPE(unserialize_call->param) == IS_ARRAY);
571
572 if (!delayed_call_failed) {
573 #if PHP_VERSION_ID >= 80000
574 /* Copy the parameter for __unserialize so that changes in __unserialize won't mutate the original. */
575 // ZVAL_COPY(¶m, &unserialize_call->param);
576 BG(serialize_lock)++;
577 zend_call_known_instance_method_with_1_params(
578 obj->ce->__unserialize, obj, NULL, &unserialize_call->param);
579 if (EG(exception)) {
580 delayed_call_failed = 1;
581 GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED);
582 }
583 BG(serialize_lock)--;
584 #else
585 zval retval;
586 zval zv;
587 ZVAL_OBJ(&zv, obj);
588 /* Copy the parameter for __unserialize so that changes in __unserialize won't mutate the original. */
589 // ZVAL_COPY(¶m, &unserialize_call->param);
590 BG(serialize_lock)++;
591 if (call_user_function(CG(function_table), &zv, &unserialize_name, &retval, 1, &unserialize_call->param) == FAILURE || Z_ISUNDEF(retval))
592 {
593 delayed_call_failed = 1;
594 GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED);
595 }
596 BG(serialize_lock)--;
597 zval_ptr_dtor(&retval);
598 #endif
599 } else {
600 GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED);
601 }
602
603 zval_ptr_dtor(&unserialize_call->param);
604 } else
605 #endif
606 {
607 zend_object *obj = deferred->data.wakeup;
608 if (!delayed_call_failed) {
609 zval retval; /* return value of __wakeup */
610 zval rval;
611 ZVAL_OBJ(&rval, obj);
612 if (UNEXPECTED(call_user_function(CG(function_table), &rval, &wakeup_name, &retval, 0, 0) == FAILURE || Z_ISUNDEF(retval))) {
613 delayed_call_failed = 1;
614 GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED);
615 }
616 zval_ptr_dtor(&retval);
617 } else {
618 GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED);
619 }
620 }
621 }
622 zval_ptr_dtor_str(&wakeup_name);
623 #if PHP_VERSION_ID >= 70400 && PHP_VERSION_ID < 80000
624 zval_ptr_dtor_str(&unserialize_name);
625 #endif
626 return delayed_call_failed;
627 }
628 /* }}} */
629 /* }}} */
630
631 /* {{{ igsd_ensure_deferred_dtor_capacity(struct igbinary_serialize_data *igsd) */
igsd_ensure_deferred_dtor_capacity(struct deferred_dtor_tracker * tracker)632 static inline int igsd_ensure_deferred_dtor_capacity(struct deferred_dtor_tracker *tracker) {
633 if (tracker->count >= tracker->capacity) {
634 if (tracker->capacity == 0) {
635 tracker->capacity = 2;
636 tracker->zvals = emalloc(sizeof(tracker->zvals[0]) * tracker->capacity);
637 } else {
638 tracker->capacity *= 2;
639 zval *old_deferred_dtor = tracker->zvals;
640 tracker->zvals = erealloc(old_deferred_dtor, sizeof(tracker->zvals[0]) * tracker->capacity);
641 if (UNEXPECTED(tracker->zvals == NULL)) {
642 tracker->zvals = old_deferred_dtor;
643 return 1;
644 }
645 }
646 }
647 return 0;
648 }
649 /* }}} */
650
651 /* {{{ free_deferred_dtors(struct deferred_dtor_tracker *tracker) */
free_deferred_dtors(struct deferred_dtor_tracker * tracker)652 static inline void free_deferred_dtors(struct deferred_dtor_tracker *tracker) {
653 zval *const zvals = tracker->zvals;
654 if (zvals) {
655 const size_t n = tracker->count;
656 size_t i;
657 for (i = 0; i < n; i++) {
658 /* fprintf(stderr, "freeing i=%d id=%d refcount=%d\n", (int)i, (int)Z_OBJ_HANDLE(zvals[i]), (int)Z_REFCOUNT(zvals[i])); */
659 zval_ptr_dtor(&zvals[i]);
660 }
661 efree(zvals);
662 }
663 }
664 /* }}} */
665
666 /* {{{ igsd_addref_and_defer_dtor(struct igbinary_serialize_data *igsd, zval *z) */
igsd_addref_and_defer_dtor(struct deferred_dtor_tracker * tracker,zval * z)667 static inline int igsd_addref_and_defer_dtor(struct deferred_dtor_tracker *tracker, zval *z) {
668 if (!Z_REFCOUNTED_P(z)) {
669 return 0;
670 }
671 if (igsd_ensure_deferred_dtor_capacity(tracker)) {
672 return 1;
673 }
674
675 ZEND_ASSERT(Z_REFCOUNT_P(z) >= 1); /* Expect that there were references at the time this was serialized */
676 ZVAL_COPY(&tracker->zvals[tracker->count++], z); /* Copy and increase reference count */
677 return 0;
678 }
679 /* }}} */
680 /* {{{ igsd_defer_dtor(struct igbinary_serialize_data *igsd, zval *z) */
igsd_defer_dtor(struct deferred_dtor_tracker * tracker,zval * z)681 static inline int igsd_defer_dtor(struct deferred_dtor_tracker *tracker, zval *z) {
682 if (!Z_REFCOUNTED_P(z)) {
683 return 0;
684 }
685 if (igsd_ensure_deferred_dtor_capacity(tracker)) {
686 return 1;
687 }
688
689 ZEND_ASSERT(Z_REFCOUNT_P(z) >= 1); /* Expect that there were references at the time this was serialized */
690 ZVAL_COPY_VALUE(&tracker->zvals[tracker->count++], z); /* Copy without increasing reference count */
691 return 0;
692 }
693 /* }}} */
694 /* {{{ int igbinary_serialize(uint8_t**, size_t*, zval*) */
igbinary_serialize(uint8_t ** ret,size_t * ret_len,zval * z)695 IGBINARY_API int igbinary_serialize(uint8_t **ret, size_t *ret_len, zval *z) {
696 return igbinary_serialize_ex(ret, ret_len, z, NULL);
697 }
698 /* }}} */
699 /* {{{ int igbinary_serialize_ex(uint8_t**, size_t*, zval*, igbinary_memory_manager*) */
700 /**
701 * Serializes data, and writes the allocated byte buffer into ret and the buffer's length into ret_len.
702 * @param ret output parameter
703 * @param ret_len length of byte buffer ret
704 * @param z the zval (data) to serialize
705 * @param memory_manager (nullable) the memory manager to use for allocating/reallocating the buffer of serialized data. Used by extensions such as APCu
706 * @return 0 on success, 1 on failure
707 */
igbinary_serialize_ex(uint8_t ** ret,size_t * ret_len,zval * z,struct igbinary_memory_manager * memory_manager)708 IGBINARY_API int igbinary_serialize_ex(uint8_t **ret, size_t *ret_len, zval *z, struct igbinary_memory_manager *memory_manager) {
709 struct igbinary_serialize_data igsd;
710 uint8_t *tmpbuf;
711 int return_code;
712 // While we can't get passed references through the PHP_FUNCTIONs igbinary declares, third party code can invoke igbinary's methods with references.
713 // See https://github.com/php-memcached-dev/php-memcached/issues/326
714 if (UNEXPECTED(Z_TYPE_P(z) == IS_INDIRECT)) {
715 z = Z_INDIRECT_P(z);
716 }
717 ZVAL_DEREF(z);
718
719 if (UNEXPECTED(igbinary_serialize_data_init(&igsd, Z_TYPE_P(z) != IS_OBJECT && Z_TYPE_P(z) != IS_ARRAY))) {
720 zend_error(E_WARNING, "igbinary_serialize: cannot init igsd");
721 return 1;
722 }
723
724 igbinary_serialize_header(&igsd);
725 return_code = 0;
726
727 if (UNEXPECTED(igbinary_serialize_zval(&igsd, z) != 0)) {
728 return_code = 1;
729 goto cleanup;
730 }
731
732 /* Explicit null termination */
733 /* TODO: Stop doing this in the next major version, serialized data can contain nulls in the middle and callers should check length */
734 if (UNEXPECTED(igbinary_serialize8(&igsd, 0) != 0)) {
735 return_code = 1;
736 goto cleanup;
737 }
738
739 /* shrink buffer to the real length, ignore errors */
740 if (UNEXPECTED(memory_manager)) {
741 tmpbuf = memory_manager->alloc(igsd.buffer_size, memory_manager->context);
742 if (tmpbuf != NULL) {
743 memcpy(tmpbuf, igsd.buffer, igsd.buffer_size);
744 }
745
746 if (tmpbuf == NULL) {
747 return_code = 1;
748 goto cleanup;
749 }
750 *ret = tmpbuf;
751 *ret_len = igsd.buffer_size - 1;
752 } else {
753 /* Set return values */
754 *ret_len = igsd.buffer_size - 1;
755 *ret = igsd.buffer;
756 igsd.buffer = NULL;
757 }
758
759 cleanup:
760 igbinary_serialize_data_deinit(&igsd);
761 return return_code;
762 }
763 /* }}} */
764 /* {{{ int igbinary_unserialize(const uint8_t *, size_t, zval **) */
765 /**
766 * Unserializes the data into z
767 * @param buf the read-only buffer with the serialized data
768 * @param buf_len the length of that buffer.
769 * @param z output parameter. Will contain the unserialized value(zval).
770 * @return 0 on success, 1 on failure
771 */
igbinary_unserialize(const uint8_t * buf,size_t buf_len,zval * z)772 IGBINARY_API int igbinary_unserialize(const uint8_t *buf, size_t buf_len, zval *z) {
773 struct igbinary_unserialize_data igsd;
774 int ret = 0;
775
776 igbinary_unserialize_data_init(&igsd);
777
778 igsd.buffer = buf;
779 igsd.buffer_ptr = buf;
780 igsd.buffer_end = buf + buf_len;
781
782 if (UNEXPECTED(igbinary_unserialize_header(&igsd))) {
783 ret = 1;
784 goto cleanup;
785 }
786
787 if (UNEXPECTED(igbinary_unserialize_zval(&igsd, z, WANT_CLEAR))) {
788 ret = 1;
789 goto cleanup;
790 }
791
792 if (UNEXPECTED(igsd.buffer_ptr < igsd.buffer_end)) {
793 // https://github.com/igbinary/igbinary/issues/64
794 zend_error(E_WARNING, "igbinary_unserialize: received more data to unserialize than expected");
795 ret = 1;
796 goto cleanup;
797 }
798
799 if (UNEXPECTED(igbinary_finish_deferred_calls(&igsd))) {
800 ret = 1;
801 goto cleanup;
802 }
803 cleanup:
804 igbinary_unserialize_data_deinit(&igsd);
805
806 return ret;
807 }
808 /* }}} */
809 /* {{{ proto string igbinary_unserialize(mixed value) */
810 /**
811 * @see igbinary.php for more detailed API documentation.
812 */
PHP_FUNCTION(igbinary_unserialize)813 PHP_FUNCTION(igbinary_unserialize) {
814 char *string = NULL;
815 size_t string_len;
816
817 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &string, &string_len) == FAILURE) {
818 RETURN_NULL();
819 }
820
821 if (string_len <= 0) {
822 RETURN_FALSE;
823 }
824
825 if (igbinary_unserialize((uint8_t *)string, string_len, return_value) != 0) {
826 /* FIXME: is this a good place? a catch all */
827 zval_ptr_dtor(return_value);
828 RETURN_NULL();
829 }
830 }
831 /* }}} */
832 /* {{{ proto mixed igbinary_serialize(string value) */
833 /**
834 * @see igbinary.php for more detailed API documentation.
835 */
PHP_FUNCTION(igbinary_serialize)836 PHP_FUNCTION(igbinary_serialize) {
837 zval *z;
838 uint8_t *string;
839 size_t string_len;
840
841 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &z) == FAILURE) {
842 RETURN_NULL();
843 }
844
845 if (igbinary_serialize(&string, &string_len, z) != 0) {
846 RETURN_NULL();
847 }
848
849 RETVAL_STRINGL((char *)string, string_len);
850 efree(string);
851 }
852 /* }}} */
853 #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION)
854 /* {{{ Serializer encode function */
855 /**
856 * This provides a serializer encode function for PHP's session module (using igbinary),
857 * if igbinary was compiled with session support.
858 *
859 * Session support has to be statically compiled into php to use igbinary,
860 * due to the lack of a cross-platform way to register a session serializer/unserializer
861 * when the session module isn't available.
862 */
PS_SERIALIZER_ENCODE_FUNC(igbinary)863 PS_SERIALIZER_ENCODE_FUNC(igbinary)
864 {
865 zval *session_vars;
866 zend_string *result;
867 struct igbinary_serialize_data igsd;
868
869 session_vars = &(PS(http_session_vars));
870 if (Z_ISREF_P(session_vars)) {
871 session_vars = Z_REFVAL_P(session_vars);
872 }
873 if (igbinary_serialize_data_init(&igsd, false)) {
874 zend_error(E_WARNING, "igbinary_serialize: cannot init igsd");
875 return ZSTR_EMPTY_ALLOC();
876 }
877
878 igbinary_serialize_header(&igsd);
879
880 /** We serialize the passed in array of session_var (including the empty array, for #231) */
881 /** the same way we would serialize a regular array. */
882 /** The corresponding PS_SERIALIZER_DECODE_FUNC will unserialize the array and individually add the session variables. */
883 if (igbinary_serialize_array(&igsd, session_vars, false, false, true) != 0) {
884 zend_error(E_WARNING, "igbinary_serialize: cannot serialize session variables");
885 result = ZSTR_EMPTY_ALLOC();
886 } else {
887 /* Copy the buffer to a new zend_string */
888 result = zend_string_init((const char *)igsd.buffer, igsd.buffer_size, 0);
889 }
890
891 igbinary_serialize_data_deinit(&igsd);
892
893 return result;
894 }
895 /* }}} */
896 /* {{{ Serializer decode function */
897 /**
898 * This provides a serializer decode function for PHP's session module (using igbinary),
899 * if igbinary was compiled with session support.
900 *
901 * Session support has to be statically compiled into php to use igbinary,
902 * due to the lack of a cross-platform way to register a session serializer/unserializer
903 * when the session module isn't available.
904 *
905 * This is similar to PS_SERIALIZER_DECODE_FUNC(php) from ext/session/session.c
906 */
PS_SERIALIZER_DECODE_FUNC(igbinary)907 PS_SERIALIZER_DECODE_FUNC(igbinary) {
908 HashTable *tmp_hash;
909 zval z;
910 zval *d;
911 zend_string *key;
912 int ret = 0;
913
914 struct igbinary_unserialize_data igsd;
915
916 if (!val || vallen == 0) {
917 return SUCCESS;
918 }
919
920 if (igbinary_unserialize_data_init(&igsd) != 0) {
921 return FAILURE;
922 }
923
924 igsd.buffer = (const uint8_t *)val;
925 igsd.buffer_ptr = igsd.buffer;
926 igsd.buffer_end = igsd.buffer + vallen;
927
928 if (UNEXPECTED(igbinary_unserialize_header(&igsd))) {
929 ret = 1;
930 goto deinit;
931 }
932
933 /** The serializer serialized the session variables as an array. So, we unserialize that array. */
934 /** We then iterate over the array to set the individual session variables (managing the reference counts), then free the original array. */
935 if (UNEXPECTED(igbinary_unserialize_zval(&igsd, &z, WANT_CLEAR))) {
936 ret = 1;
937 goto deinit;
938 }
939
940 ret = igbinary_finish_deferred_calls(&igsd);
941
942 deinit:
943 igbinary_unserialize_data_deinit(&igsd);
944 if (UNEXPECTED(ret)) {
945 return FAILURE;
946 }
947
948 /* Validate that this is of the correct data type */
949 tmp_hash = HASH_OF(&z);
950 if (tmp_hash == NULL) {
951 zval_ptr_dtor(&z);
952 return FAILURE;
953 }
954
955 ZEND_HASH_FOREACH_STR_KEY_VAL(tmp_hash, key, d) {
956 if (key == NULL) { /* array key is a number, how? Skip it. */
957 /* ??? */
958 continue;
959 }
960 if (php_set_session_var(key, d, NULL)) { /* Added to session successfully */
961 /* Refcounted types such as arrays, objects, references need to have references incremented manually, so that zval_ptr_dtor doesn't clean up pointers they include. */
962 /* Non-refcounted types have the data copied. */
963 Z_TRY_ADDREF_P(d);
964 }
965 } ZEND_HASH_FOREACH_END();
966
967 zval_ptr_dtor(&z);
968
969 return SUCCESS;
970 }
971 /* }}} */
972 #endif /* HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) */
973
974 #if defined(HAVE_APCU_SUPPORT)
975 /* {{{ apc_serialize function */
APC_SERIALIZER_NAME(igbinary)976 static int APC_SERIALIZER_NAME(igbinary) ( APC_SERIALIZER_ARGS ) {
977 (void)config;
978
979 if (igbinary_serialize(buf, buf_len, (zval *)value) == 0) {
980 /* flipped semantics - We return 1 to indicate success to APCu (and 0 for failure) */
981 return 1;
982 }
983 return 0;
984 }
985 /* }}} */
986 /* {{{ apc_unserialize function */
APC_UNSERIALIZER_NAME(igbinary)987 static int APC_UNSERIALIZER_NAME(igbinary) ( APC_UNSERIALIZER_ARGS ) {
988 (void)config;
989
990 if (igbinary_unserialize(buf, buf_len, value) == 0) {
991 /* flipped semantics - We return 1 to indicate success to APCu (and 0 for failure) */
992 return 1;
993 }
994 /* Failed. free return value */
995 zval_ptr_dtor(value);
996 ZVAL_NULL(value); /* and replace the incomplete value with null just in case APCu uses it in the future */
997 return 0;
998 }
999 /* }}} */
1000 #endif
1001
1002 /* {{{ igbinary_serialize_data_init */
1003 /**
1004 * Allocates data structures needed by igbinary_serialize_data,
1005 * and sets properties of igsd to their defaults.
1006 * @param igsd the struct to initialize
1007 * @param scalar true if the data being serialized is a scalar
1008 * @param memory_manager optional override of the memory manager
1009 */
igbinary_serialize_data_init(struct igbinary_serialize_data * igsd,bool scalar)1010 inline static int igbinary_serialize_data_init(struct igbinary_serialize_data *igsd, bool scalar) {
1011 int r = 0;
1012
1013 igsd->buffer = NULL;
1014 igsd->buffer_size = 0;
1015 igsd->buffer_capacity = 32;
1016 igsd->string_count = 0;
1017
1018 igsd->buffer = emalloc(igsd->buffer_capacity);
1019 if (UNEXPECTED(igsd->buffer == NULL)) {
1020 return 1;
1021 }
1022
1023 igsd->scalar = scalar;
1024 if (scalar) {
1025 igsd->compact_strings = 0;
1026 } else {
1027 hash_si_init(&igsd->strings, 16);
1028 hash_si_ptr_init(&igsd->references, 16);
1029 igsd->references_id = 0;
1030 igsd->compact_strings = (bool)IGBINARY_G(compact_strings);
1031 igsd->deferred_dtor_tracker.zvals = NULL;
1032 igsd->deferred_dtor_tracker.count = 0;
1033 igsd->deferred_dtor_tracker.capacity = 0;
1034 }
1035
1036 return r;
1037 }
1038 /* }}} */
1039 /* {{{ igbinary_serialize_data_deinit */
1040 /** Deinits igbinary_serialize_data, freeing the allocated data structures. */
igbinary_serialize_data_deinit(struct igbinary_serialize_data * igsd)1041 inline static void igbinary_serialize_data_deinit(struct igbinary_serialize_data *igsd) {
1042 if (igsd->buffer) {
1043 efree(igsd->buffer);
1044 }
1045
1046 if (!igsd->scalar) {
1047 hash_si_deinit(&igsd->strings);
1048 hash_si_ptr_deinit(&igsd->references);
1049 free_deferred_dtors(&igsd->deferred_dtor_tracker);
1050 }
1051 }
1052 /* }}} */
1053 /* {{{ igbinary_serialize_header */
1054 /** Serializes header ("\x00\x00\x00\x02"). */
igbinary_serialize_header(struct igbinary_serialize_data * igsd)1055 inline static void igbinary_serialize_header(struct igbinary_serialize_data *igsd) {
1056 uint8_t* append_buffer = igsd->buffer;
1057 ZEND_ASSERT(igsd->buffer_size == 0);
1058 ZEND_ASSERT(igsd->buffer_capacity >= 4);
1059 append_buffer[0] = 0;
1060 append_buffer[1] = 0;
1061 append_buffer[2] = 0;
1062 append_buffer[3] = IGBINARY_FORMAT_VERSION;
1063 igsd->buffer_size = 4;
1064 }
1065 /* }}} */
igbinary_raise_capacity(struct igbinary_serialize_data * igsd,size_t size)1066 static int igbinary_raise_capacity(struct igbinary_serialize_data *igsd, size_t size) {
1067 do {
1068 igsd->buffer_capacity *= 2;
1069 } while (igsd->buffer_size + size >= igsd->buffer_capacity);
1070
1071 uint8_t *const old_buffer = igsd->buffer;
1072 igsd->buffer = erealloc(old_buffer, igsd->buffer_capacity);
1073 if (UNEXPECTED(igsd->buffer == NULL)) {
1074 /* We failed to allocate a larger buffer for the result. Free the memory used for the original buffer. */
1075 efree(old_buffer);
1076 return 1;
1077 }
1078
1079 return 0;
1080 }
1081 /* {{{ igbinary_serialize_resize */
1082 /** Expands igbinary_serialize_data if necessary. */
igbinary_serialize_resize(struct igbinary_serialize_data * igsd,size_t size)1083 inline static int igbinary_serialize_resize(struct igbinary_serialize_data *igsd, size_t size) {
1084 if (igsd->buffer_size + size < igsd->buffer_capacity) {
1085 return 0;
1086 }
1087
1088 return igbinary_raise_capacity(igsd, size);
1089 }
1090 /* }}} */
1091 /* {{{ igbinary_serialize8 */
1092 /** Serialize 8bit value. */
igbinary_serialize8(struct igbinary_serialize_data * igsd,uint8_t i)1093 inline static int igbinary_serialize8(struct igbinary_serialize_data *igsd, uint8_t i) {
1094 RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 1));
1095
1096 igsd->buffer[igsd->buffer_size++] = i;
1097 return 0;
1098 }
1099 /* }}} */
1100 /* {{{ igbinary_serialize16 */
1101 /** Serialize 16bit value. */
igbinary_serialize16(struct igbinary_serialize_data * igsd,uint16_t i)1102 inline static int igbinary_serialize16(struct igbinary_serialize_data *igsd, uint16_t i) {
1103 uint8_t *append_buffer;
1104 RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 2));
1105
1106 append_buffer = &igsd->buffer[igsd->buffer_size];
1107 append_buffer[0] = (uint8_t)(i >> 8 & 0xff);
1108 append_buffer[1] = (uint8_t)(i & 0xff);
1109 igsd->buffer_size += 2;
1110
1111 return 0;
1112 }
1113 /* }}} */
1114 /* {{{ igbinary_serialize32 */
1115 /** Serialize 32bit value. */
igbinary_serialize32(struct igbinary_serialize_data * igsd,uint32_t i)1116 inline static int igbinary_serialize32(struct igbinary_serialize_data *igsd, uint32_t i) {
1117 uint8_t *append_buffer;
1118 RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 4));
1119
1120 append_buffer = &igsd->buffer[igsd->buffer_size];
1121 append_buffer[0] = (uint8_t)(i >> 24 & 0xff);
1122 append_buffer[1] = (uint8_t)(i >> 16 & 0xff);
1123 append_buffer[2] = (uint8_t)(i >> 8 & 0xff);
1124 append_buffer[3] = (uint8_t)(i & 0xff);
1125 igsd->buffer_size += 4;
1126
1127 return 0;
1128 }
1129 /* }}} */
1130 /* {{{ igbinary_serialize64 */
1131 /** Serialize 64bit value. */
igbinary_serialize64(struct igbinary_serialize_data * igsd,uint64_t i)1132 inline static int igbinary_serialize64(struct igbinary_serialize_data *igsd, uint64_t i) {
1133 uint8_t *append_buffer;
1134 RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 8));
1135
1136 append_buffer = &igsd->buffer[igsd->buffer_size];
1137 append_buffer[0] = (uint8_t)(i >> 56 & 0xff);
1138 append_buffer[1] = (uint8_t)(i >> 48 & 0xff);
1139 append_buffer[2] = (uint8_t)(i >> 40 & 0xff);
1140 append_buffer[3] = (uint8_t)(i >> 32 & 0xff);
1141 append_buffer[4] = (uint8_t)(i >> 24 & 0xff);
1142 append_buffer[5] = (uint8_t)(i >> 16 & 0xff);
1143 append_buffer[6] = (uint8_t)(i >> 8 & 0xff);
1144 append_buffer[7] = (uint8_t)(i & 0xff);
1145 igsd->buffer_size += 8;
1146
1147 return 0;
1148 }
1149 /* }}} */
1150 /* {{{ igbinary_serialize8 */
1151 /** Serialize 8bit value + 8bit value. */
igbinary_serialize8_and_8(struct igbinary_serialize_data * igsd,uint8_t i,uint8_t v)1152 inline static int igbinary_serialize8_and_8(struct igbinary_serialize_data *igsd, uint8_t i, uint8_t v) {
1153 uint8_t *append_buffer;
1154 RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 2));
1155 append_buffer = &igsd->buffer[igsd->buffer_size];
1156
1157 append_buffer[0] = i;
1158 append_buffer[1] = v;
1159 igsd->buffer_size += 2;
1160 return 0;
1161 }
1162 /* }}} */
1163 /** Serialize 8bit value + 16bit value. */
igbinary_serialize8_and_16(struct igbinary_serialize_data * igsd,uint8_t i,uint16_t v)1164 inline static int igbinary_serialize8_and_16(struct igbinary_serialize_data *igsd, uint8_t i, uint16_t v) {
1165 uint8_t *append_buffer;
1166 RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 3));
1167 append_buffer = &igsd->buffer[igsd->buffer_size];
1168
1169 append_buffer[0] = i;
1170 append_buffer[1] = (uint8_t)(v >> 8 & 0xff);
1171 append_buffer[2] = (uint8_t)(v & 0xff);
1172 ;
1173 igsd->buffer_size += 3;
1174 return 0;
1175 }
1176 /* }}} */
1177 /** Serialize 8bit value + 32bit value. */
igbinary_serialize8_and_32(struct igbinary_serialize_data * igsd,uint8_t i,uint32_t v)1178 inline static int igbinary_serialize8_and_32(struct igbinary_serialize_data *igsd, uint8_t i, uint32_t v) {
1179 uint8_t *append_buffer;
1180 RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 5));
1181 append_buffer = &igsd->buffer[igsd->buffer_size];
1182
1183 append_buffer[0] = i;
1184 append_buffer[1] = (uint8_t)(v >> 24 & 0xff);
1185 append_buffer[2] = (uint8_t)(v >> 16 & 0xff);
1186 append_buffer[3] = (uint8_t)(v >> 8 & 0xff);
1187 append_buffer[4] = (uint8_t)(v & 0xff);
1188 ;
1189 igsd->buffer_size += 5;
1190 return 0;
1191 }
1192 /* }}} */
1193 /** Serialize 8bit value + 64bit value. */
igbinary_serialize8_and_64(struct igbinary_serialize_data * igsd,uint8_t i,uint64_t v)1194 inline static int igbinary_serialize8_and_64(struct igbinary_serialize_data *igsd, uint8_t i, uint64_t v) {
1195 uint8_t *append_buffer;
1196 RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 9));
1197 append_buffer = &igsd->buffer[igsd->buffer_size];
1198
1199 append_buffer[0] = i;
1200 append_buffer[1] = (uint8_t)(v >> 56 & 0xff);
1201 append_buffer[2] = (uint8_t)(v >> 48 & 0xff);
1202 append_buffer[3] = (uint8_t)(v >> 40 & 0xff);
1203 append_buffer[4] = (uint8_t)(v >> 32 & 0xff);
1204 append_buffer[5] = (uint8_t)(v >> 24 & 0xff);
1205 append_buffer[6] = (uint8_t)(v >> 16 & 0xff);
1206 append_buffer[7] = (uint8_t)(v >> 8 & 0xff);
1207 append_buffer[8] = (uint8_t)(v & 0xff);
1208 ;
1209 igsd->buffer_size += 9;
1210 return 0;
1211 }
1212 /* }}} */
1213 /* {{{ igbinary_serialize_null */
1214 /** Serializes null. */
igbinary_serialize_null(struct igbinary_serialize_data * igsd)1215 inline static int igbinary_serialize_null(struct igbinary_serialize_data *igsd) {
1216 return igbinary_serialize8(igsd, igbinary_type_null);
1217 }
1218 /* }}} */
1219 /* {{{ igbinary_serialize_bool */
1220 /** Serializes bool. */
igbinary_serialize_bool(struct igbinary_serialize_data * igsd,int b)1221 inline static int igbinary_serialize_bool(struct igbinary_serialize_data *igsd, int b) {
1222 return igbinary_serialize8(igsd, (uint8_t)(b ? igbinary_type_bool_true : igbinary_type_bool_false));
1223 }
1224 /* }}} */
1225 /* {{{ igbinary_serialize_long */
1226 /** Serializes zend_long. */
igbinary_serialize_long(struct igbinary_serialize_data * igsd,zend_long l)1227 inline static int igbinary_serialize_long(struct igbinary_serialize_data *igsd, zend_long l) {
1228 const bool p = l >= 0;
1229 // k is the absolute value of l
1230 const zend_ulong k = p ? (zend_ulong)l : -(zend_ulong)l;
1231
1232 if (k <= 0xff) {
1233 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_8(igsd,
1234 p ? igbinary_type_long8p : igbinary_type_long8n,
1235 (uint8_t)k
1236 ));
1237 } else if (k <= 0xffff) {
1238 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_16(igsd,
1239 p ? igbinary_type_long16p : igbinary_type_long16n,
1240 (uint16_t)k
1241 ));
1242 #if SIZEOF_ZEND_LONG == 8
1243 } else if (k <= 0xffffffff) {
1244 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd,
1245 p ? igbinary_type_long32p : igbinary_type_long32n,
1246 (uint32_t)k
1247 ));
1248 } else {
1249 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_64(igsd,
1250 p ? igbinary_type_long64p : igbinary_type_long64n,
1251 (uint64_t)k
1252 ));
1253 }
1254 #elif SIZEOF_ZEND_LONG == 4
1255 } else {
1256 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd,
1257 p ? igbinary_type_long32p : igbinary_type_long32n,
1258 (uint32_t)k
1259 ));
1260 }
1261 #else
1262 #error "Strange sizeof(zend_long)."
1263 #endif
1264
1265 return 0;
1266 }
1267 /* }}} */
1268 /* {{{ igbinary_serialize_double */
1269 /** Serializes double. */
igbinary_serialize_double(struct igbinary_serialize_data * igsd,double d)1270 inline static int igbinary_serialize_double(struct igbinary_serialize_data *igsd, double d) {
1271 union {
1272 double d;
1273 uint64_t u;
1274 } u;
1275
1276 u.d = d;
1277
1278 return igbinary_serialize8_and_64(igsd, igbinary_type_double, u.u);
1279 }
1280 /* }}} */
1281 /* {{{ igbinary_serialize_string */
1282 /**
1283 * Serializes string.
1284 *
1285 * When compact_strings is true,
1286 * this will serialize the string as igbinary_type_string* (A length followed by a character array) the first time,
1287 * and serialize subsequent references to the same string as igbinary_type_string_id*.
1288 *
1289 * When compact_strings is false, this will always serialize the string as a character array.
1290 * compact_strings speeds up serialization, but slows down serialization and uses more space to represent the serialization.
1291 *
1292 * Serializes each string once.
1293 * After first time uses pointers (igbinary_type_string_id*) instead of igbinary_type_string*.
1294 */
igbinary_serialize_string(struct igbinary_serialize_data * igsd,zend_string * s)1295 inline static int igbinary_serialize_string(struct igbinary_serialize_data *igsd, zend_string *s) {
1296 const size_t len = ZSTR_LEN(s);
1297 if (len == 0) {
1298 /* The empty string is always serialized as igbinary_serialize_string (1 byte instead of 2) */
1299 return igbinary_serialize8(igsd, igbinary_type_string_empty);
1300 }
1301
1302 if (!igsd->scalar && igsd->compact_strings) {
1303 struct hash_si_result result = hash_si_find_or_insert(&igsd->strings, s, igsd->string_count);
1304 if (result.code == hash_si_code_exists) {
1305 uint32_t value = result.value;
1306 if (value <= 0xff) {
1307 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_8(igsd, (uint8_t)igbinary_type_string_id8, (uint8_t)value));
1308 } else if (value <= 0xffff) {
1309 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_16(igsd, (uint8_t)igbinary_type_string_id16, (uint16_t)value));
1310 } else {
1311 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd, (uint8_t)igbinary_type_string_id32, value));
1312 }
1313 return 0;
1314 } else if (EXPECTED(result.code == hash_si_code_inserted)) {
1315 /* Fall through to igbinary_serialize_chararray */
1316 } else {
1317 return 1; /* Failed to allocate copy of string */
1318 }
1319 }
1320
1321 igsd->string_count++; /* A new string is being serialized - update count so that duplicate class names can be used. */
1322 if (UNEXPECTED(igsd->string_count == 0)) {
1323 zend_error(E_WARNING, "igbinary_serialize: Saw too many strings");
1324 return 1;
1325 }
1326 return igbinary_serialize_chararray(igsd, ZSTR_VAL(s), len);
1327 }
1328 /* }}} */
1329
1330 #if SIZEOF_SIZE_T > 4
igbinary_serialize_extremely_long_chararray(struct igbinary_serialize_data * igsd,const char * s,size_t len)1331 static ZEND_COLD zend_never_inline int igbinary_serialize_extremely_long_chararray(struct igbinary_serialize_data *igsd, const char *s, size_t len) {
1332 RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, len + 9));
1333 uint8_t *append_buffer = &igsd->buffer[igsd->buffer_size];
1334 append_buffer[0] = igbinary_type_string64;
1335 append_buffer[1] = (uint8_t)(len >> 56 & 0xff);
1336 append_buffer[2] = (uint8_t)(len >> 48 & 0xff);
1337 append_buffer[3] = (uint8_t)(len >> 40 & 0xff);
1338 append_buffer[4] = (uint8_t)(len >> 32 & 0xff);
1339 append_buffer[5] = (uint8_t)(len >> 24 & 0xff);
1340 append_buffer[6] = (uint8_t)(len >> 16 & 0xff);
1341 append_buffer[7] = (uint8_t)(len >> 8 & 0xff);
1342 append_buffer[8] = (uint8_t)(len & 0xff);
1343
1344 memcpy(append_buffer + 9, s, len);
1345 igsd->buffer_size += 9 + len;
1346
1347 return 0;
1348 }
1349 #endif
1350
1351 /* {{{ igbinary_serialize_chararray */
1352 /** Serializes string data as the type followed by the length followed by the raw character array. */
igbinary_serialize_chararray(struct igbinary_serialize_data * igsd,const char * s,size_t len)1353 inline static int igbinary_serialize_chararray(struct igbinary_serialize_data *igsd, const char *s, size_t len) {
1354 uint8_t *append_buffer;
1355 int offset;
1356 if (len <= 0xff) {
1357 RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, len + 2));
1358 append_buffer = &igsd->buffer[igsd->buffer_size];
1359 append_buffer[0] = igbinary_type_string8;
1360 append_buffer[1] = len;
1361 offset = 2;
1362 } else if (len <= 0xffff) {
1363 RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, len + 3));
1364 append_buffer = &igsd->buffer[igsd->buffer_size];
1365 append_buffer[0] = igbinary_type_string16;
1366 append_buffer[1] = (uint8_t)(len >> 8 & 0xff);
1367 append_buffer[2] = (uint8_t)(len & 0xff);
1368 offset = 3;
1369 } else {
1370 #if SIZEOF_SIZE_T > 4
1371 if (UNEXPECTED(len > 0xffffffff)) {
1372 return igbinary_serialize_extremely_long_chararray(igsd, s, len);
1373 }
1374 #endif
1375 RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, len + 5));
1376 append_buffer = &igsd->buffer[igsd->buffer_size];
1377 append_buffer[0] = igbinary_type_string32;
1378 append_buffer[1] = (uint8_t)(len >> 24 & 0xff);
1379 append_buffer[2] = (uint8_t)(len >> 16 & 0xff);
1380 append_buffer[3] = (uint8_t)(len >> 8 & 0xff);
1381 append_buffer[4] = (uint8_t)(len & 0xff);
1382 offset = 5;
1383 }
1384
1385 memcpy(append_buffer + offset, s, len);
1386 igsd->buffer_size += offset + len;
1387
1388 return 0;
1389 }
1390 /* }}} */
1391 /* {{{ igbinary_serialize_array */
1392 /**
1393 * Serializes an array's or object's inner properties.
1394 * If properties or keys are unexpectedly added (e.g. by __sleep() or serialize() elsewhere), this will skip serializing them.
1395 * If properties or keys are unexpectedly removed, this will add igbinary_type_null as padding for the corresponding entries.
1396 */
igbinary_serialize_array(struct igbinary_serialize_data * igsd,zval * z,bool object,bool incomplete_class,bool serialize_props)1397 inline static int igbinary_serialize_array(struct igbinary_serialize_data *igsd, zval *z, bool object, bool incomplete_class, bool serialize_props) {
1398 /* If object=true: z is IS_OBJECT */
1399 /* If object=false: z is either IS_ARRAY, or IS_REFERENCE pointing to an IS_ARRAY. */
1400 HashTable *h;
1401 /* At the time of writing, struct _zend_array had uint32_t nNumOfElements */
1402 uint32_t n;
1403 zval *d;
1404 zval *z_original;
1405
1406 zend_string *key;
1407 zend_long key_index;
1408
1409 z_original = z;
1410 ZVAL_DEREF(z);
1411
1412 ZEND_ASSERT((!object || !serialize_props) && (object || !incomplete_class));
1413
1414 /* hash */
1415 if (object) {
1416 h = zend_get_properties_for(z, ZEND_PROP_PURPOSE_SERIALIZE);
1417 n = h ? zend_hash_num_elements(h) : 0;
1418 /* incomplete class magic member */
1419
1420 if (incomplete_class && n > 0) {
1421 --n;
1422 }
1423 } else {
1424 h = Z_ARRVAL_P(z);
1425 n = zend_hash_num_elements(h);
1426 /* if it is an array or a reference to an array, then add a reference unique to that **reference** to that array */
1427 if (serialize_props) {
1428 int ref_ser = igbinary_serialize_array_ref(igsd, z_original, false);
1429 if (ref_ser != 2) {
1430 /* 1 means out of memory or error, 0 means this already exists, 2 means this is new. */
1431 return ref_ser;
1432 }
1433 }
1434 }
1435
1436 if (n <= 0xff) {
1437 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_8(igsd, igbinary_type_array8, (uint8_t)n));
1438
1439 if (n == 0) {
1440 if (object) {
1441 zend_release_properties(h);
1442 }
1443 return 0;
1444 }
1445 } else if (n <= 0xffff) {
1446 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_16(igsd, igbinary_type_array16, (uint16_t)n));
1447 } else {
1448 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd, igbinary_type_array32, n));
1449 }
1450
1451 /* serialize properties. */
1452 ZEND_HASH_FOREACH_KEY_VAL(h, key_index, key, d) {
1453 /* skip magic member in incomplete classes */
1454 /* Match it exactly and permit null bytes in the middle so that the counts will match. */
1455 if (incomplete_class && zend_string_equals_literal(key, MAGIC_MEMBER)) {
1456 continue;
1457 }
1458
1459 /* https://wiki.php.net/phpng-int - This is a declared property of an object, or an element of $GLOBALS */
1460 if (Z_TYPE_P(d) == IS_INDIRECT) {
1461 d = Z_INDIRECT_P(d);
1462 }
1463 if (Z_TYPE_P(d) == IS_UNDEF) {
1464 /* This is an undefined declared typed property of an object. */
1465 /* This can't be a value or a reference in an array - except maybe for $GLOBALS, which has other issues. */
1466 ZEND_ASSERT(!serialize_props);
1467 RETURN_1_IF_NON_ZERO(igbinary_serialize_null(igsd));
1468 continue;
1469 }
1470
1471 if (key == NULL) {
1472 /* Key is numeric */
1473 RETURN_1_IF_NON_ZERO(igbinary_serialize_long(igsd, key_index));
1474 } else {
1475 /* Key is string */
1476 RETURN_1_IF_NON_ZERO(igbinary_serialize_string(igsd, key));
1477 }
1478
1479 /* we should still add element even if it's not OK,
1480 * since we already wrote the length of the array before */
1481 RETURN_1_IF_NON_ZERO(igbinary_serialize_zval(igsd, d));
1482 } ZEND_HASH_FOREACH_END();
1483 if (object) {
1484 zend_release_properties(h);
1485 }
1486
1487 return 0;
1488 }
1489 /* }}} */
1490 /* {{{ igbinary_serialize_array_ref */
1491 /** Serializes array reference (or reference in an object). Returns 0 on success. */
1492 /* TODO: Use different result codes for missing keys and errors */
igbinary_serialize_array_ref(struct igbinary_serialize_data * igsd,zval * const z,bool object)1493 inline static int igbinary_serialize_array_ref(struct igbinary_serialize_data *igsd, zval * const z, bool object) {
1494 size_t t;
1495 zend_uintptr_t key; /* The address of the pointer to the zend_refcounted struct or other struct */
1496 static int INVALID_KEY; /* Not used, but we take the pointer of this knowing other zvals won't share it*/
1497
1498 /* Similar to php_var_serialize_intern's first part, as well as php_add_var_hash, for printing R: (reference) or r:(object) */
1499 /* However, it differs from the built in serialize() in that references to objects are preserved when serializing and unserializing? (TODO check, test for backwards compatibility) */
1500
1501 ZEND_ASSERT(Z_ISREF_P(z) || (object && Z_TYPE_P(z) == IS_OBJECT) || Z_TYPE_P(z) == IS_ARRAY);
1502 // zend_bool is_ref = Z_ISREF_P(z);
1503 /* Do I have to dereference object references so that reference ids will be the same as in php5? */
1504 /* If I do, then more tests fail. */
1505 /* is_ref || IS_OBJECT implies it has a unique refcounted struct */
1506 // NOTE: The original code would always use the same memory address - Z_COUNTED_P is the start of an object/array/reference
1507 // if (object && Z_TYPE_P(z) == IS_OBJECT) {
1508 // key = (zend_uintptr_t)Z_OBJ_P(z); /* expand object handle(uint32_t), cast to 32-bit/64-bit pointer */
1509 // } else if (is_ref) {
1510 // /* NOTE: PHP switched from `zval*` to `zval` for the values stored in HashTables.
1511 // * If an array has two references to the same ZVAL, then those references will have different zvals.
1512 // * We use Z_COUNTED_P(ref), which will be the same if (and only if) the references are the same. */
1513 // /* is_ref implies there is a unique reference counting pointer for the reference */
1514 // key = (zend_uintptr_t)Z_COUNTED_P(z);
1515 // } else if (EXPECTED(Z_TYPE_P(z) == IS_ARRAY)) {
1516 // key = (zend_uintptr_t)Z_ARR_P(z);
1517 // } else {
1518 // /* Nothing else is going to reference this when this is serialized, this isn't ref counted or an object, shouldn't be reached. */
1519 // /* Increment the reference id for the deserializer, give up. */
1520 // ++igsd->references_id;
1521 // php_error_docref(NULL, E_NOTICE, "igbinary_serialize_array_ref expected either object or reference (param object=%s), got neither (zend_type=%d)", object ? "true" : "false", (int)Z_TYPE_P(z));
1522 // return 1;
1523 // }
1524
1525 /* FIXME hack? If the top-level element was an array, we assume that it can't be a reference when we serialize it, */
1526 /* because that's the way it was serialized in php5. */
1527 /* Does this work with different forms of recursive arrays? */
1528 if (igsd->references_id == 0 && !object) {
1529 key = (zend_uintptr_t)&INVALID_KEY;
1530 } else {
1531 key = (zend_uintptr_t)z->value.ptr;
1532 }
1533
1534 t = hash_si_ptr_find_or_insert(&igsd->references, key, igsd->references_id);
1535 if (t == SIZE_MAX) {
1536 /* This is a brand new object/array/reference entry with a key equal to igsd->references_id */
1537 igsd->references_id++;
1538 if (UNEXPECTED(igsd->references_id == 0)) {
1539 zend_error(E_WARNING, "igbinary_serialize: Saw too many references");
1540 return 1;
1541 }
1542 /* We only need to call this if the array/object is new, in case __serialize or other methods return temporary arrays or modify arrays that were serialized earlier */
1543 igsd_addref_and_defer_dtor(&igsd->deferred_dtor_tracker, z);
1544 return 2;
1545 }
1546
1547 /* TODO: Properly handle running out of memory in this helper function. */
1548 /* It returns 1 both for running out of memory and inserting a new entry. */
1549 enum igbinary_type type;
1550 if (t <= 0xff) {
1551 type = object ? igbinary_type_objref8 : igbinary_type_ref8;
1552 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_8(igsd, (uint8_t)type, (uint8_t)t));
1553 } else if (t <= 0xffff) {
1554 type = object ? igbinary_type_objref16 : igbinary_type_ref16;
1555 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_16(igsd, (uint8_t)type, (uint16_t)t))
1556 } else {
1557 type = object ? igbinary_type_objref32 : igbinary_type_ref32;
1558 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd, (uint8_t)type, (uint32_t)t))
1559 }
1560 return 0;
1561 }
1562 /* }}} */
1563 /* {{{ igbinary_serialize_array_sleep_single_prop_value */
1564 /** Serializes one value of an object's properties array, for use with the __sleep function. */
igbinary_serialize_array_sleep_single_prop_value(struct igbinary_serialize_data * igsd,zval * z,zval * v,zend_class_entry * ce,zend_string * prop_name)1565 inline static int igbinary_serialize_array_sleep_single_prop_value(struct igbinary_serialize_data *igsd, zval *z, zval *v, zend_class_entry *ce, zend_string *prop_name) {
1566 /* Precondition: All args are non-null */
1567 if (Z_TYPE_P(v) == IS_INDIRECT) {
1568 v = Z_INDIRECT_P(v);
1569 if (UNEXPECTED(Z_TYPE_P(v) == IS_UNDEF)) {
1570 #if PHP_VERSION_ID >= 70400
1571 if (UNEXPECTED(zend_get_typed_property_info_for_slot(Z_OBJ_P(z), v) != NULL)) {
1572 zend_throw_error(NULL, "Typed property %s::$%s must not be accessed before initialization (in __sleep)", ZSTR_VAL(ce->name), ZSTR_VAL(prop_name));
1573 return 1;
1574 }
1575 #endif
1576 goto serialize_untyped_uninitialized_prop;
1577 }
1578 } else {
1579 if (UNEXPECTED(Z_TYPE_P(v) == IS_UNDEF)) {
1580 serialize_untyped_uninitialized_prop:
1581 php_error_docref(NULL, E_NOTICE, "\"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(prop_name));
1582 return igbinary_serialize_null(igsd);
1583 }
1584 }
1585 return igbinary_serialize_zval(igsd, v);
1586 }
1587 /* }}} */
1588 /* {{{ igbinary_serialize_array_sleep_inner */
1589 /** Serializes object's properties array with __sleep -function. */
igbinary_serialize_array_sleep_inner(struct igbinary_serialize_data * igsd,zval * z,HashTable * h,HashTable * object_properties,zend_class_entry * ce)1590 inline static int igbinary_serialize_array_sleep_inner(struct igbinary_serialize_data *igsd, zval *z, HashTable *h, HashTable *object_properties, zend_class_entry *ce) {
1591 zval *d;
1592 zval *v;
1593
1594 ZEND_HASH_FOREACH_VAL(h, d) {
1595 if (UNEXPECTED(d == NULL || Z_TYPE_P(d) != IS_STRING)) {
1596 php_error_docref(NULL, E_NOTICE, "__sleep should return an array only "
1597 "containing the names of instance-variables to "
1598 "serialize");
1599
1600 /* we should still add element even if it's not OK,
1601 * since we already wrote the length of the array before
1602 * serialize null as key-value pair */
1603 /* TODO: Allow creating a tmp string, like php's serialize() */
1604 RETURN_1_IF_NON_ZERO(igbinary_serialize_null(igsd));
1605 continue;
1606 }
1607 zend_string *prop_name = Z_STR_P(d);
1608
1609 if ((v = zend_hash_find(object_properties, prop_name)) != NULL) {
1610 RETURN_1_IF_NON_ZERO(igbinary_serialize_string(igsd, prop_name));
1611
1612 RETURN_1_IF_NON_ZERO(igbinary_serialize_array_sleep_single_prop_value(igsd, z, v, ce, prop_name));
1613 } else {
1614 zend_string *mangled_prop_name;
1615
1616 v = NULL;
1617
1618 int res;
1619 /* try private */
1620 mangled_prop_name = zend_mangle_property_name(ZSTR_VAL(ce->name), ZSTR_LEN(ce->name),
1621 ZSTR_VAL(prop_name), ZSTR_LEN(prop_name), 0);
1622 v = zend_hash_find(object_properties, mangled_prop_name);
1623
1624 /* try protected */
1625 if (v == NULL) {
1626 zend_string_efree(mangled_prop_name);
1627 mangled_prop_name = zend_mangle_property_name("*", 1,
1628 ZSTR_VAL(prop_name), ZSTR_LEN(prop_name), 0);
1629
1630 v = zend_hash_find(object_properties, mangled_prop_name);
1631 /* Neither protected nor private property exists */
1632 if (v == NULL) {
1633 zend_string_efree(mangled_prop_name);
1634
1635 php_error_docref(NULL, E_NOTICE, "\"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(prop_name));
1636 RETURN_1_IF_NON_ZERO(igbinary_serialize_string(igsd, prop_name));
1637
1638 RETURN_1_IF_NON_ZERO(igbinary_serialize_null(igsd));
1639
1640 continue;
1641 }
1642 }
1643
1644 res = igbinary_serialize_string(igsd, mangled_prop_name);
1645 /* igbinary_serialize_string will increase the reference count. */
1646 zend_string_release_ex(mangled_prop_name, 0);
1647
1648 RETURN_1_IF_NON_ZERO(res);
1649 RETURN_1_IF_NON_ZERO(igbinary_serialize_array_sleep_single_prop_value(igsd, z, v, ce, prop_name));
1650 }
1651 } ZEND_HASH_FOREACH_END();
1652
1653 return 0;
1654 }
1655 /* }}} */
1656 /* {{{ igbinary_serialize_array_sleep */
1657 /** Serializes object's properties array with __sleep -function. */
igbinary_serialize_array_sleep(struct igbinary_serialize_data * igsd,zval * z,HashTable * h,zend_class_entry * ce)1658 inline static int igbinary_serialize_array_sleep(struct igbinary_serialize_data *igsd, zval *z, HashTable *h, zend_class_entry *ce) {
1659 HashTable *object_properties;
1660 uint32_t n = zend_hash_num_elements(h);
1661
1662 /* Serialize array id. */
1663 if (n <= 0xff) {
1664 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_8(igsd, igbinary_type_array8, (uint8_t)n))
1665 if (n == 0) {
1666 return 0;
1667 }
1668 } else if (n <= 0xffff) {
1669 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_16(igsd, igbinary_type_array16, (uint16_t)n))
1670 } else {
1671 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd, igbinary_type_array32, n))
1672 }
1673
1674 object_properties = zend_get_properties_for(z, ZEND_PROP_PURPOSE_SERIALIZE);
1675
1676 int r = igbinary_serialize_array_sleep_inner(igsd, z, h, object_properties, ce);
1677 zend_release_properties(object_properties);
1678 return r;
1679 }
1680 /* }}} */
1681 /* {{{ igbinary_serialize_object_name */
1682 /**
1683 * Serialize a PHP object's class name.
1684 * Note that this deliberately ignores the compact_strings setting.
1685 */
igbinary_serialize_object_name(struct igbinary_serialize_data * igsd,zend_string * class_name)1686 inline static int igbinary_serialize_object_name(struct igbinary_serialize_data *igsd, zend_string *class_name) {
1687 struct hash_si_result result = hash_si_find_or_insert(&igsd->strings, class_name, igsd->string_count);
1688 if (result.code == hash_si_code_inserted) {
1689 const size_t name_len = ZSTR_LEN(class_name);
1690 igsd->string_count += 1;
1691 if (UNEXPECTED(igsd->string_count == 0)) {
1692 zend_error(E_WARNING, "igbinary_serialize: Saw too many strings");
1693 return 1;
1694 }
1695
1696 if (name_len <= 0xff) {
1697 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_8(igsd, igbinary_type_object8, (uint8_t)name_len))
1698 } else if (EXPECTED(name_len <= 0xffff)) {
1699 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_16(igsd, igbinary_type_object16, (uint16_t)name_len))
1700 } else {
1701 #if SIZEOF_SIZE_T > 4
1702 if (UNEXPECTED(name_len > 0xffffffff)) {
1703 zend_error(E_WARNING, "igbinary_serialize_object_name: class name does not fit in 32 bits");
1704 return 1;
1705 }
1706 #endif
1707
1708 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd, igbinary_type_object32, (uint32_t)name_len))
1709 }
1710
1711 RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, name_len));
1712
1713 memcpy(igsd->buffer + igsd->buffer_size, ZSTR_VAL(class_name), name_len);
1714 igsd->buffer_size += name_len;
1715 } else if (EXPECTED(result.code == hash_si_code_exists)) {
1716 /* already serialized string */
1717 uint32_t value = result.value;
1718 if (value <= 0xff) {
1719 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_8(igsd, (uint8_t)igbinary_type_object_id8, (uint8_t)value))
1720 } else if (value <= 0xffff) {
1721 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_16(igsd, (uint8_t)igbinary_type_object_id16, (uint16_t)value))
1722 } else {
1723 RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd, (uint8_t)igbinary_type_object_id32, (uint32_t)value))
1724 }
1725 } else {
1726 return 1; /* Failed to allocate copy of string */
1727 }
1728
1729 return 0;
1730 }
1731 /* }}} */
1732 /* igbinary_serialize_object_old_serializer_class {{{ */
igbinary_serialize_object_old_serializer_class(struct igbinary_serialize_data * igsd,zval * z,zend_class_entry * ce)1733 static ZEND_COLD int igbinary_serialize_object_old_serializer_class(struct igbinary_serialize_data* igsd, zval *z, zend_class_entry *ce) {
1734 unsigned char *serialized_data = NULL;
1735 size_t serialized_len;
1736 int r = 0;
1737
1738 if (ce->serialize(z, &serialized_data, &serialized_len, (zend_serialize_data *)NULL) == SUCCESS && !EG(exception)) {
1739 if (UNEXPECTED(igbinary_serialize_object_name(igsd, ce->name) != 0)) {
1740 goto failure;
1741 }
1742
1743 if (serialized_len <= 0xff) {
1744 if (UNEXPECTED(igbinary_serialize8_and_8(igsd, igbinary_type_object_ser8, (uint8_t)serialized_len) != 0)) {
1745 goto failure;
1746 }
1747 } else if (serialized_len <= 0xffff) {
1748 if (UNEXPECTED(igbinary_serialize8_and_16(igsd, (uint8_t)igbinary_type_object_ser16, (uint16_t)serialized_len) != 0)) {
1749 goto failure;
1750 }
1751 } else {
1752 #if SIZEOF_ZEND_LONG > 4
1753 if (UNEXPECTED(serialized_len > 0xffffffff)) {
1754 zend_error(E_WARNING, "igbinary_serialize_object_old_serializer_class: serialize() data 4GB or larger is not supported");
1755 goto failure;
1756 }
1757 #endif
1758 if (UNEXPECTED(igbinary_serialize8_and_32(igsd, (uint8_t)igbinary_type_object_ser32, (uint32_t)serialized_len) != 0)) {
1759 goto failure;
1760 }
1761 }
1762
1763 if (UNEXPECTED(igbinary_serialize_resize(igsd, serialized_len))) {
1764 goto failure;
1765 }
1766
1767 memcpy(igsd->buffer + igsd->buffer_size, serialized_data, serialized_len);
1768 igsd->buffer_size += serialized_len;
1769 } else if (EG(exception)) {
1770 /* exception, return failure */
1771 failure:
1772 r = 1;
1773 } else {
1774 /* Serialization callback failed, assume null output */
1775 r = igbinary_serialize_null(igsd);
1776 }
1777
1778 if (serialized_data) {
1779 efree(serialized_data);
1780 }
1781
1782 return r;
1783 }
1784 /* }}} */
1785 /* igbinary_var_serialize_call_magic_serialize {{{ */
1786 // Source: ext/standard/var.c from php-src
1787 #if PHP_VERSION_ID >= 70400
igbinary_var_serialize_call_magic_serialize(zval * retval,zval * obj)1788 inline static int igbinary_var_serialize_call_magic_serialize(zval *retval, zval *obj)
1789 {
1790 #if PHP_VERSION_ID >= 80000
1791 BG(serialize_lock)++;
1792 zend_call_known_instance_method_with_0_params(
1793 Z_OBJCE_P(obj)->__serialize, Z_OBJ_P(obj), retval);
1794 BG(serialize_lock)--;
1795
1796 if (EG(exception)) {
1797 zval_ptr_dtor(retval);
1798 return 1;
1799 }
1800 #else
1801 zval fname;
1802 int res;
1803
1804 ZVAL_STRINGL(&fname, "__serialize", sizeof("__serialize") - 1);
1805 // XXX does this work with the standard serializer?
1806 BG(serialize_lock)++;
1807 res = call_user_function(CG(function_table), obj, &fname, retval, 0, 0);
1808 BG(serialize_lock)--;
1809 zval_ptr_dtor_str(&fname);
1810
1811 if (res == FAILURE || Z_ISUNDEF_P(retval)) {
1812 zval_ptr_dtor(retval);
1813 return 1;
1814 }
1815 #endif
1816
1817 if (Z_TYPE_P(retval) != IS_ARRAY) {
1818 zval_ptr_dtor(retval);
1819 zend_type_error("%s::__serialize() must return an array", ZSTR_VAL(Z_OBJCE_P(obj)->name));
1820 return 1;
1821 }
1822
1823 return 0;
1824 }
1825 #endif
1826 /* }}} */
1827 /* igbinary_serialize_object_new_serializer {{{ */
1828 #if PHP_VERSION_ID >= 70400
igbinary_serialize_object_new_serializer(struct igbinary_serialize_data * igsd,zval * z,zend_class_entry * ce)1829 inline static int igbinary_serialize_object_new_serializer(struct igbinary_serialize_data* igsd, zval *z, zend_class_entry *ce) {
1830 zval retval;
1831 int r = 0;
1832
1833 // Call retval = __serialize(z), which returns an array or fails.
1834 if (igbinary_var_serialize_call_magic_serialize(&retval, z)) {
1835 // retval is already freed
1836 if (!EG(exception)) {
1837 // When does this happen?
1838 igbinary_serialize_null(igsd);
1839 return 0;
1840 }
1841 return 1;
1842 }
1843 // fprintf(stderr, "Serialize returned a value of type %d\n", Z_TYPE(retval));
1844 if (UNEXPECTED(igbinary_serialize_object_name(igsd, ce->name) != 0)) {
1845 zval_ptr_dtor(&retval);
1846 return 1;
1847 }
1848 // This serializes an object name followed by an array, in the same format used when no serializers exist.
1849 // object is false because this is the array returned by __serialize() for the object.
1850 // TODO: Add tests that this works properly when the same array is reused.
1851 r = igbinary_serialize_array(igsd, &retval, false, false, false);
1852 zval_ptr_dtor(&retval);
1853 if (UNEXPECTED(r)) {
1854 return 1;
1855 }
1856 return EG(exception) != NULL;
1857 }
1858 #endif
1859 /* }}} */
1860 /* {{{ igbinary_serialize_object_enum_case */
1861 #if PHP_VERSION_ID >= 80100
igbinary_serialize_object_enum_case(struct igbinary_serialize_data * igsd,zend_object * obj,zend_class_entry * ce)1862 inline static int igbinary_serialize_object_enum_case(struct igbinary_serialize_data *igsd, zend_object *obj, zend_class_entry *ce) {
1863 if (UNEXPECTED(igbinary_serialize_object_name(igsd, ce->name) != 0)) {
1864 return 1;
1865 }
1866 if (UNEXPECTED(igbinary_serialize8(igsd, (uint8_t)igbinary_type_enum_case))) {
1867 return 1;
1868 }
1869 zval *case_name = zend_enum_fetch_case_name(obj);
1870 ZEND_ASSERT(Z_TYPE_P(case_name) == IS_STRING);
1871 return igbinary_serialize_string(igsd, Z_STR_P(case_name));
1872 }
1873 #endif
1874 /* }}} */
1875 /* {{{ igbinary_serialize_object */
1876 /** Serialize object.
1877 * @see ext/standard/var.c
1878 * */
igbinary_serialize_object(struct igbinary_serialize_data * igsd,zval * z)1879 inline static int igbinary_serialize_object(struct igbinary_serialize_data *igsd, zval *z) {
1880 PHP_CLASS_ATTRIBUTES;
1881
1882 zend_class_entry *ce;
1883
1884 int r = 0;
1885
1886 int ref_ser = igbinary_serialize_array_ref(igsd, z, true);
1887 if (ref_ser != 2) {
1888 return ref_ser;
1889 }
1890
1891 ce = Z_OBJCE_P(z);
1892
1893 /* custom serializer */
1894 if (ce) {
1895 if (IGBINARY_IS_NOT_SERIALIZABLE(ce)) {
1896 zend_throw_exception_ex(NULL, 0, "Serialization of '%s' is not allowed", ZSTR_VAL(ce->name));
1897 return 1;
1898 }
1899
1900 #if PHP_VERSION_ID >= 80100
1901 if (ce->ce_flags & ZEND_ACC_ENUM) {
1902 return igbinary_serialize_object_enum_case(igsd, Z_OBJ_P(z), ce);
1903 }
1904 #endif
1905
1906 #if PHP_VERSION_ID >= 70400
1907 #if PHP_VERSION_ID >= 80000
1908 if (ce->__serialize)
1909 #else
1910 if (zend_hash_str_exists(&ce->function_table, "__serialize", sizeof("__serialize")-1))
1911 #endif
1912 {
1913 // fprintf(stderr, "Going to serialize %s\n", ZSTR_VAL(ce->name));
1914 return igbinary_serialize_object_new_serializer(igsd, z, ce);
1915 }
1916 #endif
1917 if (ce->serialize != NULL) {
1918 return igbinary_serialize_object_old_serializer_class(igsd, z, ce);
1919 }
1920 }
1921
1922 /* serialize class name */
1923 PHP_SET_CLASS_ATTRIBUTES(z);
1924 if (igbinary_serialize_object_name(igsd, class_name) != 0) {
1925 PHP_CLEANUP_CLASS_ATTRIBUTES();
1926 return 1;
1927 }
1928 PHP_CLEANUP_CLASS_ATTRIBUTES();
1929
1930 if (ce && ce != PHP_IC_ENTRY && zend_hash_str_exists(&ce->function_table, "__sleep", sizeof("__sleep") - 1)) {
1931 zval h;
1932 zval f;
1933 /* function name string */
1934 ZVAL_STRINGL(&f, "__sleep", sizeof("__sleep") - 1);
1935
1936 /* calling z->__sleep */
1937 r = call_user_function(CG(function_table), z, &f, &h, 0, 0);
1938
1939 zval_ptr_dtor_str(&f);
1940
1941 if (r == SUCCESS && !EG(exception)) {
1942 HashTable *ht;
1943 r = 0;
1944
1945 if (Z_TYPE(h) == IS_UNDEF) {
1946 /* FIXME: is this ok? */
1947 /* Valid, but skip */
1948 } else if ((ht = HASH_OF(&h)) != NULL) {
1949 /* NOTE: PHP permits returning an object in __sleep */
1950 r = igbinary_serialize_array_sleep(igsd, z, ht, ce);
1951 } else {
1952 php_error_docref(NULL, E_NOTICE, "__sleep should return an array only "
1953 "containing the names of instance-variables to "
1954 "serialize");
1955
1956 /* empty array */
1957 r = igbinary_serialize8_and_8(igsd, igbinary_type_array8, 0);
1958 }
1959 } else {
1960 r = 1;
1961 }
1962
1963 /* cleanup */
1964 zval_ptr_dtor(&h);
1965
1966 return r;
1967 } else {
1968 return igbinary_serialize_array(igsd, z, true, incomplete_class, false);
1969 }
1970 }
1971 /* }}} */
1972 /* {{{ igbinary_warn_serialize_resource */
igbinary_warn_serialize_resource(zval * z)1973 static ZEND_COLD int igbinary_warn_serialize_resource(zval *z) {
1974 const char *resource_type;
1975 resource_type = zend_rsrc_list_get_rsrc_type(Z_RES_P(z));
1976 if (!resource_type) {
1977 resource_type = "Unknown";
1978 }
1979 php_error_docref(NULL, E_DEPRECATED, "Cannot serialize resource(%s) and resources may be converted to objects that cannot be serialized in future php releases. Serializing the value as null instead", resource_type);
1980 return EG(exception) != NULL;
1981 }
1982 /* }}} */
1983 /* {{{ igbinary_serialize_zval */
1984 /** Serialize zval. */
igbinary_serialize_zval(struct igbinary_serialize_data * igsd,zval * z)1985 static int igbinary_serialize_zval(struct igbinary_serialize_data *igsd, zval *z) {
1986 if (Z_ISREF_P(z)) {
1987 if (Z_REFCOUNT_P(z) >= 2) {
1988 RETURN_1_IF_NON_ZERO(igbinary_serialize8(igsd, (uint8_t)igbinary_type_ref))
1989
1990 switch (Z_TYPE_P(Z_REFVAL_P(z))) {
1991 case IS_ARRAY:
1992 return igbinary_serialize_array(igsd, z, false, false, true);
1993 case IS_OBJECT:
1994 break; /* Fall through */
1995 default:
1996 {
1997 /* Serialize a reference if zval already added */
1998 int ref_ser = igbinary_serialize_array_ref(igsd, z, false);
1999 if (ref_ser != 2) {
2000 return ref_ser;
2001 }
2002 /* Fall through */
2003 }
2004 }
2005 }
2006
2007 z = Z_REFVAL_P(z);
2008 }
2009 switch (Z_TYPE_P(z)) {
2010 case IS_RESOURCE:
2011 if (igbinary_warn_serialize_resource(z)) {
2012 return 1;
2013 }
2014 return igbinary_serialize_null(igsd);
2015 case IS_OBJECT:
2016 return igbinary_serialize_object(igsd, z);
2017 case IS_ARRAY:
2018 /* if is_ref, then php5 would have called igbinary_serialize_array_ref */
2019 return igbinary_serialize_array(igsd, z, false, false, true);
2020 case IS_STRING:
2021 return igbinary_serialize_string(igsd, Z_STR_P(z));
2022 case IS_LONG:
2023 return igbinary_serialize_long(igsd, Z_LVAL_P(z));
2024 case IS_UNDEF:
2025 // https://github.com/igbinary/igbinary/issues/134
2026 // TODO: In a new major version, could have a separate type for IS_UNDEF, which would unset the property in an object context?
2027 // fallthrough
2028 case IS_NULL:
2029 return igbinary_serialize_null(igsd);
2030 case IS_TRUE:
2031 return igbinary_serialize_bool(igsd, 1);
2032 case IS_FALSE:
2033 return igbinary_serialize_bool(igsd, 0);
2034 case IS_DOUBLE:
2035 return igbinary_serialize_double(igsd, Z_DVAL_P(z));
2036 default:
2037 zend_error(E_ERROR, "igbinary_serialize_zval: zval has unknown type %d", (int)Z_TYPE_P(z));
2038 return 1;
2039 }
2040
2041 return 0;
2042 }
2043 /* }}} */
2044 /* {{{ igbinary_unserialize_data_init */
2045 /** Inits igbinary_unserialize_data. */
igbinary_unserialize_data_init(struct igbinary_unserialize_data * igsd)2046 inline static int igbinary_unserialize_data_init(struct igbinary_unserialize_data *igsd) {
2047 igsd->buffer = NULL;
2048 igsd->buffer_end = NULL;
2049 igsd->buffer_ptr = NULL;
2050
2051 igsd->strings = NULL;
2052 igsd->strings_count = 0;
2053 igsd->strings_capacity = 4;
2054
2055 igsd->references = NULL;
2056 igsd->references_count = 0;
2057 igsd->references_capacity = 4;
2058
2059 igsd->references = emalloc(sizeof(igsd->references[0]) * igsd->references_capacity);
2060 if (igsd->references == NULL) {
2061 return 1;
2062 }
2063
2064 igsd->strings = (zend_string **)emalloc(sizeof(zend_string *) * igsd->strings_capacity);
2065 if (igsd->strings == NULL) {
2066 /* We failed to allocate memory for strings. Fail and free everything we allocated */
2067 efree(igsd->references);
2068 igsd->references = NULL;
2069 return 1;
2070 }
2071
2072 /** Don't bother allocating zvals which __wakeup or __unserialize, probably not common */
2073 igsd->deferred = NULL;
2074 igsd->deferred_count = 0;
2075 igsd->deferred_capacity = 0;
2076 igsd->deferred_finished = 0;
2077
2078 igsd->deferred_dtor_tracker.zvals = NULL;
2079 igsd->deferred_dtor_tracker.count = 0;
2080 igsd->deferred_dtor_tracker.capacity = 0;
2081
2082 return 0;
2083 }
2084 /* }}} */
2085 /* {{{ igbinary_unserialize_data_deinit */
2086 /** Deinits igbinary_unserialize_data. */
igbinary_unserialize_data_deinit(struct igbinary_unserialize_data * igsd)2087 inline static void igbinary_unserialize_data_deinit(struct igbinary_unserialize_data *igsd) {
2088 if (igsd->strings) {
2089 size_t i;
2090 size_t strings_count = igsd->strings_count;
2091 zend_string **strings = igsd->strings;
2092 for (i = 0; i < strings_count; i++) {
2093 zend_string *s = strings[i];
2094 #if ZEND_DEBUG
2095 ZEND_ASSERT(GC_REFCOUNT(s) >= 1);
2096 #endif
2097 zend_string_release_ex(s, 0); /* Should only create interned or non-persistent strings when unserializing */
2098 }
2099
2100 efree(strings);
2101 }
2102
2103 if (igsd->references) {
2104 efree(igsd->references);
2105 }
2106 if (igsd->deferred) {
2107 struct deferred_call *calls = igsd->deferred;
2108 #if PHP_VERSION_ID >= 70400
2109 size_t i;
2110 size_t n = igsd->deferred_count;
2111 for (i = 0; i < n; i++) {
2112 struct deferred_call *call = &calls[i];
2113 if (call->is_unserialize) {
2114 if (!igsd->deferred_finished) {
2115 struct deferred_unserialize_call *unserialize_call = &call->data.unserialize;
2116 GC_ADD_FLAGS(unserialize_call->object, IS_OBJ_DESTRUCTOR_CALLED);
2117 zval_ptr_dtor(&unserialize_call->param);
2118 }
2119 }
2120 }
2121 #endif
2122 efree(calls);
2123 }
2124 free_deferred_dtors(&igsd->deferred_dtor_tracker);
2125
2126 return;
2127 }
2128 /* }}} */
2129 /* {{{ igbinary_unserialize_header_emit_warning */
2130 /**
2131 * Warns about invalid byte headers
2132 * Precondition: igsd->buffer_size >= 4 */
igbinary_unserialize_header_emit_warning(struct igbinary_unserialize_data * igsd,int version)2133 static ZEND_COLD void igbinary_unserialize_header_emit_warning(struct igbinary_unserialize_data *igsd, int version) {
2134 int i;
2135 char buf[9], *it;
2136 for (i = 0; i < 4; i++) {
2137 if (!isprint((int)igsd->buffer[i])) {
2138 if (version != 0 && (((unsigned int)version) & 0xff000000) == (unsigned int)version) {
2139 // Check if high order byte was set instead of low order byte
2140 zend_error(E_WARNING, "igbinary_unserialize_header: unsupported version: %u, should be %u or %u (wrong endianness?)", (unsigned int)version, 0x00000001, (unsigned int)IGBINARY_FORMAT_VERSION);
2141 return;
2142 }
2143 // Binary data, or a version number from a future release.
2144 zend_error(E_WARNING, "igbinary_unserialize_header: unsupported version: %u, should be %u or %u", (unsigned int)version, 0x00000001, (unsigned int)IGBINARY_FORMAT_VERSION);
2145 return;
2146 }
2147 }
2148
2149 /* To avoid confusion, if the first 4 bytes are all printable, print those instead of the integer representation to make debugging easier. */
2150 /* E.g. strings such as "a:2:" are emitted when an Array is serialized with serialize() instead of igbinary_serialize(), */
2151 /* and subsequently passed to igbinary_unserialize instead of unserialize(). */
2152 for (it = buf, i = 0; i < 4; i++) {
2153 char c = igsd->buffer[i];
2154 if (c == '"' || c == '\\') {
2155 *it++ = '\\';
2156 }
2157 *it++ = c;
2158 }
2159 *it = '\0';
2160 zend_error(E_WARNING, "igbinary_unserialize_header: unsupported version: \"%s\"..., should begin with a binary version header of \"\\x00\\x00\\x00\\x01\" or \"\\x00\\x00\\x00\\x%02x\"", buf, (int)IGBINARY_FORMAT_VERSION);
2161 }
2162 /* }}} */
2163 /* {{{ igbinary_unserialize_header */
2164 /** Unserialize header. Check for version. */
igbinary_unserialize_header(struct igbinary_unserialize_data * igsd)2165 inline static int igbinary_unserialize_header(struct igbinary_unserialize_data *igsd) {
2166 uint32_t version;
2167
2168 if (IGB_NEEDS_MORE_DATA(igsd, 5)) {
2169 zend_error(E_WARNING, "igbinary_unserialize_header: expected at least 5 bytes of data, got %u byte(s)", IGB_REMAINING_BYTES(igsd));
2170 return 1;
2171 }
2172
2173 version = igbinary_unserialize32(igsd);
2174
2175 /* Support older version 1 and the current format 2 */
2176 if (version == IGBINARY_FORMAT_VERSION || version == 0x00000001) {
2177 return 0;
2178 } else {
2179 igbinary_unserialize_header_emit_warning(igsd, version);
2180 return 1;
2181 }
2182 }
2183 /* }}} */
2184 /* {{{ igbinary_unserialize8 */
2185 /** Unserialize 8bit value. */
igbinary_unserialize8(struct igbinary_unserialize_data * igsd)2186 inline static uint8_t igbinary_unserialize8(struct igbinary_unserialize_data *igsd) {
2187 return *(igsd->buffer_ptr++);
2188 }
2189 /* }}} */
2190 /* {{{ igbinary_unserialize16 */
2191 /** Unserialize 16bit value. */
igbinary_unserialize16(struct igbinary_unserialize_data * igsd)2192 inline static uint16_t igbinary_unserialize16(struct igbinary_unserialize_data *igsd) {
2193 uint16_t ret =
2194 ((uint16_t)(igsd->buffer_ptr[0]) << 8) |
2195 ((uint16_t)(igsd->buffer_ptr[1]));
2196 igsd->buffer_ptr += 2;
2197 return ret;
2198 }
2199 /* }}} */
2200 /* {{{ igbinary_unserialize32 */
2201 /** Unserialize 32bit value. */
igbinary_unserialize32(struct igbinary_unserialize_data * igsd)2202 inline static uint32_t igbinary_unserialize32(struct igbinary_unserialize_data *igsd) {
2203 uint32_t ret =
2204 ((uint32_t)(igsd->buffer_ptr[0]) << 24) |
2205 ((uint32_t)(igsd->buffer_ptr[1]) << 16) |
2206 ((uint32_t)(igsd->buffer_ptr[2]) << 8) |
2207 ((uint32_t)(igsd->buffer_ptr[3]));
2208 igsd->buffer_ptr += 4;
2209 return ret;
2210 }
2211 /* }}} */
2212 /* {{{ igbinary_unserialize64 */
2213 /** Unserialize 64bit value. */
igbinary_unserialize64(struct igbinary_unserialize_data * igsd)2214 inline static uint64_t igbinary_unserialize64(struct igbinary_unserialize_data *igsd) {
2215 uint64_t ret =
2216 ((uint64_t)((igsd->buffer_ptr[0])) << 56) |
2217 ((uint64_t)((igsd->buffer_ptr[1])) << 48) |
2218 ((uint64_t)((igsd->buffer_ptr[2])) << 40) |
2219 ((uint64_t)((igsd->buffer_ptr[3])) << 32) |
2220 ((uint64_t)((igsd->buffer_ptr[4])) << 24) |
2221 ((uint64_t)((igsd->buffer_ptr[5])) << 16) |
2222 ((uint64_t)((igsd->buffer_ptr[6])) << 8) |
2223 ((uint64_t)((igsd->buffer_ptr[7])) << 0);
2224 igsd->buffer_ptr += 8;
2225 return ret;
2226 }
2227 /* }}} */
2228 /* {{{ igbinary_unserialize_long */
2229 /** Unserializes zend_long */
igbinary_unserialize_long(struct igbinary_unserialize_data * igsd,enum igbinary_type t,zend_long * ret)2230 inline static int igbinary_unserialize_long(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zend_long *ret) {
2231 uint32_t tmp32;
2232 #if SIZEOF_ZEND_LONG == 8
2233 uint64_t tmp64;
2234 #endif
2235
2236 if (t == igbinary_type_long8p || t == igbinary_type_long8n) {
2237 if (IGB_NEEDS_MORE_DATA(igsd, 1)) {
2238 zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data");
2239 return 1;
2240 }
2241
2242 *ret = (zend_long)(t == igbinary_type_long8n ? -1 : 1) * igbinary_unserialize8(igsd);
2243 } else if (t == igbinary_type_long16p || t == igbinary_type_long16n) {
2244 if (IGB_NEEDS_MORE_DATA(igsd, 2)) {
2245 zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data");
2246 return 1;
2247 }
2248
2249 *ret = (zend_long)(t == igbinary_type_long16n ? -1 : 1) * igbinary_unserialize16(igsd);
2250 } else if (t == igbinary_type_long32p || t == igbinary_type_long32n) {
2251 if (IGB_NEEDS_MORE_DATA(igsd, 4)) {
2252 zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data");
2253 return 1;
2254 }
2255
2256 /* check for boundaries (perform only one comparison in common case) */
2257 tmp32 = igbinary_unserialize32(igsd);
2258 #if SIZEOF_ZEND_LONG == 4
2259 if (UNEXPECTED(tmp32 >= 0x80000000 && (tmp32 > 0x80000000 || t == igbinary_type_long32p))) {
2260 zend_error(E_WARNING, "igbinary_unserialize_long: 64bit long on 32bit platform?");
2261 tmp32 = 0; /* t == igbinary_type_long32p ? LONG_MAX : LONG_MIN; */
2262 }
2263 #endif
2264 *ret = (zend_long)(t == igbinary_type_long32n ? -1 : 1) * tmp32;
2265 } else {
2266 ZEND_ASSERT(t == igbinary_type_long64p || t == igbinary_type_long64n);
2267 #if SIZEOF_ZEND_LONG == 8
2268 if (IGB_NEEDS_MORE_DATA(igsd, 8)) {
2269 zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data");
2270 return 1;
2271 }
2272
2273 /* check for boundaries (perform only one comparison in common case) */
2274 tmp64 = igbinary_unserialize64(igsd);
2275 if (UNEXPECTED(tmp64 >= 0x8000000000000000 && (tmp64 > 0x8000000000000000 || t == igbinary_type_long64p))) {
2276 zend_error(E_WARNING, "igbinary_unserialize_long: too big 64bit long.");
2277 tmp64 = 0; /* t == igbinary_type_long64p ? LONG_MAX : LONG_MIN */
2278 }
2279
2280 *ret = (zend_long)(t == igbinary_type_long64n ? -1 : 1) * tmp64;
2281 #elif SIZEOF_ZEND_LONG == 4
2282 /* can't put 64bit long into 32bit one, placeholder zero */
2283 *ret = 0;
2284 igbinary_unserialize64(igsd);
2285 zend_error(E_WARNING, "igbinary_unserialize_long: 64bit long on 32bit platform");
2286 #else
2287 #error "Strange sizeof(zend_long)."
2288 #endif
2289 }
2290
2291 return 0;
2292 }
2293 /* }}} */
2294 /* {{{ igbinary_unserialize_double */
2295 /** Unserializes double. */
igbinary_unserialize_double(struct igbinary_unserialize_data * igsd,double * ret)2296 inline static int igbinary_unserialize_double(struct igbinary_unserialize_data *igsd, double *ret) {
2297 union {
2298 double d;
2299 uint64_t u;
2300 } u;
2301
2302 if (IGB_NEEDS_MORE_DATA(igsd, 8)) {
2303 zend_error(E_WARNING, "igbinary_unserialize_double: end-of-data");
2304 return 1;
2305 }
2306
2307 u.u = igbinary_unserialize64(igsd);
2308
2309 *ret = u.d;
2310
2311 return 0;
2312 }
2313 /* }}} */
2314 /* {{{ igbinary_unserialize_string */
2315 /** Unserializes string. Unserializes both actual string or by string id. Returns NULL on error. */
igbinary_unserialize_string(struct igbinary_unserialize_data * igsd,enum igbinary_type t)2316 inline static zend_string *igbinary_unserialize_string(struct igbinary_unserialize_data *igsd, enum igbinary_type t) {
2317 size_t i;
2318 zend_string *zstr;
2319 if (t == igbinary_type_string_id8 || t == igbinary_type_object_id8) {
2320 if (IGB_NEEDS_MORE_DATA(igsd, 1)) {
2321 zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data");
2322 return NULL;
2323 }
2324 i = igbinary_unserialize8(igsd);
2325 } else if (t == igbinary_type_string_id16 || t == igbinary_type_object_id16) {
2326 if (IGB_NEEDS_MORE_DATA(igsd, 2)) {
2327 zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data");
2328 return NULL;
2329 }
2330 i = igbinary_unserialize16(igsd);
2331 } else if (t == igbinary_type_string_id32 || t == igbinary_type_object_id32) {
2332 if (IGB_NEEDS_MORE_DATA(igsd, 4)) {
2333 zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data");
2334 return NULL;
2335 }
2336 i = igbinary_unserialize32(igsd);
2337 } else {
2338 zend_error(E_WARNING, "igbinary_unserialize_string: unknown type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd));
2339 return NULL;
2340 }
2341
2342 if (i >= igsd->strings_count) {
2343 zend_error(E_WARNING, "igbinary_unserialize_string: string index is out-of-bounds");
2344 return NULL;
2345 }
2346
2347 zstr = igsd->strings[i];
2348 // Add one more ref (currently not using any interned strings) - Callers of this will decrease refs as needed
2349 ZEND_ASSERT(!ZSTR_IS_INTERNED(zstr));
2350 GC_ADDREF(zstr);
2351 return zstr;
2352 }
2353 /* }}} */
2354 /* igbinary_unserialize_extremely_long_chararray {{{ */
igbinary_unserialize_extremely_long_chararray(struct igbinary_unserialize_data * igsd)2355 static ZEND_COLD zend_never_inline zend_string* igbinary_unserialize_extremely_long_chararray(struct igbinary_unserialize_data *igsd) {
2356 #if SIZEOF_ZEND_LONG > 4
2357 zend_error(E_WARNING, "igbinary_unserialize_chararray: cannot unserialize 64-bit data on 32-bit platform");
2358 return NULL;
2359 #else
2360 if (IGB_NEEDS_MORE_DATA(igsd, 8)) {
2361 zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data");
2362 return NULL;
2363 }
2364 size_t l = igbinary_unserialize64(igsd);
2365 if (IGB_NEEDS_MORE_DATA(igsd, l)) {
2366 zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data");
2367 return NULL;
2368 }
2369
2370 if (igsd->strings_count + 1 > igsd->strings_capacity) {
2371 zend_string **new_strings;
2372 igsd->strings_capacity *= 2;
2373
2374 new_strings = (zend_string **)erealloc(igsd->strings, sizeof(zend_string *) * igsd->strings_capacity);
2375 if (new_strings == NULL) {
2376 // The cleanup function will take care of destroying the allocated zend_strings.
2377 return NULL;
2378 }
2379 igsd->strings = new_strings;
2380 }
2381
2382 zend_string *zstr = zend_string_init((const char*)igsd->buffer_ptr, l, 0);
2383
2384 igsd->buffer_ptr += l;
2385
2386 GC_ADDREF(zstr); /* definitely not interned. Add a reference in case the first reference gets deleted before reusing the temporary string */
2387
2388 igsd->strings[igsd->strings_count] = zstr;
2389 igsd->strings_count += 1;
2390 return zstr;
2391 #endif
2392 }
2393 /* }}} */
2394 /* {{{ igbinary_unserialize_chararray */
2395 /** Unserializes chararray of string. Returns NULL on error. */
igbinary_unserialize_chararray(struct igbinary_unserialize_data * igsd,enum igbinary_type t)2396 inline static zend_string *igbinary_unserialize_chararray(struct igbinary_unserialize_data *igsd, enum igbinary_type t) {
2397 size_t l;
2398 zend_string *zstr;
2399
2400 if (t == igbinary_type_string8 || t == igbinary_type_object8) {
2401 if (IGB_NEEDS_MORE_DATA(igsd, 1)) {
2402 zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data");
2403 return NULL;
2404 }
2405 l = igbinary_unserialize8(igsd);
2406 /* Requires converting these into interned strings. Maybe add one-char in v3 of igbinary format?
2407 if (l == 1) {
2408 zstr = ZSTR_CHAR((zend_uchar)igsd->buffer[igsd->buffer_offset]);
2409 igsd->strings[igsd->strings_count] = zstr;
2410 igsd->strings_count += 1;
2411 igsd->buffer_offset++;
2412 return zstr;
2413 }
2414 */
2415 } else if (t == igbinary_type_string16 || t == igbinary_type_object16) {
2416 if (IGB_NEEDS_MORE_DATA(igsd, 2)) {
2417 zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data");
2418 return NULL;
2419 }
2420 l = igbinary_unserialize16(igsd);
2421 } else if (EXPECTED(t == igbinary_type_string32 || t == igbinary_type_object32)) {
2422 if (IGB_NEEDS_MORE_DATA(igsd, 4)) {
2423 zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data");
2424 return NULL;
2425 }
2426 l = igbinary_unserialize32(igsd);
2427 } else if (t == igbinary_type_string64) {
2428 return igbinary_unserialize_extremely_long_chararray(igsd);
2429 } else {
2430 zend_error(E_WARNING, "igbinary_unserialize_chararray: unknown type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd));
2431 return NULL;
2432 }
2433 if (IGB_NEEDS_MORE_DATA(igsd, l)) {
2434 zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data");
2435 return NULL;
2436 }
2437
2438 if (igsd->strings_count + 1 > igsd->strings_capacity) {
2439 zend_string **new_strings;
2440 igsd->strings_capacity *= 2;
2441
2442 new_strings = (zend_string **)erealloc(igsd->strings, sizeof(zend_string *) * igsd->strings_capacity);
2443 if (new_strings == NULL) {
2444 // The cleanup function will take care of destroying the allocated zend_strings.
2445 return NULL;
2446 }
2447 igsd->strings = new_strings;
2448 }
2449
2450 zstr = zend_string_init((const char*)igsd->buffer_ptr, l, 0);
2451
2452 igsd->buffer_ptr += l;
2453
2454 GC_ADDREF(zstr); /* definitely not interned. Add a reference in case the first reference gets deleted before reusing the temporary string */
2455
2456 igsd->strings[igsd->strings_count] = zstr;
2457 igsd->strings_count += 1;
2458 return zstr;
2459 }
2460 /* }}} */
2461 /* {{{ igbinary_unserialize_array */
2462 /** Unserializes a PHP array. */
igbinary_unserialize_array(struct igbinary_unserialize_data * igsd,enum igbinary_type t,zval * const z,int flags,zend_bool create_ref)2463 inline static int igbinary_unserialize_array(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags, zend_bool create_ref) {
2464 /* WANT_REF means that z will be wrapped by an IS_REFERENCE */
2465 uint32_t n;
2466 uint32_t i;
2467
2468 enum igbinary_type key_type;
2469
2470 HashTable *h;
2471
2472 if (t == igbinary_type_array8) {
2473 if (IGB_NEEDS_MORE_DATA(igsd, 1)) {
2474 zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data");
2475 return 1;
2476 }
2477 n = igbinary_unserialize8(igsd);
2478 if (n == 0) {
2479 if (create_ref) {
2480 /* NOTE: igbinary uses ZVAL_ARR() when reading the created reference, which assumes that the original array is refcounted. */
2481 /* This would have to be fixed to switch to ZVAL_EMPTY_ARRAY for the empty array (there's probably not benefit to making that switch). */
2482 /* Otherwise, using ZVAL_EMPTY_ARRAY would segfault. */
2483 #if PHP_VERSION_ID >= 70300
2484 ZVAL_EMPTY_ARRAY(z);
2485 #else
2486 array_init_size(z, 0);
2487 #endif
2488 struct igbinary_value_ref ref;
2489 if (flags & WANT_REF) {
2490 ZVAL_NEW_REF(z, z);
2491 ref.reference.reference = Z_REF_P(z);
2492 ref.type = IG_REF_IS_REFERENCE;
2493 } else {
2494 #if PHP_VERSION_ID >= 70300
2495 ref.type = IG_REF_IS_EMPTY_ARRAY;
2496 #else
2497 ref.reference.array = Z_ARR_P(z);
2498 ref.type = IG_REF_IS_ARRAY;
2499 #endif
2500 }
2501 /* add the new array to the list of unserialized references */
2502 RETURN_1_IF_NON_ZERO(igsd_append_ref(igsd, ref) == SIZE_MAX);
2503 } else {
2504 /* This only matters if __unserialize() would get called with an empty array */
2505 #if PHP_VERSION_ID >= 70300
2506 ZVAL_EMPTY_ARRAY(z);
2507 #else
2508 array_init_size(z, 0);
2509 #endif
2510 }
2511 return 0;
2512 }
2513 } else if (t == igbinary_type_array16) {
2514 if (IGB_NEEDS_MORE_DATA(igsd, 2)) {
2515 zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data");
2516 return 1;
2517 }
2518 n = igbinary_unserialize16(igsd);
2519 } else if (t == igbinary_type_array32) {
2520 if (IGB_NEEDS_MORE_DATA(igsd, 4)) {
2521 zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data");
2522 return 1;
2523 }
2524 n = igbinary_unserialize32(igsd);
2525 } else {
2526 zend_error(E_WARNING, "igbinary_unserialize_array: unknown type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd));
2527 return 1;
2528 }
2529
2530 /* n cannot be larger than the number of minimum "objects" in the array */
2531 if (IGB_NEEDS_MORE_DATA(igsd, n)) {
2532 zend_error(E_WARNING, "igbinary_unserialize_array: data size %zu smaller that requested array length %zu.", (size_t)IGB_REMAINING_BYTES(igsd), (size_t)n);
2533 return 1;
2534 }
2535
2536 zval *z_deref = z;
2537 if (flags & WANT_REF) {
2538 if (!Z_ISREF_P(z)) {
2539 ZVAL_NEW_REF(z, z);
2540 z_deref = Z_REFVAL_P(z);
2541 }
2542 }
2543 array_init_size(z_deref, n);
2544 h = Z_ARRVAL_P(z_deref);
2545 if (create_ref) {
2546 /* Only create a reference if this is not from __unserialize(), because the existence of __unserialize can change */
2547 struct igbinary_value_ref ref;
2548 if (flags & WANT_REF) {
2549 // We converted to reference earlier.
2550 ref.reference.reference = Z_REF_P(z);
2551 ref.type = IG_REF_IS_REFERENCE;
2552 } else {
2553 ref.reference.array = Z_ARR_P(z_deref);
2554 ref.type = IG_REF_IS_ARRAY;
2555 }
2556 /* add the new array to the list of unserialized references */
2557 RETURN_1_IF_NON_ZERO(igsd_append_ref(igsd, ref) == SIZE_MAX);
2558 }
2559
2560 for (i = 0; i < n; i++) {
2561 zval *vp;
2562 zend_long key_index = 0;
2563 zend_string *key_str = NULL; /* NULL means use key_index */
2564
2565 if (IGB_NEEDS_MORE_DATA(igsd, 1)) {
2566 zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data");
2567 cleanup:
2568 zval_dtor(z);
2569 ZVAL_NULL(z);
2570 return 1;
2571 }
2572
2573 key_type = (enum igbinary_type)igbinary_unserialize8(igsd);
2574
2575 switch (key_type) {
2576 case igbinary_type_long8p:
2577 /* Manually inline igbinary_unserialize_long() for array keys from 0 to 255, because they're the most common among integers. */
2578 if (IGB_NEEDS_MORE_DATA(igsd, 1)) {
2579 zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data");
2580 goto cleanup;
2581 }
2582
2583 key_index = igbinary_unserialize8(igsd);
2584 break;
2585 case igbinary_type_long16p:
2586 /* and for array keys from 0 to 65535. */
2587 if (IGB_NEEDS_MORE_DATA(igsd, 2)) {
2588 zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data");
2589 goto cleanup;
2590 }
2591
2592 key_index = igbinary_unserialize16(igsd);
2593 break;
2594 case igbinary_type_long8n:
2595 case igbinary_type_long16n:
2596 case igbinary_type_long32p:
2597 case igbinary_type_long32n:
2598 case igbinary_type_long64p:
2599 case igbinary_type_long64n:
2600 if (UNEXPECTED(igbinary_unserialize_long(igsd, key_type, &key_index))) {
2601 goto cleanup;
2602 }
2603 break;
2604 case igbinary_type_string_id8:
2605 case igbinary_type_string_id16:
2606 case igbinary_type_string_id32:
2607 key_str = igbinary_unserialize_string(igsd, key_type);
2608 if (UNEXPECTED(key_str == NULL)) {
2609 goto cleanup;
2610 }
2611 break;
2612 case igbinary_type_string8:
2613 case igbinary_type_string16:
2614 case igbinary_type_string32:
2615 case igbinary_type_string64:
2616 key_str = igbinary_unserialize_chararray(igsd, key_type);
2617 if (UNEXPECTED(key_str == NULL)) {
2618 goto cleanup;
2619 }
2620 break;
2621 case igbinary_type_string_empty:
2622 key_str = ZSTR_EMPTY_ALLOC();
2623 break;
2624 case igbinary_type_null:
2625 continue;
2626 default:
2627 zend_error(E_WARNING, "igbinary_unserialize_array: unknown key type '%02x', position %zu", key_type, (size_t)IGB_BUFFER_OFFSET(igsd));
2628 goto cleanup;
2629 }
2630
2631 /* first add key into array so references can properly and not stack allocated zvals */
2632 /* Use NULL because inserting UNDEF into array does not add a new element */
2633 if (key_str != NULL) {
2634 vp = igbinary_zend_hash_add_or_find(h, key_str);
2635 /* If there was an old value instead of an inserted IS_NULL zval (unlikely) and that old value was refcounted, then add a reference and defer freeing that reference */
2636 if (UNEXPECTED(igsd_addref_and_defer_dtor(&igsd->deferred_dtor_tracker, vp))) {
2637 return 1;
2638 }
2639
2640 zend_string_release(key_str);
2641 } else {
2642 /* If there was an old value instead of an inserted IS_NULL zval (unlikely) and that old value was refcounted, then add a reference and defer freeing that reference */
2643 vp = igbinary_zend_hash_index_add_or_find(h, key_index);
2644 if (UNEXPECTED(igsd_addref_and_defer_dtor(&igsd->deferred_dtor_tracker, vp))) {
2645 return 1;
2646 }
2647 }
2648
2649 ZEND_ASSERT(vp != NULL);
2650 if (Z_TYPE_P(vp) == IS_INDIRECT) {
2651 /* TODO: In php 8.1+, IS_INDIRECT checks and macros may become unnecessary
2652 * due to $GLOBALS being converted to a regular array in https://wiki.php.net/rfc/restrict_globals_usage */
2653 vp = Z_INDIRECT_P(vp);
2654 }
2655
2656 ZEND_ASSERT(vp != NULL);
2657 if (UNEXPECTED(igbinary_unserialize_zval(igsd, vp, WANT_CLEAR))) {
2658 return 1;
2659 }
2660 }
2661
2662 return 0;
2663 }
2664 /* }}} */
2665 /* {{{ igbinary_unserialize_object_properties */
2666 /** Unserializes the array of object properties and adds those to the object z. */
igbinary_unserialize_object_properties(struct igbinary_unserialize_data * igsd,enum igbinary_type t,zval * const z)2667 inline static int igbinary_unserialize_object_properties(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z) {
2668 /* WANT_REF means that z will be wrapped by an IS_REFERENCE */
2669 uint32_t n;
2670
2671 zval v;
2672 zval *z_deref;
2673
2674 HashTable *h;
2675 zend_bool did_extend;
2676
2677 if (t == igbinary_type_array8) {
2678 if (IGB_NEEDS_MORE_DATA(igsd, 1)) {
2679 zend_error(E_WARNING, "igbinary_unserialize_object_properties: end-of-data");
2680 return 1;
2681 }
2682 n = igbinary_unserialize8(igsd);
2683 } else if (t == igbinary_type_array16) {
2684 if (IGB_NEEDS_MORE_DATA(igsd, 2)) {
2685 zend_error(E_WARNING, "igbinary_unserialize_object_properties: end-of-data");
2686 return 1;
2687 }
2688 n = igbinary_unserialize16(igsd);
2689 } else if (t == igbinary_type_array32) {
2690 if (IGB_NEEDS_MORE_DATA(igsd, 4)) {
2691 zend_error(E_WARNING, "igbinary_unserialize_object_properties: end-of-data");
2692 return 1;
2693 }
2694 n = igbinary_unserialize32(igsd);
2695 } else {
2696 zend_error(E_WARNING, "igbinary_unserialize_object_properties: unknown type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd));
2697 return 1;
2698 }
2699
2700 /* n cannot be larger than the number of minimum "objects" in the array */
2701 if (IGB_NEEDS_MORE_DATA(igsd, n)) {
2702 zend_error(E_WARNING, "%s: data size %zu smaller that requested array length %zu.", "igbinary_unserialize_object_properties", (size_t)IGB_REMAINING_BYTES(igsd), (size_t)n);
2703 return 1;
2704 }
2705
2706 z_deref = z;
2707 ZVAL_DEREF(z_deref);
2708
2709 /* empty array */
2710 if (n == 0) {
2711 return 0;
2712 }
2713
2714 h = HASH_OF_OBJECT(z_deref);
2715
2716 did_extend = 0;
2717
2718 do {
2719 n--;
2720 zval *vp;
2721 enum igbinary_type key_type;
2722 zend_string *key_str = NULL; /* NULL means use key_index */
2723
2724 if (IGB_NEEDS_MORE_DATA(igsd, 1)) {
2725 zend_error(E_WARNING, "igbinary_unserialize_object_properties: end-of-data");
2726 zval_dtor(z);
2727 ZVAL_NULL(z);
2728 return 1;
2729 }
2730
2731 key_type = (enum igbinary_type)igbinary_unserialize8(igsd);
2732
2733 switch (key_type) {
2734 case igbinary_type_long8p:
2735 case igbinary_type_long8n:
2736 case igbinary_type_long16p:
2737 case igbinary_type_long16n:
2738 case igbinary_type_long32p:
2739 case igbinary_type_long32n:
2740 case igbinary_type_long64p:
2741 case igbinary_type_long64n:
2742 {
2743 zend_long key_index = 0;
2744 if (UNEXPECTED(igbinary_unserialize_long(igsd, key_type, &key_index))) {
2745 zval_dtor(z);
2746 ZVAL_UNDEF(z);
2747 return 1;
2748 }
2749 key_str = zend_long_to_str(key_index);
2750 if (UNEXPECTED(key_str == NULL)) {
2751 zval_dtor(z);
2752 ZVAL_UNDEF(z);
2753 return 1;
2754 }
2755 break;
2756 }
2757 case igbinary_type_string_id8:
2758 case igbinary_type_string_id16:
2759 case igbinary_type_string_id32:
2760 key_str = igbinary_unserialize_string(igsd, key_type);
2761 if (UNEXPECTED(key_str == NULL)) {
2762 zval_dtor(z);
2763 ZVAL_UNDEF(z);
2764 return 1;
2765 }
2766 break;
2767 case igbinary_type_string8:
2768 case igbinary_type_string16:
2769 case igbinary_type_string32:
2770 case igbinary_type_string64:
2771 key_str = igbinary_unserialize_chararray(igsd, key_type);
2772 if (UNEXPECTED(key_str == NULL)) {
2773 zval_dtor(z);
2774 ZVAL_UNDEF(z);
2775 return 1;
2776 }
2777 break;
2778 case igbinary_type_string_empty:
2779 key_str = ZSTR_EMPTY_ALLOC();
2780 break;
2781 case igbinary_type_null:
2782 continue; /* Skip unserializing this element, serialized with no value. In C, this applies to loop, not switch. */
2783 default:
2784 zend_error(E_WARNING, "igbinary_unserialize_object_properties: unknown key type '%02x', position %zu", key_type, (size_t)IGB_BUFFER_OFFSET(igsd));
2785 zval_dtor(z);
2786 ZVAL_UNDEF(z);
2787 return 1;
2788 }
2789
2790 /* first add key into array so references can properly and not stack allocated zvals */
2791 /* Use NULL because inserting UNDEF into array does not add a new element */
2792 ZVAL_NULL(&v);
2793 zval *prototype_value = zend_hash_find(h, key_str);
2794 #if PHP_VERSION_ID >= 70400
2795 zend_property_info *info = NULL;
2796 #endif
2797 if (prototype_value != NULL) {
2798 if (Z_TYPE_P(prototype_value) == IS_INDIRECT) {
2799 /* This is a declared object property */
2800 prototype_value = Z_INDIRECT_P(prototype_value);
2801 #if PHP_VERSION_ID >= 70400
2802 info = zend_get_typed_property_info_for_slot(Z_OBJ_P(z_deref), prototype_value);
2803 #endif
2804 }
2805 /* This is written to avoid the overhead of a second zend_hash_update call. See https://github.com/php/php-src/pull/5095 */
2806 /* Use igsd_addref_and_defer_dtor instead (like php-src), in case of gc causing issues. */
2807 /* Something already added a reference, so just defer the destructor */
2808 if (UNEXPECTED(igsd_defer_dtor(&igsd->deferred_dtor_tracker, prototype_value))) {
2809 zend_string_release(key_str);
2810 return 1;
2811 }
2812 /* Just override the original value directly */
2813 ZVAL_COPY_VALUE(prototype_value, &v);
2814 vp = prototype_value;
2815 } else {
2816 if (!did_extend) {
2817 /* remaining_elements is at least one, because we're looping from n-1..0 */
2818 uint32_t remaining_elements = n + 1;
2819 /* Copied from var_unserializer.re. Need to ensure that IGB_REF_VAL doesn't point to invalid data. */
2820 /* Worst case: All remaining_elements of the added properties are dynamic. */
2821 zend_hash_extend(h, zend_hash_num_elements(h) + remaining_elements, (h->u.flags & HASH_FLAG_PACKED));
2822 did_extend = 1;
2823 }
2824 vp = zend_hash_add_new(h, key_str, &v);
2825 }
2826
2827 zend_string_release(key_str);
2828
2829 /* Should only be indirect for typed properties? */
2830 ZEND_ASSERT(Z_TYPE_P(vp) != IS_INDIRECT);
2831
2832 if (UNEXPECTED(igbinary_unserialize_zval(igsd, vp, WANT_CLEAR))) {
2833 /* Unserializing a property into this zval has failed. */
2834 /* zval_ptr_dtor(z); */
2835 /* zval_ptr_dtor(vp); */
2836 return 1;
2837 }
2838 #if PHP_VERSION_ID >= 70400
2839 if (UNEXPECTED(info)) {
2840 if (!zend_verify_prop_assignable_by_ref(info, vp, /* strict */ 1)) {
2841 zval_ptr_dtor(vp);
2842 ZVAL_UNDEF(vp);
2843 return 1;
2844 }
2845 if (Z_ISREF_P(vp)) {
2846 ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(vp), info);
2847 }
2848 }
2849 #endif
2850 } while (n > 0);
2851
2852 return 0;
2853 }
2854 /* }}} */
2855 /* {{{ igbinary_unserialize_object_ser */
2856 /** Unserializes object's property array. This is used to serialize objects implementing Serializable -interface. */
igbinary_unserialize_object_ser(struct igbinary_unserialize_data * igsd,enum igbinary_type t,zval * const z,zend_class_entry * ce)2857 static ZEND_COLD int igbinary_unserialize_object_ser(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, zend_class_entry *ce) {
2858 size_t n;
2859 int ret;
2860 php_unserialize_data_t var_hash;
2861
2862 if (ce->unserialize == NULL) {
2863 /* Should be impossible. */
2864 zend_error(E_WARNING, "Class %s has no unserializer", ZSTR_VAL(ce->name));
2865 return 1;
2866 }
2867
2868 if (IGBINARY_IS_NOT_UNSERIALIZABLE(ce)) {
2869 zend_throw_exception_ex(NULL, 0, "Unserialization of '%s' is not allowed", ZSTR_VAL(ce->name));
2870 return 1;
2871 }
2872
2873 if (t == igbinary_type_object_ser8) {
2874 if (IGB_NEEDS_MORE_DATA(igsd, 1)) {
2875 zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data");
2876 return 1;
2877 }
2878 n = igbinary_unserialize8(igsd);
2879 } else if (t == igbinary_type_object_ser16) {
2880 if (IGB_NEEDS_MORE_DATA(igsd, 2)) {
2881 zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data");
2882 return 1;
2883 }
2884 n = igbinary_unserialize16(igsd);
2885 } else if (t == igbinary_type_object_ser32) {
2886 if (IGB_NEEDS_MORE_DATA(igsd, 4)) {
2887 zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data");
2888 return 1;
2889 }
2890 n = igbinary_unserialize32(igsd);
2891 } else {
2892 zend_error(E_WARNING, "igbinary_unserialize_object_ser: unknown type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd));
2893 return 1;
2894 }
2895
2896 if (IGB_NEEDS_MORE_DATA(igsd, n)) {
2897 zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data");
2898 return 1;
2899 }
2900
2901 PHP_VAR_UNSERIALIZE_INIT(var_hash);
2902 ret = ce->unserialize(
2903 z,
2904 ce,
2905 (const unsigned char *)igsd->buffer_ptr,
2906 n,
2907 (zend_unserialize_data *)&var_hash
2908 );
2909 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
2910
2911 if (ret != SUCCESS || EG(exception)) {
2912 return 1;
2913 }
2914
2915 igsd->buffer_ptr += n;
2916
2917 return 0;
2918 }
2919 /* }}} */
2920 /* {{{ igbinary_unserialize_object_enum_case */
2921 #if PHP_VERSION_ID >= 80100
2922 /** Unserializes object's property array. This is used to serialize objects implementing Serializable -interface. */
igbinary_unserialize_object_enum_case(struct igbinary_unserialize_data * igsd,zval * const z,zend_class_entry * ce)2923 static int igbinary_unserialize_object_enum_case(struct igbinary_unserialize_data *igsd, zval *const z, zend_class_entry *ce) {
2924 if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_ENUM))) {
2925 zend_error(E_WARNING, "igbinary_unserialize_object_enum_case: Class '%s' is not an enum", ZSTR_VAL(ce->name));
2926 return 1;
2927 }
2928 if (IGB_NEEDS_MORE_DATA(igsd, 1)) {
2929 zend_error(E_WARNING, "igbinary_unserialize_object_enum_case: end-of-data");
2930 return 1;
2931 }
2932
2933 enum igbinary_type t = (enum igbinary_type)igbinary_unserialize8(igsd);
2934 zend_string *case_name;
2935 switch (t) {
2936 case igbinary_type_string8:
2937 case igbinary_type_string16:
2938 case igbinary_type_string32:
2939 case igbinary_type_string64:
2940 case_name = igbinary_unserialize_chararray(igsd, t);
2941 break;
2942 default:
2943 case_name = igbinary_unserialize_string(igsd, t);
2944 break;
2945 }
2946 if (UNEXPECTED(!case_name)) {
2947 return 1;
2948 }
2949
2950 zval *zv = zend_hash_find(&ce->constants_table, case_name);
2951 if (UNEXPECTED(!zv)) {
2952 zend_error(E_WARNING, "igbinary_unserialize_object_enum_case: Undefined constant %s::%s", ZSTR_VAL(ce->name), ZSTR_VAL(case_name));
2953 zend_string_release(case_name);
2954 return 1;
2955 }
2956
2957 zend_class_constant *c = Z_PTR_P(zv);
2958 if (UNEXPECTED(!(ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE))) {
2959 zend_error(E_WARNING, "igbinary_unserialize_object_enum_case: %s::%s is not an enum case", ZSTR_VAL(ce->name), ZSTR_VAL(case_name));
2960 zend_string_release(case_name);
2961 return 1;
2962 }
2963 zend_string_release(case_name);
2964 zval *value = &c->value;
2965 if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
2966 zval_update_constant_ex(value, c->ce);
2967 if (UNEXPECTED(EG(exception) != NULL)) {
2968 return 1;
2969 }
2970 }
2971 ZEND_ASSERT(Z_TYPE_P(value) == IS_OBJECT);
2972 /* increment the reference count and copy the enum case object into the constructed value. */
2973 ZVAL_COPY(z, value);
2974
2975 return 0;
2976 }
2977 #endif
2978 /* }}} */
2979 /* {{{ igbinary_unserialize_object */
2980 /** Unserialize an object.
2981 * @see ext/standard/var_unserializer.c in the php-src repo. Parts of this code are based on that.
2982 */
igbinary_unserialize_object(struct igbinary_unserialize_data * igsd,enum igbinary_type t,zval * z,int flags)2983 inline static int igbinary_unserialize_object(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *z, int flags) {
2984 zend_class_entry *ce;
2985
2986 size_t ref_n;
2987
2988 zend_string *class_name;
2989
2990 int r;
2991
2992 bool incomplete_class = false;
2993 bool is_from_serialized_data = false;
2994
2995 if (t == igbinary_type_object8 || t == igbinary_type_object16 || t == igbinary_type_object32) {
2996 class_name = igbinary_unserialize_chararray(igsd, t);
2997 } else if (t == igbinary_type_object_id8 || t == igbinary_type_object_id16 || t == igbinary_type_object_id32) {
2998 class_name = igbinary_unserialize_string(igsd, t);
2999 } else {
3000 zend_error(E_WARNING, "igbinary_unserialize_object: unknown object type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd));
3001 return 1;
3002 }
3003
3004 if (class_name == NULL) {
3005 return 1;
3006 }
3007
3008 do {
3009 zval user_func;
3010 zval retval;
3011 zval args[1];
3012 const char* user_func_name;
3013
3014 /* Try to find class directly */
3015 if (EXPECTED((ce = zend_lookup_class(class_name)) != NULL)) {
3016 /* FIXME: lookup class may cause exception in load callback */
3017 break;
3018 }
3019
3020 user_func_name = PG(unserialize_callback_func);
3021 /* Check for unserialize callback */
3022 if ((user_func_name == NULL) || (user_func_name[0] == '\0')) {
3023 incomplete_class = 1;
3024 ce = PHP_IC_ENTRY;
3025 break;
3026 }
3027
3028 /* Call unserialize callback */
3029 ZVAL_STRING(&user_func, user_func_name);
3030 ZVAL_STR(&args[0], class_name);
3031 if (call_user_function(CG(function_table), NULL, &user_func, &retval, 1, args) != SUCCESS) {
3032 php_error_docref(NULL, E_WARNING, "defined (%s) but not found", Z_STRVAL(user_func));
3033 incomplete_class = 1;
3034 ce = PHP_IC_ENTRY;
3035 zval_ptr_dtor_nogc(&user_func);
3036 break;
3037 }
3038 /* FIXME: always safe? */
3039 zval_ptr_dtor(&retval);
3040 zval_ptr_dtor_str(&user_func);
3041
3042 /* User function call may have raised an exception */
3043 if (EG(exception)) {
3044 zend_string_release_ex(class_name, 0);
3045 return 1;
3046 }
3047
3048 /* The callback function may have defined the class */
3049 ce = zend_lookup_class(class_name);
3050 if (!ce) {
3051 php_error_docref(NULL, E_WARNING, "Function %s() hasn't defined the class it was called for", PG(unserialize_callback_func));
3052 incomplete_class = true;
3053 ce = PHP_IC_ENTRY;
3054 }
3055 } while (0);
3056
3057 if (IGBINARY_IS_NOT_UNSERIALIZABLE(ce)) {
3058 zend_throw_exception_ex(NULL, 0, "Unserialization of '%s' is not allowed", ZSTR_VAL(ce->name));
3059 zend_string_release(class_name);
3060 return 1;
3061 }
3062
3063 /* add this to the list of unserialized references, get the index */
3064 if (IGB_NEEDS_MORE_DATA(igsd, 1)) {
3065 zend_error(E_WARNING, "igbinary_unserialize_object: end-of-data");
3066 zend_string_release(class_name);
3067 return 1;
3068 }
3069
3070 {
3071 /* The actual value of ref is unused. We use ref_n later in this function, after creating the object. */
3072 struct igbinary_value_ref ref = {{0}, 0};
3073 ref_n = igsd_append_ref(igsd, ref);
3074 if (UNEXPECTED(ref_n == SIZE_MAX)) {
3075 zend_string_release(class_name);
3076 return 1;
3077 }
3078 }
3079
3080 t = (enum igbinary_type)igbinary_unserialize8(igsd);
3081 switch (t) {
3082 case igbinary_type_array8:
3083 case igbinary_type_array16:
3084 case igbinary_type_array32:
3085 {
3086 if (UNEXPECTED(object_init_ex(z, ce) != SUCCESS)) {
3087 php_error_docref(NULL, E_NOTICE, "igbinary unable to create object for class entry");
3088 r = 1;
3089 break;
3090 }
3091 if (incomplete_class) {
3092 #if PHP_VERSION_ID >= 80000
3093 php_store_class_name(z, class_name);
3094 #else
3095 php_store_class_name(z, ZSTR_VAL(class_name), ZSTR_LEN(class_name));
3096 #endif
3097 #if PHP_VERSION_ID >= 70400
3098 } else {
3099 #if PHP_VERSION_ID >= 80000
3100 if (ce->__unserialize)
3101 #else
3102 if (zend_hash_str_exists(&ce->function_table, "__unserialize", sizeof("__unserialize") - 1))
3103 #endif
3104 {
3105 ZEND_ASSERT(Z_TYPE_P(z) == IS_OBJECT);
3106 struct igbinary_value_ref *ref = &IGB_REF_VAL_2(igsd, ref_n);
3107 if ((flags & WANT_REF) != 0) {
3108 ZVAL_MAKE_REF(z);
3109 ref->reference.reference = Z_REF_P(z);
3110 ref->type = IG_REF_IS_REFERENCE;
3111 } else {
3112 ref->reference.object = Z_OBJ_P(z);
3113 ref->type = IG_REF_IS_OBJECT;
3114 }
3115 /* Unserialize the array as an array for a deferred call to __unserialize */
3116 zval param;
3117 int result;
3118 zend_string_release(class_name);
3119 result = igbinary_unserialize_array(igsd, t, ¶m, 0, false);
3120 ZVAL_DEREF(z);
3121 ZEND_ASSERT(Z_TYPE_P(z) == IS_OBJECT);
3122 igsd_defer_unserialize(igsd, Z_OBJ_P(z), param);
3123 return result;
3124 }
3125 #endif
3126 }
3127 struct igbinary_value_ref *ref = &IGB_REF_VAL_2(igsd, ref_n);
3128 if ((flags & WANT_REF) != 0) {
3129 ZVAL_MAKE_REF(z);
3130 ref->reference.reference = Z_REF_P(z);
3131 ref->type = IG_REF_IS_REFERENCE;
3132 } else {
3133 ref->reference.object = Z_OBJ_P(z);
3134 ref->type = IG_REF_IS_OBJECT;
3135 }
3136
3137 r = igbinary_unserialize_object_properties(igsd, t, z);
3138 break;
3139 }
3140 case igbinary_type_object_ser8:
3141 case igbinary_type_object_ser16:
3142 case igbinary_type_object_ser32:
3143 {
3144 is_from_serialized_data = true;
3145 /* FIXME will this break if z isn't an object? */
3146 r = igbinary_unserialize_object_ser(igsd, t, z, ce);
3147 if (r != 0) {
3148 break;
3149 }
3150
3151 if (incomplete_class) {
3152 #if PHP_VERSION_ID >= 80000
3153 php_store_class_name(z, class_name);
3154 #else
3155 php_store_class_name(z, ZSTR_VAL(class_name), ZSTR_LEN(class_name));
3156 #endif
3157 }
3158 struct igbinary_value_ref *ref = &IGB_REF_VAL_2(igsd, ref_n);
3159 if ((flags & WANT_REF) != 0) {
3160 ZVAL_MAKE_REF(z);
3161 ref->reference.reference = Z_REF_P(z);
3162 ref->type = IG_REF_IS_REFERENCE;
3163 } else {
3164 ref->reference.object = Z_OBJ_P(z);
3165 ref->type = IG_REF_IS_OBJECT;
3166 }
3167 break;
3168 }
3169 case igbinary_type_enum_case:
3170 #if PHP_VERSION_ID >= 80100
3171 if (UNEXPECTED(incomplete_class)) {
3172 zend_error(E_WARNING, "igbinary_unserialize_object_enum_case: Class '%s' does not exist", ZSTR_VAL(class_name));
3173 zend_string_release(class_name);
3174 return 1;
3175 }
3176 zend_string_release(class_name);
3177 r = igbinary_unserialize_object_enum_case(igsd, z, ce);
3178 if (r) {
3179 return r;
3180 }
3181
3182 struct igbinary_value_ref *ref = &IGB_REF_VAL_2(igsd, ref_n);
3183 if ((flags & WANT_REF) != 0) {
3184 ZVAL_MAKE_REF(z);
3185 ref->reference.reference = Z_REF_P(z);
3186 ref->type = IG_REF_IS_REFERENCE;
3187 } else {
3188 ref->reference.object = Z_OBJ_P(z);
3189 ref->type = IG_REF_IS_OBJECT;
3190 }
3191 return 0;
3192 #else
3193 zend_error(E_WARNING, "igbinary_unserialize_object: Cannot unserialize enum cases prior to php 8.1 at position %zu", (size_t)IGB_BUFFER_OFFSET(igsd));
3194 r = 1;
3195 #endif
3196 break;
3197 default:
3198 zend_error(E_WARNING, "igbinary_unserialize_object: unknown object inner type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd));
3199 r = 1;
3200 }
3201 zend_string_release(class_name);
3202 class_name = NULL;
3203
3204 /* If unserialize was successful, defer the call to __wakeup if __wakeup exists for this object. */
3205 /* (But don't call __wakeup() if Serializable::unserialize was called */
3206 if (r == 0 && !is_from_serialized_data) {
3207 struct igbinary_value_ref *const ref = &IGB_REF_VAL_2(igsd, ref_n);
3208 zend_object *object;
3209 if (ref->type == IG_REF_IS_OBJECT) {
3210 object = ref->reference.object;
3211 } else if (EXPECTED(ref->type == IG_REF_IS_REFERENCE)) {
3212 /* May have created a reference while deserializing an object, if it was recursive. */
3213 zval ztemp = ref->reference.reference->val;
3214 if (UNEXPECTED(Z_TYPE(ztemp) != IS_OBJECT)) {
3215 zend_error(E_WARNING, "igbinary_unserialize_object preparing to __wakeup/__unserialize: got reference to non-object somehow (inner type '%02x', position %zu)", t, (size_t)IGB_BUFFER_OFFSET(igsd));
3216 return 1;
3217 }
3218 object = Z_OBJ(ztemp);
3219 } else {
3220 zend_error(E_WARNING, "igbinary_unserialize_object preparing to __wakeup/__unserialize: created non-object somehow (inner type '%02x', position %zu)", t, (size_t)IGB_BUFFER_OFFSET(igsd));
3221 return 1;
3222 }
3223 if (object->ce != PHP_IC_ENTRY) {
3224 if (zend_hash_str_exists(&object->ce->function_table, "__wakeup", sizeof("__wakeup") - 1)) {
3225 if (UNEXPECTED(igsd_defer_wakeup(igsd, object))) {
3226 return 1;
3227 }
3228 }
3229 }
3230 }
3231
3232 return r;
3233 }
3234 /* }}} */
3235 /* {{{ igbinary_unserialize_ref */
3236 /** Unserializes an array or object by reference. */
igbinary_unserialize_ref(struct igbinary_unserialize_data * igsd,enum igbinary_type t,zval * const z,int flags)3237 inline static int igbinary_unserialize_ref(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags) {
3238 size_t n;
3239
3240 if (t == igbinary_type_ref8 || t == igbinary_type_objref8) {
3241 if (IGB_NEEDS_MORE_DATA(igsd, 1)) {
3242 zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data");
3243 return 1;
3244 }
3245 n = igbinary_unserialize8(igsd);
3246 } else if (t == igbinary_type_ref16 || t == igbinary_type_objref16) {
3247 if (IGB_NEEDS_MORE_DATA(igsd, 2)) {
3248 zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data");
3249 return 1;
3250 }
3251 n = igbinary_unserialize16(igsd);
3252 } else if (t == igbinary_type_ref32 || t == igbinary_type_objref32) {
3253 if (IGB_NEEDS_MORE_DATA(igsd, 4)) {
3254 zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data");
3255 return 1;
3256 }
3257 n = igbinary_unserialize32(igsd);
3258 } else {
3259 zend_error(E_WARNING, "igbinary_unserialize_ref: unknown type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd));
3260 return 1;
3261 }
3262
3263 if (n >= igsd->references_count) {
3264 zend_error(E_WARNING, "igbinary_unserialize_ref: invalid reference %zu >= %zu", (size_t)n, (size_t)igsd->references_count);
3265 return 1;
3266 }
3267
3268 if (z != NULL) {
3269 /* FIXME: check with is refcountable or some such */
3270 zval_ptr_dtor(z);
3271 ZVAL_UNDEF(z);
3272 }
3273
3274 struct igbinary_value_ref *ref_ptr = &IGB_REF_VAL_2(igsd, n);
3275 struct igbinary_value_ref ref = *ref_ptr;
3276
3277 /**
3278 * Permanently convert the zval in IGB_REF_VAL() into a IS_REFERENCE if it wasn't already one.
3279 * TODO: Can there properly be multiple reference groups to an object?
3280 * Similar to https://github.com/php/php-src/blob/master/ext/standard/var_unserializer.re , for "R:"
3281 * Using `flags` because igbinary_unserialize_ref might be used both for copy on writes ($a = $b = [2]) and by PHP references($a = &$b).
3282 */
3283 if ((flags & WANT_REF) != 0) {
3284 /* Want to create an IS_REFERENCE, not just to share the same value until modified. */
3285 switch (ref.type) {
3286 case IG_REF_IS_OBJECT:
3287 ZVAL_OBJ(z, ref.reference.object);
3288 Z_ADDREF_P(z);
3289 ZVAL_MAKE_REF(z); /* Convert original zval data to a reference */
3290 /* replace the entry in IGB_REF_VAL with a reference. */
3291 ref_ptr->reference.reference = Z_REF_P(z);
3292 ref_ptr->type = IG_REF_IS_REFERENCE;
3293 break;
3294 case IG_REF_IS_ARRAY:
3295 ZVAL_ARR(z, ref.reference.array);
3296 /* All arrays built by igbinary when unserializing are refcounted, except IG_REF_IS_EMPTY_ARRAY. */
3297 /* If they were not refcounted, the ZVAL_ARR call would probably also need to be changed. */
3298 Z_ADDREF_P(z);
3299 ZVAL_MAKE_REF(z); /* Convert original zval data to a reference */
3300 /* replace the entry in IGB_REF_VAL with a reference. */
3301 ref_ptr->reference.reference = Z_REF_P(z);
3302 ref_ptr->type = IG_REF_IS_REFERENCE;
3303 break;
3304 #if PHP_VERSION_ID >= 70300
3305 case IG_REF_IS_EMPTY_ARRAY:
3306 ZVAL_EMPTY_ARRAY(z);
3307 ZVAL_MAKE_REF(z); /* Convert original zval data to a reference */
3308 /* replace the entry in IGB_REF_VAL with a reference. */
3309 ref_ptr->reference.reference = Z_REF_P(z);
3310 ref_ptr->type = IG_REF_IS_REFERENCE;
3311 break;
3312 #endif
3313 case IG_REF_IS_REFERENCE:
3314 // This is already a reference, convert into reference count.
3315 ZVAL_REF(z, ref.reference.reference);
3316 Z_ADDREF_P(z);
3317 break;
3318 }
3319 } else {
3320 switch (ref.type) {
3321 case IG_REF_IS_OBJECT:
3322 ZVAL_OBJ(z, ref.reference.object);
3323 Z_ADDREF_P(z);
3324 break;
3325 case IG_REF_IS_ARRAY:
3326 ZVAL_ARR(z, ref.reference.array);
3327 Z_ADDREF_P(z);
3328 break;
3329 #if PHP_VERSION_ID >= 70300
3330 case IG_REF_IS_EMPTY_ARRAY:
3331 ZVAL_EMPTY_ARRAY(z);
3332 break;
3333 #endif
3334 case IG_REF_IS_REFERENCE:
3335 ZVAL_COPY(z, &(ref.reference.reference->val));
3336 break;
3337 }
3338 }
3339
3340 return 0;
3341 }
3342 /* }}} */
3343 /* {{{ igbinary_unserialize_zval */
3344 /** Unserialize a zval of any serializable type (zval is PHP's internal representation of a value). */
igbinary_unserialize_zval(struct igbinary_unserialize_data * igsd,zval * const z,int flags)3345 static int igbinary_unserialize_zval(struct igbinary_unserialize_data *igsd, zval *const z, int flags) {
3346 enum igbinary_type t;
3347
3348 zend_long tmp_long;
3349 double tmp_double;
3350 zend_string *tmp_str;
3351
3352 if (IGB_NEEDS_MORE_DATA(igsd, 1)) {
3353 zend_error(E_WARNING, "igbinary_unserialize_zval: end-of-data");
3354 return 1;
3355 }
3356
3357 t = (enum igbinary_type)igbinary_unserialize8(igsd);
3358
3359 switch (t) {
3360 case igbinary_type_ref:
3361 if (UNEXPECTED(igbinary_unserialize_zval(igsd, z, WANT_REF))) {
3362 return 1;
3363 }
3364
3365 /* If it is already a ref, nothing to do */
3366 if (Z_ISREF_P(z)) {
3367 break;
3368 }
3369
3370 const zend_uchar type = Z_TYPE_P(z);
3371 /* Permanently convert the zval in IGB_REF_VAL() into a IS_REFERENCE if it wasn't already one. */
3372 /* TODO: Support multiple reference groups to the same object */
3373 /* Similar to https://github.com/php/php-src/blob/master/ext/standard/var_unserializer.re , for "R:" */
3374 ZVAL_MAKE_REF(z);
3375 switch (type) {
3376 case IS_STRING:
3377 case IS_LONG:
3378 case IS_NULL:
3379 case IS_DOUBLE:
3380 case IS_FALSE:
3381 case IS_TRUE:
3382 {
3383 struct igbinary_value_ref ref;
3384 ref.reference.reference = Z_REF_P(z);
3385 ref.type = IG_REF_IS_REFERENCE;
3386 /* add the unserialized scalar to the list of unserialized references. Objects and arrays were already added in igbinary_unserialize_zval. */
3387 RETURN_1_IF_NON_ZERO(igsd_append_ref(igsd, ref) == SIZE_MAX);
3388 break;
3389 }
3390 default:
3391 break;
3392 }
3393 break;
3394 case igbinary_type_objref8:
3395 case igbinary_type_objref16:
3396 case igbinary_type_objref32:
3397 case igbinary_type_ref8:
3398 case igbinary_type_ref16:
3399 case igbinary_type_ref32:
3400 if (UNEXPECTED(igbinary_unserialize_ref(igsd, t, z, flags))) {
3401 return 1;
3402 }
3403 break;
3404 case igbinary_type_object8:
3405 case igbinary_type_object16:
3406 case igbinary_type_object32:
3407 case igbinary_type_object_id8:
3408 case igbinary_type_object_id16:
3409 case igbinary_type_object_id32:
3410 if (UNEXPECTED(igbinary_unserialize_object(igsd, t, z, flags))) {
3411 return 1;
3412 }
3413 break;
3414 case igbinary_type_array8:
3415 case igbinary_type_array16:
3416 case igbinary_type_array32:
3417 if (UNEXPECTED(igbinary_unserialize_array(igsd, t, z, flags, true))) {
3418 return 1;
3419 }
3420 break;
3421 case igbinary_type_string_empty:
3422 ZVAL_EMPTY_STRING(z);
3423 break;
3424 case igbinary_type_string_id8:
3425 case igbinary_type_string_id16:
3426 case igbinary_type_string_id32:
3427 tmp_str = igbinary_unserialize_string(igsd, t);
3428 if (UNEXPECTED(tmp_str == NULL)) {
3429 return 1;
3430 }
3431 ZVAL_STR(z, tmp_str);
3432 break;
3433 case igbinary_type_string8:
3434 case igbinary_type_string16:
3435 case igbinary_type_string32:
3436 case igbinary_type_string64:
3437 tmp_str = igbinary_unserialize_chararray(igsd, t);
3438 if (UNEXPECTED(tmp_str == NULL)) {
3439 return 1;
3440 }
3441 ZVAL_STR(z, tmp_str);
3442 break;
3443 case igbinary_type_long8p:
3444 /* Manually inline igbinary_unserialize_long() for values from 0 to 255, because they're the most common among integers in many applications. */
3445 if (IGB_NEEDS_MORE_DATA(igsd, 1)) {
3446 zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data");
3447 return 1;
3448 }
3449
3450 ZVAL_LONG(z, igbinary_unserialize8(igsd));
3451 break;
3452 case igbinary_type_long16p:
3453 /* Manually inline igbinary_unserialize_long() for values from 0 to 255, because they're the most common among integers in many applications. */
3454 if (IGB_NEEDS_MORE_DATA(igsd, 2)) {
3455 zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data");
3456 return 1;
3457 }
3458
3459 ZVAL_LONG(z, igbinary_unserialize16(igsd));
3460 break;
3461 case igbinary_type_long8n:
3462 case igbinary_type_long16n:
3463 case igbinary_type_long32p:
3464 case igbinary_type_long32n:
3465 case igbinary_type_long64p:
3466 case igbinary_type_long64n:
3467 if (UNEXPECTED(igbinary_unserialize_long(igsd, t, &tmp_long))) {
3468 return 1;
3469 }
3470 ZVAL_LONG(z, tmp_long);
3471 break;
3472 case igbinary_type_null:
3473 ZVAL_NULL(z);
3474 break;
3475 case igbinary_type_bool_false:
3476 ZVAL_BOOL(z, 0);
3477 break;
3478 case igbinary_type_bool_true:
3479 ZVAL_BOOL(z, 1);
3480 break;
3481 case igbinary_type_double:
3482 if (UNEXPECTED(igbinary_unserialize_double(igsd, &tmp_double))) {
3483 return 1;
3484 }
3485 ZVAL_DOUBLE(z, tmp_double);
3486 break;
3487 default:
3488 zend_error(E_WARNING, "igbinary_unserialize_zval: unknown type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd));
3489 return 1;
3490 }
3491
3492 return 0;
3493 }
3494 /* }}} */
3495
3496 /*
3497 * Local variables:
3498 * tab-width: 2
3499 * c-basic-offset: 4
3500 * End:
3501 * vim600: noet sw=4 ts=4 fdm=marker
3502 * vim<600: noet sw=4 ts=4
3503 */
3504