Merge remote-tracking branch 'origin/GP-6945_caheckman_BadDataThreshold'

(Closds #9086)
This commit is contained in:
Ryan Kurtz
2026-06-15 07:42:50 -04:00
10 changed files with 93 additions and 13 deletions

View File

@@ -1421,6 +1421,7 @@ void Architecture::resetDefaultsInternal(void)
max_basetype_size = 10; // Needs to be 8 or bigger
flowoptions = FlowInfo::error_toomanyinstructions;
max_instructions = 100000;
max_baddata = 4;
infer_pointers = true;
analyze_for_loops = true;
readonlypropagate = false;

View File

@@ -183,6 +183,7 @@ public:
int4 funcptr_align; ///< How many bits of alignment a function ptr has
uint4 flowoptions; ///< options passed to flow following engine
uint4 max_instructions; ///< Maximum instructions that can be processed in one function
uint4 max_baddata; ///< Maximum number of bad instructions that one function can encounter
int4 alias_block_level; ///< Aliases blocked by 0=none, 1=struct, 2=array, 3=all
uint4 split_datatype_config; ///< Toggle for data-types splitting: Bit 0=structs, 1=arrays, 2=pointers
vector<Rule *> extra_pool_rules; ///< Extra rules that go in the main pool (cpu specific, experimental)

View File

@@ -38,6 +38,7 @@ FlowInfo::FlowInfo(Funcdata &d,PcodeOpBank &o,BlockGraph &b,vector<FuncCallSpecs
inline_recursion = (set<Address> *)0;
insn_count = 0;
insn_max = ~((uint4)0);
baddata_count = 0;
flowoverride_present = data.getOverride().hasFlowOverride();
}
@@ -72,6 +73,7 @@ FlowInfo::FlowInfo(Funcdata &d,PcodeOpBank &o,BlockGraph &b,vector<FuncCallSpecs
inline_recursion = (set<Address> *)0;
insn_count = op2->insn_count;
insn_max = op2->insn_max;
baddata_count = op2->baddata_count;
flowoverride_present = data.getOverride().hasFlowOverride();
}
@@ -80,6 +82,7 @@ void FlowInfo::clearProperties(void)
{
flags &= ~((uint4)(unimplemented_present|baddata_present|outofbounds_present));
insn_count = 0;
baddata_count = 0;
}
/// For efficiency, this method assumes the given op can actually fall-thru.
@@ -442,9 +445,10 @@ bool FlowInfo::processInstruction(const Address &curaddr,bool &startbasic)
}
}
catch(BadDataError &err) {
if ((flags & error_unimplemented)!=0)
if ((flags & error_baddata)!=0)
throw err; // rethrow
else {
countBadData(err.explain);
// Add infinite loop instruction
step = 1; // Pretend size 1
artificialHalt(curaddr,PcodeOp::badinstruction);
@@ -539,6 +543,17 @@ void FlowInfo::handleOutOfBounds(const Address &fromaddr,const Address &toaddr)
}
}
/// If the count exceeds the maximum allowable, an exception is thrown.
/// \param errMsg is the error message associated with the current bad data
void FlowInfo::countBadData(const string &errMsg)
{
if (baddata_count >= glb->max_baddata) {
throw BadDataError("Bad instruction count exceeded:\n ...\n "+ errMsg);
}
baddata_count += 1;
}
/// The address at the top stack that still needs processing is popped.
/// P-code is generated for instructions starting at this address until
/// one no longer has fall-thru flow (or some other error occurs).
@@ -622,6 +637,7 @@ void FlowInfo::reinterpreted(const Address &addr)
if ((flags & error_reinterpreted)!=0)
throw LowlevelError(s.str());
countBadData(s.str());
if ((flags & reinterpreted_present)==0) {
flags |= reinterpreted_present;
data.warningHeader(s.str());

View File

@@ -63,11 +63,12 @@ public:
error_unimplemented = 8, ///< Throw an exception for flow into unimplemented instructions
error_reinterpreted = 0x10, ///< Throw an exception for flow into previously encountered data at a difference \e cut
error_toomanyinstructions = 0x20, ///< Throw an exception if too many instructions are encountered
unimplemented_present = 0x40, ///< Indicate we have encountered unimplemented instructions
baddata_present = 0x80, ///< Indicate we have encountered flow into unaccessible data
outofbounds_present = 0x100, ///< Indicate we have encountered flow out of the specified range
reinterpreted_present = 0x200, ///< Indicate we have encountered reinterpreted data
toomanyinstructions_present = 0x400, ///< Indicate the maximum instruction threshold was reached
error_baddata = 0x40, ///< Throw an exception if an instruction cannot be decoded
unimplemented_present = 0x80, ///< Indicate we have encountered unimplemented instructions
baddata_present = 0x100, ///< Indicate we have encountered flow into unaccessible data
outofbounds_present = 0x200, ///< Indicate we have encountered flow out of the specified range
reinterpreted_present = 0x400, ///< Indicate we have encountered reinterpreted data
toomanyinstructions_present = 0x800, ///< Indicate the maximum instruction threshold was reached
possible_unreachable = 0x1000, ///< Indicate a CALL was converted to a BRANCH and some code may be unreachable
flow_forinline = 0x2000, ///< Indicate flow is being generated to in-line (a function)
record_jumploads = 0x4000 ///< Indicate that any jump table recovery should record the table structure
@@ -99,6 +100,7 @@ private:
Address maxaddr; ///< End of actual function range
bool flowoverride_present; ///< Does the function have registered flow override instructions
uint4 flags; ///< Boolean options for flow following
uint4 baddata_count; ///< Number of instructions that could not be disassembled
Funcdata *inline_head; ///< First function in the in-lining chain
set<Address> *inline_recursion; ///< Active list of addresses for function that are in-lined
set<Address> inline_base; ///< Storage for addresses of functions that are in-lined
@@ -122,6 +124,7 @@ private:
void connectBasic(void); ///< Generate edges between basic blocks
bool setFallthruBound(Address &bound); ///< Find end of the next unprocessed region
void handleOutOfBounds(const Address &fromaddr,const Address &toaddr);
void countBadData(const string &errMsg); ///< Increment bad data counter
PcodeOp *artificialHalt(const Address &addr,uint4 flag); ///< Create an artificial halt p-code op
void reinterpreted(const Address &addr); ///< Generate warning message or exception for a \e reinterpreted address
bool checkForFlowModification(FuncCallSpecs &fspecs);

View File

@@ -862,7 +862,7 @@ int4 Funcdata::inlineFlow(Funcdata *inlinefd,FlowInfo &flow,PcodeOp *callop)
Address eaddr(baseaddr.getSpace(),~((uintb)0));
inlineflow.setRange(baddr,eaddr);
inlineflow.setFlags(FlowInfo::error_outofbounds|FlowInfo::error_unimplemented|
FlowInfo::error_reinterpreted|FlowInfo::flow_forinline);
FlowInfo::error_baddata|FlowInfo::error_reinterpreted|FlowInfo::flow_forinline);
inlineflow.forwardRecursion(flow);
inlineflow.generateOps();

