# UNDF: UNDF-2026-000000412
## Classification

| Field       | Value |
|-------------|-------|
| CWE         | CWE-407 Inefficient Algorithmic Complexity |
| Severity    | MEDIUM |
| Component   | `mesonbuild/build.py:1625` |
| Function    | `BuildTarget.get_internal_static_libraries_recurse` |
| Hot path    | Static library link closure — called during build graph evaluation for every target with `link_whole` dependencies |
| Status      | PATCHED (unit test PASS) |

## Defect

`get_internal_static_libraries_recurse` uses `result` (an `OrderedSet`) as a
visited guard for `link_targets` but **omits the guard for `link_whole_targets`**.
The `link_whole_targets` branch recurses unconditionally:

```python
# mesonbuild/build.py:1625
def get_internal_static_libraries_recurse(self, result: OrderedSet[StaticTargetTypes]) -> None:
    for t in self.link_targets:
        if t.is_internal() and t not in result:   # ← guard present
            result.add(t)
            t.get_internal_static_libraries_recurse(result)
    for t in self.link_whole_targets:
        if t.is_internal():
            t.get_internal_static_libraries_recurse(result)   # ← NO guard!
```

On a diamond `link_whole` graph — `A link_whole B, C; B link_whole D; C link_whole D;
D link_whole E` — the recursion visits `D` twice and `E` twice.  At depth D the
total node visits is **O(2^D)**.

**Diamond trace:**

```
A.recurse(result={}):
  B.recurse(result={}):          # link_whole B
    D.recurse(result={}):        #   link_whole D
      E.recurse(result={}): …    #     → result = {E}
    → result = {E}
  C.recurse(result={E}):         # link_whole C
    D.recurse(result={E}):       #   link_whole D — AGAIN (no guard!)
      E.recurse(result={E}):     #     → visits E again, returns (already in result… but
                                 #       link_whole branch doesn't check!)
```

In practice `link_whole` chains in C++ projects are typically shallow (1–3
levels), giving 2–8 redundant traversals.  Generated build systems
(particularly those producing many static stub libraries) can reach D=5–7
(32–128 redundant traversals per target).

## Fix

Add the same guard used for `link_targets` to the `link_whole_targets` branch:

```python
def get_internal_static_libraries_recurse(self, result: OrderedSet[StaticTargetTypes]) -> None:
    for t in self.link_targets:
        if t.is_internal() and t not in result:
            result.add(t)
            t.get_internal_static_libraries_recurse(result)
    for t in self.link_whole_targets:
        if t.is_internal() and t not in result:   # CWE-407 fix: add guard
            result.add(t)
            t.get_internal_static_libraries_recurse(result)
```

Complexity: **O(2^D) → O(N)** where N = total number of internal static
libraries in the link closure.

Speedup at D=7: ~128x (128 visits → 1 visit per shared node).
