wave16: dart-0001/2/3 + octave-0002 + php-0003/4 + cassandra-0005 + erlang-0003 + chef-0001 — 542/240
This commit is contained in:
parent
31850d5ef6
commit
d816d3c74c
18 changed files with 1902 additions and 6 deletions
114
defects/php/patch/php-0003-interface-dedup-hashset.md
Normal file
114
defects/php/patch/php-0003-interface-dedup-hashset.md
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
# 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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue