diff options
| author | idatui <user@clank> | 2026-08-09 14:17:13 +0200 |
|---|---|---|
| committer | idatui <user@clank> | 2026-08-09 14:17:13 +0200 |
| commit | d74b6f53e0969efc586d52776fa3b6d40b92a650 (patch) | |
| tree | 8baf17c3f8f0dfa57b07863e4cf8d376d4bd1788 /docs | |
| parent | Graph: fix the triskel fallbacks, and say why when it still falls back (diff) | |
| download | ida-tui-d74b6f53e0969efc586d52776fa3b6d40b92a650.tar.gz ida-tui-d74b6f53e0969efc586d52776fa3b6d40b92a650.tar.xz ida-tui-d74b6f53e0969efc586d52776fa3b6d40b92a650.zip | |
Graph: never hand triskel a block its root cannot reach
"EMPTY BL" in the status bar is triskel's own bracket-list assertion from
its SESE pass, and it turned out to be the mild version of the problem.
Triskel's graph root is whichever node was created FIRST, and every
analysis walks out from it. Anything unreachable from that node is
undefined behaviour. We were:
- creating nodes in id order, so the root was the lowest-numbered
block rather than the entry, and
- splitting only WEAKLY connected components, which says nothing about
reachability.
A 7-block CFG whose entry has no successors -- IDA hands those out for
thunks and for dead code it could not resolve -- SEGFAULTS the
interpreter. That is unsurvivable: it takes the session down and there
is no exception to fall back from.
Now the entry is created first, orphan blocks are attached to it with
phantom edges that steer placement but are never drawn (one edge usually
adopts a whole orphan subgraph, attached at a node no other orphan
reaches), and reachability is asserted in python BEFORE crossing into
C++. This replaces the component splitting entirely: one layout instead
of N stacked side by side, and triskel gets to place the orphans.
The reproducer is now a test (t_unreachable_entry). Remaining fallbacks
on the ls corpus are 8/1200 layouts, all the upstream box-overlap
defect, all but one on 300-500 block functions.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/GRAPH_VIEW.md | 22 |
1 files changed, 16 insertions, 6 deletions
diff --git a/docs/GRAPH_VIEW.md b/docs/GRAPH_VIEW.md index d4f0180..b656179 100644 --- a/docs/GRAPH_VIEW.md +++ b/docs/GRAPH_VIEW.md @@ -98,12 +98,22 @@ ports already land spread along the box border, and — because our fork made th spacing settable — **we hand it cell counts rather than pixels**, so nothing is ever rounded and two edge lanes can never land on the same row. -What it does not do is trust the library with degenerate input, all of which is -handled before the call: self-loops (drawn as `↺`, and they make triskel throw), -disconnected components (laid out separately and stacked; IDA flowcharts do have -unreachable blocks), and the one edge in the corpus that triskel routes *through* -a block, which is detoured and then re-verified — if the detour fails the whole -layout falls back to native rather than draw an edge through the disassembly. +What it does not do is trust the library with degenerate input. Triskel's graph +root is **whichever node was created first**, and every one of its analyses walks +out from there, so anything the root cannot reach is undefined behaviour — it +throws `EMPTY BL` from its SESE bracket lists, or, when the entry block has no +successors at all, segfaults. That is not survivable: a crash in a C extension +takes the TUI with it, with no chance to fall back. So the entry is created +first, orphan blocks are attached to it with **phantom edges** that steer the +layout but are never drawn, and reachability is *asserted in python* before +crossing into C++. + +The rest is handled before the call too: self-loops (drawn as `↺`; they make +triskel throw), and edges routed through a block, which are detoured and +re-verified. Whatever is left over falls back to native rather than reach the +screen wrong — currently 8 layouts in 1200 (`ls`, three zoom levels each), all +of them triskel leaving two boxes a few columns into each other, which in a +terminal means one block's disassembly overwriting another's. ## Layout (`idatui/graph.py`, the native engine) |
