]> granicus.if.org Git - php/commitdiff
Merge branch 'PHP-5.5' into PHP-5.6
authorRemi Collet <remi@php.net>
Mon, 27 Oct 2014 06:50:42 +0000 (07:50 +0100)
committerRemi Collet <remi@php.net>
Mon, 27 Oct 2014 06:50:42 +0000 (07:50 +0100)
* PHP-5.5:
  NEWS
  Fix bug #63595 GMP memory management conflicts with other libraries using GMP

Conflicts:
ext/gmp/gmp.c

1  2 
ext/gmp/gmp.c

diff --cc ext/gmp/gmp.c
index 8aff2d6b23f77cda3712d4ee47b5676b257b0335,b1553fa16f092c38391128d22e9d398e104222cb..e9c1ad34164ae946ba88dc5061225a8db6a97d99
@@@ -246,454 -324,6 +246,430 @@@ typedef struct _gmp_temp 
  #     define MAX_BASE 36
  #endif
  
- /* {{{ gmp_emalloc
-  */
- static void *gmp_emalloc(size_t size)
- {
-       return emalloc(size);
- }
- /* }}} */
- /* {{{ gmp_erealloc
-  */
- static void *gmp_erealloc(void *ptr, size_t old_size, size_t new_size)
- {
-       return erealloc(ptr, new_size);
- }
- /* }}} */
- /* {{{ gmp_efree
-  */
- static void gmp_efree(void *ptr, size_t size)
- {
-       efree(ptr);
- }
- /* }}} */
 +#define IS_GMP(zval) \
 +      (Z_TYPE_P(zval) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zval), gmp_ce TSRMLS_CC))
 +
 +#define GET_GMP_FROM_ZVAL(zval) \
 +      (((gmp_object *) zend_object_store_get_object((zval) TSRMLS_CC))->num)
 +
 +/* The FETCH_GMP_ZVAL_* family of macros is used to fetch a gmp number
 + * (mpz_ptr) from a zval. If the zval is not a GMP instance, then we
 + * try to convert the value to a temporary gmp number using convert_to_gmp.
 + * This temporary number is stored in the temp argument, which is of type
 + * gmp_temp_t. This temporary value needs to be freed lateron using the
 + * FREE_GMP_TEMP macro.
 + *
 + * If the conversion to a gmp number fails, the macros return false.
 + * The _DEP / _DEP_DEP variants additionally free the temporary values
 + * passed in the last / last two arguments.
 + *
 + * If one zval can sometimes be fetched as a long you have to set the
 + * is_used member of the corresponding gmp_temp_t value to 0, otherwise
 + * the FREE_GMP_TEMP and *_DEP macros will not work properly.
 + *
 + * The three FETCH_GMP_ZVAL_* macros below are mostly copy & paste code
 + * as I couldn't find a way to combine them.
 + */
 +
 +#define FREE_GMP_TEMP(temp)  \
 +      if (temp.is_used) {      \
 +              mpz_clear(temp.num); \
 +      }
 +
 +#define FETCH_GMP_ZVAL_DEP_DEP(gmpnumber, zval, temp, dep1, dep2) \
 +if (IS_GMP(zval)) {                                               \
 +      gmpnumber = GET_GMP_FROM_ZVAL(zval);                          \
 +      temp.is_used = 0;                                             \
 +} else {                                                          \
 +      mpz_init(temp.num);                                           \
 +      if (convert_to_gmp(temp.num, zval, 0 TSRMLS_CC) == FAILURE) { \
 +              mpz_clear(temp.num);                                      \
 +              FREE_GMP_TEMP(dep1);                                      \
 +              FREE_GMP_TEMP(dep2);                                      \
 +              RETURN_FALSE;                                             \
 +      }                                                             \
 +      temp.is_used = 1;                                             \
 +      gmpnumber = temp.num;                                         \
 +}
 +
 +#define FETCH_GMP_ZVAL_DEP(gmpnumber, zval, temp, dep)            \
 +if (IS_GMP(zval)) {                                               \
 +      gmpnumber = GET_GMP_FROM_ZVAL(zval);                          \
 +      temp.is_used = 0;                                             \
 +} else {                                                          \
 +      mpz_init(temp.num);                                           \
 +      if (convert_to_gmp(temp.num, zval, 0 TSRMLS_CC) == FAILURE) { \
 +              mpz_clear(temp.num);                                      \
 +              FREE_GMP_TEMP(dep);                                       \
 +              RETURN_FALSE;                                             \
 +      }                                                             \
 +      temp.is_used = 1;                                             \
 +      gmpnumber = temp.num;                                         \
 +}
 +
 +#define FETCH_GMP_ZVAL(gmpnumber, zval, temp)                     \
 +if (IS_GMP(zval)) {                                               \
 +      gmpnumber = GET_GMP_FROM_ZVAL(zval);                          \
 +      temp.is_used = 0;                                             \
 +} else {                                                          \
 +      mpz_init(temp.num);                                           \
 +      if (convert_to_gmp(temp.num, zval, 0 TSRMLS_CC) == FAILURE) { \
 +              mpz_clear(temp.num);                                      \
 +              RETURN_FALSE;                                             \
 +      }                                                             \
 +      temp.is_used = 1;                                             \
 +      gmpnumber = temp.num;                                         \
 +}
 +
 +#define INIT_GMP_RETVAL(gmpnumber) \
 +      gmp_create_ex(return_value, &gmpnumber TSRMLS_CC)
 +
 +static void gmp_strval(zval *result, mpz_t gmpnum, long base);
 +static int convert_to_gmp(mpz_t gmpnumber, zval *val, int base TSRMLS_DC);
 +static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg TSRMLS_DC);
 +
 +/*
 + * The gmp_*_op functions provide an implementation for several common types
 + * of GMP functions. The gmp_zval_(unary|binary)_*_op functions have to be manually
 + * passed zvals to work on, whereas the gmp_(unary|binary)_*_op macros already
 + * include parameter parsing.
 + */
 +typedef void (*gmp_unary_op_t)(mpz_ptr, mpz_srcptr);
 +typedef int (*gmp_unary_opl_t)(mpz_srcptr);
 +
 +typedef void (*gmp_unary_ui_op_t)(mpz_ptr, unsigned long);
 +
 +typedef void (*gmp_binary_op_t)(mpz_ptr, mpz_srcptr, mpz_srcptr);
 +typedef int (*gmp_binary_opl_t)(mpz_srcptr, mpz_srcptr);
 +
 +typedef void (*gmp_binary_ui_op_t)(mpz_ptr, mpz_srcptr, unsigned long);
 +typedef void (*gmp_binary_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr);
 +typedef void (*gmp_binary_ui_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, unsigned long);
 +
 +static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, int check_b_zero TSRMLS_DC);
 +static inline void gmp_zval_binary_ui_op2(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op2_t gmp_op, gmp_binary_ui_op2_t gmp_ui_op, int check_b_zero TSRMLS_DC);
 +static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op TSRMLS_DC);
 +static inline void gmp_zval_unary_ui_op(zval *return_value, zval *a_arg, gmp_unary_ui_op_t gmp_op TSRMLS_DC);
 +
 +/* Binary operations */
 +#define gmp_binary_ui_op(op, uop) _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op, uop, 0)
 +#define gmp_binary_op(op)         _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op, NULL, 0)
 +#define gmp_binary_opl(op) _gmp_binary_opl(INTERNAL_FUNCTION_PARAM_PASSTHRU, op)
 +#define gmp_binary_ui_op_no_zero(op, uop) \
 +      _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op, uop, 1)
 +
 +/* Unary operations */
 +#define gmp_unary_op(op)         _gmp_unary_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op)
 +#define gmp_unary_opl(op)         _gmp_unary_opl(INTERNAL_FUNCTION_PARAM_PASSTHRU, op)
 +#define gmp_unary_ui_op(op)      _gmp_unary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op)
 +
 +static inline long gmp_get_long(zval *zv) /* {{{ */
 +{
 +      if (Z_TYPE_P(zv) == IS_LONG) {
 +              return Z_LVAL_P(zv);
 +      } else {
 +              zval tmp_zv;
 +              MAKE_COPY_ZVAL(&zv, &tmp_zv);
 +              convert_to_long(&tmp_zv);
 +              return Z_LVAL(tmp_zv);
 +      }
 +}
 +/* }}} */
 +
 +static void gmp_free_object_storage(gmp_object *intern TSRMLS_DC) /* {{{ */
 +{
 +      mpz_clear(intern->num);
 +
 +      zend_object_std_dtor(&intern->std TSRMLS_CC);
 +      efree(intern);
 +}
 +/* }}} */
 +
 +static inline zend_object_value gmp_create_object_ex(zend_class_entry *ce, mpz_ptr *gmpnum_target TSRMLS_DC) /* {{{ */
 +{
 +      zend_object_value retval;
 +      gmp_object *intern = emalloc(sizeof(gmp_object));
 +
 +      zend_object_std_init(&intern->std, ce TSRMLS_CC);
 +      object_properties_init(&intern->std, ce);
 +
 +      mpz_init(intern->num);
 +      *gmpnum_target = intern->num;
 +
 +      retval.handle = zend_objects_store_put(
 +              intern, (zend_objects_store_dtor_t) zend_objects_destroy_object,
 +              (zend_objects_free_object_storage_t) gmp_free_object_storage,
 +              NULL TSRMLS_CC
 +      );
 +      retval.handlers = &gmp_object_handlers;
 +
 +      return retval;
 +}
 +/* }}} */
 +
 +static zend_object_value gmp_create_object(zend_class_entry *ce TSRMLS_DC) /* {{{ */
 +{
 +      mpz_ptr gmpnum_dummy;
 +      return gmp_create_object_ex(ce, &gmpnum_dummy TSRMLS_CC);
 +}
 +/* }}} */
 +
 +static inline void gmp_create_ex(zval *target, mpz_ptr *gmpnum_target TSRMLS_DC) /* {{{ */
 +{
 +      Z_TYPE_P(target) = IS_OBJECT;
 +      Z_OBJVAL_P(target) = gmp_create_object_ex(gmp_ce, gmpnum_target TSRMLS_CC);
 +}
 +/* }}} */
 +
 +static zval *gmp_create(mpz_ptr *gmpnum_target TSRMLS_DC) /* {{{ */
 +{
 +      zval *obj;
 +      MAKE_STD_ZVAL(obj);
 +      gmp_create_ex(obj, gmpnum_target TSRMLS_CC);
 +      return obj;
 +}
 +/* }}} */
 +
 +static int gmp_cast_object(zval *readobj, zval *writeobj, int type TSRMLS_DC) /* {{{ */
 +{
 +      mpz_ptr gmpnum;
 +      switch (type) {
 +      case IS_STRING:
 +              gmpnum = GET_GMP_FROM_ZVAL(readobj);
 +              INIT_PZVAL(writeobj);
 +              gmp_strval(writeobj, gmpnum, 10);
 +              return SUCCESS;
 +      case IS_LONG:
 +              gmpnum = GET_GMP_FROM_ZVAL(readobj);
 +              INIT_PZVAL(writeobj);
 +              ZVAL_LONG(writeobj, mpz_get_si(gmpnum));
 +              return SUCCESS;
 +      case IS_DOUBLE:
 +              gmpnum = GET_GMP_FROM_ZVAL(readobj);
 +              INIT_PZVAL(writeobj);
 +              ZVAL_DOUBLE(writeobj, mpz_get_d(gmpnum));
 +              return SUCCESS;
 +      default:
 +              return FAILURE;
 +      }
 +}
 +/* }}} */
 +
 +static HashTable *gmp_get_debug_info(zval *obj, int *is_temp TSRMLS_DC) /* {{{ */
 +{
 +      HashTable *ht, *props = zend_std_get_properties(obj TSRMLS_CC);
 +      mpz_ptr gmpnum = GET_GMP_FROM_ZVAL(obj);
 +      zval *zv;
 +
 +      *is_temp = 1;
 +      ALLOC_HASHTABLE(ht);
 +      ZEND_INIT_SYMTABLE_EX(ht, zend_hash_num_elements(props) + 1, 0);
 +      zend_hash_copy(ht, props, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
 +
 +      MAKE_STD_ZVAL(zv);
 +      gmp_strval(zv, gmpnum, 10);
 +      zend_hash_update(ht, "num", sizeof("num"), &zv, sizeof(zval *), NULL);
 +
 +      return ht;
 +}
 +/* }}} */
 +
 +static zend_object_value gmp_clone_obj(zval *obj TSRMLS_DC) /* {{{ */
 +{
 +      gmp_object *old_object = zend_object_store_get_object(obj TSRMLS_CC);
 +      zend_object_value new_object_val = gmp_create_object(Z_OBJCE_P(obj) TSRMLS_CC);
 +      gmp_object *new_object = zend_object_store_get_object_by_handle(
 +              new_object_val.handle TSRMLS_CC
 +      );
 +
 +      zend_objects_clone_members(
 +              &new_object->std, new_object_val,
 +              &old_object->std, Z_OBJ_HANDLE_P(obj) TSRMLS_CC
 +      );
 +
 +      mpz_set(new_object->num, old_object->num);
 +
 +      return new_object_val;
 +}
 +/* }}} */
 +
 +static void shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2 TSRMLS_DC) {
 +      zval op2_copy;
 +      if (Z_TYPE_P(op2) != IS_LONG) {
 +              op2_copy = *op2;
 +              zval_copy_ctor(&op2_copy);
 +              convert_to_long(&op2_copy);
 +              op2 = &op2_copy;
 +      }
 +
 +      if (Z_LVAL_P(op2) < 0) {
 +              php_error_docref(NULL TSRMLS_CC, E_WARNING, "Shift cannot be negative");
 +              RETVAL_FALSE;
 +      } else {
 +              mpz_ptr gmpnum_op, gmpnum_result;
 +              gmp_temp_t temp;
 +
 +              FETCH_GMP_ZVAL(gmpnum_op, op1, temp);
 +              INIT_GMP_RETVAL(gmpnum_result);
 +              op(gmpnum_result, gmpnum_op, (unsigned long) Z_LVAL_P(op2));
 +              FREE_GMP_TEMP(temp);
 +      }
 +}
 +
 +#define DO_BINARY_UI_OP_EX(op, uop, check_b_zero)       \
 +      gmp_zval_binary_ui_op(                              \
 +              result, op1, op2, op, (gmp_binary_ui_op_t) uop, \
 +              check_b_zero TSRMLS_CC                          \
 +      );                                                  \
 +      return SUCCESS;
 +
 +#define DO_BINARY_UI_OP(op) DO_BINARY_UI_OP_EX(op, op ## _ui, 0)
 +#define DO_BINARY_OP(op) DO_BINARY_UI_OP_EX(op, NULL, 0)
 +
 +#define DO_UNARY_OP(op) \
 +      gmp_zval_unary_op(result, op1, op TSRMLS_CC); \
 +      return SUCCESS;
 +
 +static int gmp_do_operation_ex(zend_uchar opcode, zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
 +{
 +      switch (opcode) {
 +      case ZEND_ADD:
 +              DO_BINARY_UI_OP(mpz_add);
 +      case ZEND_SUB:
 +              DO_BINARY_UI_OP(mpz_sub);
 +      case ZEND_MUL:
 +              DO_BINARY_UI_OP(mpz_mul);
 +      case ZEND_POW:
 +              shift_operator_helper(mpz_pow_ui, result, op1, op2 TSRMLS_CC);
 +              return SUCCESS;
 +      case ZEND_DIV:
 +              DO_BINARY_UI_OP_EX(mpz_tdiv_q, mpz_tdiv_q_ui, 1);
 +      case ZEND_MOD:
 +              DO_BINARY_UI_OP_EX(mpz_mod, mpz_mod_ui, 1);
 +      case ZEND_SL:
 +              shift_operator_helper(mpz_mul_2exp, result, op1, op2 TSRMLS_CC);
 +              return SUCCESS;
 +      case ZEND_SR:
 +              shift_operator_helper(mpz_fdiv_q_2exp, result, op1, op2 TSRMLS_CC);
 +              return SUCCESS;
 +      case ZEND_BW_OR:
 +              DO_BINARY_OP(mpz_ior);
 +      case ZEND_BW_AND:
 +              DO_BINARY_OP(mpz_and);
 +      case ZEND_BW_XOR:
 +              DO_BINARY_OP(mpz_xor);
 +      case ZEND_BW_NOT:
 +              DO_UNARY_OP(mpz_com);
 +
 +      default:
 +              return FAILURE;
 +      }
 +}
 +/* }}} */
 +
 +static int gmp_do_operation(zend_uchar opcode, zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
 +{
 +      zval op1_copy;
 +      int retval;
 +
 +      if (result == op1) {
 +              ZVAL_COPY_VALUE(&op1_copy, op1);
 +              op1 = &op1_copy;
 +      }
 +
 +      retval = gmp_do_operation_ex(opcode, result, op1, op2 TSRMLS_CC);
 +
 +      if (retval == SUCCESS && op1 == &op1_copy) {
 +              zval_dtor(op1);
 +      }
 +
 +      return retval;
 +}
 +/* }}} */
 +
 +static int gmp_compare(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
 +{
 +      gmp_cmp(result, op1, op2 TSRMLS_CC);
 +      if (Z_TYPE_P(result) == IS_BOOL) {
 +              ZVAL_LONG(result, 1);
 +      }
 +      return SUCCESS;
 +}
 +/* }}} */
 +
 +static int gmp_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC) /* {{{ */
 +{
 +      mpz_ptr gmpnum = GET_GMP_FROM_ZVAL(object);
 +      smart_str buf = {0};
 +      zval zv, *zv_ptr = &zv;
 +      php_serialize_data_t serialize_data = (php_serialize_data_t) data;
 +
 +      PHP_VAR_SERIALIZE_INIT(serialize_data);
 +      INIT_PZVAL(zv_ptr);
 +
 +      gmp_strval(zv_ptr, gmpnum, 10);
 +      php_var_serialize(&buf, &zv_ptr, &serialize_data TSRMLS_CC);
 +      zval_dtor(zv_ptr);
 +
 +      Z_ARRVAL_P(zv_ptr) = zend_std_get_properties(object TSRMLS_CC);
 +      Z_TYPE_P(zv_ptr) = IS_ARRAY;
 +      php_var_serialize(&buf, &zv_ptr, &serialize_data TSRMLS_CC);
 +
 +      PHP_VAR_SERIALIZE_DESTROY(serialize_data);
 +      *buffer = (unsigned char *) buf.c;
 +      *buf_len = buf.len;
 +
 +      return SUCCESS;
 +}
 +/* }}} */
 +
 +static int gmp_unserialize(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC) /* {{{ */
 +{
 +      mpz_ptr gmpnum;
 +      const unsigned char *p, *max;
 +      zval zv, *zv_ptr = &zv;
 +      int retval = FAILURE;
 +      php_unserialize_data_t unserialize_data = (php_unserialize_data_t) data;
 +
 +      PHP_VAR_UNSERIALIZE_INIT(unserialize_data);
 +      gmp_create_ex(*object, &gmpnum TSRMLS_CC);
 +
 +      p = buf;
 +      max = buf + buf_len;
 +
 +      INIT_ZVAL(zv);
 +      if (!php_var_unserialize(&zv_ptr, &p, max, &unserialize_data TSRMLS_CC)
 +              || Z_TYPE_P(zv_ptr) != IS_STRING
 +              || convert_to_gmp(gmpnum, zv_ptr, 10 TSRMLS_CC) == FAILURE
 +      ) {
 +              zend_throw_exception(NULL, "Could not unserialize number", 0 TSRMLS_CC);
 +              goto exit;
 +      }
 +      zval_dtor(&zv);
 +
 +      INIT_ZVAL(zv);
 +      if (!php_var_unserialize(&zv_ptr, &p, max, &unserialize_data TSRMLS_CC)
 +              || Z_TYPE_P(zv_ptr) != IS_ARRAY
 +      ) {
 +              zend_throw_exception(NULL, "Could not unserialize properties", 0 TSRMLS_CC);
 +              goto exit;
 +      }
 +
 +      if (zend_hash_num_elements(Z_ARRVAL_P(zv_ptr)) != 0) {
 +              zend_hash_copy(
 +                      zend_std_get_properties(*object TSRMLS_CC), Z_ARRVAL_P(zv_ptr),
 +                      (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)
 +              );
 +      }
 +
 +      retval = SUCCESS;
 +exit:
 +      zval_dtor(&zv);
 +      PHP_VAR_UNSERIALIZE_DESTROY(unserialize_data);
 +      return retval;
 +}
 +/* }}} */
 +
  /* {{{ ZEND_GINIT_FUNCTION
   */
  static ZEND_GINIT_FUNCTION(gmp)
@@@ -728,14 -345,6 +704,12 @@@ ZEND_MINIT_FUNCTION(gmp
  #endif
        REGISTER_STRING_CONSTANT("GMP_VERSION", (char *)gmp_version, CONST_CS | CONST_PERSISTENT);
  
-       mp_set_memory_functions(gmp_emalloc, gmp_erealloc, gmp_efree);
 +      REGISTER_LONG_CONSTANT("GMP_MSW_FIRST", GMP_MSW_FIRST, CONST_CS | CONST_PERSISTENT);
 +      REGISTER_LONG_CONSTANT("GMP_LSW_FIRST", GMP_LSW_FIRST, CONST_CS | CONST_PERSISTENT);
 +      REGISTER_LONG_CONSTANT("GMP_LITTLE_ENDIAN", GMP_LITTLE_ENDIAN, CONST_CS | CONST_PERSISTENT);
 +      REGISTER_LONG_CONSTANT("GMP_BIG_ENDIAN", GMP_BIG_ENDIAN, CONST_CS | CONST_PERSISTENT);
 +      REGISTER_LONG_CONSTANT("GMP_NATIVE_ENDIAN", GMP_NATIVE_ENDIAN, CONST_CS | CONST_PERSISTENT);
 +
        return SUCCESS;
  }
  /* }}} */