GP-3266 Max jumptable entry option

This commit is contained in:
caheckman 2023-06-12 16:42:12 -04:00
parent 3b62fdaa95
commit d8b4f910ca
11 changed files with 93 additions and 12 deletions

View file

@ -1404,6 +1404,7 @@ void Architecture::resetDefaultsInternal(void)
alias_block_level = 2; // Block structs and arrays by default, but not more primitive data-types alias_block_level = 2; // Block structs and arrays by default, but not more primitive data-types
split_datatype_config = OptionSplitDatatypes::option_struct | OptionSplitDatatypes::option_array split_datatype_config = OptionSplitDatatypes::option_struct | OptionSplitDatatypes::option_array
| OptionSplitDatatypes::option_pointer; | OptionSplitDatatypes::option_pointer;
max_jumptable_size = 1024;
} }
/// Reset options that can be modified by the OptionDatabase. This includes /// Reset options that can be modified by the OptionDatabase. This includes

View file

@ -172,6 +172,7 @@ public:
int4 max_term_duplication; ///< Max terms duplicated without a new variable int4 max_term_duplication; ///< Max terms duplicated without a new variable
int4 max_basetype_size; ///< Maximum size of an "integer" type before creating an array type int4 max_basetype_size; ///< Maximum size of an "integer" type before creating an array type
int4 min_funcsymbol_size; ///< Minimum size of a function symbol int4 min_funcsymbol_size; ///< Minimum size of a function symbol
uint4 max_jumptable_size; ///< Maximum number of entries in a single JumpTable
bool aggressive_ext_trim; ///< Aggressively trim inputs that look like they are sign extended bool aggressive_ext_trim; ///< Aggressively trim inputs that look like they are sign extended
bool readonlypropagate; ///< true if readonly values should be treated as constants bool readonlypropagate; ///< true if readonly values should be treated as constants
bool infer_pointers; ///< True if we should infer pointers from constants that are likely addresses bool infer_pointers; ///< True if we should infer pointers from constants that are likely addresses

View file

