Newer
Older
if (CurrPhi != CurrBlock->GetLastPhi()) {
// Found Phi function for current global name.
if (IsEqType(CurrPhi->GetDefType(), UNINIT)) {
// Phi DEF is UNINIT; add Phi to the map.
pair<int, set<SMPPhiFunction, LessPhi>::iterator> TempPair(CurrPhi->GetDefSSANum(), CurrPhi);
bool Inserted = false;
map<int, set<SMPPhiFunction, LessPhi>::iterator>::iterator WhereIns;
pair<map<int, set<SMPPhiFunction, LessPhi>::iterator>::iterator, bool> Result(WhereIns, Inserted);
Result = UninitDEFPhis.insert(TempPair);
assert(Result.second == true);
}
}
} // end for all blocks
// If any Phi DEF had UNINIT as its type, set up a vector of
// iterators to instructions that have UNINIT as the DEF type
// for the current global name.
if (UninitDEFPhis.empty())
continue;
list<SMPInstr *>::iterator InstIter;
for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) {
SMPInstr *CurrInst = (*InstIter);
7023
7024
7025
7026
7027
7028
7029
7030
7031
7032
7033
7034
7035
7036
7037
7038
7039
7040
7041
7042
7043
7044
7045
7046
7047
7048
7049
7050
7051
7052
7053
7054
7055
7056
7057
7058
7059
7060
7061
7062
7063
7064
7065
7066
7067
7068
7069
7070
7071
7072
7073
7074
7075
7076
7077
7078
7079
7080
7081
7082
7083
7084
7085
7086
7087
7088
7089
7090
7091
7092
7093
7094
7095
7096
7097
7098
7099
7100
7101
7102
7103
7104
set<DefOrUse, LessDefUse>::iterator CurrDef = CurrInst->FindDef(GlobalOp);
if (CurrDef != CurrInst->GetLastDef()) {
// Found DEF of current global name.
if (IsEqType(UNINIT, CurrDef->GetType())) {
UninitDEFInsts.push_back(CurrInst);
}
}
} // end for all instructions
// Put all UNINIT Phi DEFs that have at least one USE
// that is not UNINIT onto the PhiWorkList.
map<int, set<SMPPhiFunction, LessPhi>::iterator>::iterator CurrUnPhi;
for (CurrUnPhi = UninitDEFPhis.begin(); CurrUnPhi != UninitDEFPhis.end(); ++CurrUnPhi) {
pair<int, set<SMPPhiFunction, LessPhi>::iterator> PhiDefPair(*CurrUnPhi);
if (PhiDefPair.second->HasTypedUses()) {
PhiWorkList.push_back(CurrUnPhi);
}
}
// Iterate until both work lists are empty:
while (!(PhiWorkList.empty() && InstWorkList.empty())) {
// Process Phi items first.
while (!PhiWorkList.empty()) {
// If applying the meet operator over the Phi USE types
// would produce a new DEF type, change the DEF type and
// propagate it, adding Phi functions and instructions that
// received the propagated type to their respective work lists.
map<int, set<SMPPhiFunction, LessPhi>::iterator>::iterator MapIter;
MapIter = PhiWorkList.front();
PhiWorkList.pop_front(); // remove from work list
pair<int, set<SMPPhiFunction, LessPhi>::iterator> PhiDefPair;
PhiDefPair.first = MapIter->first;
PhiDefPair.second = MapIter->second;
set<SMPPhiFunction, LessPhi>::iterator CurrPhi = PhiDefPair.second;
SMPOperandType MeetType = CurrPhi->ConditionalMeetType();
// Here we use a straight equality test, not our macros,
// because we consider it a change if the MeetType is
// profiler derived and the DEFType is not.
if (MeetType == CurrPhi->GetDefType())
continue;
// At this point, we need to set the DEFType to the MeetType
// and propagate the change. We have a map of all the
// critical Phi functions for this global name, as well
// as a vector of the relevant instructions for this name.
CurrPhi->SetDefType(MeetType);
changed = true;
int DefSSANum = CurrPhi->GetDefSSANum();
map<int, set<SMPPhiFunction, LessPhi>::iterator>::iterator PhiIter;
vector<list<SMPInstr>::iterator>::iterator InstIter;
// Propagate to Phi functions first.
for (PhiIter = UninitDEFPhis.begin(); PhiIter != UninitDEFPhis.end(); ++PhiIter) {
if (DefSSANum == PhiIter->first)
continue; // Skip the Phi that we just changed
for (size_t index = 0; index < PhiIter->second->GetPhiListSize(); ++index) {
if (DefSSANum == PhiIter->second->GetUseSSANum(index)) {
// Matched SSA # to USE. Propagate new type.
PhiIter->second->SetRefType(index, MeetType);
// Add this phi function to the work list.
PhiWorkList.push_back(PhiIter);
}
}
}
#define SMP_COND_TYPE_PROP_TO_INSTS 0
#if SMP_COND_TYPE_PROP_TO_INSTS
// Propagate to instructions with uninit DEFs of global name.
// The idea is that the instructions that hold up type propagation
// are the ones that USE and then DEF the same global name.
// For example, "increment EAX" has to know the type of
// the USE of EAX in order to set the type of the DEF.
#endif
} // end while the PhiWorkList is not empty
#if SMP_COND_TYPE_PROP_TO_INSTS
// The PhiWorkList is empty at this point, so process
// instructions on the InstWorkList.
#endif
} // end while both work lists are not empty
} // end for all global names
return changed;
} // end of SMPFunction::ConditionalTypePropagation()
#endif // end if SMP_SIMPLE_CONDITIONAL_TYPE_PROPAGATION else ...
7105
7106
7107
7108
7109
7110
7111
7112
7113
7114
7115
7116
7117
7118
7119
7120
7121
7122
7123
7124
7125
7126
7127
7128
7129
7130
7131
7132
7133
7134
7135
7136
7137
7138
7139
7140
7141
7142
7143
7144
7145
7146
7147
// Propagate signedness FG info from DEFs to USEs whenever there is no USE sign info.
bool SMPFunction::PropagateSignedness(void) {
bool changed = false;
#if STARS_AGGRESSIVE_SIGNEDNESS_PROPAGATION
map<int, struct FineGrainedInfo>::iterator UseFGIter, DefFGIter;
list<SMPBasicBlock *>::iterator BlockIter;
for (UseFGIter = this->GlobalUseFGInfoBySSA.begin(); UseFGIter != this->GlobalUseFGInfoBySSA.end(); ++UseFGIter) {
struct FineGrainedInfo UseFG = UseFGIter->second;
if (0 == (UseFG.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS)) {
// No signedness info. Propagate any signedness info from DEF.
int UseHashValue = UseFGIter->first;
unsigned short DefSignMask = this->GetDefSignMiscInfo(UseHashValue);
DefSignMask &= FG_MASK_SIGNEDNESS_BITS;
if (0 != DefSignMask) {
// DEF has signedness info.
UseFGIter->second.SignMiscInfo |= DefSignMask;
changed = true;
}
}
}
// See if we have DEF signedness info for DEFs with no corresponding USE map entries.
for (DefFGIter = this->GlobalDefFGInfoBySSA.begin(); DefFGIter != this->GlobalDefFGInfoBySSA.end(); ++DefFGIter) {
struct FineGrainedInfo DefFG = DefFGIter->second;
unsigned short DefSignMask = (DefFG.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
if (0 != DefSignMask) {
// Has signedness info. See if USE has no entry.
int DefHashValue = DefFGIter->first;
UseFGIter = this->GlobalUseFGInfoBySSA.find(DefHashValue);
if (UseFGIter == this->GlobalUseFGInfoBySSA.end()) {
this->UpdateUseSignMiscInfo(DefHashValue, DefSignMask);
changed = true;
}
}
}
// Do the same processsing for block-local registers.
for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) {
bool NewChange = (*BlockIter)->PropagateDEFSignedness();
changed = changed || NewChange;
}
#endif
return changed;
} // end of SMPFunction::PropagateSignedness()
// Detect and mark special cases before emitting numeric error annotations.
void SMPFunction::MarkSpecialNumericErrorCases(void) {
list<SMPBasicBlock *>::iterator BlockIter;
list<SMPInstr *>::reverse_iterator InstIter;
SMPBasicBlock *CurrBlock;
SMPInstr *CurrInst;
bool DebugFlag = (0 == strcmp("sub_8063BE0", this->GetFuncName()));
set<int> NonEscapingRegisterHashes; // memoization optimization: set of register/SSA# hashes that do not reach end of block
#if STARS_BUILD_LOOP_BITSET
// Now that we know how many loops we have, we can allocate the loops data structure.
this->FuncLoopsByBlock.resize(this->BlockCount);
for (size_t BlockIndex = 0; BlockIndex < this->BlockCount; ++BlockIndex) {
this->FuncLoopsByBlock.at(BlockIndex).AllocateBits(this->LoopCount);
}
if (this->LoopCount > 0) {
this->DetectLoops();
}
#endif
// Special-case preparatory analyses.
for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) {
CurrBlock = (*BlockIter);
CurrBlock->AnalyzePrepForNumericAnnotations();
}
if (this->LoopCount == 0) {
return;
// Loop through blocks and detect tight loops of hashing arithmetic.
for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) {
CurrBlock = (*BlockIter);
clc5q
committed
int BlockNum = CurrBlock->GetNumber();
#if 0
if (CurrBlock->IsLoopTailBlock() && CurrBlock->IsLoopHeaderBlock()) {
clc5q
committed
#else
if (this->IsBlockInAnyLoop(BlockNum)) {
#endif
// We have a one-block loop to itself. This is the simple case we want
// to start with, as hash functions we have observed are tight loops of arithmetic computations.
// The next question is whether we can find the kind of shift/rotate that is common to hashing, plus
// at least one addition.
bool ShiftFound = false;
bool AddFound = false;
clc5q
committed
set<DefOrUse, LessDefUse>::iterator DefIter;
NonEscapingRegisterHashes.clear();
for (InstIter = CurrBlock->GetRevInstBegin(); InstIter != CurrBlock->GetRevInstEnd(); ++InstIter) {
CurrInst = (*InstIter);
if ((!ShiftFound) && CurrInst->MDIsHashingArithmetic()) {
// If the operand being shifted is never used in any assignment or arithmetic
// except as an address register computation within a memory operand, then the
// shifted value does not reach the top of the loop and get shifts accumulated.
// In that case, we are not dealing with a shift-and-add type of hash function.
clc5q
committed
// So, do not claim success unless the later addition DEF reaches the end
clc5q
committed
DefIter = CurrInst->GetFirstNonFlagsDef();
ea_t DefAddr = CurrInst->GetAddr();
clc5q
committed
DefOp = DefIter->GetOp();
ea_t AdditionAddr = BADADDR;
ShiftFound = CurrBlock->IsDefInvolvedInAddition(DefAddr, DefOp, AdditionAddr);
clc5q
committed
if (ShiftFound) {
SMPInstr *AdditionInst = this->GetInstFromAddr(AdditionAddr);
DefIter = AdditionInst->GetFirstNonFlagsDef();
AddDefOp = DefIter->GetOp();
AddFound = CurrBlock->DoesDefReachBlockEnd(AdditionAddr, AddDefOp, DefIter->GetSSANum(), NonEscapingRegisterHashes);
clc5q
committed
if (AddFound) {
break;
}
else {
// Reset ShiftFound and look for a different shift.
ShiftFound = false;
}
}
}
}
if (ShiftFound && AddFound) {
// We found a tight hashing loop. Mark all the overflowing and underflowing opcodes as benign.
// NOTE: We could do loop-variant analysis to ensure that the shifted and added values are actually
// changing within the loop, but if they are not, they are probably not exploitable overflows anyway,
// and the loop-invariant overflow would happen on every loop iteration based on initial values, which
// is a pattern we have never seen for this kind of code.
list<SMPInstr *>::iterator ForwardInstIter;
for (ForwardInstIter = CurrBlock->GetFirstInstr(); ForwardInstIter != CurrBlock->GetLastInstr(); ++ForwardInstIter) {
CurrInst = (*ForwardInstIter);
if (CurrInst->MDIsOverflowingOpcode() || CurrInst->MDIsUnderflowingOpcode() || CurrInst->MDIsLoadEffectiveAddressInstr()) {
CurrInst->SetHashOperation();
}
}
}
} // end if loop header and loop tail
} // end for all blocks
NonEscapingRegisterHashes.clear();
return;
} // end of SMPFunction::MarkSpecialNumericErrorCases()
// Emit all annotations for the function, including all per-instruction
// annotations.
void SMPFunction::EmitAnnotations(FILE *AnnotFile, FILE *InfoAnnotFile) {
// Emit annotation for the function as a whole.
list<SMPBasicBlock *>::iterator BlockIter;
SMPBasicBlock *CurrBlock;
bool FuncHasProblems = ((!this->AnalyzedSP) || (!this->HasGoodRTLs()) || (this->HasUnresolvedIndirectCalls())
|| (this->HasUnresolvedIndirectJumps()) || (this->HasSharedChunks()));
if (this->StaticFunc) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6zu FUNC LOCAL %s ", this->FuncInfo.startEA,
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6zu FUNC GLOBAL %s ", this->FuncInfo.startEA,
switch (this->GetReturnAddressStatus())
{
case FUNC_UNKNOWN:
{
clc5q
committed
SMP_fprintf(AnnotFile, "FUNC_UNKNOWN ");
break;
}
case FUNC_SAFE:
{
clc5q
committed
SMP_fprintf(AnnotFile, "FUNC_SAFE ");
break;
}
case FUNC_UNSAFE:
{
clc5q
committed
SMP_fprintf(AnnotFile, "FUNC_UNSAFE ");
break;
}
default:
assert(0);
}
clc5q
committed
SMP_fprintf(AnnotFile, "USEFP ");
clc5q
committed
SMP_fprintf(AnnotFile, "NOFP ");
}
if (this->FuncInfo.does_return()) {
clc5q
committed
SMP_fprintf(AnnotFile, "RET ");
clc5q
committed
SMP_fprintf(AnnotFile, "NORET ");
clc5q
committed
SMP_fprintf(AnnotFile, "FUNC_LEAF ");
clc5q
committed
SMP_fprintf(AnnotFile,"%10x ", this->FuncInfo.endEA - 1);
clc5q
committed
SMP_fprintf(AnnotFile, "LIBRARY ");
SMP_fprintf(AnnotFile, "\n");
// Emit annotations about how to restore register values
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d FUNC FRAMERESTORE ", this->FuncInfo.startEA, 0);
clc5q
committed
for(int i = R_ax; i <= R_di; i++)
clc5q
committed
SMP_fprintf(AnnotFile, "%d %d %d ", i, this->SavedRegLoc[i], this->ReturnRegTypes[i]);
clc5q
committed
SMP_fprintf(AnnotFile, "ZZ\n");
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d FUNC MMSAFENESS ", this->FuncInfo.startEA, 0);
clc5q
committed
SMP_fprintf(AnnotFile, "UNSAFE\n");
clc5q
committed
SMP_fprintf(AnnotFile, "SPECSAFE\n");
clc5q
committed
SMP_fprintf(AnnotFile, "SAFE\n");
// If function has problems that limited our analyses, emit an information annotation so that
// other tools can be aware of which analyses will be sound.
if (FuncHasProblems) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6zu FUNC PROBLEM %s ", this->FuncInfo.startEA,
if (!this->AnalyzedSP) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "STACKANALYSIS ");
}
if (this->HasSharedChunks()) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "CHUNKS ");
}
if (this->HasUnresolvedIndirectJumps()) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "JUMPUNRESOLVED ");
}
if (this->HasUnresolvedIndirectCalls()) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "CALLUNRESOLVED ");
}
if (!this->HasGoodRTLs()) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "BADRTLS ");
clc5q
committed
SMP_fprintf(InfoAnnotFile, "\n");
// Find and mark special cases that will affect the integer error annotations.
this->MarkSpecialNumericErrorCases();
// Loop through all instructions in the function.
// Output optimization annotations for those
// instructions that do not require full computation
// of their memory metadata by the Memory Monitor SDT.
list<size_t> LoopList; // for current block
int CurrBlockNum = SMP_BLOCKNUM_UNINIT;
list<SMPInstr *>::iterator InstIter = Instrs.begin();
#if SMP_USE_SSA_FNOP_MARKER
++InstIter; // skip marker instruction
#endif
bool AllocSeen = false; // Reached LocalVarsAllocInstr yet?
bool DeallocTrigger = false;
for ( ; InstIter != Instrs.end(); ++InstIter) {
SMPInstr *CurrInst = (*InstIter);
ea_t addr = CurrInst->GetAddr();
CurrBlock = CurrInst->GetBlock();
int BlockNum = CurrBlock->GetNumber();
if (BlockNum != CurrBlockNum) {
CurrBlockNum = BlockNum;
if (0 < this->LoopCount) {
LoopList.clear();
this->BuildLoopList(BlockNum, LoopList);
}
}
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR BELONGTO %x \n", addr, 0, GetStartAddr());
if (this->LocalVarsAllocInstr == addr) {
AllocSeen = true;
clc5q
committed
this->EmitStackFrameAnnotations(AnnotFile, CurrInst);
clc5q
committed
int OptType = CurrInst->GetOptType();
if (5 == OptType) { // ADD or SUB
// Prevent mmStrata from extending the caller's stack frame
// to include the new allocation.
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL SafeFrameAlloc %s \n",
clc5q
committed
addr, -1, CurrInst->GetDisasm());
}
else if (CurrInst->MDIsPushInstr()) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL NoWarn %s \n",
clc5q
committed
addr, -3, CurrInst->GetDisasm());
}
clc5q
committed
// mmStrata ignores the DATAREF annotations anyway, so even though
// they are not needed, emit them for use by Strata and other tools
// in other projects besides MEDS.
this->EmitStackFrameAnnotations(AnnotFile, CurrInst);
}
// If this is the instruction which deallocated space
// for local variables, we set a flag to remind us to
// emit an annotation on the next instruction.
// mmStrata wants the instruction AFTER the
// deallocating instruction, so that it processes
// the deallocation after it happens. It inserts
// instrumentation before an instruction, not
// after, so it will insert the deallocating
// instrumentation before the first POP of callee-saved regs,
// if there are any, or before the return, otherwise.
if (addr == this->LocalVarsDeallocInstr) {
DeallocTrigger = true;
}
else if (DeallocTrigger) { // Time for annotation
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d DEALLOC STACK esp - %d %s\n", addr,
this->LocalVarsSize, this->LocalVarsSize, CurrInst->GetDisasm());
DeallocTrigger = false;
}
#ifndef SMP_REDUCED_ANALYSIS
if (this->StackPtrAnalysisSucceeded() && this->HasGoodRTLs() && !this->HasUnresolvedIndirectJumps() && !this->HasSharedChunks()) {
CurrInst->EmitTypeAnnotations(this->UseFP, AllocSeen, this->NeedsStackReferent, AnnotFile, InfoAnnotFile);
CurrInst->EmitIntegerErrorAnnotations(InfoAnnotFile, LoopList);
else
#endif
CurrInst->EmitAnnotations(this->UseFP, AllocSeen, this->NeedsStackReferent, AnnotFile, InfoAnnotFile);
if (CurrInst->MDIsReturnInstr() && this->GetReturnAddressStatus() == FUNC_SAFE)
CurrInst->EmitSafeReturn(AnnotFile);
} // end for all instructions
// Loop through all basic blocks and emit profiling request annotations
// for those blocks that have unsafe memory writes in them.
this->SafeBlocks = 0;
this->UnsafeBlocks = 0;
for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) {
CurrBlock = (*BlockIter);
if (CurrBlock->MaybeAliasedWrite()) {
++(this->UnsafeBlocks);
clc5q
committed
#if SMP_OPTIMIZE_BLOCK_PROFILING
list<SMPInstr *>::iterator CurrInst;
CurrInst = CurrBlock->GetFirstInstr();
ea_t addr = (*CurrInst)->GetAddr();
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d BLOCK PROFILECOUNT %s\n", addr,
(*CurrInst)->GetCmd().size, (*CurrInst)->GetDisasm());
clc5q
committed
#endif
}
else {
++(this->SafeBlocks);
}
}
// Free loop memory.
this->FuncLoopsByBlock.clear();
#if SMP_SHRINK_TO_FIT
vector<STARSBitSet>(this->FuncLoopsByBlock).swap(this->FuncLoopsByBlock);
#endif
return;
} // end of SMPFunction::EmitAnnotations()
// Debug output dump.
void SMPFunction::Dump(void) {
list<SMPBasicBlock *>::iterator CurrBlock;
clc5q
committed
SMP_msg("Debug dump for function: %s\n", this->GetFuncName());
SMP_msg("UseFP: %d LocalVarsAllocInstr: %x\n", this->UseFP,
this->LocalVarsAllocInstr);
for (size_t index = 0; index < this->IDom.size(); ++index) {
clc5q
committed
SMP_msg("IDOM for %zu: %d\n", index, this->IDom.at(index));
for (size_t index = 0; index < this->DomTree.size(); ++index) {
clc5q
committed
SMP_msg("DomTree for %zu: ", index);
list<int>::iterator DomIter;
for (DomIter = this->DomTree.at(index).second.begin();
DomIter != this->DomTree.at(index).second.end();
++DomIter) {
clc5q
committed
SMP_msg("%d ", *DomIter);
clc5q
committed
SMP_msg("\n");
clc5q
committed
SMP_msg("Global names: \n");
set<op_t, LessOp>::iterator NameIter;
for (NameIter = this->GlobalNames.begin(); NameIter != this->GlobalNames.end(); ++NameIter) {
clc5q
committed
SMP_msg("index: %d ", ExtractGlobalIndex(*NameIter));
PrintListOperand(*NameIter);
clc5q
committed
SMP_msg("\n");
clc5q
committed
SMP_msg("Blocks each name is defined in: \n");
for (size_t index = 0; index < this->BlocksDefinedIn.size(); ++index) {
clc5q
committed
SMP_msg("Name index: %zu Blocks: ", index);
list<int>::iterator BlockIter;
for (BlockIter = this->BlocksDefinedIn.at(index).begin();
BlockIter != this->BlocksDefinedIn.at(index).end();
++BlockIter) {
clc5q
committed
SMP_msg("%d ", *BlockIter);
clc5q
committed
SMP_msg("\n");
}
for (CurrBlock = this->Blocks.begin(); CurrBlock != this->Blocks.end(); ++CurrBlock) {
// Dump out the function number and data flow sets before the instructions.
clc5q
committed
SMP_msg("End of debug dump for function: %s\n", this->GetFuncName());
return;
} // end of SMPFunction::Dump()
// Analyzes the function to see if the return address can be marked as safe
void SMPFunction::MarkFunctionSafe() {
clc5q
committed
SMP_msg(" Analyzing function %s and isLeaf = %d \n ", this->GetFuncName(), this->IsLeaf());
bool HasCallTargets = false;
clc5q
committed
bool HasStackPointerCopy = false;
bool HasStackPointerPush = false;
bool HasIndirectGlobalWrite = false;
bool WritesAboveLocalFrame = false; // Direct writes above local frame
bool WritesAboveLocalFrameIndirect = false; // Indirect writes above local frame
clc5q
committed
bool HasIndexedStackWrite = false;
bool HasIndirectWrite = false;
bool IsIndirectCallTarget = false; // could be called indirectly
bool IsTailCallTarget = false; // could be called by jump instruction used as tail call
bool HasNoCallers = this->AllCallSources.empty();
clc5q
committed
this->ReturnAddrStatus = FUNC_SAFE;
this->SafeFunc = true;
if (!this->AllCallTargets.empty()) {
HasCallTargets = true;
#if SMP_USE_SWITCH_TABLE_INFO
if (this->UnresolvedIndirectJumps) {
#else
clc5q
committed
SMP_msg("Function %s marked as unsafe due to indirect jumps\n", this->GetFuncName());
#if SMP_DECLARE_INDIRECT_TARGETS_UNSAFE
ea_t FirstAddr = this->FirstEA;
SMP_xref_t xrefs;
for (bool ok = xrefs.SMP_first_to(FirstAddr, XREF_ALL); ok; ok = xrefs.SMP_next_to()) {
ea_t FromAddr = xrefs.GetFrom();
if (FromAddr != 0) {
if (!xrefs.GetIscode()) { // found data xref
IsIndirectCallTarget = true; // addr of func appears in data; assume indirect calls to func
}
else { // found code xref; see if it is a jump used as a tail call
// These tail calls could be a problem for fast returns if they go from unsafe to safe functions.
insn_t TempCmd;
ulong TempFeatures;
bool CmdOK = SMPGetCmd(FromAddr, TempCmd, TempFeatures);
if (!CmdOK) {
// Better be conservative and assume it could be a tail call.
IsTailCallTarget = true;
SMP_msg("ERROR: Could not decode instruction at %x from within MarkFunctionSafe(); assuming tail call\n", FromAddr);
}
else if (TempCmd.itype != MD_CALL_INSTRUCTION) { // not a call instruction; must be jump of some sort
IsTailCallTarget = true;
}
}
}
}
this->PossibleIndirectCallTarget = IsIndirectCallTarget;
this->PossibleTailCallTarget = IsTailCallTarget;
#endif
list<SMPInstr *>::iterator Instructions = Instrs.begin();
SMPInstr *CurrInst;
#if SMP_USE_SSA_FNOP_MARKER
++Instructions; // skip marker instruction
#endif
// While processing the stack pointer writes, the prologue code for
clc5q
committed
// saving the frame register and allocating local variables needs to be
// handled.
bool SaveEBP = false;
bool XferESPtoEBP = false;
for ( ; Instructions != Instrs.end(); ++Instructions) {
CurrInst = (*Instructions);
clc5q
committed
SMP_msg(" Total number of defs for this instruction %d\n", CurrInst->NumDefs());
#endif
if (!SaveEBP) { // still looking for "push ebp"
if (CurrInst->MDIsPushInstr() && CurrInst->GetCmd().Operands[0].is_reg(R_bp)) {
SaveEBP = true;
continue;
}
}
else if (!XferESPtoEBP) { // found "push ebp", looking for "mov ebp,esp"
insn_t CurrCmd = CurrInst->GetCmd();
if ((CurrCmd.itype == NN_mov)
&& (CurrInst->GetFirstDef()->GetOp().is_reg(R_bp))
&& (CurrInst->GetFirstUse()->GetOp().is_reg(R_sp))) {
XferESPtoEBP = true;
continue;
}
}
ea_t address = CurrInst->GetAddr();
if (address == this->LocalVarsAllocInstr ||
address == this->LocalVarsDeallocInstr)
continue;
if (CurrInst->MDIsStackPointerCopy(this->UseFP)) {
clc5q
committed
HasStackPointerCopy = true;
if (CurrInst->MDIsLoadEffectiveAddressInstr()) {
// If an lea instruction loads an address above
// the stack frame, we must assume that writes
// above the stack frame could occur.
op_t TempOp = CurrInst->GetLeaMemUseOp();
if (this->WritesAboveLocalFrame(TempOp, CurrInst->AreDefsNormalized()))
WritesAboveLocalFrameIndirect = true;
}
clc5q
committed
SMP_msg(" Function %s marked as unsafe due to stack pointer copy \n ", this->GetFuncName());
SMP_msg("%s %x \n", CurrInst->GetDisasm(), CurrInst->GetAddr());
if (CurrInst->MDIsPushInstr()) {
// not exactly sure how to handle this instruction
// for the moment if its a push on a esp or usefp & ebp
// mark as unsafe
if (CurrInst->GetCmd().Operands[0].is_reg(R_sp) ||
(this->UseFP && CurrInst->GetCmd().Operands[0].is_reg(R_bp))) {
clc5q
committed
HasStackPointerPush = true;
clc5q
committed
SMP_msg(" Function %s marked as unsafe due to push on ebp or esp outside of function header \n", this->GetFuncName());
SMP_msg("%s %x\n", CurrInst->GetDisasm(), CurrInst->GetAddr());
#endif
}
continue;
}
if (CurrInst->MDIsPopInstr() || CurrInst->MDIsReturnInstr()) {
// ignore pops and returns for the moment
continue;
}
set<DefOrUse, LessDefUse>::iterator setIterator;
for (setIterator = CurrInst->GetFirstDef(); setIterator != CurrInst->GetLastDef(); ++setIterator) {
op_t Operand = setIterator->GetOp();
clc5q
committed
int BaseReg;
int IndexReg;
ushort ScaleFactor;
ea_t offset;
// now o_mem can have sib byte as well, as
clc5q
committed
// reported by IDA. Check if the base reg is R_none
// and index reg is R_none. If they are, then this is
clc5q
committed
// a direct global write and can be marked safe.
clc5q
committed
MDExtractAddressFields(Operand, BaseReg, IndexReg, ScaleFactor, offset);
clc5q
committed
// go onto next def
continue;
HasIndirectGlobalWrite = true;
}
clc5q
committed
MDExtractAddressFields(Operand, BaseReg, IndexReg, ScaleFactor, offset);
bool FramePointerRelative = (this->UseFP && (BaseReg == R_bp));
bool StackPointerRelative = (BaseReg == R_sp);
if (StackPointerRelative || FramePointerRelative) {
clc5q
committed
if (IndexReg == R_none) {
bool tempWritesAboveLocalFrame = this->WritesAboveLocalFrame(Operand, CurrInst->AreDefsNormalized());
clc5q
committed
SMP_msg(" Function %s marked as unsafe due to direct write above loc "
"variables offset=%x loc=%x\n ", this->GetFuncName(),
offset, this->LocalVarsSize);
clc5q
committed
SMP_msg("Write above local frame in %s : offset: %d ",
clc5q
committed
SMP_msg("LocalVarsSize: %d OutgoingArgsSize: %d frsize: %d frregs: %d",
this->LocalVarsSize, this->OutgoingArgsSize,
this->FuncInfo.frsize, this->FuncInfo.frregs);
Instructions->Dump();
}
#endif
bool tempWritesAboveLocalFrameIndirect = this->IndexedWritesAboveLocalFrame(Operand);
/* separate indirect writes to this frame from indirect writes to another frame */
WritesAboveLocalFrameIndirect = true;
clc5q
committed
SMP_msg(" Function %s marked as unsafe due to indexed stack write above "
"loc variable offset\n", this->GetFuncName());
clc5q
committed
SMP_msg("%s %x\n", CurrInst->GetDisasm(), CurrInst->GetAddr());
clc5q
committed
SMP_msg(" Function %s marked as unsafe due to indexed stack write\n",
clc5q
committed
SMP_msg("%s %x\n", CurrInst->GetDisasm(), CurrInst->GetAddr());
/* check whether there is profiler information for this indirect reference */
clc5q
committed
HasIndirectWrite = true;
}
else if (Operand.type == o_phrase) {
// so phrase is of the form [BASE_REG + IND ]
// if the index register is missing just make sure that
// the displacement is below stack frame top
clc5q
committed
MDExtractAddressFields(Operand, BaseReg, IndexReg, ScaleFactor, offset);
// check the base reg
// if index reg is used mark as unsafe
if ((BaseReg == R_sp || (this->UseFP && BaseReg == R_bp))) {
if (IndexReg == R_none) {
/* addressing mode is *esp or *ebp */
clc5q
committed
continue;
}
else {
HasIndexedStackWrite = true;
#if SMP_DEBUG_FUNC
clc5q
committed
SMP_msg(" Function %s marked as unsafe due to indexed stack write\n", this->GetFuncName());
SMP_msg("%s %x\n", CurrInst->GetDisasm(), CurrInst->GetAddr());
clc5q
committed
#endif
}
/* check whether there is profiler information for this indirect reference */
clc5q
committed
HasIndirectWrite = true;
// else not memory, and we don't care.
} // end for all DEFs in current instruction
} // end for all instructions
clc5q
committed
// For mmStrata bounds checking of the stack frame, we don't care
// about indirect writes unless they are to the stack.
bool SpecUnsafe = (HasStackPointerCopy || HasStackPointerPush
|| HasIndexedStackWrite || this->SharedChunks
|| this->UnresolvedIndirectJumps);
bool Unsafe = SpecUnsafe || this->UnresolvedIndirectCalls;
this->SafeFunc = (!Unsafe);
this->SpecSafeFunc = (!SpecUnsafe);
this->WritesAboveRA = WritesAboveLocalFrameIndirect;
this->SafeCallee = (!Unsafe) && (!WritesAboveLocalFrameIndirect) && this->AnalyzedSP;
this->SpecSafeCallee = (!SpecUnsafe) && (!WritesAboveLocalFrameIndirect) && this->AnalyzedSP;
this->SpecNeedsStackReferent = SpecUnsafe;
this->HasIndirectWrites = (HasIndexedStackWrite || HasIndirectWrite
|| WritesAboveLocalFrameIndirect || HasIndirectGlobalWrite);
bool UnsafeReturnAddr = (Unsafe || WritesAboveLocalFrame || WritesAboveLocalFrameIndirect || HasIndirectGlobalWrite
|| HasIndirectWrite || (!this->AnalyzedSP));
#if SMP_DECLARE_INDIRECT_TARGETS_UNSAFE
if (!UnsafeReturnAddr && this->PossibleIndirectCallTarget) {
SMP_msg("INFO: Function at %x becoming UNSAFE because it is indirect call target.\n", this->FirstEA);
UnsafeReturnAddr = true;
}
else if (!UnsafeReturnAddr && this->PossibleTailCallTarget) {
SMP_msg("INFO: Function at %x becoming UNSAFE because it is tail call target.\n", this->FirstEA);
UnsafeReturnAddr = true;
}
else if (!UnsafeReturnAddr && HasNoCallers) {
SMP_msg("INFO: Function at %x becoming UNSAFE because it has no callers.\n", this->FirstEA);
UnsafeReturnAddr = true;
}
#endif
if (UnsafeReturnAddr) {
this->SetReturnAddressStatus(FUNC_UNSAFE);
clc5q
committed
SMP_msg("UNSAFE function %s ", this->GetFuncName());
SMP_msg("StackPtrCopy: %d StackPtrPush: %d IndirectGlobal: %d ",
clc5q
committed
HasStackPointerCopy, HasStackPointerPush, HasIndirectGlobalWrite);
clc5q
committed
SMP_msg("WritesAboveFrame: %d IndirectStack: %d IndirectWrite: %d ",
clc5q
committed
WritesAboveLocalFrame, HasIndexedStackWrite, HasIndirectWrite);
SMP_msg("AnalyzedSP: %d UnresolvedCalls: %d UnresolvedJumps: %d SharedChunks: %d IsLeaf: %d",
this->AnalyzedSP, this->UnresolvedIndirectCalls, this->UnresolvedIndirectJumps,
this->SharedChunks, this->IsLeaf());
SMP_msg("IndirCallTarget: %d TailCallTarget: %d HasNoCallers: %d\n", this->PossibleIndirectCallTarget,
this->PossibleTailCallTarget, HasNoCallers);
clc5q
committed
}
else if (HasCallTargets) {
this->SetReturnAddressStatus(FUNC_UNKNOWN);
clc5q
committed
}
if (this->GetReturnAddressStatus() == FUNC_SAFE)
clc5q
committed
SMP_msg("Function %s is SAFE\n", GetFuncName());
else if (this->GetReturnAddressStatus() == FUNC_SAFE)
clc5q
committed
SMP_msg("Function %s is UNSAFE\n", GetFuncName());
else if (this->GetReturnAddressStatus() == FUNC_SAFE)
clc5q
committed
SMP_msg("Function %s is UNKNOWN\n", GetFuncName());
clc5q
committed
SMP_msg("Function %s is mmSAFE\n", GetFuncName());
clc5q
committed
SMP_msg("Function %s is mmUNSAFE\n", GetFuncName());
clc5q
committed
SMP_msg("Function %s is Speculatively mmSAFE\n", GetFuncName());
clc5q
committed
SMP_msg("Function %s is Speculatively mmUNSAFE\n", GetFuncName());
clc5q
committed
return;
} // end of SMPFunction::MarkFunctionSafe()