wave16: dart-0001/2/3 + octave-0002 + php-0003/4 + cassandra-0005 + erlang-0003 + chef-0001 — 542/240

This commit is contained in:
russell@unturf.com 2026-03-27 19:36:35 -04:00
parent 31850d5ef6
commit d816d3c74c
18 changed files with 1902 additions and 6 deletions

View 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)

View file

@ -0,0 +1,102 @@
# php-0004: zend_do_inherit_interfaces — O(IF × CE) interface inheritance dedup linear scan
## Summary
| Field | Value |
|-------------|-------|
| ID | php-0004 |
| Severity | MEDIUM |
| CWE | CWE-407 (Algorithmic Complexity) |
| File | `Zend/zend_inheritance.c` |
| Function | `zend_do_inherit_interfaces` |
| Lines | 1592–1623 |
| Speedup | ~150x op-count at IF=CE=500 |
## Defect
When a class inherits from a parent or implements an interface, PHP calls
`zend_do_inherit_interfaces` to copy the interface's interface list into the
class's own interface list (without duplicates). For each of IF parent interfaces,
it does an O(CE) linear scan over the class's current `ce_num` interfaces:
```c
// zend_inheritance.c:1606
while (if_num--) {
zend_class_entry *entry = iface->interfaces[if_num];
for (i = 0; i < ce_num; i++) { // O(ce_num) scan
if (ce->interfaces[i] == entry) { // pointer equality
break;
}
}
if (i == ce_num) {
ce->interfaces[ce->num_interfaces++] = entry;
}
}
```
The outer loop is `if_num` = number of interfaces in the implemented interface.
The inner loop scans `ce_num` = current class interface count.
Total: O(IF × CE).
This function is called once per implemented interface during class linking
(`do_interface_implementation` → `zend_do_inherit_interfaces`). A class
implementing K interfaces, each with M sub-interfaces, results in K calls
each costing O(M × K×M) = O(K × M²) overall.
In Symfony/Laravel, deeply nested interface hierarchies (Countable, Traversable,
Iterator, IteratorAggregate, etc.) can produce dozens of inherited interfaces
per class.
## Root Cause
`ce->interfaces` is a flat pointer array. Duplicate detection uses a linear
scan rather than a pointer set.
## Fix
Maintain a pointer hash set that grows as interfaces are added:
```diff
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -1592,6 +1592,9 @@ static void zend_do_inherit_interfaces(...)
{
uint32_t i, ce_num, if_num = iface->num_interfaces;
ce_num = ce->num_interfaces;
+ HashTable iface_set;
+ zend_hash_init(&iface_set, ce_num + if_num, NULL, NULL, 0);
+ for (i = 0; i < ce_num; i++) {
+ zend_hash_index_add_empty_element(&iface_set, (zend_ulong)(uintptr_t)ce->interfaces[i]);
+ }
while (if_num--) {
zend_class_entry *entry = iface->interfaces[if_num];
- for (i = 0; i < ce_num; i++) {
- if (ce->interfaces[i] == entry) {
- break;
- }
- }
- if (i == ce_num) {
+ if (!zend_hash_index_find(&iface_set, (zend_ulong)(uintptr_t)entry)) {
ce->interfaces[ce->num_interfaces++] = entry;
+ zend_hash_index_add_empty_element(&iface_set, (zend_ulong)(uintptr_t)entry);
}
}
+ zend_hash_destroy(&iface_set);
```
## Measurement
Simulated with `PhpInterfaceDedupAlgorithm.java`:
| IF | CE | slow ops | fast ops | ratio |
|------|------|----------|----------|-------|
| 50 | 50 | 2,500 | 100 | 25× |
| 200 | 200 | 40,000 | 400 | 100× |
| 500 | 500 | 250,000 | 1,000 | 250× |
## References
- `Zend/zend_inheritance.c` lines 1592–1623 (`zend_do_inherit_interfaces`)
- `Zend/zend_inheritance.c` lines 1606–1616 (inner dedup loop)
- Related: php-0003 (`zend_do_implement_interfaces` same pattern)