Commit a98690d5 authored by Daniel Liew's avatar Daniel Liew
Browse files

Implemented runtime check for overshift (controllable with --check-overshift

command line argument).

Overshift is where a Shl, AShr or LShr has a shift width greater
than the bit width of the first operand. This is undefined behaviour
in LLVM so we report this as an error.

Conflicts:

	lib/Module/KModule.cpp
	tools/klee/main.cpp
parent 5e679697
......@@ -51,11 +51,13 @@ public:
std::string LibraryDir;
bool Optimize;
bool CheckDivZero;
bool CheckOvershift;
ModuleOptions(const std::string& _LibraryDir,
bool _Optimize, bool _CheckDivZero)
bool _Optimize, bool _CheckDivZero,
bool _CheckOvershift)
: LibraryDir(_LibraryDir), Optimize(_Optimize),
CheckDivZero(_CheckDivZero) {}
CheckDivZero(_CheckDivZero), CheckOvershift(_CheckOvershift) {}
};
enum LogType
......
......@@ -1399,7 +1399,7 @@ void SpecialFunctionHandler::handleOclCompile(ExecutionState &state,
Path.appendComponent("libkleeRuntimeCLKernel.bca");
Mod = klee::linkWithLibrary(Mod, Path.c_str());
unsigned moduleId = executor.addModule(Mod, Interpreter::ModuleOptions(LibraryDir.c_str(), false, true));
unsigned moduleId = executor.addModule(Mod, Interpreter::ModuleOptions(LibraryDir.c_str(), false, true,true));
executor.initializeGlobals(state, moduleId);
executor.bindModuleConstants(moduleId);
......
......@@ -72,3 +72,62 @@ bool DivCheckPass::runOnModule(Module &M) {
}
return moduleChanged;
}
char OvershiftCheckPass::ID;
bool OvershiftCheckPass::runOnModule(Module &M) {
Function *overshiftCheckFunction = 0;
bool moduleChanged = false;
for (Module::iterator f = M.begin(), fe = M.end(); f != fe; ++f) {
for (Function::iterator b = f->begin(), be = f->end(); b != be; ++b) {
for (BasicBlock::iterator i = b->begin(), ie = b->end(); i != ie; ++i) {
if (BinaryOperator* binOp = dyn_cast<BinaryOperator>(i)) {
// find all shift instructions
Instruction::BinaryOps opcode = binOp->getOpcode();
if (opcode == Instruction::Shl ||
opcode == Instruction::LShr ||
opcode == Instruction::AShr ) {
std::vector<llvm::Value*> args;
// Determine bit width of first operand
uint64_t bitWidth=i->getOperand(0)->getType()->getScalarSizeInBits();
ConstantInt *bitWidthC = ConstantInt::get(Type::getInt64Ty(getGlobalContext()),bitWidth,false);
args.push_back(bitWidthC);
CastInst *shift =
CastInst::CreateIntegerCast(i->getOperand(1),
Type::getInt64Ty(getGlobalContext()),
false, /* sign doesn't matter */
"int_cast_to_i64",
i);
args.push_back(shift);
// Lazily bind the function to avoid always importing it.
if (!overshiftCheckFunction) {
Constant *fc = M.getOrInsertFunction("klee_overshift_check",
Type::getVoidTy(getGlobalContext()),
Type::getInt64Ty(getGlobalContext()),
Type::getInt64Ty(getGlobalContext()),
NULL);
overshiftCheckFunction = cast<Function>(fc);
}
// Inject CallInstr to check if overshifting possible
#if LLVM_VERSION_CODE >= LLVM_VERSION(3, 0)
CallInst::Create(overshiftCheckFunction, args, "", &*i);
#else
CallInst::Create(overshiftCheckFunction, args.begin(), args.end(), "", &*i);
#endif
moduleChanged = true;
}
}
}
}
}
return moduleChanged;
}
......@@ -423,6 +423,7 @@ void KModule::prepare(const Interpreter::ModuleOptions &opts,
pm.add(new RaiseAsmPass());
if (opts.CheckDivZero) pm.add(new DivCheckPass());
pm.add(createLowerAtomicPass()); // Lower llvm.atomic.*
if (opts.CheckOvershift) pm.add(new OvershiftCheckPass());
if (InstrumentSIMD)
pm.add(new SIMDInstrumentationPass());
// FIXME: This false here is to work around a bug in
......@@ -474,6 +475,8 @@ void KModule::prepare(const Interpreter::ModuleOptions &opts,
*/
if (opts.CheckDivZero)
inlineChecks(module, "klee_div_zero_check");
if (opts.CheckOvershift)
inlineChecks(module, "klee_overshift_check");
// Needs to happen after linking (since ctors/dtors can be modified)
......
......@@ -151,6 +151,31 @@ public:
virtual bool runOnModule(llvm::Module &M);
};
/// This pass injects checks to check for overshifting.
///
/// Overshifting is where a Shl, LShr or AShr is performed
/// where the shift amount is greater than width of the bitvector
/// being shifted.
/// In LLVM (and in C/C++) this undefined behaviour!
///
/// Example:
/// \code
/// unsigned char x=15;
/// x << 4 ; // Defined behaviour
/// x << 8 ; // Undefined behaviour
/// x << 255 ; // Undefined behaviour
/// \endcode
class OvershiftCheckPass : public llvm::ModulePass {
static char ID;
public:
#if LLVM_VERSION_CODE < LLVM_VERSION(2, 8)
OvershiftCheckPass(): ModulePass((intptr_t) &ID) {}
#else
OvershiftCheckPass(): ModulePass(ID) {}
#endif
virtual bool runOnModule(llvm::Module &M);
};
/// LowerSwitchPass - Replace all SwitchInst instructions with chained branch
/// instructions. Note that this cannot be a BasicBlock pass because it
/// modifies the CFG!
......
//===-- klee_overshift_check.c ---------------------------------------------===//
//
// The KLEE Symbolic Virtual Machine
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <klee/klee.h>
/* This instrumentation call is used to check for overshifting.
* If we do try to do x << y or x >> y
* where
* bitWidth = sizeof(x)*8
* shift = y
*
* then we can detect overshifting (which has undefined behaviour).
*/
void klee_overshift_check(unsigned long long bitWidth, unsigned long long shift) {
if (shift >= bitWidth) {
/* Maybe we shouldn't throw an error because
* overshifting can be non-fatal? Perhaps
* we should generate a test case but carry
* on executing the state with a warning?
*/
klee_report_error("IGNORED", 0 /*Ignored */, "overshift error", "overshift.err");
}
}
// RUN: %llvmgcc %s -emit-llvm -g -O0 -c -o %t.bc
// RUN: %klee -check-overshift %t.bc 2> %t.log
// RUN: grep -c "overshift error" %t.log
// RUN: grep -c "OvershiftCheck.c:19: overshift error" %t.log
// RUN: grep -c "OvershiftCheck.c:23: overshift error" %t.log
/* This test checks that two consecutive potential overshifts
* are reported as errors.
*/
int main()
{
unsigned int x=15;
unsigned int y;
unsigned int z;
volatile unsigned int result;
/* Overshift if y>= sizeof(x) */
klee_make_symbolic(&y,sizeof(y),"shift_amount1");
result = x << y;
/* Overshift is z>= sizeof(x) */
klee_make_symbolic(&z,sizeof(z),"shift_amount2");
result = x >> z;
return 0;
}
......@@ -80,6 +80,12 @@ namespace {
cl::desc("Inject checks for division-by-zero"),
cl::init(true));
cl::opt<bool>
CheckOvershift("check-overshift",
cl::desc("Inject checks for overshift"),
cl::init(true));
// this is a fake entry, its automagically handled
cl::list<std::string>
ReadArgsFilesFake("read-args",
......@@ -341,7 +347,8 @@ int main(int argc, char **argv, char **envp) {
klee::Interpreter::ModuleOptions mOpts(libraryPath.c_str(),
/*Optimize=*/OptimizeModule,
/*CheckDivZero=*/CheckDivZero);
/*CheckDivZero=*/CheckDivZero,
/*CheckOvershift=*/CheckOvershift);
Interpreter::InterpreterOptions IOpts;
IOpts.MakeConcreteSymbolic = MakeConcreteSymbolic;
......
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