| ///=== 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; |
| } |