WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 7544d1a

Browse files
committed
fix: prevent endless loop in pre header insertion mod (#2300)
1 parent cfbe5ab commit 7544d1a

File tree

4 files changed

+184
-47
lines changed

4 files changed

+184
-47
lines changed

jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockProcessor.java

Lines changed: 93 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88
import java.util.List;
99
import java.util.Map;
1010
import java.util.Set;
11+
import java.util.stream.Collectors;
1112

1213
import org.jetbrains.annotations.Nullable;
1314
import org.slf4j.Logger;
1415
import org.slf4j.LoggerFactory;
1516

17+
import jadx.api.plugins.input.data.attributes.IJadxAttrType;
18+
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
1619
import jadx.core.dex.attributes.AFlag;
1720
import jadx.core.dex.attributes.AType;
1821
import jadx.core.dex.attributes.nodes.LoopInfo;
@@ -36,6 +39,8 @@
3639
public class BlockProcessor extends AbstractVisitor {
3740
private static final Logger LOG = LoggerFactory.getLogger(BlockProcessor.class);
3841

42+
private static final boolean DEBUG_MODS = false;
43+
3944
@Override
4045
public void visit(MethodNode mth) {
4146
if (mth.isNoCode() || mth.getBasicBlocks().isEmpty()) {
@@ -57,13 +62,27 @@ private static void processBlocksTree(MethodNode mth) {
5762
}
5863
updateCleanSuccessors(mth);
5964

65+
int blocksCount = mth.getBasicBlocks().size();
66+
int modLimit = Math.max(100, blocksCount);
67+
if (DEBUG_MODS) {
68+
mth.addAttr(new DebugModAttr());
69+
}
6070
int i = 0;
6171
while (modifyBlocksTree(mth)) {
6272
computeDominators(mth);
63-
if (i++ > 100) {
64-
throw new JadxRuntimeException("CFG modification limit reached, blocks count: " + mth.getBasicBlocks().size());
73+
if (i++ > modLimit) {
74+
mth.addWarn("CFG modification limit reached, blocks count: " + blocksCount);
75+
break;
6576
}
6677
}
78+
if (DEBUG_MODS && i != 0) {
79+
String stats = "CFG modifications count: " + i
80+
+ ", blocks count: " + blocksCount + '\n'
81+
+ mth.get(DebugModAttr.TYPE).formatStats() + '\n';
82+
mth.addDebugComment(stats);
83+
LOG.debug("Method: {}\n{}", mth, stats);
84+
mth.remove(DebugModAttr.TYPE);
85+
}
6786
checkForUnreachableBlocks(mth);
6887

6988
DominatorTree.computeDominanceFrontier(mth);
@@ -298,6 +317,9 @@ private static boolean mergeConstReturn(MethodNode mth) {
298317
}
299318
if (changed) {
300319
removeMarkedBlocks(mth);
320+
if (DEBUG_MODS) {
321+
mth.get(DebugModAttr.TYPE).addEvent("Merge const return");
322+
}
301323
}
302324
return changed;
303325
}
@@ -338,17 +360,20 @@ private static boolean independentBlockTreeMod(MethodNode mth) {
338360

339361
private static boolean simplifyLoopEnd(MethodNode mth, LoopInfo loop) {
340362
BlockNode loopEnd = loop.getEnd();
341-
if (loopEnd.getSuccessors().size() > 1) {
342-
// make loop end a simple path block
343-
BlockNode newLoopEnd = BlockSplitter.startNewBlock(mth, -1);
344-
newLoopEnd.add(AFlag.SYNTHETIC);
345-
newLoopEnd.add(AFlag.LOOP_END);
346-
BlockNode loopStart = loop.getStart();
347-
BlockSplitter.replaceConnection(loopEnd, loopStart, newLoopEnd);
348-
BlockSplitter.connect(newLoopEnd, loopStart);
349-
return true;
363+
if (loopEnd.getSuccessors().size() <= 1) {
364+
return false;
350365
}
351-
return false;
366+
// make loop end a simple path block
367+
BlockNode newLoopEnd = BlockSplitter.startNewBlock(mth, -1);
368+
newLoopEnd.add(AFlag.SYNTHETIC);
369+
newLoopEnd.add(AFlag.LOOP_END);
370+
BlockNode loopStart = loop.getStart();
371+
BlockSplitter.replaceConnection(loopEnd, loopStart, newLoopEnd);
372+
BlockSplitter.connect(newLoopEnd, loopStart);
373+
if (DEBUG_MODS) {
374+
mth.get(DebugModAttr.TYPE).addEvent("Simplify loop end");
375+
}
376+
return true;
352377
}
353378

354379
private static boolean checkLoops(MethodNode mth, BlockNode block) {
@@ -371,7 +396,6 @@ private static boolean checkLoops(MethodNode mth, BlockNode block) {
371396
if (loopsCount == 1) {
372397
LoopInfo loop = loops.get(0);
373398
return insertBlocksForContinue(mth, loop)
374-
|| insertBlockForPredecessors(mth, loop)
375399
|| insertPreHeader(mth, loop)
376400
|| simplifyLoopEnd(mth, loop);
377401
}
@@ -398,15 +422,21 @@ private static boolean insertPreHeader(MethodNode mth, LoopInfo loop) {
398422
mth.setEnterBlock(newEnterBlock);
399423
start.remove(AFlag.MTH_ENTER_BLOCK);
400424
BlockSplitter.connect(newEnterBlock, start);
401-
return true;
425+
} else {
426+
// multiple predecessors
427+
BlockNode preHeader = BlockSplitter.startNewBlock(mth, -1);
428+
preHeader.add(AFlag.SYNTHETIC);
429+
BlockNode loopEnd = loop.getEnd();
430+
for (BlockNode pred : new ArrayList<>(preds)) {
431+
if (pred != loopEnd) {
432+
BlockSplitter.replaceConnection(pred, start, preHeader);
433+
}
434+
}
435+
BlockSplitter.connect(preHeader, start);
402436
}
403-
// multiple predecessors
404-
BlockNode preHeader = BlockSplitter.startNewBlock(mth, -1);
405-
preHeader.add(AFlag.SYNTHETIC);
406-
for (BlockNode pred : new ArrayList<>(preds)) {
407-
BlockSplitter.replaceConnection(pred, start, preHeader);
437+
if (DEBUG_MODS) {
438+
mth.get(DebugModAttr.TYPE).addEvent("Insert loop pre header");
408439
}
409-
BlockSplitter.connect(preHeader, start);
410440
return true;
411441
}
412442

@@ -426,6 +456,9 @@ private static boolean insertBlocksForBreak(MethodNode mth, LoopInfo loop) {
426456
}
427457
}
428458
}
459+
if (DEBUG_MODS && change) {
460+
mth.get(DebugModAttr.TYPE).addEvent("Insert loop break blocks");
461+
}
429462
return change;
430463
}
431464

@@ -444,24 +477,10 @@ private static boolean insertBlocksForContinue(MethodNode mth, LoopInfo loop) {
444477
}
445478
}
446479
}
447-
return change;
448-
}
449-
450-
/**
451-
* Insert additional block if loop header has several predecessors (exclude back edges)
452-
*/
453-
private static boolean insertBlockForPredecessors(MethodNode mth, LoopInfo loop) {
454-
BlockNode loopHeader = loop.getStart();
455-
List<BlockNode> preds = loopHeader.getPredecessors();
456-
if (preds.size() > 2) {
457-
List<BlockNode> blocks = new ArrayList<>(preds);
458-
blocks.removeIf(block -> block.contains(AFlag.LOOP_END));
459-
BlockNode first = blocks.remove(0);
460-
BlockNode preHeader = BlockSplitter.insertBlockBetween(mth, first, loopHeader);
461-
blocks.forEach(block -> BlockSplitter.replaceConnection(block, loopHeader, preHeader));
462-
return true;
480+
if (DEBUG_MODS && change) {
481+
mth.get(DebugModAttr.TYPE).addEvent("Insert loop continue block");
463482
}
464-
return false;
483+
return change;
465484
}
466485

467486
private static boolean splitLoops(MethodNode mth, BlockNode block, List<LoopInfo> loops) {
@@ -472,17 +491,20 @@ private static boolean splitLoops(MethodNode mth, BlockNode block, List<LoopInfo
472491
break;
473492
}
474493
}
475-
if (oneHeader) {
476-
// several back edges connected to one loop header => make additional block
477-
BlockNode newLoopEnd = BlockSplitter.startNewBlock(mth, block.getStartOffset());
478-
newLoopEnd.add(AFlag.SYNTHETIC);
479-
connect(newLoopEnd, block);
480-
for (LoopInfo la : loops) {
481-
BlockSplitter.replaceConnection(la.getEnd(), block, newLoopEnd);
482-
}
483-
return true;
494+
if (!oneHeader) {
495+
return false;
484496
}
485-
return false;
497+
// several back edges connected to one loop header => make additional block
498+
BlockNode newLoopEnd = BlockSplitter.startNewBlock(mth, block.getStartOffset());
499+
newLoopEnd.add(AFlag.SYNTHETIC);
500+
connect(newLoopEnd, block);
501+
for (LoopInfo la : loops) {
502+
BlockSplitter.replaceConnection(la.getEnd(), block, newLoopEnd);
503+
}
504+
if (DEBUG_MODS) {
505+
mth.get(DebugModAttr.TYPE).addEvent("Split loops");
506+
}
507+
return true;
486508
}
487509

488510
private static boolean splitExitBlocks(MethodNode mth) {
@@ -496,6 +518,9 @@ private static boolean splitExitBlocks(MethodNode mth) {
496518
}
497519
if (changed) {
498520
updateExitBlockConnections(mth);
521+
if (DEBUG_MODS) {
522+
mth.get(DebugModAttr.TYPE).addEvent("Split exit block");
523+
}
499524
}
500525
return changed;
501526
}
@@ -691,4 +716,25 @@ private static void clearBlocksState(MethodNode mth) {
691716
block.getDominatesOn().clear();
692717
});
693718
}
719+
720+
private static final class DebugModAttr implements IJadxAttribute {
721+
static final IJadxAttrType<DebugModAttr> TYPE = IJadxAttrType.create("DebugModAttr");
722+
723+
private final Map<String, Integer> statMap = new HashMap<>();
724+
725+
public void addEvent(String name) {
726+
statMap.merge(name, 1, Integer::sum);
727+
}
728+
729+
public String formatStats() {
730+
return statMap.entrySet().stream()
731+
.map(entry -> " " + entry.getKey() + ": " + entry.getValue())
732+
.collect(Collectors.joining("\n"));
733+
}
734+
735+
@Override
736+
public IJadxAttrType<DebugModAttr> getAttrType() {
737+
return TYPE;
738+
}
739+
}
694740
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package jadx.tests.integration.loops;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import jadx.tests.api.SmaliTest;
6+
7+
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
8+
9+
public class TestLoopRestore3 extends SmaliTest {
10+
11+
@Test
12+
public void test() {
13+
disableCompilation();
14+
assertThat(getClassNodeFromSmali())
15+
.code()
16+
.countString(3, "while (");
17+
}
18+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
.class public Lloops/TestLoopRestore3;
2+
.super Ljava/lang/Object;
3+
4+
.method public final b(Ljava/lang/String;Lb/U53$b;)V
5+
.registers 8
6+
7+
iget-object v0, p0, Lb/X53;->e:Ljava/util/concurrent/atomic/AtomicReference;
8+
:goto_2
9+
invoke-virtual {v0}, Ljava/util/concurrent/atomic/AtomicReference;->get()Ljava/lang/Object;
10+
move-result-object v1
11+
move-object v2, v1
12+
check-cast v2, Ljava/util/List;
13+
move-object v3, v2
14+
check-cast v3, Ljava/lang/Iterable;
15+
instance-of v4, v3, Ljava/util/Collection;
16+
if-eqz v4, :cond_1a
17+
18+
move-object v4, v3
19+
check-cast v4, Ljava/util/Collection;
20+
invoke-interface {v4}, Ljava/util/Collection;->isEmpty()Z
21+
move-result v4
22+
if-eqz v4, :cond_1a
23+
goto :goto_33
24+
25+
:cond_1a
26+
invoke-interface {v3}, Ljava/lang/Iterable;->iterator()Ljava/util/Iterator;
27+
move-result-object v3
28+
29+
:cond_1e
30+
invoke-interface {v3}, Ljava/util/Iterator;->hasNext()Z
31+
move-result v4
32+
if-eqz v4, :cond_33
33+
34+
invoke-interface {v3}, Ljava/util/Iterator;->next()Ljava/lang/Object;
35+
move-result-object v4
36+
check-cast v4, Lb/X53$c;
37+
iget-object v4, v4, Lb/X53$c;->b:Ljava/lang/String;
38+
invoke-static {v4, p1}, Lkotlin/jvm/internal/Intrinsics;->a(Ljava/lang/Object;Ljava/lang/Object;)Z
39+
move-result v4
40+
if-eqz v4, :cond_1e
41+
goto :goto_40
42+
43+
:cond_33
44+
:goto_33
45+
check-cast v2, Ljava/util/Collection;
46+
new-instance v3, Lb/X53$c;
47+
sget-object v4, Lb/Pd2;->a:Lb/Pd2;
48+
invoke-direct {v3, p2, p1, v4}, Lb/X53$c;-><init>(Lb/U53$b;Ljava/lang/String;Ljava/util/List;)V
49+
invoke-static {v2, v3}, Lb/R31;->a0(Ljava/util/Collection;Ljava/lang/Object;)Ljava/util/ArrayList;
50+
move-result-object v2
51+
52+
:cond_40
53+
:goto_40
54+
invoke-virtual {v0, v1, v2}, Ljava/util/concurrent/atomic/AtomicReference;->compareAndSet(Ljava/lang/Object;Ljava/lang/Object;)Z
55+
move-result v3
56+
if-eqz v3, :cond_47
57+
return-void
58+
59+
:cond_47
60+
invoke-virtual {v0}, Ljava/util/concurrent/atomic/AtomicReference;->get()Ljava/lang/Object;
61+
move-result-object v3
62+
if-eq v3, v1, :cond_40
63+
goto :goto_2
64+
.end method

jadx-plugins/jadx-input-api/src/main/java/jadx/api/plugins/input/data/attributes/IJadxAttrType.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,13 @@ static <A extends IJadxAttribute> IJadxAttrType<A> create() {
1818
return new IJadxAttrType<>() {
1919
};
2020
}
21+
22+
static <A extends IJadxAttribute> IJadxAttrType<A> create(String name) {
23+
return new IJadxAttrType<>() {
24+
@Override
25+
public String toString() {
26+
return name;
27+
}
28+
};
29+
}
2130
}

0 commit comments

Comments
 (0)