Files
yosys/passes/opt/dff/opt_dff.cc
T
Lokesh Vutla 0ce1cbcae7 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 <[email protected]>
Signed-off-by: Lokesh Vutla <[email protected]>
2026-10-02 19:36:56 +02:00

147 lines
5.3 KiB
C++

/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <[email protected]>
* Copyright (C) 2020 Marcelina Kościelnicka <[email protected]>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/log.h"
#include "kernel/register.h"
#include "kernel/ff.h"
#include "passes/opt/dff/opt_dff.h"
#include <stdio.h>
#include <stdlib.h>
USING_YOSYS_NAMESPACE
YOSYS_NAMESPACE_BEGIN
OptDffWorker::OptDffWorker(const OptDffOptions &opt, Module *mod)
: opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod)
{
sat_budget = SatEffortBudget(module->design->scratchpad_get_int("opt_dff.sat_effort", 1000000000));
}
void OptDffWorker::remove_ff_bits(Cell *cell, const pool<int> &drop)
{
modwalker_ptr.reset();
FfData ff(&initvals, cell);
std::vector<int> keep;
for (int i = 0; i < ff.width; i++)
if (!drop.count(i))
keep.push_back(i);
FfData new_ff = ff.slice(keep);
new_ff.cell = cell;
new_ff.emit();
}
YOSYS_NAMESPACE_END
PRIVATE_NAMESPACE_BEGIN
struct OptDffPass : public Pass {
OptDffPass() : Pass("opt_dff", "perform DFF optimizations") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] [selection]\n");
log("\n");
log("This pass converts flip-flops to a more suitable type by merging clock enables\n");
log("and synchronous reset multiplexers, removing unused control inputs, or\n");
log("potentially removes the flip-flop altogether, converting it to a constant\n");
log("driver.\n");
log("\n");
log(" -nodffe\n");
log(" disables dff -> dffe conversion, and other transforms recognizing clock\n");
log(" enable\n");
log("\n");
log(" -nosdff\n");
log(" disables dff -> sdff conversion, and other transforms recognizing sync\n");
log(" resets\n");
log("\n");
log(" -simple-dffe\n");
log(" only enables clock enable recognition transform for obvious cases\n");
log("\n");
log(" -sat\n");
log(" additionally invoke SAT solver to detect and remove flip-flops (with\n");
log(" non-constant inputs) that can also be replaced with a constant driver,\n");
log(" or merged with equivalent flip-flops. this reasons in 2-valued logic\n");
log(" and may resolve don't-care bits, so it is incompatible with -keepdc.\n");
log(" the scratchpad option 'opt_dff.sat_effort' (solver propagation steps,\n");
log(" default 1000000000, 0 = unlimited) deterministically bounds the total\n");
log(" sat effort spent per module, remaining proofs are skipped once exceeded.\n");
log("\n");
log(" -keepdc\n");
log(" some optimizations change the behavior of the circuit with respect to\n");
log(" don't-care bits. for example in 'a+0' a single x-bit in 'a' will cause\n");
log(" all result bits to be set to x. this behavior changes when 'a+0' is\n");
log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing OPT_DFF pass (perform DFF optimizations).\n");
OptDffOptions opt;
opt.nodffe = false;
opt.nosdff = false;
opt.simple_dffe = false;
opt.keepdc = false;
opt.sat = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-nodffe") { opt.nodffe = true; continue; }
if (args[argidx] == "-nosdff") { opt.nosdff = true; continue; }
if (args[argidx] == "-simple-dffe") { opt.simple_dffe = true; continue; }
if (args[argidx] == "-keepdc") { opt.keepdc = true; continue; }
if (args[argidx] == "-sat") { opt.sat = true; continue; }
break;
}
extra_args(args, argidx, design);
// The SAT engine reasons in 2-valued logic (a constant x is treated as
// 0), so it can resolve don't-care bits to concrete values -- exactly
// what -keepdc promises not to do. Refuse the combination rather than
// silently ignore -keepdc.
if (opt.sat && opt.keepdc)
log_cmd_error("The -sat and -keepdc options are mutually exclusive.\n");
bool did_something = false;
for (auto mod : design->selected_modules()) {
OptDffWorker worker(opt, mod);
if (worker.run())
did_something = true;
// constbits also runs without -sat: it folds bits with all-constant
// inputs, -sat additionally proves bits with wire inputs
if (worker.run_constbits())
did_something = true;
if (opt.sat && worker.run_eqbits())
did_something = true;
}
if (did_something)
design->scratchpad_set_bool("opt.did_something", true);
}
} OptDffPass;
PRIVATE_NAMESPACE_END