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(&param, &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(&param, &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, &param, 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