java-topology/defects/redot/patch/redot-0003-physics3d-body-area-hashmap.patch
russell@unturf.com fdbb9a1aa9 redot: 12 CWE-407 defects, patches, outreach brief, UNDF-2026-000001231..1242
All 12 O(N²) algorithmic complexity defects confirmed in Redot Engine 26.2-alpha
(commit 360a8d3). Inherited verbatim from Godot Engine upstream. All patched.

Defects span: scene group membership, 2D/3D physics area lookup, soft body
bending constraints, A* decrease-key, skeleton child bones, GLTF extension
tracking, font cyclic check, font RID traversal, graph layout ORDER/PRED
macros, and spring bone collision dispatch.

Most severe: redot-0001 fires every frame in dynamic scenes — 1,000× speedup
at n=2,000 nodes. redot-0002/0003 fire 60Hz in physics-heavy games — 50×.

Strategy: patch Redot first, Godot follows our lead.
2026-04-03 21:00:29 -04:00

64 lines
2.2 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UNDF: UNDF-2026-000001233
# CWE-407: Algorithmic Complexity — O(N²) → O(N) in GodotBody3D::add_area() / remove_area()
#
# Defect: identical to redot-0002, 3D physics variant.
# areas.find(AreaCMP(p_area)) is O(n) — Vector linear scan —
# called from GodotAreaPair3D::pre_solve() every physics tick.
# Total cost: O(bodies × areas²) per tick. At 500 bodies × 200 areas: ~10M ops/tick.
#
# Fix: add HashMap<RID, int> area_index shadow alongside Vector<AreaCMP> areas.
# Total cost after: O(bodies × areas). 50× speedup.
#
# Complexity gate: same thresholds as redot-0002.
--- a/modules/godot_physics_3d/godot_body_3d.h
+++ b/modules/godot_physics_3d/godot_body_3d.h
@@ -114,6 +114,7 @@ class GodotBody3D : public GodotCollisionObject3D {
// ...
Vector<AreaCMP> areas;
+ HashMap<RID, int> area_index; // O(1) area lookup by RID — shadow index
// ...
@@ -157,24 +157,29 @@ public:
_FORCE_INLINE_ void add_area(GodotArea3D *p_area) {
- int index = areas.find(AreaCMP(p_area));
- if (index > -1) {
- areas.write[index].refCount += 1;
+ // FIX redot-0003: identical to redot-0002, 3D physics variant
+ RID rid = p_area->get_self();
+ HashMap<RID, int>::Iterator it = area_index.find(rid);
+ if (it != area_index.end()) {
+ areas.write[it->value].refCount += 1;
} else {
- areas.ordered_insert(AreaCMP(p_area));
+ areas.ordered_insert(AreaCMP(p_area));
+ area_index.clear();
+ for (int i = 0; i < areas.size(); i++) {
+ area_index[areas[i].area->get_self()] = i;
+ }
}
}
_FORCE_INLINE_ void remove_area(GodotArea3D *p_area) {
- int index = areas.find(AreaCMP(p_area));
- if (index > -1) {
- areas.write[index].refCount -= 1;
- if (areas[index].refCount < 1) {
- areas.remove_at(index);
+ // FIX redot-0003: identical to redot-0002, 3D physics variant
+ RID rid = p_area->get_self();
+ HashMap<RID, int>::Iterator it = area_index.find(rid);
+ if (it != area_index.end()) {
+ int index = it->value;
+ areas.write[index].refCount -= 1;
+ if (areas[index].refCount < 1) {
+ areas.remove_at(index);
+ area_index.clear();
+ for (int i = 0; i < areas.size(); i++) {
+ area_index[areas[i].area->get_self()] = i;
+ }
}
}
}