Commit 253dc8a8 by Qining Lu

Add constant blinding/pooling option for X8632 code translation.

GOAL: The goal is to remove the ability of an attacker to control immediates emitted into the text section. OPTION: The option -randomize-pool-immediates is set to none by default (-randomize-pool-immediates=none). To turn on constant blinding, set -randomize-pool-immediates=randomize; to turn on constant pooling, use -randomize-pool-immediates=pool. Not all constant integers in the input pexe file will be randomized or pooled. The signed representation of a candidate constant integer must be between -randomizeOrPoolImmediatesThreshold/2 and +randomizeOrPoolImmediatesThreshold/2. This threshold value can be set with command line option: "-randomize-pool-threshold". By default this threshold is set to 0xffff. The constants introduced by instruction lowering (e.g. constants in shifting, masking) and argument lowering are not blinded in this way. The mask used for sandboxing is not affected either. APPROACH: We use GAS syntax in these examples. Constant blinding for immediates: Original: add 0x1234, eax After: mov 0x1234+cookie, temp_reg lea -cookie[temp_reg], temp_reg add temp_reg, eax Constant blinding for memory addressing offsets: Original: mov 0x1234(eax, esi, 1), ebx After: lea 0x1234+cookie(eax), temp_reg mov -cookie(temp_reg, esi, 1), ebx We use "lea" here because it won't affect flag register, so it is safer to transform immediate-involved instructions. Constant pooling for immediates: Original: add 0x1234, eax After: mov [memory label of 0x1234], temp_reg add temp_reg, eax Constant pooling for addressing offsets: Original: mov 0x1234, eax After: mov [memory label of 0x1234], temp_reg mov temp_reg, eax Note in both cases, temp_reg may be assigned with "eax" here, depends on the liveness analysis. So this approach may not require extra register. IMPLEMENTATION: Processing: TargetX8632::randomizeOrPoolImmediate(Constant *Immediate, int32_t RegNum); TargetX8632::randomizeOrPoolImmediate(OperandX8632Mem *Memoperand, int32_t RegNum); Checking eligibility: ConstantInteger32::shouldBeRandomizedOrPooled(const GlobalContext *Ctx); ISSUES: 1. bool Ice::TargetX8632::RandomizationPoolingPaused is used to guard some translation phases to disable constant blinding/pooling temporally. Helper class BoolFlagSaver is added to latch the value of RandomizationPoolingPaused. Known phases that need to be guarded are: doLoadOpt() and advancedPhiLowering(). However, during advancedPhiLowering(), if the destination variable has a physical register allocated, constant blinding and pooling are allowed. Stopping blinding/pooling for doLoadOpt() won't hurt our randomization or pooling as the optimized addressing operands will be processed again in genCode() phase. 2. i8 and i16 constants are collected with different constant pools now, instead of sharing a same constant pool with i32 constants. This requires emitting two more pools during constants lowering, hence create two more read-only data sections in the resulting ELF and ASM. No runtime issues have been observed so far. BUG= R=stichnot@chromium.org Review URL: https://codereview.chromium.org/1185703004.
parent b0a8c24e
......@@ -106,7 +106,7 @@ cl::opt<bool>
// therefore the tests would need to be changed.
cl::opt<unsigned long long>
RandomSeed("sz-seed", cl::desc("Seed the random number generator"),
cl::init(time(0)));
cl::init(1));
cl::opt<bool> ShouldDoNopInsertion("nop-insertion",
cl::desc("Randomly insert NOPs"),
......@@ -258,6 +258,27 @@ cl::opt<std::string> OutputFilename("o", cl::desc("Override output filename"),
Ice::IceString AppName;
// Define the command line options for immediates pooling and randomization
cl::opt<Ice::RandomizeAndPoolImmediatesEnum> RandomizeAndPoolImmediatesOption(
"randomize-pool-immediates",
cl::desc("Randomize or pooling the representation of immediates"),
cl::init(Ice::RPI_None),
cl::values(clEnumValN(Ice::RPI_None, "none",
"Do not randomize or pooling immediates (default)"),
clEnumValN(Ice::RPI_Randomize, "randomize",
"Turn on immediate constants blinding"),
clEnumValN(Ice::RPI_Pool, "pool",
"Turn on immediate constants pooling"),
clEnumValEnd));
// Command line option for x86 immediate integer randomization/pooling
// threshold. Immediates whose representation are between:
// -RandomizeAndPoolImmediatesThreshold/2 and
// +RandomizeAndPoolImmediatesThreshold/2 will be randomized or pooled.
cl::opt<uint32_t> RandomizeAndPoolImmediatesThreshold(
"randomize-pool-threshold",
cl::desc("The threshold for immediates randomization and pooling"),
cl::init(0xffff));
} // end of anonymous namespace
namespace Ice {
......@@ -350,6 +371,12 @@ void ClFlags::getParsedClFlags(ClFlags &OutFlags) {
OutFlags.setMaxNopsPerInstruction(::MaxNopsPerInstruction);
OutFlags.setNopProbabilityAsPercentage(::NopProbabilityAsPercentage);
OutFlags.setVerbose(VMask);
// set for immediates randomization or pooling option
OutFlags.setRandomizeAndPoolImmediatesOption(
::RandomizeAndPoolImmediatesOption);
OutFlags.setRandomizeAndPoolImmediatesThreshold(
::RandomizeAndPoolImmediatesThreshold);
}
void ClFlags::getParsedClFlagsExtra(ClFlagsExtra &OutFlagsExtra) {
......
......@@ -135,6 +135,22 @@ public:
}
void setVerbose(VerboseMask NewValue) { VMask = NewValue; }
void
setRandomizeAndPoolImmediatesOption(RandomizeAndPoolImmediatesEnum Option) {
RandomizeAndPoolImmediatesOption = Option;
}
RandomizeAndPoolImmediatesEnum getRandomizeAndPoolImmediatesOption() const {
return RandomizeAndPoolImmediatesOption;
}
void setRandomizeAndPoolImmediatesThreshold(uint32_t Threshold) {
RandomizeAndPoolImmediatesThreshold = Threshold;
}
uint32_t getRandomizeAndPoolImmediatesThreshold() const {
return RandomizeAndPoolImmediatesThreshold;
}
// IceString accessors.
const IceString &getDefaultFunctionPrefix() const {
......@@ -211,6 +227,10 @@ private:
IceString TranslateOnly;
IceString VerboseFocusOn;
// Immediates Randomization and Pooling options
RandomizeAndPoolImmediatesEnum RandomizeAndPoolImmediatesOption;
uint32_t RandomizeAndPoolImmediatesThreshold;
size_t NumTranslationThreads; // 0 means completely sequential
uint64_t RandomSeed;
};
......
......@@ -225,6 +225,9 @@ llvm::iterator_range<typename T::reverse_iterator> reverse_range(T &Container) {
return llvm::make_range(Container.rbegin(), Container.rend());
}
// Options for pooling and randomization of immediates
enum RandomizeAndPoolImmediatesEnum { RPI_None, RPI_Randomize, RPI_Pool };
} // end of namespace Ice
#endif // SUBZERO_SRC_ICEDEFS_H
......@@ -490,12 +490,11 @@ template <typename ConstType> void ELFObjectWriter::writeConstantPool(Type Ty) {
size_t EntSize = typeWidthInBytes(Ty);
char Buf[20];
SizeT WriteAmt = std::min(EntSize, llvm::array_lengthof(Buf));
// Check that we write the full PrimType.
assert(WriteAmt == EntSize);
// Assume that writing WriteAmt bytes at a time allows us to avoid aligning
// between entries.
assert(WriteAmt % Align == 0);
// Check that we write the full PrimType.
assert(WriteAmt == sizeof(typename ConstType::PrimType));
const Elf64_Xword ShFlags = SHF_ALLOC | SHF_MERGE;
std::string SecBuffer;
llvm::raw_string_ostream SecStrBuf(SecBuffer);
......@@ -511,6 +510,8 @@ template <typename ConstType> void ELFObjectWriter::writeConstantPool(Type Ty) {
// Write the data.
for (Constant *C : Pool) {
if (!C->getShouldBePooled())
continue;
auto Const = llvm::cast<ConstType>(C);
std::string SymBuffer;
llvm::raw_string_ostream SymStrBuf(SymBuffer);
......@@ -536,6 +537,8 @@ template void ELFObjectWriter::writeConstantPool<ConstantFloat>(Type Ty);
template void ELFObjectWriter::writeConstantPool<ConstantDouble>(Type Ty);
template void ELFObjectWriter::writeConstantPool<ConstantInteger32>(Type Ty);
void ELFObjectWriter::writeAllRelocationSections() {
writeRelocationSections(RelTextSections);
writeRelocationSections(RelDataSections);
......
......@@ -225,7 +225,8 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, Ostream *OsError,
EmitQ(/*Sequential=*/Flags.isSequential()),
DataLowering(TargetDataLowering::createLowering(this)),
HasSeenCode(false),
ProfileBlockInfoVarDecl(VariableDeclaration::create()) {
ProfileBlockInfoVarDecl(VariableDeclaration::create()),
RandomizationCookie(0) {
assert(OsDump && "OsDump is not defined for GlobalContext");
assert(OsEmit && "OsEmit is not defined for GlobalContext");
assert(OsError && "OsError is not defined for GlobalContext");
......@@ -265,6 +266,14 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, Ostream *OsError,
ProfileBlockInfoVarDecl->setName("__Sz_block_profile_info");
ProfileBlockInfoVarDecl->setSuppressMangling();
ProfileBlockInfoVarDecl->setLinkage(llvm::GlobalValue::ExternalLinkage);
// Initialize the randomization cookie for constant blinding only if constant
// blinding or pooling is turned on.
// TODO(stichnot): Using RNG for constant blinding will affect the random
// number to be used in nop-insertion and randomize-regalloc.
if (Flags.getRandomizeAndPoolImmediatesOption() != RPI_None)
RandomizationCookie =
(uint32_t)RNG.next((uint64_t)std::numeric_limits<uint32_t>::max + 1);
}
void GlobalContext::translateFunctions() {
......@@ -753,7 +762,9 @@ ConstantList GlobalContext::getConstantPool(Type Ty) {
switch (Ty) {
case IceType_i1:
case IceType_i8:
return getConstPool()->Integers8.getConstantPool();
case IceType_i16:
return getConstPool()->Integers16.getConstantPool();
case IceType_i32:
return getConstPool()->Integers32.getConstantPool();
case IceType_i64:
......
......@@ -72,7 +72,8 @@ class GlobalContext {
X("Regs Saved ", RegsSaved) \
X("Frame Bytes ", FrameByte) \
X("Spills ", NumSpills) \
X("Fills ", NumFills)
X("Fills ", NumFills) \
X("R/P Imms ", NumRPImms)
//#define X(str, tag)
public:
......@@ -263,6 +264,15 @@ public:
Tls->StatsCumulative.update(CodeStats::CS_NumFills);
}
// Number of Randomized or Pooled Immediates
void statsUpdateRPImms() {
if (!getFlags().getDumpStats())
return;
ThreadContext *Tls = ICE_TLS_GET_FIELD(TLS);
Tls->StatsFunction.update(CodeStats::CS_NumRPImms);
Tls->StatsCumulative.update(CodeStats::CS_NumRPImms);
}
// These are predefined TimerStackIdT values.
enum TimerStackKind { TSK_Default = 0, TSK_Funcs, TSK_Num };
......@@ -400,6 +410,10 @@ public:
return Match.empty() || Match == SymbolName;
}
// Return the randomization cookie for diversification.
// Initialize the cookie if necessary
uint32_t getRandomizationCookie() const { return RandomizationCookie; }
private:
// Try to ensure mutexes are allocated on separate cache lines.
......@@ -495,6 +509,11 @@ private:
typedef llvm::SmallVector<char, 32> ManglerVector;
void incrementSubstitutions(ManglerVector &OldName) const;
// Randomization Cookie
// Managed by getRandomizationCookie()
GlobalLockType RandomizationCookieLock;
uint32_t RandomizationCookie;
public:
static void TlsInit() { ICE_TLS_INIT_FIELD(TLS); }
};
......
......@@ -92,7 +92,7 @@ OperandX8632Mem::OperandX8632Mem(Cfg *Func, Type Ty, Variable *Base,
Constant *Offset, Variable *Index,
uint16_t Shift, SegmentRegisters SegmentReg)
: OperandX8632(kMem, Ty), Base(Base), Offset(Offset), Index(Index),
Shift(Shift), SegmentReg(SegmentReg) {
Shift(Shift), SegmentReg(SegmentReg), Randomized(false) {
assert(Shift <= 3);
Vars = nullptr;
NumVars = 0;
......
......@@ -86,6 +86,10 @@ public:
return Operand->getKind() == static_cast<OperandKind>(kMem);
}
void setRandomized(bool R) { Randomized = R; }
bool getRandomized() const { return Randomized; }
private:
OperandX8632Mem(Cfg *Func, Type Ty, Variable *Base, Constant *Offset,
Variable *Index, uint16_t Shift, SegmentRegisters SegmentReg);
......@@ -95,6 +99,10 @@ private:
Variable *Index;
uint16_t Shift;
SegmentRegisters SegmentReg : 16;
// A flag to show if this memory operand is a randomized one.
// Randomized memory operands are generated in
// TargetX8632::randomizeOrPoolImmediate()
bool Randomized;
};
// VariableSplit is a way to treat an f64 memory location as a pair
......
......@@ -491,4 +491,22 @@ Ostream &operator<<(Ostream &Str, const RegWeight &W) {
return Str;
}
// =========== Immediate Randomization and Pooling routines ==============
// Specialization of the template member function for ConstantInteger32
// TODO(stichnot): try to move this specialization into a target-specific
// file.
template <>
bool ConstantInteger32::shouldBeRandomizedOrPooled(const GlobalContext *Ctx) {
uint32_t Threshold = Ctx->getFlags().getRandomizeAndPoolImmediatesThreshold();
if (Ctx->getFlags().getRandomizeAndPoolImmediatesOption() == RPI_None)
return false;
if (getType() != IceType_i32 && getType() != IceType_i16 &&
getType() != IceType_i8)
return false;
// The Following checks if the signed representation of Value is between
// -Threshold/2 and +Threshold/2
bool largerThanThreshold = Threshold / 2 + Value >= Threshold;
return largerThanThreshold;
}
} // end of namespace Ice
......@@ -117,9 +117,21 @@ public:
return Kind >= kConst_Base && Kind <= kConst_Num;
}
// Judge if this given immediate should be randomized or pooled
// By default should return false, only constant integers should
// truly go through this method.
virtual bool shouldBeRandomizedOrPooled(const GlobalContext *Ctx) {
(void)Ctx;
return false;
}
void setShouldBePooled(bool R) { shouldBePooled = R; }
bool getShouldBePooled() const { return shouldBePooled; }
protected:
Constant(OperandKind Kind, Type Ty, uint32_t PoolEntryID)
: Operand(Kind, Ty), PoolEntryID(PoolEntryID) {
: Operand(Kind, Ty), PoolEntryID(PoolEntryID), shouldBePooled(false) {
Vars = nullptr;
NumVars = 0;
}
......@@ -128,6 +140,9 @@ protected:
// within its constant pool. It is used for building the constant
// pool in the object code and for referencing its entries.
const uint32_t PoolEntryID;
// Whether we should pool this constant. Usually Float/Double and pooled
// Integers should be flagged true.
bool shouldBePooled;
};
// ConstantPrimitive<> wraps a primitive type.
......@@ -160,6 +175,11 @@ public:
return Operand->getKind() == K;
}
virtual bool shouldBeRandomizedOrPooled(const GlobalContext *Ctx) override {
(void)Ctx;
return false;
}
private:
ConstantPrimitive(Type Ty, PrimType Value, uint32_t PoolEntryID)
: Constant(K, Ty, PoolEntryID), Value(Value) {}
......@@ -182,6 +202,10 @@ inline void ConstantInteger32::dump(const Cfg *, Ostream &Str) const {
Str << static_cast<int32_t>(getValue());
}
// Specialization of the template member function for ConstantInteger32
template <>
bool ConstantInteger32::shouldBeRandomizedOrPooled(const GlobalContext *Ctx);
template <>
inline void ConstantInteger64::dump(const Cfg *, Ostream &Str) const {
if (!ALLOW_DUMP)
......
......@@ -596,6 +596,14 @@ protected:
VarList PhysicalRegisters[IceType_NUM];
static IceString RegNames[];
// Randomize a given immediate operand
Operand *randomizeOrPoolImmediate(Constant *Immediate,
int32_t RegNum = Variable::NoRegister);
OperandX8632Mem *
randomizeOrPoolImmediate(OperandX8632Mem *MemOperand,
int32_t RegNum = Variable::NoRegister);
bool RandomizationPoolingPaused;
private:
~TargetX8632() override {}
BoolFolding FoldingInfo;
......
; This is a smoke test of constant blinding and constant pooling.
; RUN: %p2i -i %s --filetype=obj --disassemble --args -O2 \
; RUN: -sz-seed=1 -randomize-pool-immediates=randomize \
; RUN: -randomize-pool-threshold=0x1 \
; RUN: | FileCheck %s --check-prefix=BLINDINGO2
; RUN: %p2i -i %s --filetype=obj --disassemble --args -Om1 \
; RUN: -sz-seed=1 -randomize-pool-immediates=randomize \
; RUN: -randomize-pool-threshold=0x1 \
; RUN: | FileCheck %s --check-prefix=BLINDINGOM1
; RUN: %p2i -i %s --filetype=obj --disassemble --args -O2 \
; RUN: -sz-seed=1 -randomize-pool-immediates=pool \
; RUN: -randomize-pool-threshold=0x1 \
; RUN: | FileCheck %s --check-prefix=POOLING
; RUN: %p2i -i %s --filetype=obj --disassemble --args -Om1 \
; RUN: -sz-seed=1 -randomize-pool-immediates=pool \
; RUN: -randomize-pool-threshold=0x1 \
; RUN: | FileCheck %s --check-prefix=POOLING
define i32 @add_arg_plus_200000(i32 %arg) {
entry:
%res = add i32 200000, %arg
ret i32 %res
; BLINDINGO2-LABEL: add_arg_plus_200000
; BLINDINGO2: mov [[REG:e[a-z]*]],0x34ee7
; BLINDINGO2-NEXT: lea [[REG]],{{[[]}}[[REG]]-0x41a7{{[]]}}
; BLINDINGOM1-LABEL: add_arg_plus_200000
; BLINDINGOM1: mov [[REG:e[a-z]*]],0x34ee7
; BLINDINGOM1-NEXT: lea [[REG]],{{[[]}}[[REG]]-0x41a7{{[]]}}
; POOLING-LABEL: add_arg_plus_200000
; POOLING: mov e{{[a-z]*}},DWORD PTR ds:0x0 {{[0-9a-f]*}}: R_386_32 .L$i32${{[0-9]*}}
}
define float @load_arg_plus_200000(float* %arg) {
entry:
%arg.int = ptrtoint float* %arg to i32
%addr.int = add i32 %arg.int, 200000
%addr.ptr = inttoptr i32 %addr.int to float*
%addr.load = load float, float* %addr.ptr, align 4
ret float %addr.load
; BLINDINGO2-LABEL: load_arg_plus_200000
; BLINDINGO2: lea [[REG:e[a-z]*]],{{[[]}}{{e[a-z]*}}+0x34ee7{{[]]}}
; BLINDINGOM1-LABEL: load_arg_plus_200000
; BLINDINGOM1: lea [[REG:e[a-z]*]],{{[[]}}{{e[a-z]*}}-0x41a7{{[]]}}
; POOLING-LABEL: load_arg_plus_200000
; POOLING: mov e{{[a-z]*}},DWORD PTR ds:0x0 {{[0-9a-f]*}}: R_386_32 .L$i32${{[0-9]*}}
}
define i64 @add_arg_plus_64bits(i32 %arg) {
entry:
%0 = sext i32 %arg to i64
%res = add i64 90000000000, %0
ret i64 %res
; BLINDINGO2-LABEL: add_arg_plus_64bits
; BLINDINGO2: sar [[RHI:e[a-z]*]],0x1f
; BLINDINGO2: mov [[RLO:e[a-z]*]],0xf46b45a7
; BLINDINGO2-NEXT: lea [[RLO]],{{[[]}}[[RLO]]-0x41a7{{[]]}}
; BLINDINGOM1-LABEL: add_arg_plus_64bits
; BLINDINGOM1: sar [[RHI:e[a-z]*]],0x1f
; BLINDINGOM1: mov [[RLO:e[a-z]*]],0xf46b45a7
; BLINDINGOM1-NEXT: lea [[RLO]],{{[[]}}[[RLO]]-0x41a7{{[]]}}
; POOLING-LABEL: add_arg_plus_64bits
; POOLING: mov e{{[a-z]*}},DWORD PTR ds:0x0 {{[0-9a-f]*}}: R_386_32 .L$i32${{[0-9]*}}
}
define i64 @load_arg_plus_64bits(i64* %arg) {
entry:
%arg.int = ptrtoint i64* %arg to i32
%arg.new = add i32 %arg.int, 90000
%arg.ptr = inttoptr i32 %arg.new to i64*
%arg.load = load i64, i64* %arg.ptr, align 1
ret i64 %arg.load
; BLINDINGO2-LABEL: load_arg_plus_64bits
; BLINDINGO2: lea e{{[a-z]*}},{{[[]}}e{{[a-z]*}}+0x1a137{{[]]}}
; BLINDINGO2: mov e{{[a-z]*}},DWORD PTR {{[[]}}e{{[a-z]*}}-0x41a7{{[]]}}
; BLINDINGOM1-LABEL: load_arg_plus_64bits
; BLINDINGOM1: mov e{{[a-z]*}},0x1a137
; BLINDINGOM1-NEXT: lea e{{[a-z]*}},{{[[]}}e{{[a-z]*}}-0x41a7{{[]]}}
; POOLING-LABEL: load_arg_plus_64bits
; POOLING: mov e{{[a-z]x}},DWORD PTR ds:0x0 {{[0-9a-f]*}}: R_386_32 .L$i32${{[0-9]*}}
}
define internal i32 @add_const_8bits(i32 %a) {
entry:
%a_8 = trunc i32 %a to i8
%add = add i8 %a_8, 123
%ret = zext i8 %add to i32
ret i32 %ret
; BLINDINGO2-LABEL: add_const_8bits
; BLINDINGO2: mov e{{[a-z]*}},0x4222
; BLINDINGO2-NEXT: e{{[a-z]*}},{{[[]}}e{{[a-z]*}}-0x41a7{{[]]}}
; BLINDINGOM1-LABEL: add_const_8bits
; BLINDINGOM1: mov e{{[a-z]*}},0x4222
; BLINDINGOM1-NEXT: e{{[a-z]*}},{{[[]}}e{{[a-z]*}}-0x41a7{{[]]}}
; POOLING-LABEL: add_const_8bits
; POOLING: mov {{[a-z]l}},BYTE PTR ds:0x0 {{[0-9a-f]*}}: R_386_32 .L$i8${{[0-9]*}}
}
define internal i32 @add_const_16bits(i32 %a) {
entry:
%a_16 = trunc i32 %a to i16
%add = add i16 %a_16, 32766
%ret = zext i16 %add to i32
ret i32 %ret
; BLINDINGO2-LABEL: add_const_16bits
; BLINDINGO2: mov e{{[a-z]*}},0xc1a5
; BLINDINGO2-NEXT: e{{[a-z]*}},{{[[]}}e{{[a-z]*}}-0x41a7{{[]]}}
; BLINDINGOM1-LABEL: add_const_16bits
; BLINDINGOM1: mov e{{[a-z]*}},0xc1a5
; BLINDINGOM1-NEXT: e{{[a-z]*}},{{[[]}}e{{[a-z]*}}-0x41a7{{[]]}}
; POOLING-LABEL: add_const_16bits
; POOLING: mov {{[a-z]x}},WORD PTR ds:0x0 {{[0-9a-f]*}}: R_386_32 .L$i16${{[0-9]*}}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment