java-topology/defects/ruffle-0001/patch/ruffle-0001.patch
russell@unturf.com 7786adc4c0 ruffle: 2 CWE-407 defects, MOADs 0002-0005 documented
ruffle-0001: AVM2 optimizer type_aware.rs process_jump() worklist dedup
  Vec<usize>.contains() inside while-loop over basic blocks -> O(B^2).
  Fix: companion HashSet<usize> for O(1) dedup. 249.5x at fanWidth=500.

ruffle-0002: MovieClip goto_commands depth lookup O(F*D^2) -> O(F*D).
  goto_place_object/goto_remove_object use iter().position(|o| o.depth()==d)
  inside frame-scan while-loop. Fix: HashMap<Depth, usize> index alongside
  Vec; swap_remove displacement handled correctly. 51.5x at F=500 D=100.

MOAD-0002 (Intertangle): UpdateContext god-object couples GC/AVM1/AVM2/
  audio/video/renderer/navigator/UI/storage/log/timers/input in one struct.
  Architectural, not a single-patch fix.
MOAD-0003 (Leaked Context): CURRENT_CONTEXT thread_local in web/src/lib.rs
  holds raw *mut UpdateContext<'static> (request-scoped in thread scope).
  Desktop thread_locals CALLSTACK/RENDER_INFO/SWF_INFO carry per-SWF state.
MOAD-0004: CLEAN. No verbatim credential logging found.
MOAD-0005: CLEAN. Arc<Mutex<Player>> used consistently, no unsynchronized
  cache double-check pattern.

2/2 unit tests PASS.
2026-03-31 19:57:30 -04:00

38 lines
1.5 KiB
Diff

--- a/core/src/avm2/optimizer/type_aware.rs
+++ b/core/src/avm2/optimizer/type_aware.rs
@@ -1,6 +1,7 @@
+use std::collections::HashSet;
+
// ... existing imports ...
- // Block #0 is the entry block
- let mut worklist = vec![0];
+ // Block #0 is the entry block. Companion HashSet for O(1) dedup (was O(B) Vec::contains).
+ let mut worklist = vec![0usize];
+ let mut worklist_set: HashSet<usize> = [0].into_iter().collect();
while let Some(block_idx) = worklist.pop() {
+ worklist_set.remove(&block_idx);
// In every call to process_jump, pass &mut worklist_set as the new argument.
@@ -702,7 +702,8 @@ fn process_jump<'gc>(
abstract_states: &mut [Option<AbstractState<'gc>>],
current_state: &AbstractStateRef<'_, 'gc>,
op_index_to_block_index_table: &HashMap<usize, usize>,
- worklist: &mut Vec<usize>,
+ worklist: &mut Vec<usize>,
+ worklist_set: &mut HashSet<usize>,
do_optimize: bool,
) -> Result<(), Error<'gc>> {
if do_optimize {
@@ -722,9 +722,8 @@ fn process_jump<'gc>(
abstract_states[target_block_id] = Some(current_state.to_owned());
};
- // FP reschedules blocks to the front of queue (for us, it'd be back of the vec).
- // I don't know if there's any good reason for that, but not doing it is faster.
- if !worklist.contains(&target_block_id) {
+ // O(1) dedup via companion HashSet instead of O(B) Vec::contains scan.
+ if worklist_set.insert(target_block_id) {
worklist.push(target_block_id);
}