Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ class LoweredSwitchSimplifier(StructuringOptimizationPass):
def __init__(self, func, min_distinct_cases=2, **kwargs):
super().__init__(
func,
require_structurable_graph=False,
require_gotos=False,
prevent_new_gotos=False,
simplify_ail=False,
Expand Down
42 changes: 31 additions & 11 deletions angr/analyses/decompiler/optimization_passes/optimization_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from angr.analyses.decompiler.condition_processor import ConditionProcessor
from angr.analyses.decompiler.goto_manager import Goto, GotoManager
from angr.analyses.decompiler.structuring import RecursiveStructurer, SAILRStructurer
from angr.analyses.decompiler.utils import add_labels, remove_edges_in_ailgraph
from angr.analyses.decompiler.utils import add_labels, remove_edges_in_ailgraph, is_empty_node
from angr.analyses.decompiler.counters import ControlFlowStructureCounter
from angr.project import Project

Expand Down Expand Up @@ -432,12 +432,13 @@ class StructuringOptimizationPass(OptimizationPass):
STAGE = OptimizationPassStage.DURING_REGION_IDENTIFICATION

_initial_gotos: set[Goto]
_goto_manager: GotoManager
_goto_manager: GotoManager | None
_prev_graph: networkx.DiGraph

def __init__(
self,
func,
require_structurable_graph: bool = True,
prevent_new_gotos: bool = True,
strictly_less_gotos: bool = False,
recover_structure_fails: bool = True,
Expand All @@ -450,6 +451,7 @@ def __init__(
**kwargs,
):
super().__init__(func, **kwargs)
self._require_structurable_graph = require_structurable_graph
self._prevent_new_gotos = prevent_new_gotos
self._strictly_less_gotos = strictly_less_gotos
self._recover_structure_fails = recover_structure_fails
Expand All @@ -459,6 +461,8 @@ def __init__(
self._must_improve_rel_quality = must_improve_rel_quality
self._readd_labels = readd_labels
self._edges_to_remove = edges_to_remove or []
self._goto_manager = None
self._initial_gotos = set()

# relative quality metrics (excludes gotos)
self._initial_structure_counter = None
Expand All @@ -476,13 +480,20 @@ def analyze(self):
if not ret:
return

if not self._graph_is_structurable(self._graph, initial=True):
return
# only initialize self._goto_manager if this optimization requires a structurable graph or gotos
initial_structurable: bool | None = None
if self._require_structurable_graph or self._require_gotos or self._prevent_new_gotos:
initial_structurable = self._graph_is_structurable(self._graph, initial=True)

self._initial_gotos = self._goto_manager.gotos.copy()
if self._require_gotos and not self._initial_gotos:
if self._require_structurable_graph and initial_structurable is False:
return

if self._require_gotos:
assert self._goto_manager is not None
self._initial_gotos = self._goto_manager.gotos.copy()
if not self._initial_gotos:
return

# setup for the very first analysis
self.out_graph = networkx.DiGraph(self._graph)
if self._max_opt_iters > 1:
Expand All @@ -500,7 +511,13 @@ def analyze(self):
if self._readd_labels:
self.out_graph = add_labels(self.out_graph)

if not self._graph_is_structurable(self.out_graph, readd_labels=False):
if (
self._require_structurable_graph
and self._max_opt_iters <= 1
and not self._graph_is_structurable(self.out_graph, readd_labels=False)
):
# fixed-point analysis ensures that the output graph is always structurable, otherwise it clears the output
# graph. so we only check the structurability of the graph when fixed-point analysis did not run.
self.out_graph = None
return

Expand All @@ -523,13 +540,16 @@ def analyze(self):
return

def _get_new_gotos(self):
assert self._goto_manager is not None
return self._goto_manager.gotos

def _fixed_point_analyze(self, cache=None):
had_any_changes = False
for _ in range(self._max_opt_iters):
if self._require_gotos and not self._goto_manager.gotos:
break
if self._require_gotos:
assert self._goto_manager is not None
if not self._goto_manager.gotos:
break

# backup the graph before the optimization
if self._recover_structure_fails and self.out_graph is not None:
Expand Down Expand Up @@ -590,7 +610,7 @@ def _graph_is_structurable(self, graph, readd_labels=False, initial=False) -> bo
_l.warning("Internal structuring failed for OptimizationPass on %s", self._func.name)
rs = None

if not rs or not rs.result or not rs.result.nodes or rs.result_incomplete:
if not rs or not rs.result or is_empty_node(rs.result) or rs.result_incomplete:
return False

rs = self.project.analyses.RegionSimplifier(self._func, rs.result, arg_vvars=self._arg_vvars, kb=self.kb)
Expand Down Expand Up @@ -648,7 +668,7 @@ def _improves_relative_quality(self) -> bool:
# Gotos play an important part in readability and control flow structure. We already count gotos in other parts
# of the analysis, so we don't need to count them here. However, some gotos are worse than others. Much
# like loops, trading gotos (keeping the same total, but getting worse types), is bad for decompilation.
if len(self._initial_gotos) == len(self._goto_manager.gotos) != 0:
if self._goto_manager is not None and len(self._initial_gotos) == len(self._goto_manager.gotos) != 0:
prev_labels = self._initial_structure_counter.goto_targets
curr_labels = self._current_structure_counter.goto_targets

Expand Down
5 changes: 3 additions & 2 deletions angr/analyses/decompiler/structuring/recursive_structurer.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def __init__(
self.structurer_cls = structurer_cls if structurer_cls is not None else DreamStructurer
self.structurer_options = kwargs

self.result = None
self.result: BaseNode | None = None
self.result_incomplete: bool = False

self._analyze()
Expand Down Expand Up @@ -161,6 +161,7 @@ def _get_switch_case_entries(self) -> dict[int, int]:
for jump_table_head_addr, jumptable in jump_tables.items():
if jump_table_head_addr not in func_block_addrs:
continue
assert jumptable.jumptable_entries is not None
for entry_addr in jumptable.jumptable_entries:
entries[entry_addr] = jump_table_head_addr

Expand All @@ -178,7 +179,7 @@ def _pick_incomplete_result_from_region(self, region):
continue
if node.addr == self.function.addr:
return node
if min_node is None or min_node.addr < node.addr:
if min_node is None or (min_node.addr is not None and node.addr is not None and min_node.addr < node.addr):
min_node = node

return min_node
Expand Down
Loading