blob: 2e19a04f65b46e77f7b64dc2eb6ed31f3f8d91e9 [file] [log] [blame]
//===- AllocaWithAggregateTypes.cpp - Initialize allocas with pointers -------//
//
// 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 file implements a pass that ensures that uninitialized memory created
// by alloca instructions is not used to violate memory safety. It can do this
// in one of two ways:
//
// o) Promote the allocations from stack to heap.
// o) Insert code to initialize the newly allocated memory.
//
// The current implementation implements the latter, but code for the former is
// available but disabled.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "init-allocas"
#include "safecode/Config/config.h"
#include "ConvertUnsafeAllocas.h"
#include "SCUtils.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/VectorExtras.h"
#include "llvm/Pass.h"
#include "llvm/BasicBlock.h"
#include "llvm/Type.h"
#include "llvm/Function.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Constants.h"
#include "llvm/Instructions.h"
#include "llvm/Module.h"
#include "llvm/Support/Debug.h"
#include <iostream>
using namespace llvm;
namespace {
STATISTIC (InitedAllocas, "Allocas Initialized");
}
//
// Basic LLVM Types
//
static const Type * VoidType = 0;
static const Type * Int8Type = 0;
static const Type * Int32Type = 0;
NAMESPACE_SC_BEGIN
//
// Constant: meminitvalue
//
// Description:
// This is the byte value that is used to initialize newly allocated memory.
// Pointers will be initialized to this value concatenated 4 times
// e.g., 0xcccccccc.
//
// On Linux, we use 0xcccccccc because it is an address within the kernel
// address space that is inaccessible by user-space programs. In all other
// circumstances, we use 0x00000000 (which is unmapped in most kernels and
// operating systems).
//
#ifdef LLVA_KERNEL
static const unsigned char meminitvalue = 0x00;
#else
#if defined(__linux__)
static const unsigned char meminitvalue = 0xcc;
#else
static const unsigned char meminitvalue = 0x00;
#endif
#endif
inline bool
InitAllocas::TypeContainsPointer(const Type *Ty) {
//
// FIXME:
// What this should really do is ask Pool Allocation if the given memory
// object is a pool descriptor. However, I don't think Pool Allocation
// has a good API for requesting that information.
//
// If this type is a pool descriptor type, then pretend that it doesn't
// have any pointer.
//
if (Ty == PoolType) return false;
if (Ty->getTypeID() == Type::PointerTyID)
return true;
else if (Ty->getTypeID() == Type::StructTyID) {
const StructType * structTy = cast<StructType>(Ty);
unsigned numE = structTy->getNumElements();
for (unsigned index = 0; index < numE; index++) {
if (TypeContainsPointer(structTy->getElementType(index)))
return true;
}
} else if (Ty->getTypeID() == Type::ArrayTyID) {
const ArrayType *arrayTy = cast<ArrayType>(Ty);
if (TypeContainsPointer(arrayTy->getElementType()))
return true;
}
return false;
}
//
// Method: changeType()
//
// Description:
// Determine whether the specified instruction is an allocation instruction
// that needs to have its result initialized.
//
// Inputs:
// Inst - An instruction that may be an allocation instruction.
//
// Results:
// true - This is an allocation instruction that contains pointers; it
// requires initialization.
// false - This is either not an allocation instruction or an allocation
// instruction that does not require initialization.
//
// Notes:
// 1) An allocation does not need initialization if it contains no pointers
// or is type-unknown (being type-unknown causes SAFECode to place
// load/store checks on the pointers loaded from the memory, so no
// initialization is needed).
//
// 2) We get the type of the allocated memory from DSA; we do not use the
// LLVM type of the allocation. This is because a program can allocate
// memory using a type that contains no pointer but uses the memory
// consistently as a type with pointers. For example, consider the
// following code:
//
// foo = alloc (unsigned char array[24]);
// ...
// (struct bar *)(foo)->pointer = p;
//
inline bool
InitAllocas::changeType (Instruction * Inst) {
//
// Only initialize alloca instructions.
//
if (isa<AllocaInst>(Inst)) {
// Get the DSNode for this instruction
DSNode *Node = dsnPass->getDSNode(Inst, Inst->getParent()->getParent());
//
// If this allocation has no DSNode e.g., it's a pool handle, then don't
// bother looking at it.
//
if (!Node) return false;
//
// Do not bother to change this allocation if the type is unknown;
// regular SAFECode checks will prevent anything bad from happening to
// uninitialzed pointers loaded from this memory.
//
if (Node->isNodeCompletelyFolded())
return false;
//
// If we do not know everything that happens to the pointer (i.e., it is
// incomplete or comes from external code), then go ahead and assume that
// a pointer is within it somewhere.
//
if (Node->isIncompleteNode())
return true;
//
// Scan through all types associated with the DSNode to determine if
// it contains a type that contains a pointer.
//
DSNode::type_iterator tyi;
for (tyi = Node->type_begin(); tyi != Node->type_end(); ++tyi) {
for (sv::set<const Type*>::const_iterator tyii = tyi->second->begin(),
tyee = tyi->second->end(); tyii != tyee; ++tyii) {
//
// Get the type of object allocated. If there is no type, then it is
// implicitly of void type.
//
const Type * TypeCreated = *tyii;
if (!TypeCreated) {
return false;
}
//
// If the type contains a pointer, it must be changed.
//
if (TypeContainsPointer(TypeCreated))
return true;
}
}
//
// We have scanned through all types associated with the pointer, and
// none of them contains a pointer.
//
return false;
}
//
// Type type contains no pointer.
//
return false;
}
bool
InitAllocas::doInitialization (Module & M) {
//
// Create needed LLVM types.
//
VoidType = Type::getVoidTy(getGlobalContext());
Int8Type = IntegerType::getInt8Ty(getGlobalContext());
Int32Type = IntegerType::getInt32Ty(getGlobalContext());
Type * VoidPtrType = PointerType::getUnqual(Int8Type);
//
// Add the memset function to the program.
//
memsetF = M.getOrInsertFunction ("llvm.memset.i32", VoidType,
VoidPtrType,
Int8Type,
Int32Type,
Int32Type,
NULL);
return true;
}
bool
InitAllocas::runOnFunction (Function &F) {
bool modified = false;
//
// Create needed LLVM types.
//
Type * VoidPtrType = PointerType::getUnqual(Int8Type);
// Don't bother processing external functions
if ((F.isDeclaration()) || (F.getName() == "poolcheckglobals"))
return modified;
// Get references to previous analysis passes
TargetData &TD = getAnalysis<TargetData>();
dsnPass = &getAnalysis<DSNodePass>();
paPass = dsnPass->paPass;
//
// Get the type of a pool descriptor.
//
PoolType = paPass->getPoolType(&getGlobalContext());
for (Function::iterator I = F.begin(), E = F.end(); I != E; ++I) {
for (BasicBlock::iterator IAddrBegin=I->begin(), IAddrEnd = I->end();
IAddrBegin != IAddrEnd;
++IAddrBegin) {
//
// Skip any instruction that is not a stack allocation.
//
AllocaInst * AI = dyn_cast<AllocaInst>(IAddrBegin);
if (!AI) continue;
//
// Determine if the instruction needs to be changed.
//
if (changeType (IAddrBegin)) {
// Insert object registration at the end of allocas.
Instruction * iptI = ++IAddrBegin;
--IAddrBegin;
if (AI->getParent() == (&(AI->getParent()->getParent()->getEntryBlock()))) {
BasicBlock::iterator InsertPt = AI->getParent()->begin();
while (&(*(InsertPt)) != AI)
++InsertPt;
while (isa<AllocaInst>(InsertPt))
++InsertPt;
iptI = InsertPt;
}
// Create a value that calculates the alloca's size
const Type * AllocaType = AI->getAllocatedType();
Value *AllocSize = ConstantInt::get (Int32Type,
TD.getTypeAllocSize(AllocaType));
if (AI->isArrayAllocation())
AllocSize = BinaryOperator::Create(Instruction::Mul, AllocSize,
AI->getOperand(0),
"sizetmp",
iptI);
Value * TheAlloca = castTo (AI, VoidPtrType, "cast", iptI);
std::vector<Value *> args(1, TheAlloca);
args.push_back (ConstantInt::get (Int8Type, meminitvalue));
args.push_back (AllocSize);
args.push_back (ConstantInt::get (Int32Type, 0));
CallInst::Create (memsetF, args.begin(), args.end(), "", iptI);
++InitedAllocas;
}
}
}
return modified;
}
namespace {
RegisterPass<InitAllocas> Z("initallocas",
"Initialize stack allocations with pointers");
} // end of namespace
NAMESPACE_SC_END