diff --git a/angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py b/angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py index ddd29ffc138..15a716bac82 100644 --- a/angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +++ b/angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py @@ -165,6 +165,13 @@ def _analyze(self, cache=None): # update the block self._update_block(original_head, new_head) + # sanity check that no switch head points to either itself + # or to any if-head that was merged into the new switch head; this + # would result in a successor node no longer being present in the graph + if any(onode not in graph_copy for onode in original_nodes): + self.out_graph = None + return + # add edges between the head and case nodes for onode in original_nodes: successors = list(graph_copy.successors(onode)) diff --git a/tests/test_decompiler.py b/tests/test_decompiler.py index 9a292ed4f8e..b2e78a8f24e 100644 --- a/tests/test_decompiler.py +++ b/tests/test_decompiler.py @@ -2274,6 +2274,32 @@ def test_switch_case_shared_case_nodes_b2sum_digest(self, decompiler_options=Non assert d.codegen.text.count("switch") == 1 + @for_all_structuring_algos + def test_no_switch_case_touch_touch(self, decompiler_options=None): + # node 0x40015b is an if-node that is merged into a switch case node with other if-node's that + # have it as a successor, resulting in a switch that point's to its old heads; in this case, + # the switch should not exist at all AND not crash + bin_path = os.path.join(test_location, "x86_64", "decompiler", "touch_touch_no_switch.o") + proj = angr.Project(bin_path, auto_load_libs=False) + + all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes( + "AMD64", "linux" + ) + all_optimization_passes = [ + p + for p in all_optimization_passes + if p is not angr.analyses.decompiler.optimization_passes.EagerReturnsSimplifier + ] + + cfg = proj.analyses.CFGFast(normalize=True, data_references=True) + f = proj.kb.functions["touch"] + d = proj.analyses[Decompiler].prep()( + f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes + ) + self._print_decompilation_result(d) + + assert d.codegen.text.count("switch") == 0 + if __name__ == "__main__": unittest.main()