@ -2217,7 +2217,7 @@ void JumpTable::recoverModel(Funcdata *fd)
{ {
if (jmodel != (JumpModel *)0) { if (jmodel != (JumpModel *)0) {
if (jmodel->isOverride()) { // If preexisting model is override if (jmodel->isOverride()) { // If preexisting model is override
jmodel->recoverModel(fd,indirect,0,maxtablesize); jmodel->recoverModel(fd,indirect,0,glb->max_jumptable_size);
return; return;
} }
delete jmodel; // Otherwise this is an old attempt we should remove delete jmodel; // Otherwise this is an old attempt we should remove
@ -2228,18 +2228,18 @@ void JumpTable::recoverModel(Funcdata *fd)
if (op->code() == CPUI_CALLOTHER) { if (op->code() == CPUI_CALLOTHER) {
JumpAssisted *jassisted = new JumpAssisted(this); JumpAssisted *jassisted = new JumpAssisted(this);
jmodel = jassisted; jmodel = jassisted;
if (jmodel->recoverModel(fd,indirect,addresstable.size(),maxtablesize)) if (jmodel->recoverModel(fd,indirect,addresstable.size(),glb->max_jumptable_size))
return; return;
} }
} }
JumpBasic *jbasic = new JumpBasic(this); JumpBasic *jbasic = new JumpBasic(this);
jmodel = jbasic; jmodel = jbasic;
if (jmodel->recoverModel(fd,indirect,addresstable.size(),maxtablesize)) if (jmodel->recoverModel(fd,indirect,addresstable.size(),glb->max_jumptable_size))
return; return;
jmodel = new JumpBasic2(this); jmodel = new JumpBasic2(this);
((JumpBasic2 *)jmodel)->initializeStart(jbasic->getPathMeld()); ((JumpBasic2 *)jmodel)->initializeStart(jbasic->getPathMeld());
delete jbasic; delete jbasic;
if (jmodel->recoverModel(fd,indirect,addresstable.size(),maxtablesize)) if (jmodel->recoverModel(fd,indirect,addresstable.size(),glb->max_jumptable_size))
return; return;
delete jmodel; delete jmodel;
jmodel = (JumpModel *)0; jmodel = (JumpModel *)0;
@ -2343,7 +2343,6 @@ JumpTable::JumpTable(Architecture *g,Address ad)
switchVarConsume = ~((uintb)0); switchVarConsume = ~((uintb)0);
defaultBlock = -1; defaultBlock = -1;
lastBlock = -1; lastBlock = -1;
maxtablesize = 1024;
maxaddsub = 1; maxaddsub = 1;
maxleftright = 1; maxleftright = 1;
maxext = 1; maxext = 1;
@ -2364,7 +2363,6 @@ JumpTable::JumpTable(const JumpTable *op2)
switchVarConsume = ~((uintb)0); switchVarConsume = ~((uintb)0);
defaultBlock = -1; defaultBlock = -1;
lastBlock = op2->lastBlock; lastBlock = op2->lastBlock;
maxtablesize = op2->maxtablesize;
maxaddsub = op2->maxaddsub; maxaddsub = op2->maxaddsub;
maxleftright = op2->maxleftright; maxleftright = op2->maxleftright;
maxext = op2->maxext; maxext = op2->maxext;
@ -2682,7 +2680,7 @@ bool JumpTable::recoverLabels(Funcdata *fd)
} }
else { else {
jmodel = new JumpModelTrivial(this); jmodel = new JumpModelTrivial(this);
jmodel->recoverModel(fd,indirect,addresstable.size(),maxtablesize); jmodel->recoverModel(fd,indirect,addresstable.size(),glb->max_jumptable_size);
jmodel->buildAddresses(fd,indirect,addresstable,(vector<LoadTable> *)0); jmodel->buildAddresses(fd,indirect,addresstable,(vector<LoadTable> *)0);
trivialSwitchOver(); trivialSwitchOver();
jmodel->buildLabels(fd,addresstable,label,origmodel); jmodel->buildLabels(fd,addresstable,label,origmodel);

View file

@ -535,7 +535,6 @@ class JumpTable {
uintb switchVarConsume; ///< Bits of the switch variable being consumed uintb switchVarConsume; ///< Bits of the switch variable being consumed
int4 defaultBlock; ///< The out-edge corresponding to the \e default switch destination (-1 = undefined) int4 defaultBlock; ///< The out-edge corresponding to the \e default switch destination (-1 = undefined)
int4 lastBlock; ///< Block out-edge corresponding to last entry in the address table int4 lastBlock; ///< Block out-edge corresponding to last entry in the address table
uint4 maxtablesize; ///< Maximum table size we allow to be built (sanity check)
uint4 maxaddsub; ///< Maximum ADDs or SUBs to normalize uint4 maxaddsub; ///< Maximum ADDs or SUBs to normalize
uint4 maxleftright; ///< Maximum shifts to normalize uint4 maxleftright; ///< Maximum shifts to normalize
uint4 maxext; ///< Maximum extensions to normalize uint4 maxext; ///< Maximum extensions to normalize
@ -561,7 +560,6 @@ public:
const Address &getOpAddress(void) const { return opaddress; } ///< Get the address of the BRANCHIND for the switch const Address &getOpAddress(void) const { return opaddress; } ///< Get the address of the BRANCHIND for the switch
PcodeOp *getIndirectOp(void) const { return indirect; } ///< Get the BRANCHIND PcodeOp PcodeOp *getIndirectOp(void) const { return indirect; } ///< Get the BRANCHIND PcodeOp
void setIndirectOp(PcodeOp *ind) { opaddress = ind->getAddr(); indirect = ind; } ///< Set the BRANCHIND PcodeOp void setIndirectOp(PcodeOp *ind) { opaddress = ind->getAddr(); indirect = ind; } ///< Set the BRANCHIND PcodeOp
void setMaxTableSize(uint4 val) { maxtablesize = val; } ///< Set the maximum entries allowed in the address table
void setNormMax(uint4 maddsub,uint4 mleftright,uint4 mext) { void setNormMax(uint4 maddsub,uint4 mleftright,uint4 mext) {
maxaddsub = maddsub; maxleftright = mleftright; maxext = mext; } ///< Set the switch variable normalization model restrictions maxaddsub = maddsub; maxleftright = mleftright; maxext = mext; } ///< Set the switch variable normalization model restrictions
void setOverride(const vector<Address> &addrtable,const Address &naddr,uintb h,uintb sv); void setOverride(const vector<Address> &addrtable,const Address &naddr,uintb h,uintb sv);

View file

@ -1160,6 +1160,6 @@ ElementId ELEM_VAL = ElementId("val",8);
ElementId ELEM_VALUE = ElementId("value",9); ElementId ELEM_VALUE = ElementId("value",9);
ElementId ELEM_VOID = ElementId("void",10); ElementId ELEM_VOID = ElementId("void",10);
ElementId ELEM_UNKNOWN = ElementId("XMLunknown",271); // Number serves as next open index ElementId ELEM_UNKNOWN = ElementId("XMLunknown",272); // Number serves as next open index
} // End namespace ghidra } // End namespace ghidra

View file

@ -58,6 +58,7 @@ ElementId ELEM_SPLITDATATYPE = ElementId("splitdatatype",270);
ElementId ELEM_STRUCTALIGN = ElementId("structalign",208); ElementId ELEM_STRUCTALIGN = ElementId("structalign",208);
ElementId ELEM_TOGGLERULE = ElementId("togglerule",209); ElementId ELEM_TOGGLERULE = ElementId("togglerule",209);
ElementId ELEM_WARNING = ElementId("warning",210); ElementId ELEM_WARNING = ElementId("warning",210);
ElementId ELEM_JUMPTABLEMAX = ElementId("jumptablemax",271);
/// If the parameter is "on" return \b true, if "off" return \b false. /// If the parameter is "on" return \b true, if "off" return \b false.
/// Any other value causes an exception. /// Any other value causes an exception.
@ -120,6 +121,7 @@ OptionDatabase::OptionDatabase(Architecture *g)
registerOption(new OptionAllowContextSet()); registerOption(new OptionAllowContextSet());
registerOption(new OptionSetAction()); registerOption(new OptionSetAction());
registerOption(new OptionSetLanguage()); registerOption(new OptionSetLanguage());
registerOption(new OptionJumpTableMax());
registerOption(new OptionJumpLoad()); registerOption(new OptionJumpLoad());
registerOption(new OptionToggleRule()); registerOption(new OptionToggleRule());
registerOption(new OptionAliasBlock()); registerOption(new OptionAliasBlock());
@ -794,6 +796,26 @@ string OptionSetLanguage::apply(Architecture *glb,const string &p1,const string
return res; return res;
} }
/// \class OptionJumpTableMax
/// \brief Set the maximum number of entries that can be recovered for a single jump table
///
/// This option is an unsigned integer value used during analysis of jump tables. It serves as a
/// sanity check that the recovered number of entries for a jump table is reasonable and
/// also acts as a resource limit on the number of destination addresses that analysis will attempt
/// to follow from a single indirect jump.
string OptionJumpTableMax::apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const
{
istringstream s(p1);
s.unsetf(ios::dec | ios::hex | ios::oct);
uint4 val = 0;
s >> val;
if (val==0)
throw ParseError("Must specify integer maximum");
glb->max_jumptable_size = val;
return "Maximum jumptable size set to "+p1;
}
/// \class OptionJumpLoad /// \class OptionJumpLoad
/// \brief Toggle whether the decompiler should try to recover the table used to evaluate a switch /// \brief Toggle whether the decompiler should try to recover the table used to evaluate a switch
/// ///

View file

@ -64,6 +64,7 @@ extern ElementId ELEM_SPLITDATATYPE; ///< Marshaling element \<splitdatatype>
extern ElementId ELEM_STRUCTALIGN; ///< Marshaling element \<structalign> extern ElementId ELEM_STRUCTALIGN; ///< Marshaling element \<structalign>
extern ElementId ELEM_TOGGLERULE; ///< Marshaling element \<togglerule> extern ElementId ELEM_TOGGLERULE; ///< Marshaling element \<togglerule>
extern ElementId ELEM_WARNING; ///< Marshaling element \<warning> extern ElementId ELEM_WARNING; ///< Marshaling element \<warning>
extern ElementId ELEM_JUMPTABLEMAX; ///< Marshaling element \<jumptablemax>
/// \brief Base class for options classes that affect the configuration of the Architecture object /// \brief Base class for options classes that affect the configuration of the Architecture object
/// ///
@ -293,6 +294,12 @@ public:
virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const; virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const;
}; };
class OptionJumpTableMax : public ArchOption {
public:
OptionJumpTableMax(void) { name = "jumptablemax"; } ///< Constructor
virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const;
};
class OptionJumpLoad : public ArchOption { class OptionJumpLoad : public ArchOption {
public: public:
OptionJumpLoad(void) { name = "jumpload"; } ///< Constructor OptionJumpLoad(void) { name = "jumpload"; } ///< Constructor

View file

@ -2984,6 +2984,17 @@
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="GeneralMaxJumptable">
<term><emphasis role="bold">Max Entries per Jumptable</emphasis></term>
<listitem>
<para>
This option sets the maximum number of entries (addresses) that the Decompiler can
recover from analyzing a single jumptable. This serves as a sanity check that the recovered
dimensions of a jumptable are reasonable and places an upper limit on the sheer number
of addresses the Decompiler is willing to trace from a single indirect jump.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</informalexample> </informalexample>
</para> </para>

View file

@ -125,6 +125,17 @@
traces control flow into more than the indicated number of instructions. traces control flow into more than the indicated number of instructions.
</p> </p>
</dd> </dd>
<dt>
<a name="GeneralMaxJumptable"></a><span class="term"><span class="bold"><strong>Max Entries per Jumptable</strong></span></span>
</dt>
<dd>
<p>
This option sets the maximum number of entries (addresses) that the Decompiler can
recover from analyzing a single jumptable. This serves as a sanity check that the recovered
dimensions of a jumptable are reasonable and places an upper limit on the sheer number
of addresses the Decompiler is willing to trace from a single indirect jump.
</p>
</dd>
</dl></div> </dl></div>
</div> </div>
<p> <p>

View file

@ -37,8 +37,8 @@ import ghidra.program.model.lang.CompilerSpec.EvaluationModelType;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.ElementId; import ghidra.program.model.pcode.ElementId;
import ghidra.program.model.pcode.Encoder; import ghidra.program.model.pcode.Encoder;
import ghidra.program.model.symbol.NameTransformer;
import ghidra.program.model.symbol.IdentityNameTransformer; import ghidra.program.model.symbol.IdentityNameTransformer;
import ghidra.program.model.symbol.NameTransformer;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
/** /**
@ -211,6 +211,7 @@ public class DecompileOptions {
public static final int SUGGESTED_DECOMPILE_TIMEOUT_SECS = 30; public static final int SUGGESTED_DECOMPILE_TIMEOUT_SECS = 30;
public static final int SUGGESTED_MAX_PAYLOAD_BYTES = 50; public static final int SUGGESTED_MAX_PAYLOAD_BYTES = 50;
public static final int SUGGESTED_MAX_INSTRUCTIONS = 100000; // Must match Architecture::resetDefaultsInternal public static final int SUGGESTED_MAX_INSTRUCTIONS = 100000; // Must match Architecture::resetDefaultsInternal
public static final int SUGGESTED_MAX_JUMPTABLE_ENTRIES = 1024; // Must match Architecture::resetDefaultsInternal
public enum CommentStyleEnum { public enum CommentStyleEnum {
@ -388,11 +389,13 @@ public class DecompileOptions {
private final static String DECOMPILE_TIMEOUT = "Decompiler Timeout (seconds)"; private final static String DECOMPILE_TIMEOUT = "Decompiler Timeout (seconds)";
private final static String PAYLOAD_LIMIT = "Decompiler Max-Payload (MBytes)"; private final static String PAYLOAD_LIMIT = "Decompiler Max-Payload (MBytes)";
private final static String MAX_INSTRUCTIONS = "Max Instructions per Function"; private final static String MAX_INSTRUCTIONS = "Max Instructions per Function";
private final static String MAX_JUMPTABLE_ENTRIES = "Max Entries per Jumptable";
private final static Boolean LINE_NUMBER_DEF = Boolean.TRUE; private final static Boolean LINE_NUMBER_DEF = Boolean.TRUE;
private boolean displayLineNumbers; private boolean displayLineNumbers;
private int decompileTimeoutSeconds; private int decompileTimeoutSeconds;
private int payloadLimitMBytes; private int payloadLimitMBytes;
private int maxIntructionsPer; private int maxIntructionsPer;
private int maxJumpTableEntries;
private int cachedResultsSize; private int cachedResultsSize;
private DecompilerLanguage displayLanguage; // Output language displayed by the decompiler private DecompilerLanguage displayLanguage; // Output language displayed by the decompiler
@ -435,6 +438,7 @@ public class DecompileOptions {
decompileTimeoutSeconds = SUGGESTED_DECOMPILE_TIMEOUT_SECS; decompileTimeoutSeconds = SUGGESTED_DECOMPILE_TIMEOUT_SECS;
payloadLimitMBytes = SUGGESTED_MAX_PAYLOAD_BYTES; payloadLimitMBytes = SUGGESTED_MAX_PAYLOAD_BYTES;
maxIntructionsPer = SUGGESTED_MAX_INSTRUCTIONS; maxIntructionsPer = SUGGESTED_MAX_INSTRUCTIONS;
maxJumpTableEntries = SUGGESTED_MAX_JUMPTABLE_ENTRIES;
cachedResultsSize = SUGGESTED_CACHED_RESULTS_SIZE; cachedResultsSize = SUGGESTED_CACHED_RESULTS_SIZE;
nameTransformer = null; nameTransformer = null;
} }
@ -492,6 +496,7 @@ public class DecompileOptions {
decompileTimeoutSeconds = opt.getInt(DECOMPILE_TIMEOUT, SUGGESTED_DECOMPILE_TIMEOUT_SECS); decompileTimeoutSeconds = opt.getInt(DECOMPILE_TIMEOUT, SUGGESTED_DECOMPILE_TIMEOUT_SECS);
payloadLimitMBytes = opt.getInt(PAYLOAD_LIMIT, SUGGESTED_MAX_PAYLOAD_BYTES); payloadLimitMBytes = opt.getInt(PAYLOAD_LIMIT, SUGGESTED_MAX_PAYLOAD_BYTES);
maxIntructionsPer = opt.getInt(MAX_INSTRUCTIONS, SUGGESTED_MAX_INSTRUCTIONS); maxIntructionsPer = opt.getInt(MAX_INSTRUCTIONS, SUGGESTED_MAX_INSTRUCTIONS);
maxJumpTableEntries = opt.getInt(MAX_JUMPTABLE_ENTRIES, SUGGESTED_MAX_JUMPTABLE_ENTRIES);
cachedResultsSize = opt.getInt(CACHED_RESULTS_SIZE_MSG, SUGGESTED_CACHED_RESULTS_SIZE); cachedResultsSize = opt.getInt(CACHED_RESULTS_SIZE_MSG, SUGGESTED_CACHED_RESULTS_SIZE);
grabFromFieldOptions(fieldOptions); grabFromFieldOptions(fieldOptions);
@ -689,6 +694,9 @@ public class DecompileOptions {
opt.registerOption(MAX_INSTRUCTIONS, SUGGESTED_MAX_INSTRUCTIONS, opt.registerOption(MAX_INSTRUCTIONS, SUGGESTED_MAX_INSTRUCTIONS,
new HelpLocation(HelpTopics.DECOMPILER, "GeneralMaxInstruction"), new HelpLocation(HelpTopics.DECOMPILER, "GeneralMaxInstruction"),
"The maximum number of instructions decompiled in a single function"); "The maximum number of instructions decompiled in a single function");
opt.registerOption(MAX_JUMPTABLE_ENTRIES, SUGGESTED_MAX_JUMPTABLE_ENTRIES,
new HelpLocation(HelpTopics.DECOMPILER, "GeneralMaxJumptable"),
"The maximum number of entries that can be recovered from a single jumptable");
opt.registerThemeColorBinding(HIGHLIGHT_CURRENT_VARIABLE_MSG, opt.registerThemeColorBinding(HIGHLIGHT_CURRENT_VARIABLE_MSG,
HIGHLIGHT_CURRENT_VARIABLE_COLOR.getId(), HIGHLIGHT_CURRENT_VARIABLE_COLOR.getId(),
new HelpLocation(HelpTopics.DECOMPILER, "DisplayCurrentHighlight"), new HelpLocation(HelpTopics.DECOMPILER, "DisplayCurrentHighlight"),
@ -830,6 +838,9 @@ public class DecompileOptions {
if (maxIntructionsPer != SUGGESTED_MAX_INSTRUCTIONS) { if (maxIntructionsPer != SUGGESTED_MAX_INSTRUCTIONS) {
appendOption(encoder, ELEM_MAXINSTRUCTION, Integer.toString(maxIntructionsPer), "", ""); appendOption(encoder, ELEM_MAXINSTRUCTION, Integer.toString(maxIntructionsPer), "", "");
} }
if (maxJumpTableEntries != SUGGESTED_MAX_JUMPTABLE_ENTRIES) {
appendOption(encoder, ELEM_JUMPTABLEMAX, Integer.toString(maxJumpTableEntries), "", "");
}
appendOption(encoder, ELEM_PROTOEVAL, protoEvalModel, "", ""); appendOption(encoder, ELEM_PROTOEVAL, protoEvalModel, "", "");
encoder.closeElement(ELEM_OPTIONSLIST); encoder.closeElement(ELEM_OPTIONSLIST);
} }
@ -1222,6 +1233,26 @@ public class DecompileOptions {
maxIntructionsPer = num; maxIntructionsPer = num;
} }
/**
* If the number of entries in a single jumptable exceeds this value, the decompiler will
* not recover the table and control flow from the indirect jump corresponding to the table
* will not be followed.
* @return the maximum number of entries
*/
public int getMaxJumpTableEntries() {
return maxJumpTableEntries;
}
/**
* Set the maximum number of entries the decompiler will recover from a single jumptable.
* If the number exceeds this, the table is not recovered and control flow from the
* corresponding indirect jump is not followed.
* @param num is the number of entries
*/
public void setMaxJumpTableEntries(int num) {
maxJumpTableEntries = num;
}
/** /**
* @return the style in which comments are printed in decompiler output * @return the style in which comments are printed in decompiler output
*/ */

View file

@ -422,5 +422,6 @@ public record ElementId(String name, int id) {
new ElementId("command_getuseropname", COMMAND_GETUSEROPNAME); new ElementId("command_getuseropname", COMMAND_GETUSEROPNAME);
public static final ElementId ELEM_SPLITDATATYPE = new ElementId("splitdatatype", 270); public static final ElementId ELEM_SPLITDATATYPE = new ElementId("splitdatatype", 270);
public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 271); public static final ElementId ELEM_JUMPTABLEMAX = new ElementId("jumptablemax", 271);
public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 272);
} }