fix: improve limit calculation for type updates in type inference (#854)

This commit is contained in:
Skylot
2020-07-10 18:36:17 +01:00
parent 31434186ab
commit 3e9f4a5060
5 changed files with 42 additions and 16 deletions
@@ -49,6 +49,7 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
private final ICodeReader codeReader;
private final boolean methodIsVirtual;
private final int insnsCount;
private boolean noCode;
private int regsCount;
@@ -85,9 +86,16 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
this.mthInfo = MethodInfo.fromData(classNode.root(), mthData);
this.parentClass = classNode;
this.accFlags = new AccessInfo(mthData.getAccessFlags(), AFType.METHOD);
this.noCode = mthData.getCodeReader() == null;
this.codeReader = noCode ? null : mthData.getCodeReader().copy();
this.methodIsVirtual = !mthData.isDirect();
ICodeReader codeReader = mthData.getCodeReader();
this.noCode = codeReader == null;
if (noCode) {
this.codeReader = null;
this.insnsCount = 0;
} else {
this.codeReader = codeReader.copy();
this.insnsCount = codeReader.getInsnsCount();
}
unload();
}
@@ -596,8 +604,7 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
}
/**
* Stat method.
* Calculate instructions count as a measure of method size
* Calculate instructions count at currect stage
*/
public long countInsns() {
if (instructions != null) {
@@ -609,6 +616,13 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
return -1;
}
/**
* Raw instructions count in method bytecode
*/
public int getInsnsCount() {
return insnsCount;
}
@Override
public boolean isVarArg() {
return accFlags.isVarArgs();
@@ -49,6 +49,7 @@ import jadx.core.utils.BlockUtils;
import jadx.core.utils.InsnList;
import jadx.core.utils.InsnUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxOverflowException;
@JadxVisitor(
name = "Type Inference",
@@ -88,10 +89,14 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
if (Consts.DEBUG_TYPE_INFERENCE) {
LOG.info("Start type inference in method: {}", mth);
}
for (Function<MethodNode, Boolean> resolver : resolvers) {
if (resolver.apply(mth) && checkTypes(mth)) {
return;
try {
for (Function<MethodNode, Boolean> resolver : resolvers) {
if (resolver.apply(mth) && checkTypes(mth)) {
return;
}
}
} catch (Exception e) {
mth.addError("Type inference failed with exception", e);
}
}
@@ -148,6 +153,8 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
if (immutableType != null) {
applyImmutableType(mth, ssaVar, immutableType);
}
} catch (JadxOverflowException e) {
throw e;
} catch (Exception e) {
LOG.error("Failed to set immutable type for var: {}", ssaVar, e);
}
@@ -156,6 +163,8 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
private boolean setBestType(MethodNode mth, SSAVar ssaVar) {
try {
return calculateFromBounds(mth, ssaVar);
} catch (JadxOverflowException e) {
throw e;
} catch (Exception e) {
LOG.error("Failed to calculate best type for var: {}", ssaVar, e);
return false;
@@ -166,12 +166,7 @@ public final class TypeUpdate {
return CHANGED;
}
updateInfo.requestUpdate(arg, candidateType);
if (updateInfo.getUpdates().size() > 500) {
if (Consts.DEBUG_TYPE_INFERENCE) {
LOG.error("Type update error: too deep update tree");
}
return REJECT;
}
updateInfo.checkUpdatesCount();
try {
TypeUpdateResult result = runListeners(updateInfo, arg, candidateType);
if (result == REJECT) {
@@ -6,15 +6,18 @@ import java.util.List;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.exceptions.JadxOverflowException;
public class TypeUpdateInfo {
private final MethodNode mth;
private final TypeUpdateFlags flags;
private final List<TypeUpdateEntry> updates = new ArrayList<>();
private final int updatesLimitCount;
public TypeUpdateInfo(MethodNode mth, TypeUpdateFlags flags) {
this.mth = mth;
this.flags = flags;
this.updatesLimitCount = mth.getInsnsCount() * 5; // maximum registers count to update at once
}
public void requestUpdate(InsnArg arg, ArgType changeType) {
@@ -53,6 +56,12 @@ public class TypeUpdateInfo {
updates.removeIf(updateEntry -> updateEntry.getArg() == arg);
}
public void checkUpdatesCount() {
if (updates.size() > updatesLimitCount) {
throw new JadxOverflowException("Type inference error: update tree size limit reached");
}
}
public MethodNode getMth() {
return mth;
}
@@ -51,13 +51,12 @@ public class ErrorsCounter {
String msg = formatMsg(node, error);
if (PRINT_MTH_SIZE && node instanceof MethodNode) {
long insnsCount = ((MethodNode) node).countInsns();
msg = "[" + insnsCount + "] " + msg;
msg = "[" + ((MethodNode) node).getInsnsCount() + "] " + msg;
}
if (e == null) {
LOG.error(msg);
} else if (e instanceof StackOverflowError) {
LOG.error(msg);
LOG.error("{}, error: StackOverflowError", msg);
} else if (e instanceof JadxOverflowException) {
// don't print full stack trace
String details = e.getMessage();