msgpack/php/msgpack_unpack.c
2010-07-17 18:46:28 +09:00

830 lines
23 KiB
C

#include "php.h"
#include "php_ini.h"
#include "ext/standard/php_incomplete_class.h"
#include "ext/standard/php_var.h"
#include "php_msgpack.h"
#include "msgpack_pack.h"
#include "msgpack_unpack.h"
#include "msgpack/unpack_define.h"
#define VAR_ENTRIES_MAX 1024
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3)
# define Z_ADDREF_PP(ppz) ZVAL_ADDREF(*(ppz))
# define Z_SET_ISREF_PP(ppz) (*(ppz))->is_ref = 1
# define Z_UNSET_ISREF_PP(ppz) (*(ppz))->is_ref = 0
#endif
typedef struct
{
zval *data[VAR_ENTRIES_MAX];
long used_slots;
void *next;
} var_entries;
inline static void msgpack_var_push(
php_unserialize_data_t *var_hashx, zval **rval)
{
var_entries *var_hash, *prev = NULL;
if (!var_hashx)
{
return;
}
var_hash = var_hashx->first;
while (var_hash && var_hash->used_slots == VAR_ENTRIES_MAX)
{
prev = var_hash;
var_hash = var_hash->next;
}
if (!var_hash)
{
var_hash = emalloc(sizeof(var_entries));
var_hash->used_slots = 0;
var_hash->next = 0;
if (!var_hashx->first)
{
var_hashx->first = var_hash;
}
else
{
prev->next = var_hash;
}
}
var_hash->data[var_hash->used_slots++] = *rval;
}
inline static int msgpack_var_access(
php_unserialize_data_t *var_hashx, long id, zval ***store)
{
var_entries *var_hash = var_hashx->first;
while (id >= VAR_ENTRIES_MAX &&
var_hash && var_hash->used_slots == VAR_ENTRIES_MAX)
{
var_hash = var_hash->next;
id -= VAR_ENTRIES_MAX;
}
if (!var_hash)
{
return !SUCCESS;
}
if (id < 0 || id >= var_hash->used_slots)
{
return !SUCCESS;
}
*store = &var_hash->data[id];
return SUCCESS;
}
inline static int msgpack_unserialize_array(
zval **return_value, msgpack_unserialize_data *mpsd,
ulong ct, php_unserialize_data_t *var_hash TSRMLS_DC)
{
ulong i;
HashTable *ht;
msgpack_var_push(var_hash, return_value);
if (Z_TYPE_PP(return_value) != IS_ARRAY)
{
array_init(*return_value);
}
ht = HASH_OF(*return_value);
for (i = 0; i < ct; i++)
{
zval *val;
/* value */
ALLOC_INIT_ZVAL(val);
if (msgpack_unserialize_zval(&val, mpsd, var_hash TSRMLS_CC) <= 0)
{
if (MSGPACK_G(error_display))
{
zend_error(E_WARNING,
"[msgpack] (msgpack_unserialize_array) "
"Invalid value");
}
zval_ptr_dtor(&val);
return MSGPACK_UNPACK_PARSE_ERROR;
}
/* update */
zend_hash_index_update(ht, i, &val, sizeof(zval *), NULL);
}
return MSGPACK_UNPACK_SUCCESS;
}
inline static int msgpack_unserialize_object_type(
zval **return_value, msgpack_unserialize_data *mpsd,
php_unserialize_data_t *var_hash, zend_bool is_ref TSRMLS_DC)
{
int ret = MSGPACK_UNPACK_SUCCESS;
zval *key, *val, **rval;
ALLOC_INIT_ZVAL(key);
if (msgpack_unserialize_zval(&key, mpsd, NULL TSRMLS_CC) <= 0)
{
zval_ptr_dtor(&key);
ZVAL_BOOL(*return_value, 0);
return MSGPACK_UNPACK_PARSE_ERROR;
}
ALLOC_INIT_ZVAL(val);
if (is_ref)
{
ret = msgpack_unserialize_zval(&val, mpsd, NULL TSRMLS_CC);
}
else
{
ret = msgpack_unserialize_zval(&val, mpsd, var_hash TSRMLS_CC);
}
if (ret <= 0)
{
zval_ptr_dtor(&val);
zval_ptr_dtor(&key);
ZVAL_BOOL(*return_value, 0);
return MSGPACK_UNPACK_PARSE_ERROR;
}
if (!var_hash ||
msgpack_var_access(var_hash, Z_LVAL_P(val) - 1, &rval) != SUCCESS)
{
if (MSGPACK_G(error_display))
{
zend_error(E_WARNING,
"[msgpack] (msgpack_unserialize_object_key) "
"Invalid references value: %ld",
Z_LVAL_P(val) - 1);
}
ret = MSGPACK_UNPACK_CONTINUE;
}
else
{
if (*return_value != NULL)
{
zval_ptr_dtor(return_value);
}
*return_value = *rval;
Z_ADDREF_PP(return_value);
if (is_ref)
{
Z_SET_ISREF_PP(return_value);
}
else
{
Z_UNSET_ISREF_PP(return_value);
}
}
zval_ptr_dtor(&key);
zval_ptr_dtor(&val);
return ret;
}
inline static int msgpack_unserialize_object(
zval **return_value, msgpack_unserialize_data *mpsd,
ulong ct, php_unserialize_data_t *var_hash TSRMLS_DC)
{
int ret = MSGPACK_UNPACK_SUCCESS;
zend_class_entry *ce, **pce;
bool incomplete_class = false;
zval *user_func, *retval_ptr, **args[1], *arg_func_name;
HashTable *ht;
zval *key, *val;
int object = 1;
int custom_object = 0;
/* Get class */
ALLOC_INIT_ZVAL(key);
if (msgpack_unserialize_zval(&key, mpsd, NULL TSRMLS_CC) <= 0)
{
if (MSGPACK_G(error_display))
{
zend_error(E_WARNING,
"[msgpack] (msgpack_unserialize_object) "
"Invalid sign key");
}
zval_ptr_dtor(&key);
ZVAL_BOOL(*return_value, 0);
return MSGPACK_UNPACK_PARSE_ERROR;
}
ct--;
if (Z_TYPE_P(key) == IS_NULL)
{
ALLOC_INIT_ZVAL(val);
if (msgpack_unserialize_zval(&val, mpsd, NULL TSRMLS_CC) <= 0)
{
if (MSGPACK_G(error_display))
{
zend_error(E_WARNING,
"[msgpack] (msgpack_unserialize_object) "
"Invalid sign value");
}
zval_ptr_dtor(&val);
zval_ptr_dtor(&key);
ZVAL_BOOL(*return_value, 0);
return MSGPACK_UNPACK_PARSE_ERROR;
}
if (Z_TYPE_P(val) == IS_LONG)
{
switch (Z_LVAL_P(val))
{
case MSGPACK_SERIALIZE_TYPE_REFERENCE:
ret = msgpack_unserialize_object_type(
return_value, mpsd, var_hash, 1 TSRMLS_CC);
zval_ptr_dtor(&key);
zval_ptr_dtor(&val);
return ret;
case MSGPACK_SERIALIZE_TYPE_OBJECT:
ret = msgpack_unserialize_object_type(
return_value, mpsd, var_hash, 0 TSRMLS_CC);
zval_ptr_dtor(&key);
zval_ptr_dtor(&val);
return ret;
case MSGPACK_SERIALIZE_TYPE_CUSTOM_OBJECT:
custom_object = 1;
zval_ptr_dtor(&val);
ALLOC_INIT_ZVAL(val);
if (msgpack_unserialize_zval(
&val, mpsd, NULL TSRMLS_CC) <= 0)
{
zval_ptr_dtor(&key);
zval_ptr_dtor(&val);
ZVAL_BOOL(*return_value, 0);
return MSGPACK_UNPACK_PARSE_ERROR;
}
break;
default:
zval_ptr_dtor(&key);
zval_ptr_dtor(&val);
ZVAL_BOOL(*return_value, 0);
return MSGPACK_UNPACK_PARSE_ERROR;
}
}
}
else
{
object = 0;
msgpack_var_push(var_hash, return_value);
if (Z_TYPE_PP(return_value) != IS_ARRAY)
{
array_init(*return_value);
}
ht = HASH_OF(*return_value);
ALLOC_INIT_ZVAL(val);
if (msgpack_unserialize_zval(&val, mpsd, var_hash TSRMLS_CC) <= 0)
{
if (MSGPACK_G(error_display))
{
zend_error(E_WARNING,
"[msgpack] (msgpack_unserialize_object) "
"Invalid sign value");
}
zval_ptr_dtor(&val);
zval_ptr_dtor(&key);
return MSGPACK_UNPACK_PARSE_ERROR;
}
/* update */
switch (Z_TYPE_P(key))
{
case IS_LONG:
zend_hash_index_update(
ht, Z_LVAL_P(key), &val, sizeof(zval *), NULL);
break;
case IS_STRING:
zend_symtable_update(
ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1,
&val, sizeof(zval *), NULL);
break;
default:
zval_ptr_dtor(&key);
zval_ptr_dtor(&val);
return MSGPACK_UNPACK_PARSE_ERROR;
}
}
zval_ptr_dtor(&key);
if (object)
{
convert_to_string(val);
do {
/* Try to find class directly */
if (zend_lookup_class(
Z_STRVAL_P(val), Z_STRLEN_P(val), &pce TSRMLS_CC) == SUCCESS)
{
ce = *pce;
break;
}
/* Check for unserialize callback */
if ((PG(unserialize_callback_func) == NULL) ||
(PG(unserialize_callback_func)[0] == '\0'))
{
incomplete_class = 1;
ce = PHP_IC_ENTRY;
break;
}
/* Call unserialize callback */
ALLOC_INIT_ZVAL(user_func);
ZVAL_STRING(user_func, PG(unserialize_callback_func), 1);
args[0] = &arg_func_name;
ALLOC_INIT_ZVAL(arg_func_name);
ZVAL_STRING(arg_func_name, Z_STRVAL_P(val), 1);
if (call_user_function_ex(
CG(function_table), NULL, user_func, &retval_ptr,
1, args, 0, NULL TSRMLS_CC) != SUCCESS)
{
if (MSGPACK_G(error_display))
{
zend_error(E_WARNING,
"[msgpack] (msgpack_unserialize_object) "
"defined (%s) but not found", Z_STRVAL_P(val));
}
incomplete_class = 1;
ce = PHP_IC_ENTRY;
zval_ptr_dtor(&user_func);
zval_ptr_dtor(&arg_func_name);
break;
}
if (retval_ptr)
{
zval_ptr_dtor(&retval_ptr);
}
/* The callback function may have defined the class */
if (zend_lookup_class(
Z_STRVAL_P(val), Z_STRLEN_P(val), &pce TSRMLS_CC) == SUCCESS)
{
ce = *pce;
}
else
{
if (MSGPACK_G(error_display))
{
zend_error(E_WARNING,
"[msgpack] (msgpack_unserialize_object) "
"Function %s() hasn't defined the class"
"it was called for", Z_STRVAL_P(val));
}
incomplete_class = true;
ce = PHP_IC_ENTRY;
}
zval_ptr_dtor(&user_func);
zval_ptr_dtor(&arg_func_name);
} while(0);
if (EG(exception))
{
if (MSGPACK_G(error_display))
{
zend_error(E_WARNING,
"[msgpack] (msgpack_unserialize_object) "
"Exception error");
}
zval_ptr_dtor(&val);
ZVAL_BOOL(*return_value, 0);
return MSGPACK_UNPACK_PARSE_ERROR;
}
msgpack_var_push(var_hash, return_value);
object_init_ex(*return_value, ce);
/* store incomplete class name */
if (incomplete_class)
{
php_store_class_name(
*return_value, Z_STRVAL_P(val), Z_STRLEN_P(val));
}
zval_ptr_dtor(&val);
/* implementing Serializable */
if (custom_object)
{
zval *rval;
if (ce->unserialize == NULL)
{
if (MSGPACK_G(error_display))
{
zend_error(E_WARNING,
"[msgpack] (msgpack_unserialize_object) "
"Class %s has no unserializer", ce->name);
}
return MSGPACK_UNPACK_PARSE_ERROR;
}
/* value */
ALLOC_INIT_ZVAL(rval);
if (msgpack_unserialize_zval(&rval, mpsd, var_hash TSRMLS_CC) <= 0)
{
zval_ptr_dtor(&rval);
return MSGPACK_UNPACK_PARSE_ERROR;
}
ce->unserialize(
return_value, ce,
(const unsigned char *)Z_STRVAL_P(rval), Z_STRLEN_P(rval) + 1,
(zend_unserialize_data *)var_hash TSRMLS_CC);
zval_ptr_dtor(&key);
zval_ptr_dtor(&rval);
return ret;
}
ht = HASH_OF(*return_value);
}
/* object property */
while (ct-- > 0)
{
zval *rval;
/* key */
ALLOC_INIT_ZVAL(key);
if (msgpack_unserialize_zval(&key, mpsd, NULL TSRMLS_CC) <= 0)
{
zval_ptr_dtor(&key);
return MSGPACK_UNPACK_PARSE_ERROR;
}
/* value */
ALLOC_INIT_ZVAL(rval);
if (msgpack_unserialize_zval(&rval, mpsd, var_hash TSRMLS_CC) <= 0)
{
zval_ptr_dtor(&rval);
zval_ptr_dtor(&key);
return MSGPACK_UNPACK_PARSE_ERROR;
}
/* update */
switch (Z_TYPE_P(key))
{
case IS_LONG:
zend_hash_index_update(
ht, Z_LVAL_P(key), &rval, sizeof(zval *), NULL);
break;
case IS_STRING:
zend_symtable_update(
ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1,
&rval, sizeof(zval *), NULL);
break;
default:
zval_ptr_dtor(&key);
zval_ptr_dtor(&rval);
return MSGPACK_UNPACK_PARSE_ERROR;
}
zval_ptr_dtor(&key);
}
/* wakeup */
if (object && Z_OBJCE_PP(return_value) != PHP_IC_ENTRY &&
zend_hash_exists(&Z_OBJCE_PP(return_value)->function_table,
"__wakeup", sizeof("__wakeup")))
{
zval f, *h = NULL;
INIT_PZVAL(&f);
ZVAL_STRINGL(&f, "__wakeup", sizeof("__wakeup") - 1, 0);
call_user_function_ex(
CG(function_table), return_value, &f, &h, 0, 0, 1, NULL TSRMLS_CC);
if (h)
{
zval_ptr_dtor(&h);
}
if (EG(exception))
{
ret = MSGPACK_UNPACK_PARSE_ERROR;
}
}
return ret;
}
int msgpack_unserialize_zval(
zval **return_value, msgpack_unserialize_data *mpsd,
php_unserialize_data_t *var_hash TSRMLS_DC)
{
const unsigned char* data = mpsd->data;
const unsigned char* const pe = mpsd->data + mpsd->length;
const void* n = NULL;
unsigned int trail = 0;
unsigned int cs = CS_HEADER;
int ret;
unsigned int ct;
#define next_cs(p) ((unsigned int)*p & 0x1f)
#define finish_zval_long(val_) \
msgpack_var_push(var_hash, return_value); \
ZVAL_LONG(*return_value, val_); \
goto _finish
#define finish_zval_null() \
msgpack_var_push(var_hash, return_value); \
ZVAL_NULL(*return_value); \
goto _finish
#define finish_zval_bool(val_) \
msgpack_var_push(var_hash, return_value); \
ZVAL_BOOL(*return_value, val_); \
goto _finish
#define finish_zval_double(val_) \
msgpack_var_push(var_hash, return_value); \
ZVAL_DOUBLE(*return_value, val_); \
goto _finish
#define finish_zval_string(val_, len_) \
msgpack_var_push(var_hash, return_value); \
if (len_ == 0) { ZVAL_STRINGL(*return_value, "", 0, 1); } \
else { ZVAL_STRINGL(*return_value, val_, len_, 1); } \
goto _finish
#define finish_zval_array(count_) \
ct = count_; \
cs = CS_HEADER; \
++(mpsd->data); \
mpsd->length = (pe - mpsd->data); \
if (msgpack_unserialize_array(return_value, mpsd, ct, var_hash TSRMLS_CC) <= 0) { goto _failed; } \
goto _finish_end
#define finish_zval_object(count_) \
ct = count_; \
cs = CS_HEADER; \
++(mpsd->data); \
mpsd->length = (pe - mpsd->data); \
if (msgpack_unserialize_object(return_value, mpsd, ct, var_hash TSRMLS_CC) <= 0) { goto _failed; } \
goto _finish_end
#define again_fixed_trail(_cs, trail_len) \
trail = trail_len; \
cs = _cs; \
goto _fixed_trail_again
#define again_fixed_trail_if_zero(_cs, trail_len, ifzero) \
trail = trail_len; \
if(trail == 0) { goto ifzero; } \
cs = _cs; \
goto _fixed_trail_again
if (mpsd->length <= 0)
{
goto _failed;
}
if (mpsd->data == pe)
{
goto _out;
}
do
{
switch (cs)
{
case CS_HEADER:
switch (*(mpsd->data))
{
case 0x00 ... 0x7f: /* Positive Fixnum */
finish_zval_long(*(uint8_t*)mpsd->data);
case 0xe0 ... 0xff: /* Negative Fixnum */
finish_zval_long(*(int8_t*)mpsd->data);
case 0xc0 ... 0xdf: /* Variable */
switch (*(mpsd->data))
{
case 0xc0: /* nil */
finish_zval_null();
case 0xc2: /* false */
finish_zval_bool(0);
case 0xc3: /* true */
finish_zval_bool(1);
case 0xca: /* float */
case 0xcb: /* double */
case 0xcc: /* unsigned int 8 */
case 0xcd: /* unsigned int 16 */
case 0xce: /* unsigned int 32 */
case 0xcf: /* unsigned int 64 */
case 0xd0: /* signed int 8 */
case 0xd1: /* signed int 16 */
case 0xd2: /* signed int 32 */
case 0xd3: /* signed int 64 */
again_fixed_trail(
next_cs(mpsd->data),
1 << (((unsigned int)*(mpsd->data)) & 0x03));
case 0xda: /* raw 16 */
case 0xdb: /* raw 32 */
case 0xdc: /* array 16 */
case 0xdd: /* array 32 */
case 0xde: /* map 16 */
case 0xdf: /* map 32 */
again_fixed_trail(
next_cs(mpsd->data),
2 << (((unsigned int)*(mpsd->data)) & 0x01));
default:
goto _failed;
}
case 0xa0 ... 0xbf: /* FixRaw */
again_fixed_trail_if_zero(
ACS_RAW_VALUE, (unsigned int)*(mpsd->data) & 0x1f,
_raw_zero);
case 0x90 ... 0x9f: /* FixArray */
finish_zval_array(((unsigned int)*(mpsd->data)) & 0x0f);
case 0x80 ... 0x8f: /* FixMap */
finish_zval_object(((unsigned int)*(mpsd->data)) & 0x0f);
default:
goto _failed;
}
_fixed_trail_again:
++(mpsd->data);
default:
if ((size_t)(pe - (mpsd->data)) < trail)
{
goto _out;
}
n = (mpsd->data);
mpsd->data += trail - 1;
switch (cs)
{
case CS_FLOAT:
{
union { uint32_t i; float f; } mem;
mem.i = _msgpack_load32(uint32_t, n);
finish_zval_double(mem.f);
}
case CS_DOUBLE:
{
union { uint64_t i; double f; } mem;
mem.i = _msgpack_load64(uint64_t, n);
finish_zval_double(mem.f);
}
case CS_UINT_8:
finish_zval_long(*(uint8_t*)n);
case CS_UINT_16:
finish_zval_long(_msgpack_load16(uint16_t, n));
case CS_UINT_32:
finish_zval_long(_msgpack_load32(uint32_t, n));
case CS_UINT_64:
finish_zval_long(_msgpack_load64(uint64_t, n));
case CS_INT_8:
finish_zval_long(*(int8_t*)n);
case CS_INT_16:
finish_zval_long(_msgpack_load16(int16_t, n));
case CS_INT_32:
finish_zval_long(_msgpack_load32(int32_t, n));
case CS_INT_64:
finish_zval_long(_msgpack_load64(int64_t, n));
case CS_RAW_16:
again_fixed_trail_if_zero(
ACS_RAW_VALUE, _msgpack_load16(uint16_t, n),
_raw_zero);
case CS_RAW_32:
again_fixed_trail_if_zero(
ACS_RAW_VALUE, _msgpack_load32(uint32_t, n),
_raw_zero);
case ACS_RAW_VALUE:
_raw_zero:
finish_zval_string(n, trail);
case CS_ARRAY_16:
finish_zval_array(_msgpack_load16(uint16_t, n));
case CS_ARRAY_32:
finish_zval_array(_msgpack_load32(uint32_t, n));
/* FIXME security guard */
case CS_MAP_16:
finish_zval_object(_msgpack_load16(uint16_t, n));
case CS_MAP_32:
finish_zval_object(_msgpack_load32(uint32_t, n));
/* FIXME security guard */
default:
goto _failed;
}
}
cs = CS_HEADER;
++(mpsd->data);
}
while (mpsd->data != pe);
goto _out;
_finish:
++(mpsd->data);
_finish_end:
ret = MSGPACK_UNPACK_EXTRA_BYTES;
goto _end;
_failed:
ret = MSGPACK_UNPACK_PARSE_ERROR;
goto _end;
_out:
ret = MSGPACK_UNPACK_CONTINUE;
goto _end;
_end:
mpsd->offset = mpsd->data - data;
mpsd->length = pe - mpsd->data;
if (ret == MSGPACK_UNPACK_EXTRA_BYTES && mpsd->length == 0)
{
ret = MSGPACK_UNPACK_SUCCESS;
}
return ret;
}
#undef finish_zval_long
#undef finish_zval_null
#undef finish_zval_bool
#undef finish_zval_double
#undef finish_zval_string
#undef finish_zval_array
#undef finish_zval_object
#undef again_fixed_trail
#undef again_fixed_trail_if_zero
#undef next_cs