From 0ce1cbcae7af1902b421ff0acd0e00a8e8ba9de8 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Thu, 1 Oct 2026 13:10:17 +0000 Subject: [PATCH] opt_dff: Fix ModWalker use-after-free between constbits and eqbits Commit fae99416d ("Only init modwalker once.") introduced a shared ModWalker in OptDffWorker reused across run_constbits() and run_eqbits(). However, when run_constbits() proves a flip-flop bit is constant, the below sequence occurs: OptDffPass::execute() | +-> worker.run_constbits() | |-> fold_const_bits() | | `-> add_const_target() | | `-> worker.get_modwalker() #[caches FF Cell* info in modwalker] | |-> ... | `-> worker.remove_ff_bits() | `-> new_ff.emit() #[deletes FF Cell* info in module. cell info in modwalker is stale] | `-> worker.run_eqbits() |-> gather_initial_eq_classes() `-> filter_classes_sim() |-> BitSim::sim() #[passes modwalker which has stale cell info] `-> sim.eval_bit() |-> cell = modwalker...find().cell #[derives stale cell info] `-> if (cell->is_builtin_ff()) #[*** crashed as cell pointer is already freed *** ] So, reset modwalker_ptr in remove_ff_bits() whenever flip-flop bits are removed so run_eqbits() rebuilds a fresh ModWalker only when the module netlist was modified. Reported-by: Sean Luchen Signed-off-by: Lokesh Vutla --- passes/opt/dff/opt_dff.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/passes/opt/dff/opt_dff.cc b/passes/opt/dff/opt_dff.cc index 4e6d459df..3d684c5f7 100644 --- a/passes/opt/dff/opt_dff.cc +++ b/passes/opt/dff/opt_dff.cc @@ -37,6 +37,8 @@ OptDffWorker::OptDffWorker(const OptDffOptions &opt, Module *mod) void OptDffWorker::remove_ff_bits(Cell *cell, const pool &drop) { + modwalker_ptr.reset(); + FfData ff(&initvals, cell); std::vector keep; for (int i = 0; i < ff.width; i++)