blob: 773b3dd9201966a248de87558acd9647001d9f9b [file] [log] [blame]
//===- DetectDanglingPointers.cpp - Insert calls to mark objects read-only --//
//
// The SAFECode Compiler
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass instruments a program so that it marks the shadow pages of
// heap objects read-only; this is used for the dangling pointer detection as
// described in the DSN 2006 paper "Efficiently Detecting All Dangling Pointer
// Uses in Production Servers."
//
// Notes:
// o) This pass must be run before the pass that adds poolunregister() calls.
// This is because the run-time must change the memory protections before
// unregistering the object.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "dpchecks"
#include "llvm/ADT/Statistic.h"
#include "safecode/SAFECode.h"
#include "safecode/SAFECodeConfig.h"
#include "safecode/Support/AllocatorInfo.h"
#include "safecode/DetectDanglingPointers.h"
#include <iostream>
#include <utility>
#include <vector>
char NAMESPACE_SC::DetectDanglingPointers::ID = 0;
NAMESPACE_SC_BEGIN
// Statistics
STATISTIC (Changes, "Number of Shadowing Calls Inserted");
//
// Method: createFunctionProtos()
//
// Description:
// Create the function prototypes for shadowing and unshadowing objects.
//
void
DetectDanglingPointers::createFunctionProtos (Module & M) {
//
// Get basic integer and pointer types.
//
const Type * Int8Type = IntegerType::getInt8Ty(getGlobalContext());
const Type * Int32Type = IntegerType::getInt32Ty(getGlobalContext());
Type * VoidPtrTy = PointerType::getUnqual(Int8Type);
//
// Get the function that unshadows heap objects.
//
std::vector<const Type *> Arg(1, VoidPtrTy);
FunctionType * Ty = FunctionType::get(VoidPtrTy, Arg, false);
ProtectObj = M.getOrInsertFunction("pool_unshadow", Ty);
//
// Get the function that shadows heap objects.
//
Arg.push_back (Int32Type);
Ty = FunctionType::get(VoidPtrTy, Arg, false);
ShadowObj = M.getOrInsertFunction("pool_shadow", Ty);
return;
}
void
DetectDanglingPointers::processFrees (Module & M,
std::set<Function *> & FreeFuncs) {
//
// Scan through all uses of all heap deallocation functions. For each one,
// insert a call to the run-time library that will change the page
// protections so that reads and writes to the object will cause a hardware
// fault.
//
std::vector<std::pair<CallInst *, Value *> > Worklist;
SAFECodeConfiguration::AllocatorInfoListTy::iterator i;
for (i = SCConfig.alloc_begin(); i != SCConfig.alloc_end(); ++i) {
// Get the allocator information structure
AllocatorInfo * info = *i;
//
// Clear the work list.
//
Worklist.clear();
// Reference to the deallocation function
Function * freeFunc = M.getFunction(info->getFreeCallName());
if (freeFunc) {
//
// Record the deallocation function in the set so that we can quickly
// look it up later.
//
FreeFuncs.insert (freeFunc);
//
// Iterate over all uses of the free function and add instrumentation.
//
Value::use_iterator it, end;
for (it = freeFunc->use_begin(), end = freeFunc->use_end();
it != end;
++it) {
if (CallInst * CI = dyn_cast<CallInst>(*it)) {
//
// Backup one instruction since the preceding instruction should be
// a call to poolunregister().
//
BasicBlock::iterator InsertPt = CI;
assert (InsertPt != CI->getParent()->begin());
--InsertPt;
//
// Create the call.
//
Value * Pointer = info->getFreedPointer (CI);
CallInst * OrigPtr = CallInst::Create (ProtectObj,
Pointer,
"",
InsertPt);
//
// Add to the worklist the call instruction that we will need to
// change and the new pointer value that should be freed.
//
Worklist.push_back (std::make_pair(CI, OrigPtr));
}
}
}
//
// Update the statistics if the worklist has any elements. This avoids
// printing a statistic of zero in the results.
//
if (Worklist.size()) Changes += Worklist.size();
//
// Go through the work list and change all of the deallocation calls to
// use the original pointer returned from the pool_unshadow() call.
//
while (Worklist.size()) {
CallInst * FreeCall = Worklist.back().first;
Value * OrigPtr = Worklist.back().second;
Worklist.pop_back();
FreeCall->setOperand (2, OrigPtr);
}
}
return;
}
bool
DetectDanglingPointers::runOnModule (Module & M) {
//
// If dangling pointer protection is disabled, do nothing.
//
if (!(SCConfig.dpChecks())) return false;
//
// Get prerequisite analysis results.
//
intrinPass = &getAnalysis<InsertSCIntrinsic>();
//
// Create the functions for shadowing and unshadowing objects.
//
createFunctionProtos (M);
//
// Process the deallocation functions first. This allows us to collect the
// a list of the deallocation functions while instrumenting them so that they
// free the originally allocated object and not the shadow object.
//
std::set<Function *> FreeFuncs;
processFrees (M, FreeFuncs);
//
// Scan through all calls to allocation functions. For each allocation,
// add a call after it to remap the object to a shadow object. Then, replace
// all uses of the original pointer with the shadow pointer.
//
SAFECodeConfiguration::AllocatorInfoListTy::iterator i;
for (i = SCConfig.alloc_begin(); i != SCConfig.alloc_end(); ++i) {
// Get the allocator information structure
AllocatorInfo * info = *i;
// Reference to the allocation function
Function * allocFunc = M.getFunction(info->getAllocCallName());
if (allocFunc) {
//
// Iterate over all uses of the allocation function.
//
Value::use_iterator it, end;
for (it = allocFunc->use_begin(), end = allocFunc->use_end();
it != end;
++it) {
if (CallInst * CI = dyn_cast<CallInst>(*it)) {
if (CI->getCalledFunction() == allocFunc) {
//
// This is an allocation site. Add a call after it to create a
// shadow copy of the allocated object.
//
std::vector<Value *> args;
args.push_back (CI);
args.push_back (info->getAllocSize (CI));
BasicBlock::iterator InsertPt = CI;
++InsertPt;
CallInst * Shadow = CallInst::Create (ShadowObj, args.begin(), args.end(), "", InsertPt);
//
// Replace all uses of the originally allocated pointer with the
// shadow pointer.
//
CI->replaceAllUsesWith (Shadow);
//
// The previous statement modified the call to pool_shadow() so
// that it takes its return value as its argument. Change its
// argument back to the original allocated object.
//
Shadow->setOperand (1, CI);
//
// Update the statistics.
//
++Changes;
}
}
}
}
}
//
// We most likely changed something; conservatively claim that we made
// modifications.
//
return true;
}
NAMESPACE_SC_END