View File

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

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -33,6 +33,7 @@ ElementId ELEM_DEFAULTPROTOTYPE = ElementId("defaultprototype",183);
ElementId ELEM_ERRORREINTERPRETED = ElementId("errorreinterpreted",184);
ElementId ELEM_ERRORTOOMANYINSTRUCTIONS = ElementId("errortoomanyinstructions",185);
ElementId ELEM_ERRORUNIMPLEMENTED = ElementId("errorunimplemented",186);
ElementId ELEM_BADDATACOUNT = ElementId("baddatacount",290);
ElementId ELEM_EXTRAPOP = ElementId("extrapop",187);
ElementId ELEM_IGNOREUNIMPLEMENTED = ElementId("ignoreunimplemented",188);
ElementId ELEM_INDENTINCREMENT = ElementId("indentincrement",189);
@@ -100,6 +101,7 @@ OptionDatabase::OptionDatabase(Architecture *g)
registerOption(new OptionErrorUnimplemented());
registerOption(new OptionErrorReinterpreted());
registerOption(new OptionErrorTooManyInstructions());
registerOption(new OptionBadDataCount());
registerOption(new OptionDefaultPrototype());
registerOption(new OptionInferConstPtr());
registerOption(new OptionForLoops());
@@ -782,6 +784,33 @@ string OptionErrorTooManyInstructions::apply(Architecture *glb,const string &p1,
return res;
}
/// \class OptionErrorBadData
/// \brief Toggle whether disassembling bytes that can't be decoded or at a non-existent address is considered a fatal error.
///
/// If the first parameter is "on" then any BadDataError encountered while following flow will cause a fatal error.
/// Otherwise, artificial halts are generated at addresses causing the BadDataError, allowing analysis to continue.
string OptionBadDataCount::apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const
{
uint4 newMax;
string res;
if (p1.size() == 0) {
newMax = 0xffffffff;
res = "No limit on instructions that cannot be disassembled";
}
else {
newMax = 0xdeadbeef;
istringstream s1(p1);
s1.unsetf(ios::dec | ios::hex | ios::oct); // Let user specify base
s1 >> newMax;
if (newMax == 0xdeadbeef)
throw ParseError("Bad baddatacount parameter");
res = "Maximum instructions that cannot be disassembled set to " + p1;
}
glb->max_baddata = newMax;
return res;
}
/// \class OptionProtoEval
/// \brief Set the prototype model to use when evaluating the parameters of the \e current function
///

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -39,6 +39,7 @@ extern ElementId ELEM_DEFAULTPROTOTYPE; ///< Marshaling element \<defaultprotot
extern ElementId ELEM_ERRORREINTERPRETED; ///< Marshaling element \<errorreinterpreted>
extern ElementId ELEM_ERRORTOOMANYINSTRUCTIONS; ///< Marshaling element \<errortoomanyinstructions>
extern ElementId ELEM_ERRORUNIMPLEMENTED; ///< Marshaling element \<errorunimplemented>
extern ElementId ELEM_BADDATACOUNT; ///< Marshaling element \<errorbaddata>
extern ElementId ELEM_EXTRAPOP; ///< Marshaling element \<extrapop>
extern ElementId ELEM_IGNOREUNIMPLEMENTED; ///< Marshaling element \<ignoreunimplemented>
extern ElementId ELEM_INDENTINCREMENT; ///< Marshaling element \<indentincrement>
@@ -283,6 +284,12 @@ public:
virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const;
};
class OptionBadDataCount : public ArchOption {
public:
OptionBadDataCount(void) { name = "baddatacount"; } ///< Constructor
virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const;
};
class OptionProtoEval : public ArchOption {
public:
OptionProtoEval(void) { name = "protoeval"; } ///< Constructor

View File

@@ -477,6 +477,7 @@ public class DecompileOptions {
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 int SUGGESTED_MAX_BADDATA = 4; // Must match Architecture::resetDefaultsInternal
private boolean displayLineNumbers;
private int decompileTimeoutSeconds;
@@ -485,6 +486,8 @@ public class DecompileOptions {
private int maxJumpTableEntries;
private int cachedResultsSize;
private int maxBadData; // Maximum number of bad data exceptions caught before aborting
private DecompilerLanguage displayLanguage; // Output language displayed by the decompiler
private NameTransformer nameTransformer; // Transformer applied to data-type/function names
@@ -533,6 +536,7 @@ public class DecompileOptions {
maxIntructionsPer = SUGGESTED_MAX_INSTRUCTIONS;
maxJumpTableEntries = SUGGESTED_MAX_JUMPTABLE_ENTRIES;
cachedResultsSize = SUGGESTED_CACHED_RESULTS_SIZE;
maxBadData = SUGGESTED_MAX_BADDATA;
nameTransformer = null;
}
@@ -981,6 +985,9 @@ public class DecompileOptions {
if (maxJumpTableEntries != SUGGESTED_MAX_JUMPTABLE_ENTRIES) {
appendOption(encoder, ELEM_JUMPTABLEMAX, Integer.toString(maxJumpTableEntries), "", "");
}
if (maxBadData != SUGGESTED_MAX_BADDATA) {
appendOption(encoder, ELEM_BADDATACOUNT, Integer.toString(maxBadData), "", "");
}
appendOption(encoder, ELEM_PROTOEVAL, protoEvalModel, "", "");
encoder.closeElement(ELEM_OPTIONSLIST);
}
@@ -1325,6 +1332,13 @@ public class DecompileOptions {
return displayLanguage;
}
/**
* @return the maximum number of times the decompiler will catch a "bad data" exception
*/
public int getMaxBadData() {
return maxBadData;
}
/**
* Retrieve the transformer being applied to data-type, function, and namespace names.
* If no transform is being applied, a pass-through object is returned.
@@ -1377,6 +1391,14 @@ public class DecompileOptions {
this.noCastPrint = noCastPrint;
}
/**
* Set the maximum number of times the decompiler will catch a "bad data" exception
* @param val is the maximum value
*/
public void setMaxBadData(int val) {
maxBadData = val;
}
/**
* Set the source programming language that decompiler output should be rendered in.
* @param val is the source language

View File

@@ -290,6 +290,7 @@ public record ElementId(String name, int id) {
new ElementId("errortoomanyinstructions", 185);
public static final ElementId ELEM_ERRORUNIMPLEMENTED =
new ElementId("errorunimplemented", 186);
public static final ElementId ELEM_BADDATACOUNT = new ElementId("baddatacount", 290);
public static final ElementId ELEM_EXTRAPOP = new ElementId("extrapop", 187);
public static final ElementId ELEM_IGNOREUNIMPLEMENTED =
new ElementId("ignoreunimplemented", 188);
@@ -460,5 +461,5 @@ public record ElementId(String name, int id) {
public static final ElementId ELEM_EXTRA_STACK = new ElementId("extra_stack", 287);
public static final ElementId ELEM_CONSUME_REMAINING = new ElementId("consume_remaining", 288);
public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 290);
public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 291);
}