Newer
Older
if ((BADADDR != this->CallTarget) && (!this->IsCallUsedAsJump())) {
// We have a resolved call target address, either via direct or indirect call.
string FuncName = this->GetTrimmedCalledFunctionName();
ZST_SysCallType FuncCallType = GetCallTypeFromFuncName(FuncName);
ZST_Policy FuncCallPolicy = GetPolicyFromCallType(FuncCallType);
if (ZST_DISALLOW == FuncCallPolicy) {
if ((NULL != this->GetBlock()) && (NULL != this->GetBlock()->GetFunc())) {
qfprintf(ZST_AlarmFile, "ALARM: Call to %s will be disallowed at %x in %s\n",
FuncName.c_str(), this->address, this->GetBlock()->GetFunc()->GetFuncName());
}
else {
qfprintf(ZST_AlarmFile, "ALARM: Call to %s will be disallowed at %x\n", FuncName.c_str(), this->address);
}
qfprintf(ZST_AlarmFile, "ALARM REASON: Call policy is DISALLOW for all calls of type %s\n", CallTypeNames[FuncCallType]);
qfprintf(InfoAnnotFile, "%10x %6d INSTR SECURITYCALL Disallow 1 1 %s \n", addr, this->SMPcmd.size, disasm);
}
}
#if SMP_DEBUG_MEM
if (MemDest || MemSrc) {
msg("OptType: %d %s", OptType, disasm);
this->PrintOperands();
}
#endif
// Emit appropriate optimization annotations.
bool SDTInstrumentation = false;
switch (OptType) {
case 0: // SDT will have to handle these
{
#if SMP_DEBUG_TYPE0
msg("OptType 0: %x %s\n", addr, disasm);
#endif
// mmStrata wants to suppress warnings on the PUSH
// instructions that precede the LocalVarsAllocInstr
// (i.e. the PUSHes of callee-saved regs).
if ((!AllocSeen || !NeedsFrame) && this->MDIsPushInstr()) {
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL NoWarn %s \n",
clc5q
committed
NoWarnFlag = true;
}
else {
SDTInstrumentation = true;
}
break;
}
case 1: // nothing for SDT to do
{ qfprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
break;
}
case 4: // INC, DEC, etc.: no SDT work unless MemDest
{ if (MemDest || MemSrc) {
SDTInstrumentation = true;
break; // treat as category 0
}
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL Always1stSrc %s \n",
addr, -1, disasm);
++AnnotationCount[OptType];
break;
}
case 5: // ADD, etc.: If numeric 2nd src operand, no SDT work.
{ if (MemDest || MemSrc) {
SDTInstrumentation = true;
break; // treat as category 0
}
if (SecondSrcOperandImmNum
&& !this->MDIsFrameAllocInstr()
#if SPECIAL_CASE_CARRY_BORROW
&& (this->SMPcmd.itype != NN_adc)
&& (this->SMPcmd.itype != NN_sbb)
#endif
) { // treat as category 1
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
}
clc5q
committed
else {
SDTInstrumentation = true;
}
break;
}
case 6: // Only OS code should include these; problem for SDT
{ if (MemDest) {
SDTInstrumentation = true;
break; // treat as category 0
}
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL AlwaysPTR %s \n",
addr, -OptType, disasm);
++AnnotationCount[OptType];
break;
}
case 8: // Implicitly writes to EDX:EAX, always numeric.
{ qfprintf(AnnotFile, "%10x %6d INSTR LOCAL n EDX EAX ZZ %s %s \n",
addr, -2, OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
SDTInstrumentation = true;
break;
}
case 9: // Either writes to FP reg (cat. 1) or memory (cat. 0)
{ if (MemDest) {
clc5q
committed
#if SMP_DEBUG2
// MemDest seems to happen too much.
msg("Floating point MemDest: %s \n", disasm);
#endif
SDTInstrumentation = true;
break; // treat as category 0
}
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
break;
}
case 10: // Implicitly writes to EDX:EAX and ECX, always numeric.
{ qfprintf(AnnotFile, "%10x %6d INSTR LOCAL n EDX EAX ECX ZZ %s %s \n",
addr, -2, OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
SDTInstrumentation = true;
break;
}
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
default: // 2,3,7: Optimization possibilities depend on operands
{
#if SMP_DEBUG2
if (OptType == 3) { // MOV instr class
if (MemDest) {
msg("MemDest on MOV: %s\n", disasm);
}
else if (!SecondSrcOperandNum) {
msg("MOV: not 2nd op numeric: %s\n", disasm);
this->PrintOperands();
}
}
#endif
SDTInstrumentation = true;
if (MemDest) {
#if SMP_DEBUG_XOR
if (OptType == 2)
msg("MemDest on OptType 2: %s\n", disasm);
#endif
break; // treat as category 0
}
if ((OptType == 2) || (OptType == 7) || SecondSrcOperandImmNum) {
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s %s %s \n",
addr, -2, this->DestString(OptType),
OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
}
break;
}
} // end switch (OptType)
// always emit stack constant annotations, in case strata is
// instrumenting all instructions, or trying to verify speculative annotations.
this->AnnotateStackConstants(UseFP, AnnotFile);
// If mmStrata is going to have to deal with the
// instruction, then we can annotate EBP and ESP
// relative constant offsets. If we have emitted
// an annotation of type -1, there is no point
// in telling mmStrata about these constants.
// Likewise, we can tell mmStrata if a MemDest is an
// non-directly-accessed child object.
clc5q
committed
if (SDTInstrumentation || NoWarnFlag) {
if (strlen(this->DeadRegsString) > 0) {
// Optimize by informing mmStrata of dead registers. It can avoid saving
// and restoring dead state. This is particularly important for EFLAGS,
// as restoring the flags is a pipeline serializing instruction.
qfprintf(AnnotFile, "%10x %6d INSTR DEADREGS %s ZZ %s \n",
addr, this->SMPcmd.size, this->DeadRegsString, disasm);
}
#if SMP_CHILDACCESS_ALL_CODE
int ChildOffset, ChildSize;
if (MemDest && !OrphanCode
&& ProfInfo->GetMemoryAccessInfo()->ComputeNonDirectAccessRegion(addr,
ChildOffset, ChildSize)) {
qfprintf(AnnotFile, "%10x %6d INSTR CHILDACCESS %d %d ZZ %s \n",
addr, this->SMPcmd.size, ChildOffset, ChildSize, disasm);
}
#endif
}
return;
} // end of SMPInstr::EmitAnnotations()
/**
* Emits Safe Returns
* Mark the type of the annotation as "-4". Currently the SDT is ignoring this
* annotation.
*/
void SMPInstr::EmitSafeReturn(FILE *AnnotFile)
{
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL SafeReturn %s\n",
this->address, -4, disasm);
// Emit all annotations for the instruction using RTL type inference.
void SMPInstr::EmitTypeAnnotations(bool UseFP, bool AllocSeen, bool NeedsFrame, FILE *AnnotFile, FILE *InfoAnnotFile) {
ea_t addr = this->address;
flags_t InstrFlags = getFlags(addr);
int TypeGroup = SMPTypeCategory[this->SMPcmd.itype];
bool NumericDEFs = this->AllDefsNumeric(); // all DEFs are NUMERIC or CODEPTR
bool ProfiledDEFs = this->AnyDefsProfiled(); // Some DEFs come from the profiler
bool UnusedMetadata = this->AllDefMetadataUnused();
bool MemDest = this->HasDestMemoryOperand();
bool MemSrc = this->HasSourceMemoryOperand();
bool SecondSrcOperandImmNum = this->IsSecondSrcOperandNumeric(InstrFlags); // assumes 2nd source is imm or not-numeric??
clc5q
committed
bool NoWarnFlag = false; // NOWARN annotation emitted?
bool CarryBorrow = ((this->SMPcmd.itype == NN_adc)
|| (this->SMPcmd.itype == NN_sbb));
// Do we have the special case in which a non-NUMERIC comes into
// an add with carry or subtract with borrow and the result
// has been inferred to be NUMERIC?
bool TypeChange = CarryBorrow && (!IsNumeric(this->AddSubUseType))
&& NumericDEFs;
SMPMetadataType DefMetadataType = this->GetDefMetadataType();
ProfilerInformation *ProfInfo;
ProfInfo = this->BasicBlock->GetFunc()->GetProg()->GetProfInfo();
++OptCount[this->OptType]; // keep count for debugging info
if (this->IsNop())
TypeGroup = 1; // no-op idioms need their category reset
// Emit appropriate optimization annotations.
bool SDTInstrumentation = false;
// If the instruction is a CALL (or INDIR_CALL that has been resolved to
// a single target address), then we need to see if the call is to a
// function that has been forbidden by a security policy. If so, we
// need to output a security alert.
// In the near future, we will output SPRI instrumentation to prevent
// the system/library call from executing.
if ((BADADDR != this->CallTarget) && (!this->IsCallUsedAsJump())) {
// We have a resolved call target address, either via direct or indirect call.
string FuncName = this->GetTrimmedCalledFunctionName();
ZST_SysCallType FuncCallType = GetCallTypeFromFuncName(FuncName);
ZST_Policy FuncCallPolicy = GetPolicyFromCallType(FuncCallType);
if (ZST_DISALLOW == FuncCallPolicy) {
qfprintf(ZST_AlarmFile, "ALARM: Call to %s will be disallowed at %x in %s\n",
FuncName.c_str(), this->address, this->GetBlock()->GetFunc()->GetFuncName());
qfprintf(ZST_AlarmFile, "ALARM REASON: Call policy is DISALLOW for all calls of type %s\n", CallTypeNames[FuncCallType]);
qfprintf(InfoAnnotFile, "%10x %6d INSTR SECURITYCALL Disallow 1 1 %s \n", addr, this->SMPcmd.size, disasm);
}
}
// If the DEF metadata is all unused, mmStrata can skip the instruction.
// We omit this for groups 1 and 14, so that the metadata analysis
// does not get statistical credit for instructions that were already
// getting -1 annotations without analysis.
// We also cannot skip NN_adc and NN_sbb instructions that change the
// type of the incoming register.
if ((1 != TypeGroup) && (14 != TypeGroup) && (!this->MDIsInterruptCall())
&& !TypeChange) {
if (UnusedMetadata) {
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL MetadataUnused %s \n",
++AnnotationCount[this->OptType];
return;
}
else if (DEF_METADATA_REDUNDANT == DefMetadataType) {
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL MetadataRedundant %s \n",
addr, -1, disasm);
++AnnotationCount[this->OptType];
return;
}
else if (DEF_METADATA_PROF_REDUNDANT == DefMetadataType) {
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL MetadataRedundant %s \n",
addr, -257, disasm);
++AnnotationCount[this->OptType];
clc5q
committed
// Profiler annotations could be backed off due to false
// positives, in which case we will need stack constant
// annotations.
this->AnnotateStackConstants(UseFP, AnnotFile);
switch (TypeGroup) {
case 0: // SDT will have to handle these
case 11: // PUSH/POP **!!** What if we push/pop NUMERIC type? Optimize?
// --jdh
// pop numeric's can be optimized with a numericdef annotation.
// numeric push's can't immediately be optimized, but if the stack location
// can be proven as dead metadata, then perhaps optimize.
// --jdh
// mmStrata wants to suppress warnings on the PUSH
// instructions that precede the LocalVarsAllocInstr
// (i.e. the PUSHes of callee-saved regs).
if ((!AllocSeen || !NeedsFrame) && this->MDIsPushInstr()) {
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL NoWarn %s \n",
addr, -3, disasm);
clc5q
committed
NoWarnFlag = true;
else if (this->MDIsPopInstr() && NumericDEFs) {
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType),
disasm);
++AnnotationCount[this->OptType];
}
else {
SDTInstrumentation = true;
}
break;
case 1: // nothing for SDT to do
case 14:
if (MemDest) {
msg("ERROR: MemDest in Type Category 1 or 14: %x %s\n", addr, disasm);
SDTInstrumentation = true;
break;
}
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[this->OptType], disasm);
++AnnotationCount[this->OptType];
break;
case 4: // INC, DEC, etc.: no SDT work unless MemDest
if (MemDest || MemSrc) { // pretty conservative here?
// could be more aggressive if we know there's no overflow. -- jdh
SDTInstrumentation = true;
break; // treat as category 0
}
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL Always1stSrc %s \n",
addr, -1, disasm);
case 5: // ADD, etc.: If numeric 2nd src operand, no SDT work.
if (MemDest) {
SDTInstrumentation = true;
break; // treat as category 0
}
#endif
this->SetAddSubSourceType();
if (SecondSrcOperandImmNum
&& !this->MDIsFrameAllocInstr()
&& !TypeChange
#if SPECIAL_CASE_CARRY_BORROW
&& (!CarryBorrow)
#endif
) {
// treat as category 1
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[this->OptType], disasm);
++AnnotationCount[this->OptType];
else if (IsEqType(NUMERIC, this->AddSubSourceType)
&& !this->MDIsFrameAllocInstr()
&& !TypeChange
#if SPECIAL_CASE_CARRY_BORROW
&& (!CarryBorrow)
#endif
) {
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL 2ndSrcNumeric %s \n",
addr, -1, disasm);
}
else if (NumericDEFs) {
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm);
#if SMP_OPTIMIZE_ADD_TO_NUMERIC
else if ((NN_add == this->SMPcmd.itype) && (!MemSrc)
&& IsNumeric(this->AddSubUseType)) {
// reg1 := reg1 + reg2, where reg1 comes in as NUMERIC,
// means that reg1 will get DEFed to the type of reg2,
// whatever it is. If reg2 were known to be NUMERIC,
// we would have hit one of the annotation cases above.
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL %s := %s ZZ AddToNumeric %s \n",
addr, -5, RegNames[this->AddSubUseOp.reg],
RegNames[this->AddSubSourceOp.reg], disasm);
++AnnotationCount[this->OptType];
}
#endif
else {
SDTInstrumentation = true;
}
break;
case 6: // Only OS code should include these; problem for SDT
SDTInstrumentation = true;
break; // treat as category 0
}
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL AlwaysPTR %s \n",
addr, -OptType, disasm);
break;
case 8: // Implicitly writes to EDX:EAX, always numeric.
if (this->OptType == 10) { // writes to ECX also
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL n EDX EAX ECX ZZ %s %s \n",
addr, -2, OptExplanation[this->OptType], disasm);
}
else {
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL n EDX EAX ZZ %s %s \n",
addr, -2, OptExplanation[this->OptType], disasm);
}
SDTInstrumentation = true;
break;
case 9: // Either writes to FP reg (cat. 1) or memory (cat. 0)
if (MemDest) {
SDTInstrumentation = true;
#if 0
if (NumericDEFs) {
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm);
}
#endif
}
else {
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[this->OptType], disasm);
++AnnotationCount[this->OptType];
}
break;
clc5q
committed
case 10: // AND, OR, etc.: If all DEFs have been inferred to be
// NUMERIC, then output optimizing annotation.
SDTInstrumentation = true;
if (MemDest) { // **!!** optimize with numeric annotation in future
break; // treat as category 0
}
else if (NumericDEFs) { // NUMERIC result because of NUMERIC sources
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm);
}
break;
case 12: // Exchange, exchange and add, conditional exchange: All NUMERIC
// sources ==> NUMERIC DEFs, so nothing for mmStrata to do.
if (MemDest) { // **!!** optimize with numeric annotation in future
SDTInstrumentation = true;
break; // treat as category 0
}
else if (NumericDEFs) { // NUMERIC result because of NUMERIC sources
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n", addr,
ProfiledDEFs ? -256-1 : -1, OptExplanation[TypeGroup], disasm);
}
else
SDTInstrumentation = true;
break;
case 15: // Floating point, NUMERIC, possible memory destination.
// If not memory destination, fpreg dest, so nothing for mmStrata to do.
if (MemDest) { // **!!** optimize with numeric annotation in future
SDTInstrumentation = true;
break; // treat as category 0
}
else { // NUMERIC floating register result; these regs are always NUMERIC
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n", addr,
-1, OptExplanation[TypeGroup], disasm);
}
break;
default: // 2,3,7: Optimization possibilities depend on operands
SDTInstrumentation = true;
if (MemDest) {
break; // treat as category 0
}
if ((OptType == 2) || (OptType == 7) || SecondSrcOperandImmNum) {
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s %s %s \n",
addr, -2, this->DestString(this->OptType),
OptExplanation[this->OptType], disasm);
++AnnotationCount[this->OptType];
}
else if (NumericDEFs) { // NUMERIC move instruction
qfprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm);
}
break;
} // end switch (OptType)
// always annotate stack constants for the profiler, etc.
this->AnnotateStackConstants(UseFP, AnnotFile);
// If mmStrata is going to have to deal with the
// instruction, then we can annotate EBP and ESP
// relative constant offsets. If we have emitted
// an annotation of type -1, there is no point
// in telling mmStrata about these constants.
// Likewise, we can tell mmStrata if a MemDest is an
// non-directly-accessed child object.
int ChildOffset, ChildSize;
clc5q
committed
if (SDTInstrumentation || NoWarnFlag) {
if (strlen(this->DeadRegsString) > 0) {
// Optimize by informing mmStrata of dead registers. It can avoid saving
// and restoring dead state. This is particularly important for EFLAGS,
// as restoring the flags is a pipeline serializing instruction.
qfprintf(AnnotFile, "%10x %6d INSTR DEADREGS %s ZZ %s \n",
addr, this->SMPcmd.size, this->DeadRegsString, disasm);
}
if (MemDest && ProfInfo->GetMemoryAccessInfo()->ComputeNonDirectAccessRegion(addr,
ChildOffset, ChildSize)) {
qfprintf(AnnotFile, "%10x %6d INSTR CHILDACCESS %d %d ZZ %s \n",
addr, this->SMPcmd.size, ChildOffset, ChildSize, disasm);
}
#if SMP_IDENTIFY_POINTER_ADDRESS_REG
if (MemDest) {
assert(o_void != this->DestMemOp.type);
set<DefOrUse, LessDefUse>::iterator PtrUse;
PtrUse = this->GetPointerAddressReg(this->DestMemOp);
if (PtrUse != this->GetLastUse()) { // found POINTER addr reg USE
if (PtrUse->GetOp().type == o_reg) {
ushort PtrReg = PtrUse->GetOp().reg;
qfprintf(AnnotFile, "%10x %6d INSTR POINTER reg %s ZZ %s \n",
addr, this->SMPcmd.size, RegNames[PtrReg], disasm);
}
}
}
#endif
}
return;
} // end of SMPInstr::EmitTypeAnnotations()
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
// union of sign masks from 2 reg USEs for binary arithmetic
unsigned short SMPInstr::SignMaskUnionFromUseRegs(void) {
unsigned short UnionMask = 0;
unsigned short UseSignMask, DefSignMask;
set<DefOrUse, LessDefUse>::iterator UseIter;
op_t UseOp;
size_t RegOpCount = 0;
int UseHashValue;
struct FineGrainedInfo UseFGInfo, DefFGInfo;
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
UseOp = UseIter->GetOp();
if (MDIsGeneralPurposeReg(UseOp)) {
++RegOpCount;
UseHashValue = HashGlobalNameAndSSA(UseOp, UseIter->GetSSANum());
bool LocalUseName = this->BasicBlock->IsLocalName(UseOp);
if (LocalUseName) {
// Local name, find in basic block maps.
UseFGInfo = this->BasicBlock->GetUseFGInfo(UseHashValue);
}
else { // Global name, find in global maps.
UseFGInfo = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue);
}
UseSignMask = (FG_MASK_SIGNEDNESS_BITS & UseFGInfo.SignMiscInfo);
if (0 == UseSignMask) {
// Try to get signedness from DEF.
if (LocalUseName) {
// Local name, find in basic block maps.
DefFGInfo = this->BasicBlock->GetDefFGInfo(UseHashValue);
}
else { // Global name, find in global maps.
DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(UseHashValue);
}
DefSignMask = (FG_MASK_SIGNEDNESS_BITS & DefFGInfo.SignMiscInfo);
UnionMask |= DefSignMask;
}
else {
UnionMask |= UseSignMask;
}
}
}
if (2 > RegOpCount) { // only interested in binary arithmetic on two registers
UnionMask = 0;
}
return UnionMask;
} // SMPInstr::SignMaskUnionFromUseRegs()
// emit check annotations for signedness, overflow, truncation, etc.
void SMPInstr::EmitIntegerErrorAnnotations(FILE *InfoAnnotFile) {
set<DefOrUse, LessDefUse>::iterator UseIter, DefIter;
op_t UseOp, DefOp;
unsigned short UseWidthInfo, DefWidthInfo, SourceDefWidthInfo;
unsigned short UseSignInfo, DefSignInfo, SourceDefSignInfo;
unsigned short UseSignMask, DefSignMask, SourceDefSignMask;
struct FineGrainedInfo UseFGInfo, DefFGInfo, SourceDefFGInfo;
size_t UseBitWidth, DefBitWidth, UseMaxBitWidth, SourceDefBitWidth;
ea_t DefAddr;
int UseHashValue, DefHashValue;
bool OverflowOpcode = this->MDIsOverflowingOpcode();
bool UnderflowOpcode = this->MDIsUnderflowingOpcode();
bool CheckForOverflow;
bool UseIsSigned, DefIsSigned, UseIsUnsigned, DefIsUnsigned, SourceDefIsSigned, SourceDefIsUnsigned;
bool UseSignMixed, SourceDefSignMixed; // inconsistent signedness
bool UseFP = this->BasicBlock->GetFunc()->UsesFramePointer();
bool SignednessCheckEmitted = false;
// Case 1: Overflow on addition.
// Case 2: Underflow on subtraction.
if (OverflowOpcode || UnderflowOpcode) {
// If the flags register DEF is dead, we need a CHECK OVERFLOW/UNDERFLOW annotation.
DefOp = InitOp;
DefOp.type = o_reg;
DefOp.reg = MD_FLAGS_REG;
DefIter = this->FindDef(DefOp);
assert(DefIter != this->GetLastDef());
if (this->BasicBlock->IsDefDead(this->address, DefOp)) {
DefIter = this->GetFirstNonFlagsDef();
assert(DefIter != this->GetLastDef());
DefOp = DefIter->GetOp();
// Don't worry about stack space allocation instructions. The
// program will crash long before the stack pointer underflows
// below zero.
if (!((o_reg == DefOp.type) && DefOp.is_reg(MD_STACK_POINTER_REG))) {
if (o_reg == DefOp.type) {
DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum());
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
if (this->BasicBlock->IsLocalName(DefOp)) {
// Local name, find in basic block maps.
DefFGInfo = this->BasicBlock->GetDefFGInfo(DefHashValue);
}
else { // Global name, find in global maps.
DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue);
}
}
else if (MDIsStackAccessOpnd(DefOp, UseFP)) {
bool success = this->BasicBlock->GetFunc()->MDGetFGStackLocInfo(this->address, DefOp, DefFGInfo);
assert(success);
}
else { // non-stack memory address; we know nothing about it.
DefFGInfo.SignMiscInfo = 0;
DefFGInfo.SizeInfo = 0;
}
DefSignInfo = DefFGInfo.SignMiscInfo;
DefSignMask = DefSignInfo & FG_MASK_SIGNEDNESS_BITS;
DefWidthInfo = DefFGInfo.SizeInfo;
DefBitWidth = LargestBitWidthFromMask(DefWidthInfo);
if (0 == DefBitWidth) {
// Could happen for non-stack memory operands, for example.
DefBitWidth = MD_NORMAL_MACHINE_BITWIDTH;
}
if (DefSignMask == 0) {
// If no sign info from DEF, see if we can get it from the USE regs.
DefSignMask = this->SignMaskUnionFromUseRegs();
}
if (OverflowOpcode) {
qfprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %d ",
this->address, this->SMPcmd.size, SignednessStrings[DefSignMask], DefBitWidth,
MDGetRegName(DefOp), disasm);
}
else { // must be UnderflowOpcode
qfprintf(InfoAnnotFile, "%10x %6d INSTR CHECK UNDERFLOW %s %d ",
this->address, this->SMPcmd.size, SignednessStrings[DefSignMask], DefBitWidth,
MDGetRegName(DefOp), disasm);
}
AnnotPrintOperand(DefOp, InfoAnnotFile);
qfprintf(InfoAnnotFile, " ZZ %s \n", this->disasm);
}
} // end if flags reg is dead
} // end cases 1-2
// Case 3: Overflow on multiplication with upper bits discarded.
if (this->MDIsMultiply()) {
// There are four overflow sub-cases for x86: (A) the multiplication result
// can go into EDX:EAX for 32x32=>64 bit multiplication; (B) the result
// can go into DX:AX for 16x16=>32 bit; (C) the result can be in AX
// for 8x8=>16 bit; (D) see below. The latter case (C) will be detected most easily
// as a truncation in a later instruction, i.e. if only AL gets stored
// later, then we check the AH bits at that time for a truncation
// error. Because our SSA numbering lumps AL, AH, AX, and EAX into
// a single canonicalized register, we would have a hard time using
// SSA-based def-use chains to determine if AH is dead.
// For the other two sub-cases, the question is whether EDX becomes dead
// starting with the DEF of EDX in the multiply instruction.
// Case (D) is where the multiply instruction discards the upper bits
// of the multiply.
// Sub-cases A&B are detected by checking if EDX is dead, and if so, then
// emitting an annotation to check for the overflow flag. The x86 sets
// overflow and carry flags on multiplication instructions based on whether
// the result carries out of the lower half of the result to the upper half.
// Sub-case D is also detected using flags, but we don't need to check whether EDX
// is dead. We just need to detect that EDX is not in the DEF set in the
// first place. We have a private member flag for that case.
CheckForOverflow = false;
if (this->AreMultiplicationBitsDiscarded()) { // Sub-case D
CheckForOverflow = true;
assert(this->RTL.GetCount() > 0);
DefOp = this->RTL.GetRT(0)->GetLeftOperand();
DefIter = this->FindDef(DefOp);
assert(DefIter != this->GetLastDef());
DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum());
}
else {
// If the instruction were EDX:=EDX*foo, then it would be
// the multiplication bits discarded case and would not
// reach this else clause. Therefore, if we find EDX in
// the DEF set, it is holding upper result bits of the
// multiplication and we have the potential for sub-cases A&B
// but not sub-case C. So, we check to see if the DEF of EDX
// is dead.
DefOp = InitOp;
DefOp.type = o_reg;
DefOp.reg = R_dx;
DefIter = this->FindDef(DefOp);
if (DefIter != this->GetLastDef()) {
// We found DEF of EDX, so it is not AX:=AL*op8 sub-case C.
// Now, is DEF of EDX dead (i.e. no uses?)
CheckForOverflow = this->BasicBlock->IsDefDead(this->address, DefOp);
if (CheckForOverflow) {
DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum());
}
}
} // end if sub-case D else if sub-case A or B
if (CheckForOverflow) { // need an annotation
if (this->BasicBlock->IsLocalName(DefOp)) {
// Local name, find in basic block maps.
DefFGInfo = this->BasicBlock->GetDefFGInfo(DefHashValue);
}
else { // Global name, find in global maps.
DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue);
}
DefWidthInfo = DefFGInfo.SizeInfo;
DefBitWidth = LargestBitWidthFromMask(DefWidthInfo);
DefSignMask = (DefFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
if (DefSignMask == 0) {
// If no sign info from DEF, see if we can get it from the USE regs.
DefSignMask = this->SignMaskUnionFromUseRegs();
}
// Next two statements exclude the inconsistent sign case and the no sign info known case.
DefIsSigned = (FG_MASK_SIGNED == DefSignMask); // exact, not bit-mask-AND
DefIsUnsigned = (FG_MASK_UNSIGNED == DefSignMask); // exact, not bit-mask-AND
if (this->MDIsUnsignedArithmetic() || DefIsUnsigned) {
qfprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW UNSIGNED %d %s ZZ %s \n",
this->address, this->SMPcmd.size, DefBitWidth, RegNames[DefOp.reg], disasm);
}
else if (this->MDIsSignedArithmetic() || DefIsSigned) {
qfprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW SIGNED %d %s ZZ %s \n",
this->address, this->SMPcmd.size, DefBitWidth, RegNames[DefOp.reg], disasm);
}
else {
qfprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW UNKNOWNSIGN %d %s ZZ %s \n",
this->address, this->SMPcmd.size, DefBitWidth, RegNames[DefOp.reg], disasm);
}
} // end of case 3
// Case 4: Signedness error on move.
// Case 5: Truncation error on move.
UseOp = this->GetMoveSource();
if ((3 == this->OptType) && (o_reg == UseOp.type)) {
// Possibilities for a move: reg to reg, mem to reg, or reg to mem. If we load
// from memory into a register, we cannot track signedness in memory unless it
// is a stack location. In that case, we record the signedness in the stack
// map and transfer it to the reg DEF in SMPInstr.MDSetWidthSignInfo(). That
// determines the signedness of the reg DEF and it cannot be in conflict with
// the stack memory USE. The load from stack to reg also determines width
// of the stack operand and we cannot have a truncation. So, we can restrict
// our analysis of cases 4-5 to register-source move instructions, as we
// have done in the condition above.
//
// Similarly, we cannot detect a signedness conflict if the destination is a
// memory location that is not known to be a particular stack offset location.
//
// So, we only concern ourselves with signedness errors
// when the USE operand of the move is a register, and the destination is another
// register or a stack location.
//
// We can have a truncation error and a signedness error on a single instruction, so
// we group them into common code. For example, move the lower half of a 32-bit unsigned
// into a 16-bit signed destination. Upper bits set to 1 and discarded would be a
// truncation, and setting the sign bit of the 16-bit signed destination would be a
// signedness error.
//
// NOTE: Signedness errors are different from overflow and truncation errors. We
// can have incomplete knowledge about an instructions operands and still determine
// that truncation occurred. For example, if we do not know whether register EAX
// is signed or unsigned, we can still say that storing only AX is a truncation error
// if the upper half of EAX is a mixture of one and zero bits. If EAX is unsigned,
// we could be more specific and insist that the upper half be all zero bits; if EAX
// is signed, we could insist that the upper half of EAX be the sign-extension of AX.
// We can avoid false positives by only declaring a truncation error when the upper
// half of EAX is not all zero bits or all one bits. This approach allows a few
// potential false negatives. With signedness, if we don't know the signedness
// of one of the operands, we can only avoid false positives by doing no checks at
// all.
UseIter = this->FindUse(UseOp);
assert(UseIter != this->GetLastUse());
UseBitWidth = 8 * GetOpDataSize(UseOp);
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
// Now, the question is: Are we storing fewer bits than
// we were using in our computations in this DEF-USE chain?
// E.g. if we computed using 32 bits and then only store 16,
// we have potential truncation error. But if we computed
// using 16 bits all along, we have already checked for 16-bit
// overflows on arithmetic in the DU chain and there can be no
// truncation on this store.
op_t SearchOp = UseOp;
// Canonicalize sub-regs for searching DEFs and USEs.
SearchOp.reg = MDCanonicalizeSubReg(UseOp.reg);
SearchOp.dtyp = dt_dword;
UseHashValue = HashGlobalNameAndSSA(SearchOp, UseIter->GetSSANum());
if (this->BasicBlock->IsLocalName(SearchOp)) {
// Local name, find in basic block maps.
SourceDefFGInfo = this->BasicBlock->GetDefFGInfo(UseHashValue);
UseFGInfo = this->BasicBlock->GetUseFGInfo(UseHashValue);
}
else { // Global name, find in global maps.
SourceDefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(UseHashValue);
UseFGInfo = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue);
}
SourceDefWidthInfo = SourceDefFGInfo.SizeInfo;
UseWidthInfo = UseFGInfo.SizeInfo;
SourceDefBitWidth = LargestBitWidthFromMask(SourceDefWidthInfo);
UseMaxBitWidth = LargestBitWidthFromMask(UseWidthInfo);
UseSignMask = (UseFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
SourceDefSignInfo = SourceDefFGInfo.SignMiscInfo;
SourceDefSignMask = (SourceDefSignInfo & FG_MASK_SIGNEDNESS_BITS);
#if 1
// If we have no signedness info at all for the UseSignMask, but
// the SourceDefSignMask has info, then we want to use the
// SourceDefSignMask as our signedness info. This is because of
// simple instruction sequences in which no signedness can be inferred
// from the use, e.g.:
//
// imul eax,ebx ; eax := eax*ebx and eax&ebx are signed
// mov [ebp-8],al ; store al on stack
//
// If we don't know the signedness of stack location [ebp-8],
// then we will end up in the S->? case below and we will emit
// a CHECK TRUNCATION UNKNOWNSIGN annotation. This discards the
// knowledge we have that EAX is signed, from the IMUL opcode.
if (0 == UseSignMask) {
UseSignMask = SourceDefSignMask;
}
#endif
// Next four statements exclude the inconsistent sign case and the no sign info known case.
UseIsSigned = (FG_MASK_SIGNED == UseSignMask); // exact, not bit-mask-AND
UseIsUnsigned = (FG_MASK_UNSIGNED == UseSignMask); // exact, not bit-mask-AND
SourceDefIsSigned = (FG_MASK_SIGNED == SourceDefSignMask); // exact, not bit-mask-AND
SourceDefIsUnsigned = (FG_MASK_UNSIGNED == SourceDefSignMask); // exact, not bit-mask-AND
UseSignMixed = (FG_MASK_INCONSISTENT_SIGN == UseSignMask); // exclude uninit sign case
SourceDefSignMixed = (FG_MASK_INCONSISTENT_SIGN == SourceDefSignMask); // exclude uninit sign case
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
// Not only the CHECK SIGNEDNESS annotations depend on the signedness of the
// source and destination operands. The CHECK TRUNCATION annotations come
// in SIGNED, UNSIGNED, and UNKNOWNSIGN variants, so we need to get the
// signedness of the destination operand before we proceeed.
DefOp = this->RTL.GetRT(0)->GetLeftOperand(); // RTL must be dest := rhs
op_t DestSearchOp = DefOp;
bool StackDestination;
if (o_reg == DestSearchOp.type) {
StackDestination = false;
DestSearchOp.reg = MDCanonicalizeSubReg(DefOp.reg);
DestSearchOp.dtyp = dt_dword;
}
else if (!(MDIsStackAccessOpnd(DefOp, UseFP))) {
// If destination of move is not a register and is not
// a stack location, we cannot track its signedness and width.
return;
}
else {
StackDestination = true;
}
DefIter = this->FindDef(DestSearchOp);
if (StackDestination) {
// Fetch FG info from stack map.
bool success = this->GetBlock()->GetFunc()->MDGetFGStackLocInfo(this->address, DefOp, DefFGInfo);
assert(success);
}
else {
// Fetch FG info from register FG info maps.
DefHashValue = HashGlobalNameAndSSA(DestSearchOp, DefIter->GetSSANum());
if (this->BasicBlock->IsLocalName(DestSearchOp)) {
// Local name, find in basic block maps.
DefFGInfo = this->BasicBlock->GetDefFGInfo(DefHashValue);
}
else { // Global name, find in global maps.
DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue);
DefSignMask = (DefFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
// Next two statements exclude the inconsistent sign case and the no sign info known case.
DefIsSigned = (FG_MASK_SIGNED == DefSignMask); // exact, not bit-mask-AND
DefIsUnsigned = (FG_MASK_UNSIGNED == DefSignMask); // exact, not bit-mask-AND
// If we set the (source) DEF bit width to 0, it means we wanted to have the USEs determine
// the width. This happens on sign-extended and zero-extended loads. If we zero-extend
// a 16-bit value to 32 bits, then immediately store the lower 16 bits to a 16-bit location,
// then the upper bits cannot have any overflow info yet. But if we do 32-bit arithmetic
// on the zero-extended value, and then store the lower 16 bits, we need to check for
// truncation. So, the key is whether the value ever got used as a 32-bit value. If it
// did, check for truncation; if not, there is no need to check.
if ((SourceDefBitWidth > UseBitWidth)
|| ((SourceDefBitWidth == 0) && (UseMaxBitWidth > UseBitWidth))) {
// Original DEF (or subsequent USE) was wider than what we are storing now.
unsigned short SourceDefReg = SearchOp.reg;
unsigned short UseReg = UseOp.reg;
if (SourceDefBitWidth == 0) { // Convert for printing annotation.
SourceDefBitWidth = 8 * GetOpDataSize(SearchOp);
}
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
// OK, we need to check for possible truncation. But, how we check depends on the
// signedness combinations of the source and destination operands of the move.
// Each operand can be signed, unsigned, or of unknown sign (and we lump the
// inconsistent sign case into the unknown sign case). So, we have a set of 3x3=9
// possible combinations of signedness.
// Now we have the DefSignMask to compare to the UseSignMask. The nine possible
// combinations, and the annotations we want to emit for each, are shown below.
// S = SIGNED, U = UNSIGNED, and ? = unknown or inconsistent sign.
// S => U indicates a SIGNED being stored into an UNSIGNED, for example.
// Assume without loss of generality that register EAX is the source of
// all the move instructions, and that only subword register AX is being stored.
// We can perform all truncation and signedness checks on EAX just prior to
// the move instruction, which is cheaper than performing checks on the
// destination if the destination is in memory.
//
// U => U
// U => S
// S => U
// U => ?
// ? => U
//
// In these first five cases, EAX must be the zero-extension of AX else there is
// a truncation error. In the three cases in which the source (EAX/AX) is UNSIGNED,
// discarding upper bits that are not zero is obviously truncation. In the case
// of S => U, if the upper bits of EAX are not all zeroes, then we either have
// a large positive value of EAX that is being truncated, or EAX is negative and
// the lower bits will be misinterpreted in the unsigned destination. Finally,
// the ? => U case must be either U => U or S => U, and these two cases already
// share the demand that EAX be the zero-extension of AX. So, these five cases
// will receive the annotation: CHECK TRUNCATION UNSIGNED 32 EAX 16 AX which
// means that EAX is tested against AX to see if it is the 32-bit zero-extension
// of 16-bit reg AX.
// In the U => S case, we can have a signedness error as well as truncation. Even
// if the truncation check passes (all upper half bits of EAX are zero), the top
// bit of AX might be 1, and this will be misinterpreted as a sign bit in the
// destination. So, this case receives a second annotation: CHECK SIGNEDNESS SIGNED 16 AX.
// In the two cases that involve signedness uncertainty, there are possible signedness
// errors that we are not checking ar tun-time, because we do not have enough information
// to perform the checks without generating many more false positives than true positives.
// As a result, false negatives on signedness can occur.
//
// On to more of the 9 combinations:
//
// S => S
//
// In this case, EAX must be the sign-extension of AX. Because the destination is also
// signed, nothing is lost if the sign-extension bits (all zeroes or all ones) are dropped.
// We emit a CHECK TRUNCATION SIGNED 32 EAX 16 AX annotation to test EAX == sign-extended AX.
//
// S => ?
// ? => S
// ? => ?
//
// These final three cases all involve at least one operand of unknown signedness, and no
// operands that are known to be unsigned. In each case, there are two possibilities:
// either EAX must be the sign-extension of AX, or EAX must be the zero-extension of AX.
// Because of the uncertainty that is represented by the question marks, we are not sure
// which of these two cases we are dealing with. However, rather than just give up and
// perform no run-time checks (to avoid false positives), we can still perform a run-time
// check that will catch (perhaps most) true positives while causing no false positives.
// We can insist that EAX must be EITHER the sign-extension or the zero-extension of AX.
// To be neither type of extension of AX implies that some sort of truncation is happening.
// So, we emit a CHECK TRUNCATION UNKNOWNSIGN 32 EAX 16 AX annotation, and the Strata
// instrumentation will check for either EAX == sign-extended AX or EAX == zero-extended AX
// being true. If neither is true, we raise a true positive alert. False negatives on
// signedness errors are the result of the uncertainty, but all truncations are detected
// for all nine cases.
if (DefIsUnsigned || UseIsUnsigned) {
// First five cases above: any UNSIGNED operand leads to CHECK TRUNCATION UNSIGNED annotation.
qfprintf(InfoAnnotFile, "%10x %6d INSTR CHECK TRUNCATION UNSIGNED %d %s %d %s ZZ %s \n",
this->address, this->SMPcmd.size, SourceDefBitWidth,
MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp), disasm);
if (UseIsUnsigned && DefIsSigned) {
qfprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %d %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
else if (DefIsSigned && UseIsSigned) {