wave20: ghidra-0001 UNDF-1303 (6.5x-27x RecoveredClassHelper) + 9 clean-scan additions
Flagship: ghidra RecoveredClassHelper.addVftableReferencesToFunctionMapping + addFunctionsToClassMapping. Each insert does List.contains + ArrayList copy on every add, giving O(F*R^2) per binary. Map<F, LinkedHashSet<T>> rewrite gives O(F*R) and 27x speedup at F=2k R=500. Reverse-engineering large C++ binaries (1000+ classes, 10k+ vtable refs) sees seconds-to-minutes per RecoverClassesFromRTTIScript run today. Wave 20 honor roll: cri-o, runc, youki, quickjs, hermes, ripgrep, radare2, monero, mitmproxy. Cumulative: 115 projects.
This commit is contained in:
parent
3a9c7a3e75
commit
dce02d80df
7 changed files with 465 additions and 0 deletions
75
docs/tickets/ghidra-0001-recoveredclasshelper-list-to-set.md
Normal file
75
docs/tickets/ghidra-0001-recoveredclasshelper-list-to-set.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# ghidra-0001: RecoveredClassHelper — O(F×R²) List.contains + ArrayList copy on every add
|
||||
|
||||
**Target:** NationalSecurityAgency/ghidra
|
||||
**Severity:** HIGH
|
||||
**CWE:** CWE-407 (Inefficient Algorithmic Complexity)
|
||||
**MOAD:** MOAD-0001 (A Sedimentary Defect)
|
||||
**File:** `Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java:218-237, 256-275`
|
||||
**Language:** Java
|
||||
**Status:** open
|
||||
|
||||
## Description
|
||||
|
||||
`RecoveredClassHelper` builds two maps during ghidra's C++ class recovery analysis:
|
||||
- `functionToVftableRefsMap: Map<Function, List<Address>>`
|
||||
- `functionToClassesMap: Map<Function, List<RecoveredClass>>`
|
||||
|
||||
Each insert path checks for membership via `List.contains` (O(R) linear scan), then copies the existing `ArrayList` into a new `ArrayList` (O(R) defensive copy), then `Map.replace`s the entry. Per-function cost: **O(R²)** for R items added. Per-binary cost: **O(F × R²)** where F = function count, R = references-per-function.
|
||||
|
||||
Real-world scale: large reverse-engineered C++ binaries (malware analysis, OS kernels, AAA games) routinely have 1000+ classes and 10k+ vftable references. Class recovery analysis scripts run into seconds-to-minutes per binary today.
|
||||
|
||||
## Root Cause
|
||||
|
||||
```java
|
||||
// RecoveredClassHelper.java:218 — addVftableReferencesToFunctionMapping
|
||||
for (Address vtableReference : keySet) {
|
||||
if (functionToVftableRefsMap.containsKey(function)) {
|
||||
List<Address> referenceList = functionToVftableRefsMap.get(function);
|
||||
if (!referenceList.contains(vtableReference)) { // O(R) per call
|
||||
List<Address> newList = new ArrayList<>(referenceList); // O(R) copy per add
|
||||
newList.add(vtableReference);
|
||||
functionToVftableRefsMap.replace(function, referenceList, newList);
|
||||
}
|
||||
} else {
|
||||
List<Address> newList = new ArrayList<>();
|
||||
newList.add(vtableReference);
|
||||
functionToVftableRefsMap.put(function, newList);
|
||||
}
|
||||
}
|
||||
|
||||
// RecoveredClassHelper.java:256 — addFunctionsToClassMapping (same pattern)
|
||||
```
|
||||
|
||||
Each add does both a linear scan and a list copy. Across F functions × R references each: **O(F × R²)**.
|
||||
|
||||
## Fix
|
||||
|
||||
Replace `Map<Function, List<T>>` with `Map<Function, LinkedHashSet<T>>`. `LinkedHashSet` preserves insertion order (so callers iterating in the order references were discovered see the same order) AND gives O(1) `add` + `contains`. The defensive ArrayList copy on every add disappears entirely.
|
||||
|
||||
```java
|
||||
private final Map<Function, LinkedHashSet<Address>> functionToVftableRefSetMap = new HashMap<>();
|
||||
private final Map<Function, LinkedHashSet<RecoveredClass>> functionToClassesSetMap = new HashMap<>();
|
||||
|
||||
// Insert path becomes:
|
||||
functionToVftableRefSetMap
|
||||
.computeIfAbsent(function, k -> new LinkedHashSet<>())
|
||||
.add(vtableReference);
|
||||
```
|
||||
|
||||
Public API readers wrap the set as `List` for downstream-script compatibility:
|
||||
|
||||
```java
|
||||
public List<Address> getVftableReferences(Function function) {
|
||||
LinkedHashSet<Address> set = functionToVftableRefSetMap.get(function);
|
||||
return set == null ? null : new ArrayList<>(set);
|
||||
}
|
||||
```
|
||||
|
||||
## Severity Note
|
||||
|
||||
Hot path on every C++ class recovery script invocation. Bench (defects/ghidra/bench/) shows 6.5× speedup at F=100 R=50, scaling to 27× at F=2000 R=500. Reverse engineering analysts running ghidra on large binaries see this latency as part of "RecoverClassesFromRTTIScript" wall-clock time.
|
||||
|
||||
## Complexity Gate
|
||||
|
||||
- F=2000 functions × R=500 refs: fixed must complete in <250ms
|
||||
- k-scaling 5×: time ratio must be <17.5×
|
||||
Loading…
Add table
Add a link
Reference in a new issue