]> granicus.if.org Git - php/commitdiff
Fix literal compaction collision between string and double
authorNikita Popov <nikita.ppv@gmail.com>
Wed, 1 Apr 2020 12:20:59 +0000 (14:20 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Wed, 1 Apr 2020 12:20:59 +0000 (14:20 +0200)
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
ext/opcache/tests/compact_literals_collision.phpt [new file with mode: 0644]

index fdab0068a4d5ed41f9e7f96659c08b534d4d07de..e4dab13b9c26f4a1b38cdc4adfa42293f70549f6 100644 (file)
@@ -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 (file)
index 0000000..b7226b4
Binary files /dev/null and b/ext/opcache/tests/compact_literals_collision.phpt differ