# UNDF: UNDF-2026-000000498 # php-0003: zend_do_implement_interfaces — O(I²) interface dedup linear scan ## Summary | Field | Value | |-------------|-------| | ID | php-0003 | | Severity | MEDIUM | | CWE | CWE-407 (Algorithmic Complexity) | | File | `Zend/zend_inheritance.c` | | Function | `zend_do_implement_interfaces` | | Lines | 2259–2290 | | Speedup | ~250x op-count at I=500 interfaces | ## Defect When PHP links a class that implements I interfaces (including inherited ones), `zend_do_implement_interfaces` builds a deduplicated `interfaces[]` array. For each new interface, it scans the already-accumulated array linearly: ```c // zend_inheritance.c:2259 for (i = 0; i < ce->num_interfaces; i++) { zend_class_entry *iface = interfaces[num_parent_interfaces + i]; // ... for (uint32_t j = 0; j < num_interfaces; j++) { // O(num_interfaces) if (interfaces[j] == iface) { // pointer equality // skip duplicate ... break; } } if (iface) { interfaces[num_interfaces] = iface; num_interfaces++; } } ``` The outer loop runs `ce->num_interfaces` times. The inner loop scans up to `num_interfaces` (parent + accumulated so far) each time. Total: O(P×I + I²) where P = parent interface count, I = new interface count. In deeply nested interface hierarchies (Symfony, Laravel, Drupal), a class can implement dozens of transitive interfaces. E.g., a class implementing 5 interfaces where each interface extends 10 others = 50+ total interfaces. ## Root Cause The `interfaces[]` array is a flat C array with no corresponding hash set. Duplicate detection uses pointer equality but relies on a linear scan. ## Fix Use a temporary `HashTable` (or a pointer-keyed hash set) to track which interface pointers have already been added: ```diff --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -2251,7 +2251,9 @@ static void zend_do_implement_interfaces(...) { uint32_t num_parent_interfaces = ce->parent ? ce->parent->num_interfaces : 0; uint32_t num_interfaces = num_parent_interfaces; + HashTable iface_set; + zend_hash_init(&iface_set, num_parent_interfaces + ce->num_interfaces, NULL, NULL, 0); + /* pre-populate with parent interfaces */ + for (uint32_t k = 0; k < num_parent_interfaces; k++) { + zend_hash_index_add_empty_element(&iface_set, (zend_ulong)(uintptr_t)interfaces[k]); + } for (i = 0; i < ce->num_interfaces; i++) { zend_class_entry *iface = interfaces[num_parent_interfaces + i]; - /* O(num_interfaces) linear scan for duplicate */ - for (uint32_t j = 0; j < num_interfaces; j++) { - if (interfaces[j] == iface) { - /* duplicate found */ - ... - iface = NULL; - break; - } - } + /* O(1) hash lookup for duplicate */ + if (zend_hash_index_find(&iface_set, (zend_ulong)(uintptr_t)iface) != NULL) { + /* duplicate: inherit constants but don't add to interfaces[] */ + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&iface->constants_table, key, c) { + do_inherit_constant_check(ce, c, key); + } ZEND_HASH_FOREACH_END(); + continue; + } if (iface) { interfaces[num_interfaces] = iface; + zend_hash_index_add_empty_element(&iface_set, (zend_ulong)(uintptr_t)iface); num_interfaces++; } } + zend_hash_destroy(&iface_set); ``` ## Measurement Simulated with `PhpInterfaceDedupAlgorithm.java`: | I | slow ops | fast ops | ratio | |------|----------|----------|-------| | 50 | 1,275 | 50 | 25× | | 200 | 20,100 | 200 | 100× | | 500 | 125,250 | 500 | 250× | ## References - `Zend/zend_inheritance.c` lines 2251–2312 (`zend_do_implement_interfaces`) - `Zend/zend_inheritance.c` lines 2268–2285 (inner dedup loop) - Related: php-0004 (`zend_do_inherit_interfaces` same pattern)