diff --git a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj index 83d9c47357..0c558bf4dd 100644 --- a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj +++ b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj @@ -1922,7 +1922,7 @@ void PragmaSpecifier() : { { LOOKAHEAD(3) PragmaSpecifier() | - id= ( | | )* + id= ( | | | )* ([ [ (ds1=PragmaConstant())+ [ ( ds2=PragmaConstant() ) [ ( ds3=PragmaConstant() ) [ ( PragmaConstant() )+ ] ] ] ] ] ) { if (id.image.equals("pack") && ds1 != null) { diff --git a/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/CParserTest.h b/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/CParserTest.h index 27717692a4..08a2a243c0 100644 --- a/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/CParserTest.h +++ b/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/CParserTest.h @@ -176,6 +176,9 @@ int (__stdcall * GetSectionBlock) ( #pragma region Input compatibility macros +// nothing will parse after this line if the this fails + #pragma test for, pragma, with, commas outside parens + #pragma warning(disable) #pragma warning(disable:4035 4793) // re-enable below diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc index 1e6dea66b9..62508298f6 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc @@ -1404,6 +1404,7 @@ void Architecture::resetDefaultsInternal(void) 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 | OptionSplitDatatypes::option_pointer; + max_jumptable_size = 1024; } /// Reset options that can be modified by the OptionDatabase. This includes diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh index 421bed574c..c2c4d8e3c0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh @@ -172,6 +172,7 @@ public: 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 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 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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/double.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/double.cc index 8eb11963ef..49dd39f4ea 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/double.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/double.cc @@ -787,10 +787,14 @@ bool SplitVarnode::isAddrTiedContiguous(Varnode *lo,Varnode *hi,Address &res) if (!hi->isAddrTied()) return false; // Make sure there is no explicit symbol that would prevent the pieces from being joined - SymbolEntry *entry = lo->getSymbolEntry(); - if ((entry != (SymbolEntry *)0)&&(entry->getOffset()==0)) return false; - entry = hi->getSymbolEntry(); - if ((entry != (SymbolEntry *)0)&&(entry->getOffset()==0)) return false; + SymbolEntry *entryLo = lo->getSymbolEntry(); + SymbolEntry *entryHi = hi->getSymbolEntry(); + if (entryLo != (SymbolEntry *)0 || entryHi != (SymbolEntry *)0) { + if (entryLo == (SymbolEntry *)0 || entryHi == (SymbolEntry *)0) + return false; // One is marked with a symbol, the other is not + if (entryLo->getSymbol() != entryHi->getSymbol()) + return false; // They are part of different symbols + } AddrSpace *spc = lo->getSpace(); if (spc != hi->getSpace()) return false; uintb looffset = lo->getOffset(); @@ -3093,6 +3097,12 @@ bool IndirectForm::verify(Varnode *h,Varnode *l,PcodeOp *ind) if (affector != PcodeOp::getOpFromConst(indlo->getIn(1)->getAddr())) continue; // hi and lo must be affected by same op reslo = indlo->getOut(); if (reslo->getSpace()->getType() == IPTR_INTERNAL) return false; // Indirect must not be through a temporary + if (reslo->isAddrTied() || reshi->isAddrTied()) { + Address addr; + // If one piece is address tied, the other must be as well, and they must fit together as contiguous whole + if (!SplitVarnode::isAddrTiedContiguous(reslo, reshi, addr)) + return false; + } return true; } return false; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc index ee449456ce..c07aea7235 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.cc @@ -2217,7 +2217,7 @@ void JumpTable::recoverModel(Funcdata *fd) { if (jmodel != (JumpModel *)0) { if (jmodel->isOverride()) { // If preexisting model is override - jmodel->recoverModel(fd,indirect,0,maxtablesize); + jmodel->recoverModel(fd,indirect,0,glb->max_jumptable_size); return; } 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) { JumpAssisted *jassisted = new JumpAssisted(this); jmodel = jassisted; - if (jmodel->recoverModel(fd,indirect,addresstable.size(),maxtablesize)) + if (jmodel->recoverModel(fd,indirect,addresstable.size(),glb->max_jumptable_size)) return; } } JumpBasic *jbasic = new JumpBasic(this); jmodel = jbasic; - if (jmodel->recoverModel(fd,indirect,addresstable.size(),maxtablesize)) + if (jmodel->recoverModel(fd,indirect,addresstable.size(),glb->max_jumptable_size)) return; jmodel = new JumpBasic2(this); ((JumpBasic2 *)jmodel)->initializeStart(jbasic->getPathMeld()); delete jbasic; - if (jmodel->recoverModel(fd,indirect,addresstable.size(),maxtablesize)) + if (jmodel->recoverModel(fd,indirect,addresstable.size(),glb->max_jumptable_size)) return; delete jmodel; jmodel = (JumpModel *)0; @@ -2343,7 +2343,6 @@ JumpTable::JumpTable(Architecture *g,Address ad) switchVarConsume = ~((uintb)0); defaultBlock = -1; lastBlock = -1; - maxtablesize = 1024; maxaddsub = 1; maxleftright = 1; maxext = 1; @@ -2364,7 +2363,6 @@ JumpTable::JumpTable(const JumpTable *op2) switchVarConsume = ~((uintb)0); defaultBlock = -1; lastBlock = op2->lastBlock; - maxtablesize = op2->maxtablesize; maxaddsub = op2->maxaddsub; maxleftright = op2->maxleftright; maxext = op2->maxext; @@ -2682,7 +2680,7 @@ bool JumpTable::recoverLabels(Funcdata *fd) } else { 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 *)0); trivialSwitchOver(); jmodel->buildLabels(fd,addresstable,label,origmodel); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh index fe3de7cbd1..d985052f38 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/jumptable.hh @@ -535,7 +535,6 @@ class JumpTable { uintb switchVarConsume; ///< Bits of the switch variable being consumed 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 - uint4 maxtablesize; ///< Maximum table size we allow to be built (sanity check) uint4 maxaddsub; ///< Maximum ADDs or SUBs to normalize uint4 maxleftright; ///< Maximum shifts 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 PcodeOp *getIndirectOp(void) const { return indirect; } ///< Get 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) { maxaddsub = maddsub; maxleftright = mleftright; maxext = mext; } ///< Set the switch variable normalization model restrictions void setOverride(const vector
&addrtable,const Address &naddr,uintb h,uintb sv); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc index 14564cbaf2..9979b81400 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc @@ -1160,6 +1160,6 @@ ElementId ELEM_VAL = ElementId("val",8); ElementId ELEM_VALUE = ElementId("value",9); 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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc index 20db3d3d3b..a496b4f8e0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc @@ -58,6 +58,7 @@ ElementId ELEM_SPLITDATATYPE = ElementId("splitdatatype",270); ElementId ELEM_STRUCTALIGN = ElementId("structalign",208); ElementId ELEM_TOGGLERULE = ElementId("togglerule",209); 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. /// Any other value causes an exception. @@ -120,6 +121,7 @@ OptionDatabase::OptionDatabase(Architecture *g) registerOption(new OptionAllowContextSet()); registerOption(new OptionSetAction()); registerOption(new OptionSetLanguage()); + registerOption(new OptionJumpTableMax()); registerOption(new OptionJumpLoad()); registerOption(new OptionToggleRule()); registerOption(new OptionAliasBlock()); @@ -794,6 +796,26 @@ string OptionSetLanguage::apply(Architecture *glb,const string &p1,const string 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 /// \brief Toggle whether the decompiler should try to recover the table used to evaluate a switch /// diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/options.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/options.hh index a58d77d583..9c08babf21 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/options.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/options.hh @@ -64,6 +64,7 @@ extern ElementId ELEM_SPLITDATATYPE; ///< Marshaling element \ extern ElementId ELEM_STRUCTALIGN; ///< Marshaling element \ extern ElementId ELEM_TOGGLERULE; ///< Marshaling element \ extern ElementId ELEM_WARNING; ///< Marshaling element \ +extern ElementId ELEM_JUMPTABLEMAX; ///< Marshaling element \ /// \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; }; +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 { public: OptionJumpLoad(void) { name = "jumpload"; } ///< Constructor diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/partialunion.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/partialunion.xml index 489cba0888..55c9e55e7f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/datatests/partialunion.xml +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/partialunion.xml @@ -33,7 +33,7 @@ dec print C lo fu partial1 - map unionfacet structunion 1 r0x1006ee 20603f6a8a0b89 + map unionfacet structunion 1 r0x1006ee 10603f2c20ffa6 dec print C quit diff --git a/Ghidra/Features/Decompiler/src/main/doc/decompileplugin.xml b/Ghidra/Features/Decompiler/src/main/doc/decompileplugin.xml index 94dc93faf1..816c0d771b 100644 --- a/Ghidra/Features/Decompiler/src/main/doc/decompileplugin.xml +++ b/Ghidra/Features/Decompiler/src/main/doc/decompileplugin.xml @@ -2984,6 +2984,17 @@ + + Max Entries per Jumptable + + + 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. + + + diff --git a/Ghidra/Features/Decompiler/src/main/help/help/topics/DecompilePlugin/DecompilerOptions.html b/Ghidra/Features/Decompiler/src/main/help/help/topics/DecompilePlugin/DecompilerOptions.html index 849c6ae40a..1eb7f91fcf 100644 --- a/Ghidra/Features/Decompiler/src/main/help/help/topics/DecompilePlugin/DecompilerOptions.html +++ b/Ghidra/Features/Decompiler/src/main/help/help/topics/DecompilePlugin/DecompilerOptions.html @@ -125,6 +125,17 @@ traces control flow into more than the indicated number of instructions.

+
+Max Entries per Jumptable +
+
+

+ 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. +

+

diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java index 80645f95ec..d7e7ff9120 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java @@ -37,8 +37,8 @@ import ghidra.program.model.lang.CompilerSpec.EvaluationModelType; import ghidra.program.model.listing.Program; import ghidra.program.model.pcode.ElementId; import ghidra.program.model.pcode.Encoder; -import ghidra.program.model.symbol.NameTransformer; import ghidra.program.model.symbol.IdentityNameTransformer; +import ghidra.program.model.symbol.NameTransformer; 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_MAX_PAYLOAD_BYTES = 50; 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 { @@ -388,11 +389,13 @@ public class DecompileOptions { private final static String DECOMPILE_TIMEOUT = "Decompiler Timeout (seconds)"; 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_JUMPTABLE_ENTRIES = "Max Entries per Jumptable"; private final static Boolean LINE_NUMBER_DEF = Boolean.TRUE; private boolean displayLineNumbers; private int decompileTimeoutSeconds; private int payloadLimitMBytes; private int maxIntructionsPer; + private int maxJumpTableEntries; private int cachedResultsSize; private DecompilerLanguage displayLanguage; // Output language displayed by the decompiler @@ -435,6 +438,7 @@ public class DecompileOptions { decompileTimeoutSeconds = SUGGESTED_DECOMPILE_TIMEOUT_SECS; payloadLimitMBytes = SUGGESTED_MAX_PAYLOAD_BYTES; maxIntructionsPer = SUGGESTED_MAX_INSTRUCTIONS; + maxJumpTableEntries = SUGGESTED_MAX_JUMPTABLE_ENTRIES; cachedResultsSize = SUGGESTED_CACHED_RESULTS_SIZE; nameTransformer = null; } @@ -492,6 +496,7 @@ public class DecompileOptions { decompileTimeoutSeconds = opt.getInt(DECOMPILE_TIMEOUT, SUGGESTED_DECOMPILE_TIMEOUT_SECS); payloadLimitMBytes = opt.getInt(PAYLOAD_LIMIT, SUGGESTED_MAX_PAYLOAD_BYTES); 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); grabFromFieldOptions(fieldOptions); @@ -689,6 +694,9 @@ public class DecompileOptions { opt.registerOption(MAX_INSTRUCTIONS, SUGGESTED_MAX_INSTRUCTIONS, new HelpLocation(HelpTopics.DECOMPILER, "GeneralMaxInstruction"), "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, HIGHLIGHT_CURRENT_VARIABLE_COLOR.getId(), new HelpLocation(HelpTopics.DECOMPILER, "DisplayCurrentHighlight"), @@ -830,6 +838,9 @@ public class DecompileOptions { if (maxIntructionsPer != SUGGESTED_MAX_INSTRUCTIONS) { 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, "", ""); encoder.closeElement(ELEM_OPTIONSLIST); } @@ -1222,6 +1233,26 @@ public class DecompileOptions { 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 */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java index bf9ff4950e..d7a813c678 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java @@ -422,5 +422,6 @@ public record ElementId(String name, int id) { new ElementId("command_getuseropname", COMMAND_GETUSEROPNAME); 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); }