From 14b770d4074616f7f5dcf026422a91a8b7419d90 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 1 Apr 2020 14:20:59 +0200 Subject: Fix literal compaction collision between string and double For the sake of simplicity I'm using a separate hashtable, rather than trying to do hash perturabation on the double strings. --- ext/opcache/Optimizer/compact_literals.c | 11 ++++++++--- ext/opcache/tests/compact_literals_collision.phpt | Bin 0 -> 252 bytes 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 ext/opcache/tests/compact_literals_collision.phpt diff --git a/ext/opcache/Optimizer/compact_literals.c b/ext/opcache/Optimizer/compact_literals.c index fdab0068a4..e4dab13b9c 100644 --- a/ext/opcache/Optimizer/compact_literals.c +++ b/ext/opcache/Optimizer/compact_literals.c @@ -125,7 +125,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx int l_false = -1; int l_true = -1; int l_empty_arr = -1; - HashTable hash; + HashTable hash, double_hash; zend_string *key = NULL; void *checkpoint = zend_arena_checkpoint(ctx->arena); int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot; @@ -329,6 +329,8 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx /* Merge equal constants */ j = 0; zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0); + /* Use separate hashtable for doubles stored as string keys, to avoid collisions. */ + zend_hash_init(&double_hash, 0, NULL, NULL, 0); map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int)); memset(map, 0, op_array->last_literal * sizeof(int)); for (i = 0; i < op_array->last_literal; i++) { @@ -411,12 +413,12 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } break; case IS_DOUBLE: - if ((pos = zend_hash_str_find(&hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double))) != NULL) { + if ((pos = zend_hash_str_find(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double))) != NULL) { map[i] = Z_LVAL_P(pos); } else { map[i] = j; ZVAL_LONG(&zv, j); - zend_hash_str_add(&hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv); + zend_hash_str_add(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv); if (i != j) { op_array->literals[j] = op_array->literals[i]; info[j] = info[i]; @@ -494,7 +496,10 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx break; } } + + /* Only clean "hash", as it will be reused in the loop below. */ zend_hash_clean(&hash); + zend_hash_destroy(&double_hash); op_array->last_literal = j; const_slot = zend_arena_alloc(&ctx->arena, j * 6 * sizeof(int)); diff --git a/ext/opcache/tests/compact_literals_collision.phpt b/ext/opcache/tests/compact_literals_collision.phpt new file mode 100644 index 0000000000..b7226b40f5 Binary files /dev/null and b/ext/opcache/tests/compact_literals_collision.phpt differ -- cgit v1.2.1