blob: f190e754178963327d70ddd87eb55f07dae1388a [file] [log] [blame]
///=== SoftBound/SoftBoundCETS.cpp --*- C++ -*=====///
// Pointer based Spatial and Temporal Memory Safety Pass
//Copyright (c) 2011 Santosh Nagarakatte, Milo M. K. Martin. All rights reserved.
// Developed by: Santosh Nagarakatte, Milo M.K. Martin,
// Jianzhou Zhao, Steve Zdancewic
// Department of Computer and Information Sciences,
// University of Pennsylvania
// http://www.cis.upenn.edu/acg/softbound/
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the names of Santosh Nagarakatte, Milo M. K. Martin,
// Jianzhou Zhao, Steve Zdancewic, University of Pennsylvania, nor
// the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior
// written permission.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// WITH THE SOFTWARE.
//===---------------------------------------------------------------------===//
#include "SoftBound/SoftBoundCETSPass.h"
static cl::opt<bool>
spatial_safety
("softboundcets_spatial_safety",
cl::desc("perform transformation for spatial safety"),
cl::init(true));
static cl::opt<bool>
temporal_safety
("softboundcets_temporal_safety",
cl::desc("perform transformation for temporal safety"),
cl::init(true));
static cl::opt<bool>
store_only
("softboundcets_store_only",
cl::desc("perform store only checking"),
cl::init(false));
static cl::opt<bool>
metadata_prop_only
("softboundcets_mdprop_only",
cl::desc("perform only metadata propagation"),
cl::init(false));
static cl::opt<bool>
LOADCHECKS
("softboundcets_spatial_safety_load_checks",
cl::desc("introduce load dereference checks for spatial safety"),
cl::init(true));
static cl::opt<bool>
STORECHECKS
("softboundcets_spatial_safety_store_checks",
cl::desc("introduce store dereference checks for spatial safety"),
cl::init(true));
static cl::opt<bool>
TEMPORALLOADCHECKS
("softboundcets_temporal_load_checks",
cl::desc("introduce temporal load dereference checks"),
cl::init(true));
static cl::opt<bool>
TEMPORALSTORECHECKS
("softboundcets_temporal_store_checks",
cl::desc("introduce temporal store dereference checks"),
cl::init(true));
static cl::opt<bool>
FUNCDOMTEMPORALCHECKOPT
("softboundcets_func_dom_temporal_check_opt",
cl::desc("eliminate function based redundant checks with dominator analysis"),
cl::init(true));
static cl::opt<bool>
STRUCTOPT
("softboundcets_struct_opt",
cl::desc("enable or disable structure optimization"),
cl::init(true));
static cl::opt<bool>
BOUNDSCHECKOPT
("softboundcets_bounds_check_opt",
cl::desc("enable dominator based load dereference check elimination"),
cl::init(true));
static cl::opt<bool>
SHRINKBOUNDS
("softboundcets_shrink_bounds",
cl::desc("enable shrinking bounds for the softboundboundcetswithss pass"),
cl::init(false));
static cl::opt<bool>
MEMCOPYCHECK
("softboundcets_memcopy_check",
cl::desc("check memcopy calls"),
cl::init(true));
static cl::opt<bool>
GLOBALCONSTANTOPT
("softboundcets_global_const_opt",
cl::desc("global constant expressions are not checked"),
cl::init(false));
static cl::opt<bool>
CALLCHECKS
("softboundcets_call_checks",
cl::desc("introduce call checks"),
cl::init(true));
static cl::opt<bool>
INDIRECTCALLCHECKS
("softboundcets_indirect_call_checks",
cl::desc("introduce indirect call checks"),
cl::init(false));
static cl::opt<bool>
OPAQUECALLS
("softboundcets_opaque_calls",
cl::desc("consider all calls as opaque for func_dom_check_elimination"),
cl::init(true));
static cl::opt<bool>
TEMPORALBOUNDSCHECKOPT
("softboundcets_temporal_bounds_check_opt",
cl::desc("enable or disable temporal dominator based check elimination"),
cl::init(true));
static cl::opt<bool>
STACKTEMPORALCHECKOPT
("softboundcets_stack_temporal_check_opt",
cl::desc("eliminate temporal checks for stack variables"),
cl::init(true));
static cl::opt<bool>
GLOBALTEMPORALCHECKOPT
("softboundcets_global_temporal_check_opt",
cl::desc("eliminate temporal checks for global variables"),
cl::init(true));
static cl::opt<bool>
BBDOMTEMPORALCHECKOPT
("softboundcets_bb_dom_temporal_check_opt",
cl::desc("eliminate redundant checks in the basic block"),
cl::init(true));
static cl::opt<bool>
unsafe_byval_opt
("unsafe_byval_opt",
cl::desc("Unbound byval attributed pointers so that check always succeeds"),
cl::init(false));
char SoftBoundCETSPass:: ID = 0;
static RegisterPass<SoftBoundCETSPass> P ("SoftBoundCETSPass",
"SoftBound Pass for Spatial Safety");
//
// Method: getAssociateFuncLock()
//
// Description:
//
// This method looks up the "lock" for global variables associated
// with the function. Every will have a getGlobalLockAddr function
// inserted at the beginning which will serve as the lock for all the
// global variables used in the function.
//
//
// Inputs:
//
// Pointer_inst: An instruction that is manipulating a global pointer
// value.
//
// Return value:
//
// Returns the "lock associated with the function. Should never return
// NULL.
//
Value*
SoftBoundCETSPass:: getAssociatedFuncLock(Value* PointerInst){
Instruction* inst = dyn_cast<Instruction>(PointerInst);
Value* tmp_lock = NULL;
if (!inst) {
assert(0 && "Function does not have global lock?");
return NULL;
}
if (m_func_global_lock.count(inst->getParent()->getParent()->getName())) {
tmp_lock = m_func_global_lock[inst->getParent()->getParent()->getName()];
}
return tmp_lock;
}
//
// Method: initializeSoftBoundVariables()
//
// Description:
// This function initializes the Function*'s that will be
// inserted by the SoftBound/CETS Pass
//
// Input:
//
// module: Input module that has either the function definitions or
// the function prototypes for the SoftBound/CETS functions
//
void SoftBoundCETSPass::initializeSoftBoundVariables(Module& module) {
m_spatial_load_dereference_check =
module.getFunction("__softboundcets_spatial_load_dereference_check");
assert(m_spatial_load_dereference_check &&
"__softboundcets_spatial_load_dereference_check function type null?");
m_spatial_store_dereference_check =
module.getFunction("__softboundcets_spatial_store_dereference_check");
assert(m_spatial_store_dereference_check &&
"__softboundcets_spatial_store_dereference_check function type null?");
m_temporal_load_dereference_check =
module.getFunction("__softboundcets_temporal_load_dereference_check");
assert(m_temporal_load_dereference_check &&
"__softboundcets_temporal_load_dereference_check function type null?");
m_temporal_global_lock_function =
module.getFunction("__softboundcets_get_global_lock");
assert(m_temporal_global_lock_function &&
"__softboundcets_get_global_lock function type null?");
m_temporal_store_dereference_check =
module.getFunction("__softboundcets_temporal_store_dereference_check");
assert(m_temporal_store_dereference_check &&
" __softboundcets_temporal_store_dereference_check function type null?");
m_introspect_metadata =
module.getFunction("__softboundcets_introspect_metadata");
assert(m_introspect_metadata &&
"__softboundcets_introspect_metadata null?");
m_copy_metadata = module.getFunction("__softboundcets_copy_metadata");
assert(m_copy_metadata && "__softboundcets_copy_metadata NULL?");
m_shadow_stack_allocate =
module.getFunction("__softboundcets_allocate_shadow_stack_space");
assert(m_shadow_stack_allocate &&
"__softboundcets_allocate_shadow_stack_space NULL?");
m_shadow_stack_deallocate =
module.getFunction("__softboundcets_deallocate_shadow_stack_space");
assert(m_shadow_stack_deallocate &&
"__softboundcets_deallocate_shadow_stack_space NULL?");
m_shadow_stack_base_load =
module.getFunction("__softboundcets_load_base_shadow_stack");
assert(m_shadow_stack_base_load &&
"__softboundcets_load_base_shadow_stack NULL?");
m_shadow_stack_bound_load =
module.getFunction("__softboundcets_load_bound_shadow_stack");
assert(m_shadow_stack_bound_load &&
"__softboundcets_load_bound_shadow_stack NULL?");
m_shadow_stack_key_load =
module.getFunction("__softboundcets_load_key_shadow_stack");
assert(m_shadow_stack_key_load &&
"__softboundcets_load_key_shadow_stack NULL?");
m_shadow_stack_lock_load =
module.getFunction("__softboundcets_load_lock_shadow_stack");
assert(m_shadow_stack_lock_load &&
"__softboundcets_load_lock_shadow_stack NULL?");
m_shadow_stack_base_store =
module.getFunction("__softboundcets_store_base_shadow_stack");
assert(m_shadow_stack_base_store &&
"__softboundcets_store_base_shadow_stack NULL?");
m_shadow_stack_bound_store =
module.getFunction("__softboundcets_store_bound_shadow_stack");
assert(m_shadow_stack_bound_store &&
"__softboundcets_store_bound_shadow_stack NULL?");
m_shadow_stack_key_store =
module.getFunction("__softboundcets_store_key_shadow_stack");
assert(m_shadow_stack_key_store &&
"__softboundcets_store_key_shadow_stack NULL?");
m_shadow_stack_lock_store =
module.getFunction("__softboundcets_store_lock_shadow_stack");
assert(m_shadow_stack_lock_store &&
"__softboundcets_store_lock_shadow_stack NULL?");
m_temporal_stack_memory_allocation =
module.getFunction("__softboundcets_stack_memory_allocation");
assert(m_temporal_stack_memory_allocation &&
"__softboundcets_stack_memory_allocation");
m_temporal_stack_memory_deallocation =
module.getFunction("__softboundcets_stack_memory_deallocation");
assert(m_temporal_stack_memory_deallocation &&
"__softboundcets_stack_memory_deallocation not defined?");
m_load_base_bound_func = module.getFunction("__softboundcets_metadata_load");
assert(m_load_base_bound_func && "__softboundcets_metadata_load null?");
m_store_base_bound_func = module.getFunction("__softboundcets_metadata_store");
assert(m_store_base_bound_func && "__softboundcets_metadata_store null?");
m_call_dereference_func =
module.getFunction("__softboundcets_spatial_call_dereference_check");
assert(m_call_dereference_func &&
"__softboundcets_spatial_call_dereference_check function null??");
m_void_ptr_type = PointerType::getUnqual(Type::getInt8Ty(module.getContext()));
size_t inf_bound;
if (m_is_64_bit) {
m_key_type = Type::getInt64Ty(module.getContext());
} else {
m_key_type = Type::getInt32Ty(module.getContext());
}
if (m_is_64_bit) {
inf_bound = (size_t) pow(2, 48);
} else {
inf_bound = (size_t) (2147483647);
}
ConstantInt* infinite_bound;
if (m_is_64_bit) {
infinite_bound =
ConstantInt::get(Type::getInt64Ty(module.getContext()), inf_bound, false);
} else {
infinite_bound =
ConstantInt::get(Type::getInt32Ty(module.getContext()), inf_bound, false);
}
m_infinite_bound_ptr = ConstantExpr::getIntToPtr(infinite_bound,
m_void_ptr_type);
PointerType* vptrty = dyn_cast<PointerType>(m_void_ptr_type);
m_void_null_ptr = ConstantPointerNull::get(vptrty);
PointerType* sizet_ptr_ty = NULL;
if (m_is_64_bit) {
sizet_ptr_ty =
PointerType::getUnqual(Type::getInt64Ty(module.getContext()));
} else{
sizet_ptr_ty =
PointerType::getUnqual(Type::getInt32Ty(module.getContext()));
}
m_sizet_null_ptr = ConstantPointerNull::get(sizet_ptr_ty);
m_constantint32ty_one =
ConstantInt::get(Type::getInt32Ty(module.getContext()), 1);
m_constantint32ty_zero =
ConstantInt::get(Type::getInt32Ty(module.getContext()), 0);
m_constantint64ty_one =
ConstantInt::get(Type::getInt64Ty(module.getContext()), 1);
m_constantint64ty_zero =
ConstantInt::get(Type::getInt64Ty(module.getContext()), 0);
if (m_is_64_bit) {
m_constantint_one = m_constantint64ty_one;
m_constantint_zero = m_constantint64ty_zero;
} else {
m_constantint_one = m_constantint32ty_one;
m_constantint_zero = m_constantint32ty_zero;
}
}
// Method: hasAllocaInst()
//
// Description:
//
// This function checks whether internal function has an alloca
// instruction in the function. This function is useful to determine
// whether we need to allocate a key and a lock for the function or
// not.
//
bool SoftBoundCETSPass::isAllocaPresent(Function* func){
for(Function::iterator bb_begin = func->begin(), bb_end = func->end();
bb_begin != bb_end; ++bb_begin) {
for(BasicBlock::iterator i_begin = bb_begin->begin(),
i_end = bb_begin->end(); i_begin != i_end; ++i_begin){
Instruction* alloca_inst = dyn_cast<Instruction>(i_begin);
if(isa<AllocaInst>(alloca_inst) && m_present_in_original.count(alloca_inst)){
return true;
}
}
}
return false;
}
//
// Method: getFunctionKeyLock()
//
// Description:
//
// This function introduces a memory allocation call for allocating a
// new "key" and "lock" for the stack frames on function entry. This
// function also stores the key and lock in the reference Value*
// arguments provided to the function. Further, key and lock is
// allocated only when temporal checking is performed.
//
// Inputs:
//
// func: Function* of the function performing the allocation
// func_key: Value* & is the reference argument to return the key
// func_lock: Value* & is the reference_argument to return the lock
// func_xmm_lock: Value* & is the reference argument that will be
// eventually used to return the key and lock as wide parameters.
//
void
SoftBoundCETSPass::getFunctionKeyLock(Function* func,
Value* & func_key,
Value* & func_lock,
Value* & func_xmm_key_lock) {
Instruction* func_alloca_inst = NULL;
func_key = NULL;
func_lock = NULL;
func_xmm_key_lock = NULL;
if (!temporal_safety)
return;
if(!isAllocaPresent(func))
return;
func_alloca_inst = dyn_cast<Instruction>(func->begin()->begin());
assert(func_alloca_inst && "func begin null?");
addMemoryAllocationCall(func, func_key,
func_lock, func_alloca_inst, true);
return;
}
//
// Method: addMemoryAllocationCall()
//
// This function introduces a call to the C-handler function for
// allocating key and lock for stack frames. After the handler call,
// it performs the load of the key and the lock to use it as the
// metadata for pointers pointing to stack allocations in the
// function.
//
// Inputs:
//
// func: Function for which the key and the lock is being allocated
//
// ptr_key: Reference argument to return the key after the key and lock
// allocation
//
// ptr_lock: Reference argument to return the lock after
// the key and lock allocation
//
// insert_at: Instruction* before which the C-handler is introduced
//
// is_stack: boolean flag indicating whether a stack allocation is
// being performed. (Obsolete now. It was used earlier with llvm-2.6
// where malloc was an IR instruction.)
//
// Outputs:
//
// A new key and lock is allocated by the C-handler and then returned
// via reference arguments that is used as key and lock for pointers
// pointing to stack allocations in the function.
//
void
SoftBoundCETSPass::addMemoryAllocationCall(Function* func,
Value* & ptr_key,
Value* & ptr_lock,
Instruction* insert_at,
bool is_stack) {
SmallVector<Value*, 8> args;
Instruction* first_inst_func = cast<Instruction>(func->begin()->begin());
AllocaInst* lock_alloca = new AllocaInst(m_void_ptr_type,
"lock_alloca",
first_inst_func);
AllocaInst* key_alloca = new AllocaInst(Type::getInt64Ty(func->getContext()),
"key_alloca", first_inst_func);
args.push_back(lock_alloca);
args.push_back(key_alloca);
Instruction*
flc_call = CallInst::Create(m_temporal_stack_memory_allocation,
args, "", first_inst_func);
//
// Load the key and lock from the reference arguments passed to the
// C-handler
//
Instruction* next_inst = getNextInstruction(flc_call);
Instruction* alloca_lock = new LoadInst(lock_alloca,
"lock.load", next_inst);
Instruction* alloca_key = new LoadInst(key_alloca,
"key.load", next_inst);
ptr_key = alloca_key;
ptr_lock = alloca_lock;
}
//
// Method: transformMain()
//
// Description:
//
// This method renames the function "main" in the module as
// pseudo_main. The C-handler has the main function which calls
// pseudo_main. Actually transformation of the main takes places in
// two steps. Step1: change the name to pseudo_main and Step2:
// Function renaming to append the function name with softboundcets_
//
// Inputs:
// module: Input module with the function main
//
// Outputs:
//
// Changed module with any function named "main" is changed to
// "pseudo_main"
//
// Comments:
//
// This function is doing redundant work. We should probably use
// renameFunction to accomplish the task. The key difference is that
// transform renames it the function as either pseudo_main or
// softboundcets_pseudo_main which is subsequently renamed to
// softboundcets_pseudo_main in the first case by renameFunction
//
void SoftBoundCETSPass::transformMain(Module& module) {
Function* main_func = module.getFunction("main");
//
// If the program doesn't have main then don't do anything
//
if (!main_func) return;
Type* ret_type = main_func->getReturnType();
const FunctionType* fty = main_func->getFunctionType();
std::vector<Type*> params;
AttributeSet param_attrs_vec = main_func->getAttributes();
// Get the attributes of the arguments
int arg_index = 1;
for(Function::arg_iterator i = main_func->arg_begin(),
e = main_func->arg_end();
i != e; ++i, arg_index++) {
params.push_back(i->getType());
}
FunctionType* nfty = FunctionType::get(ret_type, params, fty->isVarArg());
Function* new_func = NULL;
// create the new function
new_func = Function::Create(nfty, main_func->getLinkage(),
"softboundcets_pseudo_main");
// set the new function attributes
new_func->copyAttributesFrom(main_func);
new_func->setAttributes(param_attrs_vec);
main_func->getParent()->getFunctionList().insert(main_func, new_func);
main_func->replaceAllUsesWith(new_func);
//
// Splice the instructions from the old function into the new
// function and set the arguments appropriately
//
new_func->getBasicBlockList().splice(new_func->begin(),
main_func->getBasicBlockList());
Function::arg_iterator arg_i2 = new_func->arg_begin();
for(Function::arg_iterator arg_i = main_func->arg_begin(),
arg_e = main_func->arg_end();
arg_i != arg_e; ++arg_i) {
arg_i->replaceAllUsesWith(arg_i2);
arg_i2->takeName(arg_i);
++arg_i2;
arg_index++;
}
//
// Remove the old function from the module
//
main_func->eraseFromParent();
}
//
// Method: isFuncDefSoftBound
//
// Description:
//
// This function checks if the input function name is a
// SoftBound/CETS defined function
//
bool SoftBoundCETSPass::isFuncDefSoftBound(const std::string &str) {
if (m_func_def_softbound.getNumItems() == 0) {
m_func_wrappers_available["system"] = true;
m_func_wrappers_available["setreuid"] = true;
m_func_wrappers_available["mkstemp"] = true;
m_func_wrappers_available["getuid"] = true;
m_func_wrappers_available["getrlimit"] = true;
m_func_wrappers_available["setrlimit"] = true;
m_func_wrappers_available["fread"] = true;
m_func_wrappers_available["umask"] = true;
m_func_wrappers_available["mkdir"] = true;
m_func_wrappers_available["chroot"] = true;
m_func_wrappers_available["rmdir"] = true;
m_func_wrappers_available["stat"] = true;
m_func_wrappers_available["fputc"] = true;
m_func_wrappers_available["fileno"] = true;
m_func_wrappers_available["fgetc"] = true;
m_func_wrappers_available["strncmp"] = true;
m_func_wrappers_available["log"] = true;
m_func_wrappers_available["fwrite"] = true;
m_func_wrappers_available["atof"] = true;
m_func_wrappers_available["feof"] = true;
m_func_wrappers_available["remove"] = true;
m_func_wrappers_available["acos"] = true;
m_func_wrappers_available["atan2"] = true;
m_func_wrappers_available["sqrtf"] = true;
m_func_wrappers_available["expf"] = true;
m_func_wrappers_available["exp2"] = true;
m_func_wrappers_available["floorf"] = true;
m_func_wrappers_available["ceil"] = true;
m_func_wrappers_available["ceilf"] = true;
m_func_wrappers_available["floor"] = true;
m_func_wrappers_available["sqrt"] = true;
m_func_wrappers_available["fabs"] = true;
m_func_wrappers_available["abs"] = true;
m_func_wrappers_available["srand"] = true;
m_func_wrappers_available["srand48"] = true;
m_func_wrappers_available["pow"] = true;
m_func_wrappers_available["fabsf"] = true;
m_func_wrappers_available["tan"] = true;
m_func_wrappers_available["tanf"] = true;
m_func_wrappers_available["tanl"] = true;
m_func_wrappers_available["log10"] = true;
m_func_wrappers_available["sin"] = true;
m_func_wrappers_available["sinf"] = true;
m_func_wrappers_available["sinl"] = true;
m_func_wrappers_available["cos"] = true;
m_func_wrappers_available["cosf"] = true;
m_func_wrappers_available["cosl"] = true;
m_func_wrappers_available["exp"] = true;
m_func_wrappers_available["ldexp"] = true;
m_func_wrappers_available["tmpfile"] = true;
m_func_wrappers_available["ferror"] = true;
m_func_wrappers_available["ftell"] = true;
m_func_wrappers_available["fstat"] = true;
m_func_wrappers_available["fflush"] = true;
m_func_wrappers_available["fputs"] = true;
m_func_wrappers_available["fopen"] = true;
m_func_wrappers_available["fdopen"] = true;
m_func_wrappers_available["fseek"] = true;
m_func_wrappers_available["ftruncate"] = true;
m_func_wrappers_available["popen"] = true;
m_func_wrappers_available["fclose"] = true;
m_func_wrappers_available["pclose"] = true;
m_func_wrappers_available["rewind"] = true;
m_func_wrappers_available["readdir"] = true;
m_func_wrappers_available["opendir"] = true;
m_func_wrappers_available["closedir"] = true;
m_func_wrappers_available["rename"] = true;
m_func_wrappers_available["sleep"] = true;
m_func_wrappers_available["getcwd"] = true;
m_func_wrappers_available["chown"] = true;
m_func_wrappers_available["isatty"] = true;
m_func_wrappers_available["chdir"] = true;
m_func_wrappers_available["strcmp"] = true;
m_func_wrappers_available["strcasecmp"] = true;
m_func_wrappers_available["strncasecmp"] = true;
m_func_wrappers_available["strlen"] = true;
m_func_wrappers_available["strpbrk"] = true;
m_func_wrappers_available["gets"] = true;
m_func_wrappers_available["fgets"] = true;
m_func_wrappers_available["perror"] = true;
m_func_wrappers_available["strspn"] = true;
m_func_wrappers_available["strcspn"] = true;
m_func_wrappers_available["memcmp"] = true;
m_func_wrappers_available["memchr"] = true;
m_func_wrappers_available["rindex"] = true;
m_func_wrappers_available["strtoul"] = true;
m_func_wrappers_available["strtod"] = true;
m_func_wrappers_available["strtol"] = true;
m_func_wrappers_available["strchr"] = true;
m_func_wrappers_available["strrchr"] = true;
m_func_wrappers_available["strcpy"] = true;
m_func_wrappers_available["abort"] = true;
m_func_wrappers_available["rand"] = true;
m_func_wrappers_available["atoi"] = true;
m_func_wrappers_available["puts"] = true;
m_func_wrappers_available["exit"] = true;
m_func_wrappers_available["strtok"] = true;
m_func_wrappers_available["strdup"] = true;
m_func_wrappers_available["strcat"] = true;
m_func_wrappers_available["strncat"] = true;
m_func_wrappers_available["strncpy"] = true;
m_func_wrappers_available["strstr"] = true;
m_func_wrappers_available["signal"] = true;
m_func_wrappers_available["clock"] = true;
m_func_wrappers_available["atol"] = true;
m_func_wrappers_available["realloc"] = true;
m_func_wrappers_available["calloc"] = true;
m_func_wrappers_available["malloc"] = true;
m_func_wrappers_available["mmap"] = true;
m_func_wrappers_available["putchar"] = true;
m_func_wrappers_available["times"] = true;
m_func_wrappers_available["strftime"] = true;
m_func_wrappers_available["localtime"] = true;
m_func_wrappers_available["time"] = true;
m_func_wrappers_available["drand48"] = true;
m_func_wrappers_available["free"] = true;
m_func_wrappers_available["lrand48"] = true;
m_func_wrappers_available["ctime"] = true;
m_func_wrappers_available["difftime"] = true;
m_func_wrappers_available["toupper"] = true;
m_func_wrappers_available["tolower"] = true;
m_func_wrappers_available["setbuf"] = true;
m_func_wrappers_available["getenv"] = true;
m_func_wrappers_available["atexit"] = true;
m_func_wrappers_available["strerror"] = true;
m_func_wrappers_available["unlink"] = true;
m_func_wrappers_available["close"] = true;
m_func_wrappers_available["open"] = true;
m_func_wrappers_available["read"] = true;
m_func_wrappers_available["write"] = true;
m_func_wrappers_available["lseek"] = true;
m_func_wrappers_available["gettimeofday"] = true;
m_func_wrappers_available["select"] = true;
m_func_wrappers_available["__errno_location"] = true;
m_func_wrappers_available["__ctype_b_loc"] = true;
m_func_wrappers_available["__ctype_toupper_loc"] = true;
m_func_wrappers_available["__ctype_tolower_loc"] = true;
m_func_wrappers_available["qsort"] = true;
m_func_def_softbound["__softboundcets_introspect_metadata"] = true;
m_func_def_softbound["__softboundcets_copy_metadata"] = true;
m_func_def_softbound["__softboundcets_allocate_shadow_stack_space"] = true;
m_func_def_softbound["__softboundcets_load_base_shadow_stack"] = true;
m_func_def_softbound["__softboundcets_load_bound_shadow_stack"] = true;
m_func_def_softbound["__softboundcets_load_key_shadow_stack"] = true;
m_func_def_softbound["__softboundcets_load_lock_shadow_stack"] = true;
m_func_def_softbound["__softboundcets_store_base_shadow_stack"] = true;
m_func_def_softbound["__softboundcets_store_bound_shadow_stack"] = true;
m_func_def_softbound["__softboundcets_store_key_shadow_stack"] = true;
m_func_def_softbound["__softboundcets_store_lock_shadow_stack"] = true;
m_func_def_softbound["__softboundcets_deallocate_shadow_stack_space"] = true;
m_func_def_softbound["__softboundcets_trie_allocate"] = true;
m_func_def_softbound["__shrinkBounds"] = true;
m_func_def_softbound["__softboundcets_spatial_load_dereference_check"] = true;
m_func_def_softbound["__softboundcets_spatial_store_dereference_check"] = true;
m_func_def_softbound["__softboundcets_spatial_call_dereference_check"] = true;
m_func_def_softbound["__softboundcets_temporal_load_dereference_check"] = true;
m_func_def_softbound["__softboundcets_temporal_store_dereference_check"] = true;
m_func_def_softbound["__softboundcets_stack_memory_allocation"] = true;
m_func_def_softbound["__softboundcets_memory_allocation"] = true;
m_func_def_softbound["__softboundcets_get_global_lock"] = true;
m_func_def_softbound["__softboundcets_add_to_free_map"] = true;
m_func_def_softbound["__softboundcets_check_remove_from_free_map"] = true;
m_func_def_softbound["__softboundcets_allocation_secondary_trie_allocate"] = true;
m_func_def_softbound["__softboundcets_allocation_secondary_trie_allocate_range"] = true;
m_func_def_softbound["__softboundcets_allocate_lock_location"] = true;
m_func_def_softbound["__softboundcets_memory_deallocation"] = true;
m_func_def_softbound["__softboundcets_stack_memory_deallocation"] = true;
m_func_def_softbound["__softboundcets_metadata_load"] = true;
m_func_def_softbound["__softboundcets_metadata_store"] = true;
m_func_def_softbound["__hashProbeAddrOfPtr"] = true;
m_func_def_softbound["__memcopyCheck"] = true;
m_func_def_softbound["__memcopyCheck_i64"] = true;
m_func_def_softbound["__softboundcets_global_init"] = true;
m_func_def_softbound["__softboundcets_init"] = true;
m_func_def_softbound["__softboundcets_abort"] = true;
m_func_def_softbound["__softboundcets_printf"] = true;
m_func_def_softbound["__softboundcets_stub"] = true;
m_func_def_softbound["safe_mmap"] = true;
m_func_def_softbound["safe_calloc"] = true;
m_func_def_softbound["safe_malloc"] = true;
m_func_def_softbound["safe_free"] = true;
m_func_def_softbound["__assert_fail"] = true;
m_func_def_softbound["assert"] = true;
m_func_def_softbound["__strspn_c2"] = true;
m_func_def_softbound["__strcspn_c2"] = true;
m_func_def_softbound["__strtol_internal"] = true;
m_func_def_softbound["__stroul_internal"] = true;
m_func_def_softbound["ioctl"] = true;
m_func_def_softbound["error"] = true;
m_func_def_softbound["__strtod_internal"] = true;
m_func_def_softbound["__strtoul_internal"] = true;
m_func_def_softbound["fflush_unlocked"] = true;
m_func_def_softbound["full_write"] = true;
m_func_def_softbound["safe_read"] = true;
m_func_def_softbound["_IO_getc"] = true;
m_func_def_softbound["_IO_putc"] = true;
m_func_def_softbound["__xstat"] = true;
m_func_def_softbound["select"] = true;
m_func_def_softbound["_setjmp"] = true;
m_func_def_softbound["longjmp"] = true;
m_func_def_softbound["fork"] = true;
m_func_def_softbound["pipe"] = true;
m_func_def_softbound["dup2"] = true;
m_func_def_softbound["execv"] = true;
m_func_def_softbound["compare_pic_by_pic_num_desc"] = true;
m_func_def_softbound["wprintf"] = true;
m_func_def_softbound["vfprintf"] = true;
m_func_def_softbound["vsprintf"] = true;
m_func_def_softbound["fprintf"] = true;
m_func_def_softbound["printf"] = true;
m_func_def_softbound["sprintf"] = true;
m_func_def_softbound["snprintf"] = true;
m_func_def_softbound["scanf"] = true;
m_func_def_softbound["fscanf"] = true;
m_func_def_softbound["sscanf"] = true;
m_func_def_softbound["asprintf"] = true;
m_func_def_softbound["vasprintf"] = true;
m_func_def_softbound["__fpending"] = true;
m_func_def_softbound["fcntl"] = true;
m_func_def_softbound["vsnprintf"] = true;
m_func_def_softbound["fwrite_unlocked"] = true;
m_func_def_softbound["__overflow"] = true;
m_func_def_softbound["__uflow"] = true;
m_func_def_softbound["execlp"] = true;
m_func_def_softbound["execl"] = true;
m_func_def_softbound["waitpid"] = true;
m_func_def_softbound["dup"] = true;
m_func_def_softbound["setuid"] = true;
m_func_def_softbound["_exit"] = true;
m_func_def_softbound["funlockfile"] = true;
m_func_def_softbound["flockfile"] = true;
m_func_def_softbound["__option_is_short"] = true;
}
// Is the function name in the above list?
if (m_func_def_softbound.count(str) > 0) {
return true;
}
// FIXME: handling new intrinsics which have isoc99 in their name
if (str.find("isoc99") != std::string::npos){
return true;
}
// If the function is an llvm intrinsic, don't transform it
if (str.find("llvm.") == 0) {
return true;
}
return false;
}
//
// Method: identifyFuncToTrans
//
// Description: This function traverses the module and identifies the
// functions that need to be transformed by SoftBound/CETS
//
void SoftBoundCETSPass::identifyFuncToTrans(Module& module) {
for (Module::iterator fb_it = module.begin(), fe_it = module.end();
fb_it != fe_it; ++fb_it) {
Function* func = dyn_cast<Function>(fb_it);
assert(func && " Not a function");
// Check if the function is defined in the module
if (!func->isDeclaration()) {
if (isFuncDefSoftBound(func->getName()))
continue;
m_func_softboundcets_transform[func->getName()] = true;
if (hasPtrArgRetType(func)) {
m_func_to_transform[func->getName()] = true;
}
}
}
}
//
// Method: introduceGlobalLockFunction()
//
// Description:
//
// This function introduces the function to retrieve the lock for the
// global variables. This function should be introduced only once for
// every function in the entry block of the function.
//
Value* SoftBoundCETSPass::introduceGlobalLockFunction(Instruction* insert_at){
SmallVector<Value*, 8> args;
Value* call_inst = CallInst::Create(m_temporal_global_lock_function,
args, "", insert_at);
return call_inst;
}
//
// Method: castToVoidPtr()
//
// Description:
//
// This function introduces a bitcast instruction in the IR when an
// input operand that is a pointer type is not of type i8*. This is
// required as all the SoftBound/CETS handlers take i8*s
//
Value*
SoftBoundCETSPass:: castToVoidPtr(Value* operand, Instruction* insert_at) {
Value* cast_bitcast = operand;
if (operand->getType() != m_void_ptr_type) {
cast_bitcast = new BitCastInst(operand, m_void_ptr_type,
"bitcast",
insert_at);
}
return cast_bitcast;
}
//
// Method: hasPtrArgRetType()
//
// Description:
//
// This function checks if the function has either pointer arguments
// or returns a pointer value. This function is used to determine
// whether shadow stack loads/stores need to be introduced for
// metadata propagation.
//
bool SoftBoundCETSPass::hasPtrArgRetType(Function* func) {
const Type* ret_type = func->getReturnType();
if (isa<PointerType>(ret_type))
return true;
for (Function::arg_iterator i = func->arg_begin(), e = func->arg_end();
i != e; ++i) {
if (isa<PointerType>(i->getType()))
return true;
}
return false;
}
//
// Method: addStoreBaseBoundFunc
//
// Description:
//
// This function inserts metadata stores into the bitcode whenever a
// pointer is being stored to memory.
//
// Inputs:
//
// pointer_dest: address where the pointer being stored
//
// pointer_base, pointer_bound, pointer_key, pointer_lock: metadata
// associated with the pointer being stored
//
// pointer : pointer being stored to memory
//
// size_of_type: size of the access
//
// insert_at: the insertion point in the bitcode before which the
// metadata store is introduced.
//
void SoftBoundCETSPass::addStoreBaseBoundFunc(Value* pointer_dest,
Value* pointer_base,
Value* pointer_bound,
Value* pointer_key,
Value* pointer_lock,
Value* pointer,
Value* size_of_type,
Instruction* insert_at) {
Value* pointer_base_cast = NULL;
Value* pointer_bound_cast = NULL;
Value* pointer_dest_cast = castToVoidPtr(pointer_dest, insert_at);
if (spatial_safety) {
pointer_base_cast = castToVoidPtr(pointer_base, insert_at);
pointer_bound_cast = castToVoidPtr(pointer_bound, insert_at);
}
// Value* pointer_cast = castToVoidPtr(pointer, insert_at);
SmallVector<Value*, 8> args;
args.push_back(pointer_dest_cast);
if (spatial_safety) {
args.push_back(pointer_base_cast);
args.push_back(pointer_bound_cast);
}
if (temporal_safety) {
args.push_back(pointer_key);
args.push_back(pointer_lock);
}
CallInst::Create(m_store_base_bound_func, args, "", insert_at);
}
//
// The metadata propagation for PHINode occurs in two passes. In the
// first pass, SoftBound/CETS transformation just creates the metadata
// PHINodes and records it in the maps maintained by
// SoftBound/CETS. In the second pass, it populates the incoming
// values of the PHINodes. This two pass approach ensures that every
// incoming value of the original PHINode will have metadata in the
// SoftBound/CETS maps
//
//
// Method: handlePHIPass1()
//
// Description:
//
// This function creates a PHINode for the metadata in the bitcode for
// pointer PHINodes. It is important to note that this function just
// creates the PHINode and does not populate the incoming values of
// the PHINode, which is handled by the handlePHIPass2.
//
void SoftBoundCETSPass::handlePHIPass1(PHINode* phi_node) {
// Not a Pointer PHINode, then just return
if (!isa<PointerType>(phi_node->getType()))
return;
unsigned num_incoming_values = phi_node->getNumIncomingValues();
if (spatial_safety) {
PHINode* base_phi_node = PHINode::Create(m_void_ptr_type,
num_incoming_values,
"phi.base",
phi_node);
PHINode* bound_phi_node = PHINode::Create(m_void_ptr_type,
num_incoming_values,
"phi.bound",
phi_node);
Value* base_phi_node_value = base_phi_node;
Value* bound_phi_node_value = bound_phi_node;
associateBaseBound(phi_node, base_phi_node_value, bound_phi_node_value);
}
if (temporal_safety) {
PHINode* key_phi_node =
PHINode::Create(Type::getInt64Ty(phi_node->getType()->getContext()),
num_incoming_values,
"phi.key", phi_node);
PHINode* lock_phi_node = PHINode::Create(m_void_ptr_type,
num_incoming_values,
"phi.lock", phi_node);
associateKeyLock(phi_node, key_phi_node, lock_phi_node);
}
}
//
// Method: handlePHIPass2()
//
// Description: This pass fills the incoming values for the metadata
// PHINodes inserted in the first pass. There are four cases that
// needs to be handled for each incoming value. First, if the
// incoming value is a ConstantPointerNull, then base, bound, key,
// lock will be default values. Second, the incoming value can be an
// undef which results in default metadata values. Third, Global
// variables need to get the same base and bound for each
// occurence. So we maintain a map which maps the base and boundfor
// each global variable in the incoming value. Fourth, by default it
// retrieves the metadata from the SoftBound/CETS maps.
// Check if we need separate global variable and constant expression
// cases.
void SoftBoundCETSPass::handlePHIPass2(PHINode* phi_node) {
// Work to be done only for pointer PHINodes.
if (!isa<PointerType>(phi_node->getType()))
return;
PHINode* base_phi_node = NULL;
PHINode* bound_phi_node = NULL;
PHINode* key_phi_node = NULL;
PHINode* lock_phi_node = NULL;
// Obtain the metada PHINodes
if (spatial_safety) {
base_phi_node = dyn_cast<PHINode>(getAssociatedBase(phi_node));
bound_phi_node = dyn_cast<PHINode>(getAssociatedBound(phi_node));
}
if (temporal_safety) {
key_phi_node = dyn_cast<PHINode>(getAssociatedKey(phi_node));
Value* func_lock = getAssociatedFuncLock(phi_node);
lock_phi_node= dyn_cast<PHINode>(getAssociatedLock(phi_node, func_lock));
}
std::map<Value*, Value*> globals_base;
std::map<Value*, Value*> globals_bound;
std::map<Value*, Value*> globals_key;
std::map<Value*, Value*> globals_lock;
unsigned num_incoming_values = phi_node->getNumIncomingValues();
for (unsigned m = 0; m < num_incoming_values; m++) {
Value* incoming_value = phi_node->getIncomingValue(m);
BasicBlock* bb_incoming = phi_node->getIncomingBlock(m);
if (isa<ConstantPointerNull>(incoming_value)) {
if (spatial_safety) {
base_phi_node->addIncoming(m_void_null_ptr, bb_incoming);
bound_phi_node->addIncoming(m_void_null_ptr, bb_incoming);
}
if (temporal_safety) {
key_phi_node->addIncoming(m_constantint64ty_zero, bb_incoming);
lock_phi_node->addIncoming(m_void_null_ptr, bb_incoming);
}
continue;
} // ConstantPointerNull ends
// The incoming vlaue can be a UndefValue
if (isa<UndefValue>(incoming_value)) {
if (spatial_safety) {
base_phi_node->addIncoming(m_void_null_ptr, bb_incoming);
bound_phi_node->addIncoming(m_void_null_ptr, bb_incoming);
}
if (temporal_safety) {
key_phi_node->addIncoming(m_constantint64ty_zero, bb_incoming);
lock_phi_node->addIncoming(m_void_null_ptr, bb_incoming);
}
continue;
} // UndefValue ends
Value* incoming_value_base = NULL;
Value* incoming_value_bound = NULL;
Value* incoming_value_key = NULL;
Value* incoming_value_lock = NULL;
// handle global variables
GlobalVariable* gv = dyn_cast<GlobalVariable>(incoming_value);
if (gv) {
if (spatial_safety) {
if (!globals_base.count(gv)) {
Value* tmp_base = NULL;
Value* tmp_bound = NULL;
getGlobalVariableBaseBound(incoming_value, tmp_base, tmp_bound);
assert(tmp_base && "base of a global variable null?");
assert(tmp_bound && "bound of a global variable null?");
Function * PHI_func = phi_node->getParent()->getParent();
Instruction* PHI_func_entry = PHI_func->begin()->begin();
incoming_value_base = castToVoidPtr(tmp_base, PHI_func_entry);
incoming_value_bound = castToVoidPtr(tmp_bound, PHI_func_entry);
globals_base[incoming_value] = incoming_value_base;
globals_bound[incoming_value] = incoming_value_bound;
} else {
incoming_value_base = globals_base[incoming_value];
incoming_value_bound = globals_bound[incoming_value];
}
} // spatial safety ends
if (temporal_safety) {
incoming_value_key = m_constantint64ty_one;
Value* tmp_lock =
m_func_global_lock[phi_node->getParent()->getParent()->getName()];
incoming_value_lock = tmp_lock;
}
} // global variable ends
// handle constant expressions
Constant* given_constant = dyn_cast<Constant>(incoming_value);
if (given_constant) {
if (spatial_safety) {
if (!globals_base.count(incoming_value)) {
Value* tmp_base = NULL;
Value* tmp_bound = NULL;
getConstantExprBaseBound(given_constant, tmp_base, tmp_bound);
assert(tmp_base && tmp_bound &&
"[handlePHIPass2] tmp_base tmp_bound, null?");
Function* PHI_func = phi_node->getParent()->getParent();
Instruction* PHI_func_entry = PHI_func->begin()->begin();
incoming_value_base = castToVoidPtr(tmp_base, PHI_func_entry);
incoming_value_bound = castToVoidPtr(tmp_bound, PHI_func_entry);
globals_base[incoming_value] = incoming_value_base;
globals_bound[incoming_value] = incoming_value_bound;
}
else{
incoming_value_base = globals_base[incoming_value];
incoming_value_bound = globals_bound[incoming_value];
}
} // spatial safety ends
if (temporal_safety) {
incoming_value_key = m_constantint64ty_one;
Value* tmp_lock =
m_func_global_lock[phi_node->getParent()->getParent()->getName()];
incoming_value_lock = tmp_lock;
}
}
// handle values having map based pointer base and bounds
if(spatial_safety && checkBaseBoundMetadataPresent(incoming_value)){
incoming_value_base = getAssociatedBase(incoming_value);
incoming_value_bound = getAssociatedBound(incoming_value);
}
if(temporal_safety && checkKeyLockMetadataPresent(incoming_value)){
incoming_value_key = getAssociatedKey(incoming_value);
Value* func_lock = getAssociatedFuncLock(phi_node);
incoming_value_lock = getAssociatedLock(incoming_value, func_lock);
}
if(spatial_safety){
assert(incoming_value_base &&
"[handlePHIPass2] incoming_value doesn't have base?");
assert(incoming_value_bound &&
"[handlePHIPass2] incoming_value doesn't have bound?");
base_phi_node->addIncoming(incoming_value_base, bb_incoming);
bound_phi_node->addIncoming(incoming_value_bound, bb_incoming);
}
if(temporal_safety){
assert(incoming_value_key &&
"[handlePHIPass2] incoming_value doesn't have key?");
assert(incoming_value_lock &&
"[handlePHIPass2] incoming_value doesn't have lock?");
key_phi_node->addIncoming(incoming_value_key, bb_incoming);
lock_phi_node->addIncoming(incoming_value_lock, bb_incoming);
}
} // Iterating over incoming values ends
if(spatial_safety){
assert(base_phi_node && "[handlePHIPass2] base_phi_node null?");
assert(bound_phi_node && "[handlePHIPass2] bound_phi_node null?");
}
if(temporal_safety){
assert(key_phi_node && "[handlePHIPass2] key_phi_node null?");
assert(lock_phi_node && "[handlePHIPass2] lock_phi_node null?");
}
unsigned n_values = phi_node->getNumIncomingValues();
if(spatial_safety){
unsigned n_base_values = base_phi_node->getNumIncomingValues();
unsigned n_bound_values = bound_phi_node->getNumIncomingValues();
assert((n_values == n_base_values) &&
"[handlePHIPass2] number of values different for base");
assert((n_values == n_bound_values) &&
"[handlePHIPass2] number of values different for bound");
}
if(temporal_safety){
unsigned n_key_values = key_phi_node->getNumIncomingValues();
unsigned n_lock_values = lock_phi_node->getNumIncomingValues();
assert((n_values == n_key_values) &&
"[handlePHIPass2] number of values different for key");
assert((n_values == n_lock_values) &&
"[handlePHIPass2] number of values different for lock");
}
}
//
// Method: propagateMetadata
//
// Descripton;
//
// This function propagates the metadata from the source to the
// destination in the map for pointer arithmetic operations~(gep) and
// bitcasts. This is the place where we need to shrink bounds.
//
void
SoftBoundCETSPass:: propagateMetadata(Value* pointer_operand,
Instruction* inst,
int instruction_type){
// Need to just propagate the base and bound here if I am not
// shrinking bounds
if (spatial_safety) {
if(checkBaseBoundMetadataPresent(inst)){
// Metadata added to the map in the first pass
return;
}
}
if (temporal_safety) {
if (checkKeyLockMetadataPresent(inst)){
// Metadata added to the map in the first pass
return;
}
}
if(isa<ConstantPointerNull>(pointer_operand)) {
if(spatial_safety){
associateBaseBound(inst, m_void_null_ptr, m_void_null_ptr);
}
if(temporal_safety){
associateKeyLock(inst, m_constantint64ty_zero, m_void_null_ptr);
}
return;
}
if (spatial_safety) {
if (checkBaseBoundMetadataPresent(pointer_operand)) {
Value* tmp_base = getAssociatedBase(pointer_operand);
Value* tmp_bound = getAssociatedBound(pointer_operand);
associateBaseBound(inst, tmp_base, tmp_bound);
} else{
if(isa<Constant>(pointer_operand)) {
Value* tmp_base = NULL;
Value* tmp_bound = NULL;
Constant* given_constant = dyn_cast<Constant>(pointer_operand);
getConstantExprBaseBound(given_constant, tmp_base, tmp_bound);
assert(tmp_base && "gep with cexpr and base null?");
assert(tmp_bound && "gep with cexpr and bound null?");
tmp_base = castToVoidPtr(tmp_base, inst);
tmp_bound = castToVoidPtr(tmp_bound, inst);
associateBaseBound(inst, tmp_base, tmp_bound);
} // Constant case ends here
// Could be in the first pass, do nothing here
}
}// Spatial safety ends here
if(temporal_safety){
if(checkKeyLockMetadataPresent(pointer_operand)){
Value* tmp_key = getAssociatedKey(pointer_operand);
Value* func_lock = getAssociatedFuncLock(inst);
Value* tmp_lock = getAssociatedLock(pointer_operand, func_lock);
associateKeyLock(inst, tmp_key, tmp_lock);
}
else{
if(isa<Constant>(pointer_operand)){
Value* func_lock =
m_func_global_lock[inst->getParent()->getParent()->getName()];
associateKeyLock(inst, m_constantint64ty_one, func_lock);
}
}
} // Temporal safety ends here
}
//
// Method: handleBitCast
//
// Description: Propagate metadata from source to destination with
// pointer bitcast operations.
void SoftBoundCETSPass::handleBitCast(BitCastInst* bitcast_inst) {
Value* pointer_operand = bitcast_inst->getOperand(0);
propagateMetadata(pointer_operand, bitcast_inst, SBCETS_BITCAST);
}
//
// Method: getGlobalVariableBaseBound
// Description: This function returns the base and bound for the
// global variables in the input reference arguments. This function
// may now be obsolete. We should try to use getConstantExprBaseBound
// instead in all places.
void
SoftBoundCETSPass::getGlobalVariableBaseBound(Value* operand,
Value* & operand_base,
Value* & operand_bound){
GlobalVariable* gv = dyn_cast<GlobalVariable>(operand);
Module* module = gv->getParent();
assert(gv && "[getGlobalVariableBaseBound] not a global variable?");
std::vector<Constant*> indices_base;
Constant* index_base =
ConstantInt::get(Type::getInt32Ty(module->getContext()), 0);
indices_base.push_back(index_base);
Constant* base_exp = ConstantExpr::getGetElementPtr(gv, indices_base);
std::vector<Constant*> indices_bound;
Constant* index_bound =
ConstantInt::get(Type::getInt32Ty(module->getContext()), 1);
indices_bound.push_back(index_bound);
Constant* bound_exp = ConstantExpr::getGetElementPtr(gv, indices_bound);
operand_base = base_exp;
operand_bound = bound_exp;
}
//
// Method: introduceShadowStackAllocation
//
// Description: For every function call that has a pointer argument or
// a return value, shadow stack is used to propagate metadata. This
// function inserts the shadow stack allocation C-handler that
// reserves space in the shadow stack by reserving the requiste amount
// of space based on the input passed to it(number of pointer
// arguments/return).
void SoftBoundCETSPass:: introduceShadowStackAllocation(CallInst* call_inst){
// Count the number of pointer arguments and whether a pointer return
int pointer_args_return = getNumPointerArgsAndReturn(call_inst);
if(pointer_args_return == 0)
return;
Value* total_ptr_args;
total_ptr_args =
ConstantInt::get(Type::getInt32Ty(call_inst->getType()->getContext()),
pointer_args_return, false);
SmallVector<Value*, 8> args;
args.push_back(total_ptr_args);
CallInst::Create(m_shadow_stack_allocate, args, "", call_inst);
}
//
// Method: introduceShadowStackStores
//
// Description: This function inserts a call to the shadow stack store
// C-handler that stores the metadata, before the function call in the
// bitcode for pointer arguments.
void
SoftBoundCETSPass::introduceShadowStackStores(Value* ptr_value,
Instruction* insert_at,
int arg_no){
if(!isa<PointerType>(ptr_value->getType()))
return;
Value* argno_value;
argno_value =
ConstantInt::get(Type::getInt32Ty(ptr_value->getType()->getContext()),
arg_no, false);
if(spatial_safety){
Value* ptr_base = getAssociatedBase(ptr_value);
Value* ptr_bound = getAssociatedBound(ptr_value);
Value* ptr_base_cast = castToVoidPtr(ptr_base, insert_at);
Value* ptr_bound_cast = castToVoidPtr(ptr_bound, insert_at);
SmallVector<Value*, 8> args;
args.push_back(ptr_base_cast);
args.push_back(argno_value);
CallInst::Create(m_shadow_stack_base_store, args, "", insert_at);
args.clear();
args.push_back(ptr_bound_cast);
args.push_back(argno_value);
CallInst::Create(m_shadow_stack_bound_store, args, "", insert_at);
}
if(temporal_safety){
Value* ptr_key = getAssociatedKey(ptr_value);
Value* func_lock = getAssociatedFuncLock(insert_at);
Value* ptr_lock = getAssociatedLock(ptr_value, func_lock);
SmallVector<Value*, 8> args;
args.clear();
args.push_back(ptr_key);
args.push_back(argno_value);
CallInst::Create(m_shadow_stack_key_store, args, "", insert_at);
args.clear();
args.push_back(ptr_lock);
args.push_back(argno_value);
CallInst::Create(m_shadow_stack_lock_store, args, "", insert_at);
}
}
//
// Method: introduceShadowStackDeallocation
//
// Description: This function inserts a call to the C-handler that
// deallocates the shadow stack space on function exit.
void
SoftBoundCETSPass:: introduceShadowStackDeallocation(CallInst* call_inst,
Instruction* insert_at){
int pointer_args_return = getNumPointerArgsAndReturn(call_inst);
if(pointer_args_return == 0)
return;
SmallVector<Value*, 8> args;
CallInst::Create(m_shadow_stack_deallocate, args, "", insert_at);
}
//
// Method: getNumPointerArgsAndReturn
//
// Description: Returns the number of pointer arguments and return.
//
int SoftBoundCETSPass:: getNumPointerArgsAndReturn(CallInst* call_inst){
int total_pointer_count = 0;
AttributeSet param_attrs_vec;
call_inst->setAttributes(param_attrs_vec);
CallSite cs(call_inst);
for(unsigned i = 0; i < cs.arg_size(); i++){
Value* arg_value = cs.getArgument(i);
if(isa<PointerType>(arg_value->getType())){
total_pointer_count++;
}
}
if (total_pointer_count != 0) {
// Reserve one for the return address if it has atleast one
// pointer argument
total_pointer_count++;
} else{
// Increment the pointer arg return if the call instruction
// returns a pointer
if(isa<PointerType>(call_inst->getType())){
total_pointer_count++;
}
}
return total_pointer_count;
}
//
// Method: introduceShadowStackLoads
//
// Description: This function introduces calls to the C-handlers that
// performs the loads from the shadow stack to retrieve the metadata.
// This function also associates the loaded metadata with the pointer
// arguments in the SoftBound/CETS maps.
void
SoftBoundCETSPass::introduceShadowStackLoads(Value* ptr_value,
Instruction* insert_at,
int arg_no){
if (!isa<PointerType>(ptr_value->getType()))
return;
Value* argno_value;
argno_value =
ConstantInt::get(Type::getInt32Ty(ptr_value->getType()->getContext()),
arg_no, false);
SmallVector<Value*, 8> args;
if(spatial_safety){
args.clear();
args.push_back(argno_value);
Value* base = CallInst::Create(m_shadow_stack_base_load, args, "",
insert_at);
args.clear();
args.push_back(argno_value);
Value* bound = CallInst::Create(m_shadow_stack_bound_load, args, "",
insert_at);
associateBaseBound(ptr_value, base, bound);
}
if(temporal_safety){
args.clear();
args.push_back(argno_value);
Value* key = CallInst::Create(m_shadow_stack_key_load, args, "", insert_at);
args.clear();
args.push_back(argno_value);
Value* lock = CallInst::Create(m_shadow_stack_lock_load, args, "",
insert_at);
associateKeyLock(ptr_value, key, lock);
}
}
//
// Method: dissociateKeyLock
//
// Description: This function removes the key lock metadata associated
// with the pointer operand in the SoftBound/CETS maps.
void SoftBoundCETSPass:: dissociateKeyLock(Value* pointer_operand){
if(m_pointer_key.count(pointer_operand)){
m_pointer_key.erase(pointer_operand);
}
if(m_pointer_lock.count(pointer_operand)){
m_pointer_lock.erase(pointer_operand);
}
assert((m_pointer_key.count(pointer_operand) == 0) &&
"dissociating key failed");
assert((m_pointer_lock.count(pointer_operand) == 0) &&
"dissociating lock failed");
}
//
// Method: dissociateBaseBound
//
// Description: This function removes the base/bound metadata
// associated with the pointer operand in the SoftBound/CETS maps.
void SoftBoundCETSPass::dissociateBaseBound(Value* pointer_operand){
if(m_pointer_base.count(pointer_operand)){
m_pointer_base.erase(pointer_operand);
}
if(m_pointer_bound.count(pointer_operand)){
m_pointer_bound.erase(pointer_operand);
}
assert((m_pointer_base.count(pointer_operand) == 0) &&
"dissociating base failed\n");
assert((m_pointer_bound.count(pointer_operand) == 0) &&
"dissociating bound failed");
}
//
// Method: associateKeyLock
//
// Description: This function associates the key lock with the pointer
// operand in the SoftBound/CETS maps.
void SoftBoundCETSPass::associateKeyLock(Value* pointer_operand,
Value* pointer_key,
Value* pointer_lock){
if(m_pointer_key.count(pointer_operand)){
dissociateKeyLock(pointer_operand);
}
if(pointer_key->getType() != m_key_type)
assert(0 && "key does not the right type ");
if(pointer_lock->getType() != m_void_ptr_type)
assert(0 && "lock does not have the right type");
m_pointer_key[pointer_operand] = pointer_key;
if (m_pointer_lock.count(pointer_operand))
assert(0 && "lock already has an entry in the map");
m_pointer_lock[pointer_operand] = pointer_lock;
}
//
// Method: associateBaseBound
//
// Description: This function associates the base bound with the
// pointer operand in the SoftBound/CETS maps.
void SoftBoundCETSPass::associateBaseBound(Value* pointer_operand,
Value* pointer_base,
Value* pointer_bound){
if(m_pointer_base.count(pointer_operand)){
dissociateBaseBound(pointer_operand);
}
if(pointer_base->getType() != m_void_ptr_type){
assert(0 && "base does not have a void pointer type ");
}
m_pointer_base[pointer_operand] = pointer_base;
if(m_pointer_bound.count(pointer_operand)){
assert(0 && "bound map already has an entry in the map");
}
if(pointer_bound->getType() != m_void_ptr_type) {
assert(0 && "bound does not have a void pointer type ");
}
m_pointer_bound[pointer_operand] = pointer_bound;
}
//
// Method: handleSelect
//
// This function propagates the metadata with Select IR instruction.
// Select instruction is also handled in two passes.
void SoftBoundCETSPass::handleSelect(SelectInst* select_ins, int pass) {
if (!isa<PointerType>(select_ins->getType()))
return;
Value* condition = select_ins->getOperand(0);
Value* operand_base[2];
Value* operand_bound[2];
Value* operand_key[2];
Value* operand_lock[2];
for(unsigned m = 0; m < 2; m++) {
Value* operand = select_ins->getOperand(m+1);
if (spatial_safety) {
operand_base[m] = NULL;
operand_bound[m] = NULL;
if (checkBaseBoundMetadataPresent(operand)) {
operand_base[m] = getAssociatedBase(operand);
operand_bound[m] = getAssociatedBound(operand);
}
if (isa<ConstantPointerNull>(operand) &&
!checkBaseBoundMetadataPresent(operand)) {
operand_base[m] = m_void_null_ptr;
operand_bound[m] = m_void_null_ptr;
}
Constant* given_constant = dyn_cast<Constant>(operand);
if(given_constant) {
getConstantExprBaseBound(given_constant,
operand_base[m],
operand_bound[m]);
}
assert(operand_base[m] != NULL &&
"operand doesn't have base with select?");
assert(operand_bound[m] != NULL &&
"operand doesn't have bound with select?");
// Introduce a bit cast if the types don't match
if (operand_base[m]->getType() != m_void_ptr_type) {
operand_base[m] = new BitCastInst(operand_base[m], m_void_ptr_type,
"select.base", select_ins);
}
if (operand_bound[m]->getType() != m_void_ptr_type) {
operand_bound[m] = new BitCastInst(operand_bound[m], m_void_ptr_type,
"select_bound", select_ins);
}
} //Spatial safety ends
if (temporal_safety){
operand_key[m] = NULL;
operand_lock[m] = NULL;
if (checkKeyLockMetadataPresent(operand)){
operand_key[m] = getAssociatedKey(operand);
Value* func_lock = getAssociatedFuncLock(select_ins);
operand_lock[m] = getAssociatedLock(operand, func_lock);
}
if (isa<ConstantPointerNull>(operand) &&
!checkKeyLockMetadataPresent(operand)){
operand_key[m] = m_constantint64ty_zero;
operand_lock[m] = m_void_null_ptr;
}
Constant* given_constant = dyn_cast<Constant>(operand);
if(given_constant){
operand_key[m] = m_constantint64ty_one;
operand_lock[m] =
m_func_global_lock[select_ins->getParent()->getParent()->getName()];
}
assert(operand_key[m] != NULL &&
"operand doesn't have key with select?");
assert(operand_lock[m] != NULL &&
"operand doesn't have lock with select?");
} // Temporal safety ends
} // for loop ends
if (spatial_safety) {
SelectInst* select_base = SelectInst::Create(condition,
operand_base[0],
operand_base[1],
"select.base",
select_ins);
SelectInst* select_bound = SelectInst::Create(condition,
operand_bound[0],
operand_bound[1],
"select.bound",
select_ins);
associateBaseBound(select_ins, select_base, select_bound);
}
if(temporal_safety){
SelectInst* select_key = SelectInst::Create(condition,
operand_key[0],
operand_key[1],
"select.key",
select_ins);
SelectInst* select_lock = SelectInst::Create(condition,
operand_lock[0],
operand_lock[1],
"select.lock",
select_ins);
associateKeyLock(select_ins, select_key, select_lock);
}
}
//
// Method: checkBaseBoundMetadataPresent()
//
// Description:
// Checks if the metadata is present in the SoftBound/CETS maps.
bool
SoftBoundCETSPass::checkBaseBoundMetadataPresent(Value* pointer_operand){
if(m_pointer_base.count(pointer_operand) &&
m_pointer_bound.count(pointer_operand)){
return true;
}
return false;
}
//
// Method: checkKeyLockMetadataPresent()
//
// Description:
// Checks if the metadata is present in the SoftBound/CETS maps.
bool
SoftBoundCETSPass::checkKeyLockMetadataPresent(Value* pointer_operand){
if(m_pointer_key.count(pointer_operand) &&
m_pointer_lock.count(pointer_operand)){
return true;
}
return false;
}
//
// Method: handleReturnInst
//
// Description:
// This function inserts C-handler calls to store
// metadata for return values in the shadow stack.
void SoftBoundCETSPass:: handleReturnInst(ReturnInst* ret){
Value* pointer = ret->getReturnValue();
if(pointer == NULL){
return;
}
if(isa<PointerType>(pointer->getType())){
introduceShadowStackStores(pointer, ret, 0);
}
}
//
// Method: handleGlobalSequentialTypeInitializer
//
// Description: This performs the initialization of the metadata for
// the pointers in the global segments that are initialized with
// non-zero values.
//
// Comments: This function requires review and rewrite
void
SoftBoundCETSPass::handleGlobalSequentialTypeInitializer(Module& module,
GlobalVariable* gv) {
// Sequential type can be an array type, a pointer type
const SequentialType* init_seq_type =
dyn_cast<SequentialType>((gv->getInitializer())->getType());
assert(init_seq_type &&
"[handleGlobalSequentialTypeInitializer] initializer null?");
Instruction* init_function_terminator = getGlobalInitInstruction(module);
if(gv->getInitializer()->isNullValue())
return;
if(isa<ArrayType>(init_seq_type)){
const ArrayType* init_array_type = dyn_cast<ArrayType>(init_seq_type);
if(isa<StructType>(init_array_type->getElementType())){
// It is an array of structures
// Check whether the structure has a pointer, if it has a
// pointer then, we need to store the base and bound of the
// pointer into the metadata space. However, if the structure
// does not have any pointer, we can make a quick exit in
// processing this global
//
bool struct_has_pointers = false;
StructType* init_struct_type =
dyn_cast<StructType>(init_array_type->getElementType());
CompositeType* struct_comp_type =
dyn_cast<CompositeType>(init_struct_type);
assert(struct_comp_type && "struct composite type null?");
assert(init_struct_type &&
"Array of structures and struct type null?");
unsigned num_struct_elements = init_struct_type->getNumElements();
for(unsigned i = 0; i < num_struct_elements; i++) {
Type* element_type = struct_comp_type->getTypeAtIndex(i);
if(isa<PointerType>(element_type)){
struct_has_pointers = true;
}
}
if(!struct_has_pointers)
return;
// Here implies, global variable is an array of structures with
// a pointer. Thus for each pointer we need to store the base
// and bound
size_t num_array_elements = init_array_type->getNumElements();
ConstantArray* const_array =
dyn_cast<ConstantArray>(gv->getInitializer());
if(!const_array)
return;
for( unsigned i = 0; i < num_array_elements ; i++) {
Constant* struct_constant = const_array->getOperand(i);
assert(struct_constant &&
"Initializer structure type but not a constant?");
// Constant has zero initializer
if(struct_constant->isNullValue())
continue;
for( unsigned j = 0 ; j < num_struct_elements; j++) {
const Type* element_type = init_struct_type->getTypeAtIndex(j);
if(isa<PointerType>(element_type)){
Value* initializer_opd = struct_constant->getOperand(j);
Value* operand_base = NULL;
Value* operand_bound = NULL;
Constant* given_constant = dyn_cast<Constant>(initializer_opd);
assert(given_constant &&
"[handleGlobalStructTypeInitializer] not a constant?");
getConstantExprBaseBound(given_constant, operand_base, operand_bound);
// Creating the address of ptr
Constant* index0 =
ConstantInt::get(Type::getInt32Ty(module.getContext()), 0);
Constant* index1 =
ConstantInt::get(Type::getInt32Ty(module.getContext()), i);
Constant* index2 =
ConstantInt::get(Type::getInt32Ty(module.getContext()), j);
std::vector<Constant *> indices_addr_ptr;
indices_addr_ptr.push_back(index0);
indices_addr_ptr.push_back(index1);
indices_addr_ptr.push_back(index2);
Constant* Indices[3] = {index0, index1, index2};
Constant* addr_of_ptr = ConstantExpr::getGetElementPtr(gv, Indices);
Type* initializer_type = initializer_opd->getType();
Value* initializer_size = getSizeOfType(initializer_type);
Value* operand_key = NULL;
Value* operand_lock = NULL;
if(temporal_safety){
operand_key = m_constantint_one;
operand_lock =
introduceGlobalLockFunction(init_function_terminator);
}
addStoreBaseBoundFunc(addr_of_ptr, operand_base, operand_bound,
operand_key, operand_lock, initializer_opd,
initializer_size, init_function_terminator);
}
} // Iterating over struct element ends
} // Iterating over array element ends
}/// Array of Structures Ends
if (isa<PointerType>(init_array_type->getElementType())){
// It is a array of pointers
}
} // Array type case ends
if(isa<PointerType>(init_seq_type)){
// individual pointer stores
Value* initializer_base = NULL;
Value* initializer_bound = NULL;
Value* initializer = gv->getInitializer();
Constant* given_constant = dyn_cast<Constant>(initializer);
getConstantExprBaseBound(given_constant,
initializer_base,
initializer_bound);
Type* initializer_type = initializer->getType();
Value* initializer_size = getSizeOfType(initializer_type);
Value* operand_key = NULL;
Value* operand_lock = NULL;
if(temporal_safety){
operand_key = m_constantint_one;
operand_lock =
introduceGlobalLockFunction(init_function_terminator);
}
addStoreBaseBoundFunc(gv, initializer_base, initializer_bound, operand_key,
operand_lock, initializer, initializer_size,
init_function_terminator);
}
}
// Method: handleGlobalStructTypeInitializer()
//
// Description: handles the global
// initialization for global variables which are of struct type and
// have a pointer as one of their fields and is globally
// initialized
//
// Comments: This function requires review and rewrite
void
SoftBoundCETSPass::
handleGlobalStructTypeInitializer(Module& module,
StructType* init_struct_type,
Constant* initializer,
GlobalVariable* gv,
std::vector<Constant*> indices_addr_ptr,
int length) {
// TODO:URGENT: Do I handle nesxted structures
// has zero initializer
if(initializer->isNullValue())
return;
Instruction* first = getGlobalInitInstruction(module);
unsigned num_elements = init_struct_type->getNumElements();
Constant* constant = dyn_cast<Constant>(initializer);
assert(constant &&
"[handleGlobalStructTypeInit] global stype with init but not CA?");
for(unsigned i = 0; i < num_elements ; i++) {
CompositeType* struct_comp_type =
dyn_cast<CompositeType>(init_struct_type);
assert(struct_comp_type && "not a struct type?");
Type* element_type = struct_comp_type->getTypeAtIndex(i);
if(isa<PointerType>(element_type)){
Value* initializer_opd = constant->getOperand(i);
Value* operand_base = NULL;
Value* operand_bound = NULL;
Value* operand_key = NULL;
Value* operand_lock = NULL;
Constant* addr_of_ptr = NULL;
if(temporal_safety){
operand_key = m_constantint_one;
operand_lock = introduceGlobalLockFunction(first);
}
if(spatial_safety){
Constant* given_constant = dyn_cast<Constant>(initializer_opd);
assert(given_constant &&
"[handleGlobalStructTypeInitializer] not a constant?");
getConstantExprBaseBound(given_constant, operand_base, operand_bound);
// Creating the address of ptr
// Constant* index1 =
// ConstantInt::get(Type::getInt32Ty(module.getContext()), 0);
Constant* index2 = ConstantInt::get(Type::getInt32Ty(module.getContext()), i);
// indices_addr_ptr.push_back(index1);
indices_addr_ptr.push_back(index2);
length++;
addr_of_ptr = ConstantExpr::getGetElementPtr(gv, indices_addr_ptr);
}
Type* initializer_type = initializer_opd->getType();
Value* initializer_size = getSizeOfType(initializer_type);
addStoreBaseBoundFunc(addr_of_ptr, operand_base,
operand_bound, operand_key,
operand_lock, initializer_opd,
initializer_size, first);
if(spatial_safety){
indices_addr_ptr.pop_back();
length--;
}
continue;
}
if(isa<StructType>(element_type)){
StructType* child_element_type =
dyn_cast<StructType>(element_type);
Constant* struct_initializer =
dyn_cast<Constant>(constant->getOperand(i));
Constant* index2 =
ConstantInt::get(Type::getInt32Ty(module.getContext()), i);
indices_addr_ptr.push_back(index2);
length++;
handleGlobalStructTypeInitializer(module, child_element_type,
struct_initializer, gv,
indices_addr_ptr, length);
indices_addr_ptr.pop_back();
length--;
continue;
}
}
}
//
// Method: getConstantExprBaseBound
//
// Description: This function uniform handles all global constant
// expression and obtains the base and bound for these expressions
// without introducing any extra IR modifications.
void SoftBoundCETSPass::getConstantExprBaseBound(Constant* given_constant,
Value* & tmp_base,
Value* & tmp_bound){
if(isa<ConstantPointerNull>(given_constant)){
tmp_base = m_void_null_ptr;
tmp_bound = m_void_null_ptr;
return;
}
ConstantExpr* cexpr = dyn_cast<ConstantExpr>(given_constant);
tmp_base = NULL;
tmp_bound = NULL;
if(cexpr) {
assert(cexpr && "ConstantExpr and Value* is null??");
switch(cexpr->getOpcode()) {
case Instruction::GetElementPtr:
{
Constant* internal_constant = dyn_cast<Constant>(cexpr->getOperand(0));
getConstantExprBaseBound(internal_constant, tmp_base, tmp_bound);
break;
}
case BitCastInst::BitCast:
{
Constant* internal_constant = dyn_cast<Constant>(cexpr->getOperand(0));
getConstantExprBaseBound(internal_constant, tmp_base, tmp_bound);
break;
}
case Instruction::IntToPtr:
{
tmp_base = m_void_null_ptr;
tmp_bound = m_void_null_ptr;
return;
break;
}
default:
{
break;
}
} // Switch ends
} else {
const PointerType* func_ptr_type =
dyn_cast<PointerType>(given_constant->getType());
if(isa<FunctionType>(func_ptr_type->getElementType())) {
tmp_base = m_void_null_ptr;
tmp_bound = m_infinite_bound_ptr;
return;
}
// Create getElementPtrs to create the base and bound
std::vector<Constant*> indices_base;
std::vector<Constant*> indices_bound;
GlobalVariable* gv = dyn_cast<GlobalVariable>(given_constant);
// TODO: External globals get zero base and infinite_bound
if(gv && !gv->hasInitializer()) {
tmp_base = m_void_null_ptr;
tmp_bound = m_infinite_bound_ptr;
return;
}
Constant* index_base0 =
Constant::
getNullValue(Type::getInt32Ty(given_constant->getType()->getContext()));
Constant* index_bound0 =
ConstantInt::
get(Type::getInt32Ty(given_constant->getType()->getContext()), 1);
indices_base.push_back(index_base0);
indices_bound.push_back(index_bound0);
Constant* gep_base = ConstantExpr::getGetElementPtr(given_constant,
indices_base);
Constant* gep_bound = ConstantExpr::getGetElementPtr(given_constant,
indices_bound);
tmp_base = gep_base;
tmp_bound = gep_bound;
}
}
//
// Methods: getAssociatedBase, getAssociatedBound, getAssociatedKey,
// getAssociatedLock
//
// Description: Retrieves the metadata from SoftBound/CETS maps
//
Value*
SoftBoundCETSPass::getAssociatedBase(Value* pointer_operand) {
if(isa<Constant>(pointer_operand)){
Value* base = NULL;
Value* bound = NULL;
Constant* ptr_constant = dyn_cast<Constant>(pointer_operand);
getConstantExprBaseBound(ptr_constant, base, bound);
return base;
}
if(!m_pointer_base.count(pointer_operand)){
pointer_operand->dump();
}
assert(m_pointer_base.count(pointer_operand) &&
"Base absent. Try compiling with -simplifycfg option?");
Value* pointer_base = m_pointer_base[pointer_operand];
assert(pointer_base && "base present in the map but null?");
if(pointer_base->getType() != m_void_ptr_type)
assert(0 && "base in the map does not have the right type");
return pointer_base;
}
Value*
SoftBoundCETSPass::getAssociatedBound(Value* pointer_operand) {
if(isa<Constant>(pointer_operand)){
Value* base = NULL;
Value* bound = NULL;
Constant* ptr_constant = dyn_cast<Constant>(pointer_operand);
getConstantExprBaseBound(ptr_constant, base, bound);
return bound;
}
assert(m_pointer_bound.count(pointer_operand) &&
"Bound absent.");
Value* pointer_bound = m_pointer_bound[pointer_operand];
assert(pointer_bound &&
"bound present in the map but null?");
if(pointer_bound->getType() != m_void_ptr_type)
assert(0 && "bound in the map does not have the right type");
return pointer_bound;
}
Value*
SoftBoundCETSPass::getAssociatedKey(Value* pointer_operand) {
if(!temporal_safety){
return NULL;
}
if(isa<Constant>(pointer_operand)){
return m_constantint_one;
}
if(!m_pointer_key.count(pointer_operand)){
pointer_operand->dump();
}
assert(m_pointer_key.count(pointer_operand) &&
"Key absent. Try compiling with -simplifycfg option?");
Value* pointer_key = m_pointer_key[pointer_operand];
assert(pointer_key && "key present in the map but null?");
if(pointer_key->getType() != m_key_type)
assert(0 && "key in the map does not have the right type");
return pointer_key;
}
Value*
SoftBoundCETSPass::getAssociatedLock(Value* pointer_operand, Value* func_lock){
if(!temporal_safety){
return NULL;
}
if(isa<GlobalVariable>(pointer_operand)){
return func_lock;
}
if(isa<Constant>(pointer_operand)){
return func_lock;
}
if(!m_pointer_lock.count(pointer_operand)){
pointer_operand->dump();
}
assert(m_pointer_lock.count(pointer_operand) &&
"Lock absent. Try compiling with -simplifycfg option?");
Value* pointer_lock = m_pointer_lock[pointer_operand];
assert(pointer_lock && "lock present in the map but null?");
if(pointer_lock->getType() != m_void_ptr_type)
assert(0 && "lock in the map does not have the right type");
return pointer_lock;
}
//
// Method: transformFunctionName
//
// Description:
//
// This function returns the transformed name for the function. This
// function appends softboundcets_ to the input string.
std::string
SoftBoundCETSPass::transformFunctionName(const std::string &str) {
// If the function name starts with this prefix, don't just
// concatenate, but instead transform the string
return "softboundcets_" + str;
}
void SoftBoundCETSPass::addMemcopyCheck(CallInst* call_inst) {
if(!MEMCOPYCHECK)
return;
// TODO: FIXME do something here
}
//
// Method: getSizeOfType
//
// Description: This function returns the size of the memory access
// based on the type of the pointer which is being dereferenced. This
// function is used to pass the size of the access in many checks to
// perform byte granularity checking.
//
// Comments: May we should use DataLayout instead of m_is_64_bit
// according Criswell's comments.
Value* SoftBoundCETSPass:: getSizeOfType(Type* input_type) {
// Create a Constant Pointer Null of the input type. Then get a
// getElementPtr of it with next element access cast it to unsigned
// int
const PointerType* ptr_type = dyn_cast<PointerType>(input_type);
if (isa<FunctionType>(ptr_type->getElementType())) {
if (m_is_64_bit) {
return ConstantInt::get(Type::getInt64Ty(ptr_type->getContext()), 0);
} else{
return ConstantInt::get(Type::getInt32Ty(ptr_type->getContext()), 0);
}
}
const SequentialType* seq_type = dyn_cast<SequentialType>(input_type);
Constant* int64_size = NULL;
assert(seq_type && "pointer dereference and it is not a sequential type\n");
StructType* struct_type = dyn_cast<StructType>(input_type);
if(struct_type){
if(struct_type->isOpaque()){
if(m_is_64_bit) {
return ConstantInt::get(Type::getInt64Ty(seq_type->getContext()), 0);
}
else {
return ConstantInt::get(Type::getInt32Ty(seq_type->getContext()), 0);
}
}
}
if(m_is_64_bit) {
if(!seq_type->getElementType()->isSized()){
return ConstantInt::get(Type::getInt64Ty(seq_type->getContext()), 0);
}
int64_size = ConstantExpr::getSizeOf(seq_type->getElementType());
return int64_size;
} else {
// doing what ConstantExpr::getSizeOf() does
Constant* gep_idx =
ConstantInt::get(Type::getInt32Ty(seq_type->getContext()), 1);
PointerType* ptr_type = PointerType::getUnqual(seq_type->getElementType());
Constant* gep_temp = ConstantExpr::getNullValue(ptr_type);
Constant* gep = ConstantExpr::getGetElementPtr(gep_temp, gep_idx);
Type* int64Ty = Type::getInt64Ty(seq_type->getContext());
return ConstantExpr::getPtrToInt(gep, int64Ty);
}
assert(0 && "not handled type?");
return NULL;
}
//
//
// Method: addLoadStoreChecks
//
// Description: This function inserts calls to C-handler spatial
// safety check functions and elides the check if the map says it is
// not necessary to check.
void
SoftBoundCETSPass::addLoadStoreChecks(Instruction* load_store,
std::map<Value*, int>& FDCE_map) {
if(!spatial_safety)
return;
SmallVector<Value*, 8> args;
Value* pointer_operand = NULL;
if(isa<LoadInst>(load_store)) {
if(!LOADCHECKS)
return;
LoadInst* ldi = dyn_cast<LoadInst>(load_store);
assert(ldi && "not a load instruction");
pointer_operand = ldi->getPointerOperand();
}
if(isa<StoreInst>(load_store)){
if(!STORECHECKS)
return;
StoreInst* sti = dyn_cast<StoreInst>(load_store);
assert(sti && "not a store instruction");
// The pointer where the element is being stored is the second
// operand
pointer_operand = sti->getOperand(1);
}
assert(pointer_operand && "pointer operand null?");
// If it is a null pointer which is being loaded, then it must seg
// fault, no dereference check here
if(isa<ConstantPointerNull>(pointer_operand))
return;
// Find all uses of pointer operand, then check if it dominates and
//if so, make a note in the map
GlobalVariable* gv = dyn_cast<GlobalVariable>(pointer_operand);
if(gv && GLOBALCONSTANTOPT && !isa<SequentialType>(gv->getType())) {
return;
}
if(BOUNDSCHECKOPT) {
// Enable dominator based dereference check optimization only when
// suggested
if(FDCE_map.count(load_store)) {
return;
}
// FIXME: Add more comments here Iterate over the uses
for(Value::use_iterator ui = pointer_operand->use_begin(),
ue = pointer_operand->use_end();
ui != ue; ++ui) {
Instruction* temp_inst = dyn_cast<Instruction>(*ui);
if(!temp_inst)
continue;
if(temp_inst == load_store)
continue;
if(!isa<LoadInst>(temp_inst) && !isa<StoreInst>(temp_inst))
continue;
if(isa<StoreInst>(temp_inst)){
if(temp_inst->getOperand(1) != pointer_operand){
// When a pointer is a being stored at at a particular
// address, don't elide the check
continue;
}
}
if(m_dominator_tree->dominates(load_store, temp_inst)) {
if(!FDCE_map.count(temp_inst)) {
FDCE_map[temp_inst] = true;
continue;
}
}
} // Iterating over uses ends
} // BOUNDSCHECKOPT ends
Value* tmp_base = NULL;
Value* tmp_bound = NULL;
Constant* given_constant = dyn_cast<Constant>(pointer_operand);
if(given_constant ) {
if(GLOBALCONSTANTOPT)
return;
getConstantExprBaseBound(given_constant, tmp_base, tmp_bound);
}
else {
tmp_base = getAssociatedBase(pointer_operand);
tmp_bound = getAssociatedBound(pointer_operand);
}
Value* bitcast_base = castToVoidPtr(tmp_base, load_store);
args.push_back(bitcast_base);
Value* bitcast_bound = castToVoidPtr(tmp_bound, load_store);
args.push_back(bitcast_bound);
Value* cast_pointer_operand_value = castToVoidPtr(pointer_operand,
load_store);
args.push_back(cast_pointer_operand_value);
// Pushing the size of the type
Type* pointer_operand_type = pointer_operand->getType();
Value* size_of_type = getSizeOfType(pointer_operand_type);
args.push_back(size_of_type);
if(isa<LoadInst>(load_store)){
CallInst::Create(m_spatial_load_dereference_check, args, "", load_store);
}
else{
CallInst::Create(m_spatial_store_dereference_check, args, "", load_store);
}
return;
}
//
// Method: optimizeGlobalAndStackVariables
//
// Description: This function elides temporal safety checks for stack
// and global variables.
bool
SoftBoundCETSPass::
optimizeGlobalAndStackVariableChecks(Instruction* load_store) {
Value* pointer_operand = NULL;
if(isa<LoadInst>(load_store)){
pointer_operand = load_store->getOperand(0);
} else{
pointer_operand = load_store->getOperand(1);
}
while(true) {
if(isa<AllocaInst>(pointer_operand)){
if(STACKTEMPORALCHECKOPT){
return true;
} else{
return false;
}
}
if(isa<GlobalVariable>(pointer_operand)){
if(GLOBALTEMPORALCHECKOPT){
return true;
} else{
return false;
}
}
if(isa<BitCastInst>(pointer_operand)){
BitCastInst* bitcast_inst = dyn_cast<BitCastInst>(pointer_operand);
pointer_operand = bitcast_inst->getOperand(0);
continue;
}
if(isa<GetElementPtrInst>(pointer_operand)){
GetElementPtrInst* gep = dyn_cast<GetElementPtrInst>(pointer_operand);
pointer_operand = gep->getOperand(0);
continue;
} else{
return false;
}
}
}
//
// Method: bbTemporalCheckElimination
//
// Description: This function eliminates the redundant temporal safety
// checks in the basic block
//
// Comments: Describe the algorithm here
bool
SoftBoundCETSPass::bbTemporalCheckElimination(Instruction* load_store,
std::map<Value*, int>& BBTCE_map){
if(!BBDOMTEMPORALCHECKOPT)
return false;
if(BBTCE_map.count(load_store))
return true;
// Check if the operand is a getelementptr, then get the first
// operand and check for all other load/store instructions in the
// current basic block and check if they are pointer operands are
// getelementptrs. If so, check if it is same the pointer being
// checked now
Value* pointer_operand = getPointerLoadStore(load_store);
Value* gep_source = NULL;
if (isa<GetElementPtrInst>(pointer_operand)) {
GetElementPtrInst* ptr_gep = cast<GetElementPtrInst>(pointer_operand);
gep_source = ptr_gep->getOperand(0);
} else {
gep_source = pointer_operand;
}
// Iterate over all other instructions in this basic block and look
// for gep_instructions with the same source
BasicBlock* bb_curr = load_store->getParent();
assert(bb_curr && "bb null?");
Instruction* next_inst = getNextInstruction(load_store);
BasicBlock* next_inst_bb = next_inst->getParent();
while((next_inst_bb == bb_curr) &&
(next_inst != bb_curr->getTerminator())) {
if(isa<CallInst>(next_inst) && OPAQUECALLS)
break;
if(checkLoadStoreSourceIsGEP(next_inst, gep_source)){
BBTCE_map[next_inst] = 1;
}
next_inst = getNextInstruction(next_inst);
next_inst_bb = next_inst->getParent();
}
return false;
}
//
// Method:getPointerLoadStore
//
// Description: This function obtains the pointer operand which is
// being dereferenced in the memory access.
Value*
SoftBoundCETSPass::getPointerLoadStore(Instruction* load_store) {
Value* pointer_operand = NULL;
if (isa<LoadInst>(load_store)) {
pointer_operand = load_store->getOperand(0);
}
if (isa<StoreInst>(load_store)) {
pointer_operand = load_store->getOperand(1);
}
assert((pointer_operand != NULL) && "pointer_operand null");
return pointer_operand;
}
//
// Method : checkLoadSourceIsGEP
//
// Description: This function is used to optimize temporal checks by
// identifying the root object of the pointer being dereferenced. If
// the pointer being deferenced is a bitcast or a GEP instruction then
// the source of GEP/bitcast is noted and checked to ascertain whether
// any check to the root object has been performed and not killed.
//
// Comments:
//
// TODO: A detailed algorithm here
bool
SoftBoundCETSPass::checkLoadStoreSourceIsGEP(Instruction* load_store,
Value* gep_source){
Value* pointer_operand = NULL;
if(!isa<LoadInst>(load_store) && !isa<StoreInst>(load_store))
return false;
if(isa<LoadInst>(load_store)){
pointer_operand = load_store->getOperand(0);
}
if(isa<StoreInst>(load_store)){
pointer_operand = load_store->getOperand(1);
}
assert(pointer_operand && "pointer_operand null?");
if(!isa<GetElementPtrInst>(pointer_operand))
return false;
GetElementPtrInst* gep_ptr = dyn_cast<GetElementPtrInst>(pointer_operand);
assert(gep_ptr && "gep_ptr null?");
Value* gep_ptr_operand = gep_ptr->getOperand(0);
if(gep_ptr_operand == gep_source)
return true;
return false;
}
//
// Method: funcTemporalCheckElimination
//
// Description: This function elides temporal checks for by performing
// root object identification at the function level.
bool
SoftBoundCETSPass::funcTemporalCheckElimination(Instruction* load_store,
std::map<Value*, int>& FTCE_map) {
if(!FUNCDOMTEMPORALCHECKOPT)
return false;
if(FTCE_map.count(load_store))
return true;
Value* pointer_operand = getPointerLoadStore(load_store);
Value* gep_source = NULL;
if(isa<GetElementPtrInst>(pointer_operand)){
GetElementPtrInst* ptr_gep = dyn_cast<GetElementPtrInst>(pointer_operand);
assert(ptr_gep && "[bbTemporalCheckElimination] gep_inst null?");
gep_source = ptr_gep->getOperand(0);
}
else {
gep_source = pointer_operand;
}
BasicBlock* bb_curr = load_store->getParent();
assert(bb_curr && "bb null?");
std::set<BasicBlock*> bb_visited;
std::queue<BasicBlock*> bb_worklist;
bb_worklist.push(bb_curr);
BasicBlock* bb = NULL;
while(bb_worklist.size() != 0){
bb = bb_worklist.front();
assert(bb && "Not a BasicBlock?");
bb_worklist.pop();
if(bb_visited.count(bb)){
continue;
}
bb_visited.insert(bb);
bool break_flag = false;
// Iterating over the successors and adding the successors to the
// work list
// if this is the current basic block under question
if(bb == bb_curr) {
// bbTemporalCheckElimination should handle this
Instruction* next_inst = getNextInstruction(load_store);
BasicBlock* next_inst_bb = next_inst->getParent();
while((next_inst_bb == bb_curr) &&
(next_inst != bb_curr->getTerminator())) {
if(isa<CallInst>(next_inst) && OPAQUECALLS){
break_flag = true;
break;
}
if(checkLoadStoreSourceIsGEP(next_inst, gep_source)){
if(m_dominator_tree->dominates(load_store, next_inst)){
FTCE_map[next_inst] = 1;
}
}
next_inst = getNextInstruction(next_inst);
next_inst_bb = next_inst->getParent();
}
} else {
for(BasicBlock::iterator i = bb->begin(), ie = bb->end(); i != ie; ++i){
Instruction* new_inst = dyn_cast<Instruction>(i);
if(isa<CallInst>(new_inst) && OPAQUECALLS){
break_flag = true;
break;
}
if(checkLoadStoreSourceIsGEP(new_inst, gep_source)){
if(m_dominator_tree->dominates(load_store, new_inst)){
FTCE_map[new_inst] = 1;
}
}
} // Iterating over the instructions in the basic block ends
}
for(succ_iterator si = succ_begin(bb), se = succ_end(bb); si != se; ++si) {
if(break_flag)
break;
BasicBlock* next_bb = cast<BasicBlock>(*si);
bb_worklist.push(next_bb);
}
} // Worklist algorithm ends
return false;
}
bool
SoftBoundCETSPass::optimizeTemporalChecks(Instruction* load_store,
std::map<Value*, int>& BBTCE_map,
std::map<Value*, int>& FTCE_map) {
if(optimizeGlobalAndStackVariableChecks(load_store))
return true;
if(bbTemporalCheckElimination(load_store, BBTCE_map))
return true;
if(funcTemporalCheckElimination(load_store, FTCE_map))
return true;
return false;
}
void
SoftBoundCETSPass::addTemporalChecks(Instruction* load_store,
std::map<Value*,int>& BBTCE_map,
std::map<Value*,int>& FTCE_map) {
SmallVector<Value*, 8> args;
Value* pointer_operand = NULL;
if(!temporal_safety)
return;
if(optimizeTemporalChecks(load_store, BBTCE_map, FTCE_map))
return;
if(isa<LoadInst>(load_store)) {
if(!TEMPORALLOADCHECKS)
return;
LoadInst* ldi = dyn_cast<LoadInst>(load_store);
assert(ldi && "not a load instruction");
pointer_operand = ldi->getPointerOperand();
}
if(isa<StoreInst>(load_store)){
if(!TEMPORALSTORECHECKS)
return;
StoreInst* sti = dyn_cast<StoreInst>(load_store);
assert(sti && "not a store instruction");
// The pointer where the element is being stored is the second
// operand
pointer_operand = sti->getOperand(1);
}
assert(pointer_operand && "pointer_operand null?");
if(isa<ConstantPointerNull>(pointer_operand))
return;
// Do not insert checks for globals and constant expressions
GlobalVariable* gv = dyn_cast<GlobalVariable>(pointer_operand);
if(gv) {
return;
}
Constant* given_constant = dyn_cast<Constant>(pointer_operand);
if(given_constant)
return;
/* Find all uses of pointer operand, then check if it
* dominates and if so, make a note in the map
*/
if(TEMPORALBOUNDSCHECKOPT) {
/* Enable dominator based dereference check optimization only
* when suggested
*/
if(FTCE_map.count(load_store)) {
return;
}
/* iterate over the uses */
for(Value::use_iterator ui = pointer_operand->use_begin(),
ue = pointer_operand->use_end(); ui != ue; ++ui) {
Instruction* temp_inst = cast<Instruction>(*ui);
if(!temp_inst)
continue;
if(temp_inst == load_store)
continue;
if(!isa<LoadInst>(temp_inst) && !isa<StoreInst>(temp_inst))
continue;
if(isa<StoreInst>(temp_inst)){
if(temp_inst->getOperand(1) != pointer_operand){
/* when a pointer is a being stored at at a particular
* address, don't elide the check
*/
continue;
}
}
if(m_dominator_tree->dominates(load_store, temp_inst)) {
if(!FTCE_map.count(temp_inst)) {
FTCE_map[temp_inst] = true;
continue;
}
}
} /* Iterating over uses ends */
} /* TEMPORALBOUNDSCHECKOPT ends */
Value* tmp_key = NULL;
Value* tmp_lock = NULL;
Value* tmp_base = NULL;
Value* tmp_bound = NULL;
tmp_key = getAssociatedKey(pointer_operand);
Value* func_tmp_lock = getAssociatedFuncLock(load_store);
tmp_lock = getAssociatedLock(pointer_operand, func_tmp_lock);
if(spatial_safety){
tmp_base = getAssociatedBase(pointer_operand);
tmp_bound = getAssociatedBound(pointer_operand);
}
assert(tmp_key && "[addTemporalChecks] pointer does not have key?");
assert(tmp_lock && "[addTemporalChecks] pointer does not have lock?");
Value* bitcast_lock = castToVoidPtr(tmp_lock, load_store);
args.push_back(bitcast_lock);
args.push_back(tmp_key);
if(spatial_safety){
args.push_back(tmp_base);
args.push_back(tmp_bound);
}
if(isa<LoadInst>(load_store)){
CallInst::Create(m_temporal_load_dereference_check, args, "", load_store);
}
else {
CallInst::Create(m_temporal_store_dereference_check, args, "", load_store);
}
return;
}
void SoftBoundCETSPass::addDereferenceChecks(Function* func) {
m_dominator_tree = &getAnalysis<DominatorTree>(*func);
if(func->isVarArg())
return;
if(metadata_prop_only)
return;
// if(func->getName() == "inflate_codes")
// return;
/* intra-procedural load dererference check elimination map */
std::map<Value*, int> func_deref_check_elim_map;
std::map<Value*, int> func_temporal_check_elim_map;
/* WorkList Algorithm for adding dereference checks. Each basic
* block is visited only once. We start by visiting the current
* basic block, then pushing all the successors of the current
* basic block on to the queue if it has not been visited
*/
std::set<BasicBlock*> bb_visited;
std::queue<BasicBlock*> bb_worklist;
Function:: iterator bb_begin = func->begin();
BasicBlock* bb = dyn_cast<BasicBlock>(bb_begin);
assert(bb && "Not a basic block and I am adding dereference checks?");
bb_worklist.push(bb);
while(bb_worklist.size() != 0) {
bb = bb_worklist.front();
assert(bb && "Not a BasicBlock?");
bb_worklist.pop();
if(bb_visited.count(bb)) {
/* Block already visited */
continue;
}
/* If here implies basic block not visited */
/* Insert the block into the set of visited blocks */
bb_visited.insert(bb);
/* Iterating over the successors and adding the successors to
* the worklist
*/
for(succ_iterator si = succ_begin(bb), se = succ_end(bb); si != se; ++si) {
BasicBlock* next_bb = *si;
assert(next_bb && "Not a basic block and I am adding to the base and bound worklist?");
bb_worklist.push(next_bb);
}
/* basic block load deref check optimization */
std::map<Value*, int> bb_deref_check_map;
std::map<Value*, int> bb_temporal_check_elim_map;
/* structure check optimization */
std::map<Value*, int> bb_struct_check_opt;
for(BasicBlock::iterator i = bb->begin(), ie = bb->end(); i != ie; ++i){
Value* v1 = dyn_cast<Value>(i);
Instruction* new_inst = dyn_cast<Instruction>(i);
/* Do the dereference check stuff */
if(!m_present_in_original.count(v1))
continue;
if(isa<LoadInst>(new_inst)){
if(store_only)
continue;
addLoadStoreChecks(new_inst, func_deref_check_elim_map);
addTemporalChecks(new_inst, bb_temporal_check_elim_map, func_temporal_check_elim_map);
continue;
}
if(isa<StoreInst>(new_inst)){
addLoadStoreChecks(new_inst, func_deref_check_elim_map);
addTemporalChecks(new_inst, bb_temporal_check_elim_map, func_temporal_check_elim_map);
continue;
}
/* check call through function pointers */
if(isa<CallInst>(new_inst)) {
if(!CALLCHECKS) {
continue;
}
SmallVector<Value*, 8> args;
CallInst* call_inst = dyn_cast<CallInst>(new_inst);
Value* tmp_base = NULL;
Value* tmp_bound = NULL;
assert(call_inst && "call instruction null?");
Function* func = call_inst->getCalledFunction();
if(func){
/* add memcopy checks if it is a memcopy function */
addMemcopyCheck(call_inst);
continue;
}
if(!INDIRECTCALLCHECKS)
continue;
/* TODO:URGENT : indirect function call checking commented
* out for the time being to test other aspect of the code,
* problem was with spec benchmarks perl and h264. They were
* primarily complaining that the use of a function did not
* have base and bound in the map
*/
/* here implies its an indirect call */
Value* indirect_func_called = call_inst->getOperand(0);
Constant* func_constant = dyn_cast<Constant>(indirect_func_called);
if(func_constant) {
getConstantExprBaseBound(func_constant, tmp_base, tmp_bound);
}
else {
tmp_base = getAssociatedBase(indirect_func_called);
tmp_bound = getAssociatedBound(indirect_func_called);
}
/* Add BitCast Instruction for the base */
Value* bitcast_base = castToVoidPtr(tmp_base, new_inst);
args.push_back(bitcast_base);
/* Add BitCast Instruction for the bound */
Value* bitcast_bound = castToVoidPtr(tmp_bound, new_inst);
args.push_back(bitcast_bound);
Value* pointer_operand_value = castToVoidPtr(indirect_func_called, new_inst);
args.push_back(pointer_operand_value);
CallInst::Create(m_call_dereference_func, args, "", new_inst);
continue;
} /* Call check ends */
}
}
}
void SoftBoundCETSPass::renameFunctions(Module& module){
bool change = false;
do{
change = false;
for(Module::iterator ff_begin = module.begin(), ff_end = module.end();
ff_begin != ff_end; ++ff_begin){
Function* func_ptr = dyn_cast<Function>(ff_begin);
if(m_func_transformed.count(func_ptr->getName()) ||
isFuncDefSoftBound(func_ptr->getName())){
continue;
}
m_func_transformed[func_ptr->getName()] = true;
m_func_transformed[transformFunctionName(func_ptr->getName())] = true;
bool is_external = func_ptr->isDeclaration();
renameFunctionName(func_ptr, module, is_external);
change = true;
break;
}
}while(change);
}
/* Renames a function by changing the function name to softboundcets_*
for only those functions have wrappers
*/
void SoftBoundCETSPass:: renameFunctionName(Function* func,
Module& module,
bool external) {
Type* ret_type = func->getReturnType();
const FunctionType* fty = func->getFunctionType();
std::vector<Type*> params;
if(!m_func_wrappers_available.count(func->getName()))
return;
if(func->getName() == "softboundcets_pseudo_main")
return;
AttributeSet param_attrs_vec;
#if 0
const AttrListPtr& pal = func->getAttributes();
if(Attributes attrs = pal.getRetAttributes())
param_attrs_vec.push_back(AttributeWithIndex::get(0, attrs));
#endif
int arg_index = 1;
for(Function::arg_iterator i = func->arg_begin(), e = func->arg_end();
i != e; ++i, arg_index++) {
params.push_back(i->getType());
#if 0
if(Attributes attrs = pal.getParamAttributes(arg_index))
param_attrs_vec.push_back(AttributeWithIndex::get(params.size(), attrs));
#endif
}
FunctionType* nfty = FunctionType::get(ret_type, params, fty->isVarArg());
Function* new_func = Function::Create(nfty, func->getLinkage(), transformFunctionName(func->getName()));
new_func->copyAttributesFrom(func);
new_func->setAttributes(param_attrs_vec);
func->getParent()->getFunctionList().insert(func, new_func);
if(!external) {
SmallVector<Value*, 16> call_args;
new_func->getBasicBlockList().splice(new_func->begin(), func->getBasicBlockList());
Function::arg_iterator arg_i2 = new_func->arg_begin();
for(Function::arg_iterator arg_i = func->arg_begin(), arg_e = func->arg_end();
arg_i != arg_e; ++arg_i) {
arg_i->replaceAllUsesWith(arg_i2);
arg_i2->takeName(arg_i);
++arg_i2;
arg_index++;
}
}
func->replaceAllUsesWith(new_func);
func->eraseFromParent();
}
void SoftBoundCETSPass::handleAlloca (AllocaInst* alloca_inst,
Value* alloca_key,
Value* alloca_lock,
Value* func_xmm_key_lock,
BasicBlock* bb,
BasicBlock::iterator& i) {
Value *alloca_inst_value = alloca_inst;
if(spatial_safety){
/* Get the base type of the alloca object For alloca instructions,
* instructions need to inserted after the alloca instruction LLVM
* provides interface for inserting before. So use the iterators
* and handle the case
*/
BasicBlock::iterator nextInst = i;
nextInst++;
Instruction* next = dyn_cast<Instruction>(nextInst);
assert(next && "Cannot increment the instruction iterator?");
unsigned num_operands = alloca_inst->getNumOperands();
/* For any alloca instruction, base is bitcast of alloca, bound is bitcast of alloca_ptr + 1
*/
PointerType* ptr_type = PointerType::get(alloca_inst->getAllocatedType(), 0);
Type* ty1 = ptr_type;
// Value* alloca_inst_temp_value = alloca_inst;
BitCastInst* ptr = new BitCastInst(alloca_inst, ty1, alloca_inst->getName(), next);
Value* ptr_base = castToVoidPtr(alloca_inst_value, next);
Value* intBound;
if(num_operands == 0) {
if(m_is_64_bit) {
intBound = ConstantInt::get(Type::getInt64Ty(alloca_inst->getType()->getContext()), 1, false);
}
else{
intBound = ConstantInt::get(Type::getInt32Ty(alloca_inst->getType()->getContext()), 1, false);
}
}
else {
intBound = alloca_inst->getOperand(0);
}
GetElementPtrInst* gep = GetElementPtrInst::Create(ptr,
intBound,
"mtmp",
next);
Value *bound_ptr = gep;
Value* ptr_bound = castToVoidPtr(bound_ptr, next);
associateBaseBound(alloca_inst_value, ptr_base, ptr_bound);
}
if(temporal_safety){
associateKeyLock(alloca_inst_value, alloca_key, alloca_lock);
}
}
void SoftBoundCETSPass::handleStore(StoreInst* store_inst) {
Value* operand = store_inst->getOperand(0);
Value* pointer_dest = store_inst->getOperand(1);
Instruction* insert_at = getNextInstruction(store_inst);
/* If a pointer is being stored, then the base and bound
* corresponding to the pointer must be stored in the shadow space
*/
if(!isa<PointerType>(operand->getType()))
return;
if(isa<ConstantPointerNull>(operand)) {
/* it is a constant pointer null being stored
* store null to the shadow space
*/
#if 0
StructType* ST = dyn_cast<StructType>(operand->getType());
if(ST){
if(ST->isOpaque()){
DEBUG(errs()<<"Opaque type found\n");
}
}
Value* size_of_type = getSizeOfType(operand->getType());
#endif
Value* size_of_type = NULL;
addStoreBaseBoundFunc(pointer_dest, m_void_null_ptr,
m_void_null_ptr, m_constantint64ty_zero,
m_void_null_ptr, m_void_null_ptr,
size_of_type, insert_at);
return;
}
/* if it is a global expression being stored, then add add
* suitable base and bound
*/
Value* tmp_base = NULL;
Value* tmp_bound = NULL;
Value* tmp_key = NULL;
Value* tmp_lock = NULL;
// Value* xmm_base_bound = NULL;
// Value* xmm_key_lock = NULL;
Constant* given_constant = dyn_cast<Constant>(operand);
if(given_constant) {
if(spatial_safety){
getConstantExprBaseBound(given_constant, tmp_base, tmp_bound);
assert(tmp_base && "global doesn't have base");
assert(tmp_bound && "global doesn't have bound");
}
if(temporal_safety){
tmp_key = m_constantint_one;
Value* func_lock = m_func_global_lock[store_inst->getParent()->getParent()->getName()];
tmp_lock = func_lock;
}
}
else {
/* storing an external function pointer */
if(spatial_safety){
if(!checkBaseBoundMetadataPresent(operand)) {
return;
}
}
if(temporal_safety){
if(!checkKeyLockMetadataPresent(operand)){
return;
}
}
if(spatial_safety){
tmp_base = getAssociatedBase(operand);
tmp_bound = getAssociatedBound(operand);
}
if(temporal_safety){
tmp_key = getAssociatedKey(operand);
Value* func_lock = getAssociatedFuncLock(store_inst);
tmp_lock = getAssociatedLock(operand, func_lock);
}
}
/* Store the metadata into the metadata space
*/
// Type* stored_pointer_type = operand->getType();
Value* size_of_type = NULL;
// Value* size_of_type = getSizeOfType(stored_pointer_type);
addStoreBaseBoundFunc(pointer_dest, tmp_base, tmp_bound, tmp_key, tmp_lock, operand, size_of_type, insert_at);
}
// Currently just a placeholder for functions introduced by us
bool SoftBoundCETSPass::checkIfFunctionOfInterest(Function* func) {
if(isFuncDefSoftBound(func->getName()))
return false;
if(func->isDeclaration())
return false;
/* TODO: URGENT: Need to do base and bound propagation in variable
* argument functions
*/
#if 0
if(func.isVarArg())
return false;
#endif
return true;
}
Instruction* SoftBoundCETSPass:: getGlobalInitInstruction(Module& module){
Function* global_init_function = module.getFunction("__softboundcets_global_init");
assert(global_init_function && "no __softboundcets_global_init function??");
Instruction *global_init_terminator = NULL;
bool return_inst_flag = false;
for(Function::iterator fi = global_init_function->begin(), fe = global_init_function->end(); fi != fe; ++fi) {
BasicBlock* bb = dyn_cast<BasicBlock>(fi);
assert(bb && "basic block null");
Instruction* bb_term = dyn_cast<Instruction>(bb->getTerminator());
assert(bb_term && "terminator null?");
if(isa<ReturnInst>(bb_term)) {
assert((return_inst_flag == false) && "has multiple returns?");
return_inst_flag = true;
global_init_terminator = dyn_cast<ReturnInst>(bb_term);
assert(global_init_terminator && "return inst null?");
}
}
assert(global_init_terminator && "global init does not have return, strange");
return global_init_terminator;
}
void SoftBoundCETSPass::handleGEP(GetElementPtrInst* gep_inst) {
Value* getelementptr_operand = gep_inst->getPointerOperand();
propagateMetadata(getelementptr_operand, gep_inst, SBCETS_GEP);
}
void SoftBoundCETSPass::handleMemcpy(CallInst* call_inst){
Function* func = call_inst->getCalledFunction();
if(!func)
return;
assert(func && "function is null?");
CallSite cs(call_inst);
Value* arg1 = cs.getArgument(0);
Value* arg2 = cs.getArgument(1);
Value* arg3 = cs.getArgument(2);
SmallVector<Value*, 8> args;
args.push_back(arg1);
args.push_back(arg2);
args.push_back(arg3);
if(arg3->getType() == Type::getInt64Ty(arg3->getContext())){
CallInst::Create(m_copy_metadata, args, "", call_inst);
}
else{
// CallInst::Create(m_copy_metadata, args, "", call_inst);
}
args.clear();
#if 0
Value* arg1_base = castToVoidPtr(getAssociatedBase(arg1), call_inst);
Value* arg1_bound = castToVoidPtr(getAssociatedBound(arg1), call_inst);
Value* arg2_base = castToVoidPtr(getAssociatedBase(arg2), call_inst);
Value* arg2_bound = castToVoidPtr(getAssociatedBound(arg2), call_inst);
args.push_back(arg1);
args.push_back(arg1_base);
args.push_back(arg1_bound);
args.push_back(arg2);
args.push_back(arg2_base);
args.push_back(arg2_bound);
args.push_back(arg3);
CallInst::Create(m_memcopy_check,args.begin(), args.end(), "", call_inst);
#endif
return;
}
void
SoftBoundCETSPass:: iterateCallSiteIntroduceShadowStackStores(CallInst* call_inst){
int pointer_args_return = getNumPointerArgsAndReturn(call_inst);
if(pointer_args_return == 0)
return;
int pointer_arg_no = 1;
CallSite cs(call_inst);
for(unsigned i = 0; i < cs.arg_size(); i++){
Value* arg_value = cs.getArgument(i);
if(isa<PointerType>(arg_value->getType())){
introduceShadowStackStores(arg_value, call_inst, pointer_arg_no);
pointer_arg_no++;
}
}
}
void SoftBoundCETSPass::handleExtractValue(ExtractValueInst* EVI){
if(spatial_safety){
associateBaseBound(EVI, m_void_null_ptr, m_infinite_bound_ptr);
}
if(temporal_safety){
Value* func_temp_lock = getAssociatedFuncLock(EVI);
associateKeyLock(EVI, m_constantint64ty_one, func_temp_lock);
}
return;
}
void SoftBoundCETSPass::handleCall(CallInst* call_inst) {
// Function* func = call_inst->getCalledFunction();
Value* mcall = call_inst;
#if 0
CallingConv::ID id = call_inst->getCallingConv();
if(id == CallingConv::Fast){
printf("fast calling convention not handled\n");
exit(1);
}
#endif
Function* func = call_inst->getCalledFunction();
if(func && func->getName().find("llvm.memcpy") == 0){
handleMemcpy(call_inst);
return;
}
if(func && isFuncDefSoftBound(func->getName())){
if(spatial_safety){
associateBaseBound(call_inst, m_void_null_ptr, m_void_null_ptr);
}
if(temporal_safety){
associateKeyLock(call_inst, m_constantint64ty_zero, m_void_null_ptr);
}
return;
}
Instruction* insert_at = getNextInstruction(call_inst);
// call_inst->setCallingConv(CallingConv::C);
introduceShadowStackAllocation(call_inst);
iterateCallSiteIntroduceShadowStackStores(call_inst);
if(isa<PointerType>(mcall->getType())) {
/* ShadowStack for the return value is 0 */
introduceShadowStackLoads(call_inst, insert_at, 0);
}
introduceShadowStackDeallocation(call_inst,insert_at);
}
void SoftBoundCETSPass::handleIntToPtr(IntToPtrInst* inttoptrinst) {
Value* inst = inttoptrinst;
if(spatial_safety){
associateBaseBound(inst, m_void_null_ptr, m_void_null_ptr);
}
if(temporal_safety){
associateKeyLock(inst, m_constantint64ty_zero, m_void_null_ptr);
}
}
void SoftBoundCETSPass::gatherBaseBoundPass2(Function* func){
/* WorkList Algorithm for propagating base and bound. Each basic
* block is visited only once
*/
std::set<BasicBlock*> bb_visited;
std::queue<BasicBlock*> bb_worklist;
Function::iterator bb_begin = func->begin();
BasicBlock* bb = dyn_cast<BasicBlock>(bb_begin);
assert(bb && "Not a basic block and gathering base bound in the next pass?");
bb_worklist.push(bb);
while( bb_worklist.size() != 0) {
bb = bb_worklist.front();
assert(bb && "Not a BasicBlock?");
bb_worklist.pop();
if( bb_visited.count(bb)) {
/* Block already visited */
continue;
}
/* If here implies basic block not visited */
/* Insert the block into the set of visited blocks */
bb_visited.insert(bb);
/* Iterating over the successors and adding the successors to
* the work list
*/
for(succ_iterator si = succ_begin(bb), se = succ_end(bb); si != se; ++si) {
BasicBlock* next_bb = *si;
assert(next_bb && "Not a basic block and I am adding to the base and bound worklist?");
bb_worklist.push(next_bb);
}
for(BasicBlock::iterator i = bb->begin(), ie = bb->end(); i != ie; ++i) {
Value* v1 = dyn_cast<Value>(i);
Instruction* new_inst = dyn_cast<Instruction>(i);
// If the instruction is not present in the original, no instrumentaion
if(!m_present_in_original.count(v1))
continue;
switch(new_inst->getOpcode()) {
case Instruction::GetElementPtr:
{
GetElementPtrInst* gep_inst = dyn_cast<GetElementPtrInst>(v1);
assert(gep_inst && "Not a GEP instruction?");
handleGEP(gep_inst);
}
break;
case Instruction::Store:
{
StoreInst* store_inst = dyn_cast<StoreInst>(v1);
assert(store_inst && "Not a Store instruction?");
handleStore(store_inst);
}
break;
case Instruction::PHI:
{
PHINode* phi_node = dyn_cast<PHINode>(v1);
assert(phi_node && "Not a PHINode?");
handlePHIPass2(phi_node);
}
break;
case BitCastInst::BitCast:
{
BitCastInst* bitcast_inst = dyn_cast<BitCastInst>(v1);
assert(bitcast_inst && "Not a bitcast instruction?");
handleBitCast(bitcast_inst);
}
break;
case SelectInst::Select:
{
}
break;
default:
break;
}/* Switch Ends */
}/* BasicBlock iterator Ends */
}/* Function iterator Ends */
}
void SoftBoundCETSPass::introspectMetadata(Function* func, Value* ptr_value, Instruction* insert_at, int arg_no){
if(func->getName() != "debug_instrument_softboundcets")
return;
Value* ptr_base = getAssociatedBase(ptr_value);
Value* ptr_bound = getAssociatedBound(ptr_value);
Value* ptr_value_cast = castToVoidPtr(ptr_value, insert_at);
Value* ptr_base_cast = castToVoidPtr(ptr_base, insert_at);
Value* ptr_bound_cast = castToVoidPtr(ptr_bound, insert_at);
Value* argno_value;
argno_value = ConstantInt::get(Type::getInt32Ty(ptr_value->getType()->getContext()), arg_no, false);
SmallVector<Value*, 8> args;
args.push_back(ptr_value_cast);
args.push_back(ptr_base_cast);
args.push_back(ptr_bound_cast);
args.push_back(argno_value);
CallInst::Create(m_introspect_metadata, args, "", insert_at);
}
void SoftBoundCETSPass::freeFunctionKeyLock(Function* func, Value* & func_key, Value* & func_lock, Value* & func_xmm_key_lock) {
if(func_key == NULL && func_lock == NULL){
return;
}
if((func_key == NULL && func_lock != NULL) && (func_key != NULL && func_lock == NULL)){
assert(0 && "inconsistent key lock");
}
Function::iterator bb_begin = func->begin();
Instruction* next_inst = NULL;
for(Function::iterator b = func->begin(), be = func->end(); b != be ; ++b) {
BasicBlock* bb = dyn_cast<BasicBlock>(b);
assert(bb && "basic block does not exist?");
for(BasicBlock::iterator i = bb->begin(), ie = bb->end(); i != ie; ++i) {
next_inst = dyn_cast<Instruction>(i);
if(!isa<ReturnInst>(next_inst))
continue;
ReturnInst* ret = dyn_cast<ReturnInst>(next_inst);
/* Insert a call to deallocate key and lock*/
SmallVector<Value*, 8> args;
Instruction* first_inst_func = dyn_cast<Instruction>(func->begin()->begin());
assert(first_inst_func && "function doesn't have any instruction ??");
args.push_back(func_key);
CallInst::Create(m_temporal_stack_memory_deallocation, args, "", ret);
}
}
}
bool SoftBoundCETSPass::checkPtrsInST(StructType* struct_type){
StructType::element_iterator I = struct_type->element_begin();
bool ptr_flag = false;
for(StructType::element_iterator E = struct_type->element_end(); I != E; ++I){
Type* element_type = *I;
if(isa<StructType>(element_type)){
StructType* struct_element_type = dyn_cast<StructType>(element_type);
bool recursive_flag = checkPtrsInST(struct_element_type);
ptr_flag = ptr_flag | recursive_flag;
}
if(isa<PointerType>(element_type)){
ptr_flag = true;
}
if(isa<ArrayType>(element_type)){
ptr_flag = true;
}
}
return ptr_flag;
}
bool SoftBoundCETSPass::checkTypeHasPtrs(Argument* ptr_argument){
if(!ptr_argument->hasByValAttr())
return false;
SequentialType* seq_type = dyn_cast<SequentialType>(ptr_argument->getType());
assert(seq_type && "byval attribute with non-sequential type pointer, not handled?");
StructType* struct_type = dyn_cast<StructType>(seq_type->getElementType());
if(struct_type){
bool has_ptrs = checkPtrsInST(struct_type);
return has_ptrs;
}
else{
assert(0 && "non-struct byval parameters?");
}
// By default we assume any struct can return pointers
return true;
}
void SoftBoundCETSPass::gatherBaseBoundPass1 (Function * func) {
Value* func_key = NULL;
Value* func_lock = NULL;
Value* func_xmm_key_lock = NULL;
int arg_count= 0;
// std::cerr<<"transforming function with name:"<<func->getName()<< "\n";
/* Scan over the pointer arguments and introduce base and bound */
for(Function::arg_iterator ib = func->arg_begin(), ie = func->arg_end();
ib != ie; ++ib) {
if(!isa<PointerType>(ib->getType()))
continue;
/* it is a pointer, so increment the arg count */
arg_count++;
Argument* ptr_argument = dyn_cast<Argument>(ib);
Value* ptr_argument_value = ptr_argument;
Instruction* fst_inst = func->begin()->begin();
/* Urgent: Need to think about what we need to do about byval attributes */
if(ptr_argument->hasByValAttr()){
if(!unsafe_byval_opt){
if(checkTypeHasPtrs(ptr_argument)){
assert(0 && "Pointer argument has byval attributes and the underlying structure returns pointers");
}
}
if(spatial_safety){
associateBaseBound(ptr_argument_value, m_void_null_ptr, m_infinite_bound_ptr);
}
if(temporal_safety){
Value* func_temp_lock = getAssociatedFuncLock(func->begin()->begin());
associateKeyLock(ptr_argument_value, m_constantint64ty_one, func_temp_lock);
}
}
else{
introduceShadowStackLoads(ptr_argument_value, fst_inst, arg_count);
// introspectMetadata(func, ptr_argument_value, fst_inst, arg_count);
}
}
getFunctionKeyLock(func, func_key, func_lock, func_xmm_key_lock);
#if 0
if(temporal_safety){
if(func_key == NULL || func_lock == NULL){
assert(0 && "function key lock null for the function");
}
}
#endif
/* WorkList Algorithm for propagating the base and bound. Each
* basic block is visited only once. We start by visiting the
* current basic block, then push all the successors of the
* current basic block on to the queue if it has not been visited
*/
std::set<BasicBlock*> bb_visited;
std::queue<BasicBlock*> bb_worklist;
Function:: iterator bb_begin = func->begin();
BasicBlock* bb = dyn_cast<BasicBlock>(bb_begin);
assert( bb && "Not a basic block and I am gathering base and bound?");
bb_worklist.push(bb);
while(bb_worklist.size() != 0) {
bb = bb_worklist.front();
assert(bb && "Not a BasicBlock?");
bb_worklist.pop();
if( bb_visited.count(bb)) {
/* Block already visited */
continue;
}
/* If here implies basic block not visited */
/* Insert the block into the set of visited blocks */
bb_visited.insert(bb);
/* Iterating over the successors and adding the successors to
* the work list
*/
for(succ_iterator si = succ_begin(bb), se = succ_end(bb); si != se; ++si) {
BasicBlock* next_bb = *si;
assert(next_bb && "Not a basic block and I am adding to the base and bound worklist?");
bb_worklist.push(next_bb);
}
for(BasicBlock::iterator i = bb->begin(), ie = bb->end(); i != ie; ++i){
Value* v1 = dyn_cast<Value>(i);
Instruction* new_inst = dyn_cast<Instruction>(i);
/* If the instruction is not present in the original, no
* instrumentaion
*/
if(!m_present_in_original.count(v1)) {
continue;
}
/* All instructions have been defined here as defining it in
* switch causes compilation errors. Assertions have been in
* the inserted in the specific cases
*/
switch(new_inst->getOpcode()) {
case Instruction::Alloca:
{
AllocaInst* alloca_inst = dyn_cast<AllocaInst>(v1);
assert(alloca_inst && "Not an Alloca inst?");
handleAlloca(alloca_inst, func_key, func_lock, func_xmm_key_lock, bb, i);
}
break;
case Instruction::Load:
{
LoadInst* load_inst = dyn_cast<LoadInst>(v1);
assert(load_inst && "Not a Load inst?");
handleLoad(load_inst);
}
break;
case Instruction::GetElementPtr:
{
GetElementPtrInst* gep_inst = dyn_cast<GetElementPtrInst>(v1);
assert(gep_inst && "Not a GEP inst?");
handleGEP(gep_inst);
}
break;
case BitCastInst::BitCast:
{
BitCastInst* bitcast_inst = dyn_cast<BitCastInst>(v1);
assert(bitcast_inst && "Not a BitCast inst?");
handleBitCast(bitcast_inst);
}
break;
case Instruction::PHI:
{
PHINode* phi_node = dyn_cast<PHINode>(v1);
assert(phi_node && "Not a phi node?");
//printInstructionMap(v1);
handlePHIPass1(phi_node);
}
/* PHINode ends */
break;
case Instruction::Call:
{
CallInst* call_inst = dyn_cast<CallInst>(v1);
assert(call_inst && "Not a Call inst?");
handleCall(call_inst);
}
break;
case Instruction::Select:
{
SelectInst* select_insn = dyn_cast<SelectInst>(v1);
assert(select_insn && "Not a select inst?");
int pass = 1;
handleSelect(select_insn, pass);
}
break;
case Instruction::Store:
{
break;
}
case Instruction::IntToPtr:
{
IntToPtrInst* inttoptrinst = dyn_cast<IntToPtrInst>(v1);
assert(inttoptrinst && "Not a IntToPtrInst?");
handleIntToPtr(inttoptrinst);
break;
}
case Instruction::Ret:
{
ReturnInst* ret = dyn_cast<ReturnInst>(v1);
assert(ret && "not a return inst?");
handleReturnInst(ret);
}
break;
case Instruction::ExtractValue:
{
ExtractValueInst * EVI = dyn_cast<ExtractValueInst>(v1);
assert(EVI && "hanlde extract value inst?");
handleExtractValue(EVI);
}
break;
default:
if(isa<PointerType>(v1->getType()))
assert(!isa<PointerType>(v1->getType())&&
" Generating Pointer and not being handled");
}
}/* Basic Block iterator Ends */
} /* Function iterator Ends */
if(temporal_safety){
freeFunctionKeyLock(func, func_key, func_lock, func_xmm_key_lock);
}
}
/* isByValDerived: This function check whether loaded address is
dervied by a byval argument */
bool SoftBoundCETSPass:: isByValDerived(Value* pointer_operand){
int count = 0;
while(true){
count++;
if(count > 50){
assert(0 && "isByValDerived probably looping infinitely");
}
if(isa<GetElementPtrInst>(pointer_operand)){
GetElementPtrInst* gep = dyn_cast<GetElementPtrInst>(pointer_operand);
pointer_operand = gep->getOperand(0);
continue;
}
if(isa<AllocaInst>(pointer_operand)){
return false;
}
if(isa<Argument>(pointer_operand)){
Argument* arg = dyn_cast<Argument>(pointer_operand);
return arg->hasByValAttr();
}
if(isa<BitCastInst>(pointer_operand)){
BitCastInst* bitcast = dyn_cast<BitCastInst>(pointer_operand);
pointer_operand = bitcast->getOperand(0);
continue;
}
if(isa<PHINode>(pointer_operand)){
PHINode* phi_node = dyn_cast<PHINode>(pointer_operand);
unsigned num_values = phi_node->getNumIncomingValues();
bool arg_flag = false;
for(unsigned i = 0; i < num_values; i++){
Value* temp_operand = phi_node->getOperand(i);
if(isa<PHINode>(temp_operand))
return false;
arg_flag = arg_flag | isByValDerived(temp_operand);
}
return arg_flag;
}
if(isa<LoadInst>(pointer_operand)){
return false;
}
if(isa<Constant>(pointer_operand)){
return false;
}
if(isa<CallInst>(pointer_operand)){
return false;
}
}
}
/* handleLoad Takes a load_inst If the load is through a pointer
* which is a global then inserts base and bound for that global
* Also if the loaded value is a pointer then loads the base and
* bound for for the pointer from the shadow space
*/
void SoftBoundCETSPass::handleLoad(LoadInst* load_inst) {
AllocaInst* base_alloca = 0;
AllocaInst* bound_alloca = 0;
AllocaInst* key_alloca = 0;
AllocaInst* lock_alloca = 0;
SmallVector<Value*, 8> args;
if(!isa<PointerType>(load_inst->getType()))
return;
if(unsafe_byval_opt && isByValDerived(load_inst->getOperand(0))) {
if(spatial_safety){
associateBaseBound(load_inst, m_void_null_ptr, m_infinite_bound_ptr);
}
if(temporal_safety){
Value* func_lock = getAssociatedFuncLock(load_inst);
associateKeyLock(load_inst, m_constantint64ty_one, func_lock);
}
return;
}
Value* load_inst_value = load_inst;
Value* pointer_operand = load_inst->getPointerOperand();
Instruction* load = load_inst;
Instruction* insert_at = getNextInstruction(load);
/* If the load returns a pointer, then load the base and bound
* from the shadow space
*/
Value* pointer_operand_bitcast = castToVoidPtr(pointer_operand, insert_at);
Instruction* first_inst_func = dyn_cast<Instruction>(load_inst->getParent()->getParent()->begin()->begin());
assert(first_inst_func && "function doesn't have any instruction and there is load???");
/* address of pointer being pushed */
args.push_back(pointer_operand_bitcast);
if(spatial_safety){
base_alloca = new AllocaInst(m_void_ptr_type, "base.alloca", first_inst_func);
bound_alloca = new AllocaInst(m_void_ptr_type, "bound.alloca", first_inst_func);
/* base */
args.push_back(base_alloca);
/* bound */
args.push_back(bound_alloca);
}
if(temporal_safety){
key_alloca = new AllocaInst(Type::getInt64Ty(load_inst->getType()->getContext()), "key.alloca", first_inst_func);
lock_alloca = new AllocaInst(m_void_ptr_type, "lock.alloca", first_inst_func);
args.push_back(key_alloca);
args.push_back(lock_alloca);
}
CallInst::Create(m_load_base_bound_func, args, "", insert_at);
if(spatial_safety){
Instruction* base_load = new LoadInst(base_alloca, "base.load", insert_at);
Instruction* bound_load = new LoadInst(bound_alloca, "bound.load", insert_at);
associateBaseBound(load_inst_value, base_load, bound_load);
}
if(temporal_safety){
Instruction* key_load = new LoadInst(key_alloca, "key.load", insert_at);
Instruction* lock_load = new LoadInst(lock_alloca, "lock.load", insert_at);
associateKeyLock(load_inst_value, key_load, lock_load);
}
}
/* Identify the initial globals present in the program before we add
* extra base and bound for all globals
*/
void SoftBoundCETSPass::identifyInitialGlobals(Module& module) {
for(Module::global_iterator it = module.global_begin(),
ite = module.global_end();
it != ite; ++it) {
GlobalVariable* gv = dyn_cast<GlobalVariable>(it);
if(gv) {
m_initial_globals[gv] = true;
}
}
}
void SoftBoundCETSPass::addBaseBoundGlobals(Module& M){
/* iterate over the globals here */
for(Module::global_iterator it = M.global_begin(), ite = M.global_end(); it != ite; ++it){
GlobalVariable* gv = dyn_cast<GlobalVariable>(it);
if(!gv){
continue;
}
if(gv->getSection() == "llvm.metadata"){
continue;
}
if(gv->getName() == "llvm.global_ctors"){
continue;
}
if(!gv->hasInitializer())
continue;
/* gv->hasInitializer() is true */
Constant* initializer = dyn_cast<Constant>(it->getInitializer());
ConstantArray* constant_array = dyn_cast<ConstantArray>(initializer);
if(initializer && isa<CompositeType>(initializer->getType())){
if(isa<StructType>(initializer->getType())){
std::vector<Constant*> indices_addr_ptr;
Constant* index1 = ConstantInt::get(Type::getInt32Ty(M.getContext()), 0);
indices_addr_ptr.push_back(index1);
StructType* struct_type = dyn_cast<StructType>(initializer->getType());
handleGlobalStructTypeInitializer(M, struct_type, initializer, gv, indices_addr_ptr, 1);
continue;
}
if(isa<SequentialType>(initializer->getType())){
handleGlobalSequentialTypeInitializer(M, gv);
}
}
if(initializer && !constant_array){
if(isa<PointerType>(initializer->getType())){
// std::cerr<<"Pointer type initializer\n";
}
}
if(!constant_array)
continue;
int num_ca_opds = constant_array->getNumOperands();
for(int i = 0; i < num_ca_opds; i++){
Value* initializer_opd = constant_array->getOperand(i);
Instruction* first = getGlobalInitInstruction(M);
Value* operand_base = NULL;
Value* operand_bound = NULL;
Constant* global_constant_initializer = dyn_cast<Constant>(initializer_opd);
if(!isa<PointerType>(global_constant_initializer->getType())){
break;
}
getConstantExprBaseBound(global_constant_initializer, operand_base, operand_bound);
SmallVector<Value*, 8> args;
Constant* index1 = ConstantInt::get(Type::getInt32Ty(M.getContext()), 0);
Constant* index2 = ConstantInt::get(Type::getInt32Ty(M.getContext()), i);
std::vector<Constant*> indices_addr_ptr;
indices_addr_ptr.push_back(index1);
indices_addr_ptr.push_back(index2);
Constant* addr_of_ptr = ConstantExpr::getGetElementPtr(gv, indices_addr_ptr);
Type* initializer_type = initializer_opd->getType();
Value* initializer_size = getSizeOfType(initializer_type);
Value* operand_key = NULL;
Value* operand_lock = NULL;
if(temporal_safety){
operand_key = m_constantint_one;
operand_lock = introduceGlobalLockFunction(first);
}
addStoreBaseBoundFunc(addr_of_ptr, operand_base, operand_bound, operand_key, operand_lock, initializer_opd, initializer_size, first);
}
}
}
void SoftBoundCETSPass::identifyOriginalInst (Function * func) {
for(Function::iterator bb_begin = func->begin(), bb_end = func->end();
bb_begin != bb_end; ++bb_begin) {
for(BasicBlock::iterator i_begin = bb_begin->begin(),
i_end = bb_begin->end(); i_begin != i_end; ++i_begin){
Value* insn = dyn_cast<Value>(i_begin);
if(!m_present_in_original.count(insn)) {
m_present_in_original[insn] = 1;
}
else {
assert(0 && "present in original map already has the insn?");
}
if(isa<PointerType>(insn->getType())) {
if(!m_is_pointer.count(insn)){
m_is_pointer[insn] = 1;
}
}
} /* BasicBlock ends */
}/* Function ends */
}
bool SoftBoundCETSPass::runOnModule(Module& module) {
if (module.getPointerSize() == llvm::Module::Pointer64) {
m_is_64_bit = true;
} else {
m_is_64_bit = false;
}
initializeSoftBoundVariables(module);
transformMain(module);
identifyFuncToTrans(module);
identifyInitialGlobals(module);
addBaseBoundGlobals(module);
for(Module::iterator ff_begin = module.begin(), ff_end = module.end();
ff_begin != ff_end; ++ff_begin){
Function* func_ptr = dyn_cast<Function>(ff_begin);
assert(func_ptr && "Not a function??");
//
// No instrumentation for functions introduced by us for updating
// and retrieving the shadow space
//
if (!checkIfFunctionOfInterest(func_ptr)) {
continue;
}
//
// Iterating over the instructions in the function to identify IR
// instructions in the original program In this pass, the pointers
// in the original program are also identified
//
identifyOriginalInst(func_ptr);
//
// Iterate over all basic block and then each insn within a basic
// block We make two passes over the IR for base and bound
// propagation and one pass for dereference checks
//
if (temporal_safety) {
Value* func_global_lock =
introduceGlobalLockFunction(func_ptr->begin()->begin());
m_func_global_lock[func_ptr->getName()] = func_global_lock;
}
gatherBaseBoundPass1(func_ptr);
gatherBaseBoundPass2(func_ptr);
addDereferenceChecks(func_ptr);
}
renameFunctions(module);
DEBUG(errs()<<"Done with SoftBoundCETSPass\n");
/* print the external functions not wrapped */
for(Module::iterator ff_begin = module.begin(), ff_end = module.end();
ff_begin != ff_end; ++ff_begin){
Function* func_ptr = dyn_cast<Function>(ff_begin);
assert(func_ptr && "not a function??");
if(func_ptr->isDeclaration()){
if(!isFuncDefSoftBound(func_ptr->getName()) &&
!(m_func_wrappers_available.count(func_ptr->getName()))){
DEBUG(errs()<<"External function not wrapped:"<<
func_ptr->getName()<<"\n");
}
}
}
return true;
}