Commit 26c43064 by Thomas Lively

Instrumented malloc and free with dummy functions.

parent 1afb4836
Using AddressSanitizer in Subzero
=================================
AddressSanitizer is a powerful compile-time tool used to detect and report
illegal memory accesses. For a full description of the tool, see the original
`paper
<https://www.usenix.org/system/files/conference/atc12/atc12-final39.pdf>`_.
AddressSanitizer is only supported on native builds of .pexe files and cannot be
used in production.
In Subzero, AddressSanitizer depends on being able to find and instrument calls
to various functions such as malloc() and free(), and as such the .pexe file
being translated must not have had those symbols stripped. Subzero will not
complain if it is told to translate a .pexe file with its symbols stripped, but
it will not be able to find calls to malloc() and free(), so AddressSanitizer
will not work correctly in the final executable.
These are the steps to compile hello.c to an instrumented object file::
pnacl-clang -o hello.nonfinal.pexe hello.c
pnacl-finalize --no-strip-syms -o hello.pexe hello.nonfinal.pexe
pnacl-sz -fsanitize-address -filetype=obj -o hello.o hello.pexe
The resulting object file must be linked with the Subzero-specific
AddressSanitizer runtime to work correctly. A .pexe file can be compiled with
AddressSanitizer and properly linked into a final executable using
subzero/pydir/szbuild.py with the --fsanitize-address flag, i.e.::
pydir/szbuild.py --fsanitize-address hello.pexe
......@@ -15,7 +15,8 @@
///
//===----------------------------------------------------------------------===//
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
// TODO(tlively): Define and implement this library
void __asan_init(void) {
......@@ -24,6 +25,16 @@ void __asan_init(void) {
}
void __asan_check(void *addr, int size) {
printf("Check access of %p of size %d\n", addr, size);
printf("Check %d bytes at %p\n", size, addr);
return;
}
void *__asan_malloc(size_t size) {
printf("malloc() called with size %d\n", size);
return malloc(size);
}
void __asan_free(void *ptr) {
printf("free() called on %p\n", ptr);
free(ptr);
}
......@@ -22,6 +22,7 @@
#include "IceTypes.h"
#include <sstream>
#include <unordered_map>
namespace Ice {
......@@ -30,6 +31,14 @@ constexpr SizeT RzSize = 32;
const std::string RzPrefix = "__$rz";
const llvm::NaClBitcodeRecord::RecordVector RzContents =
llvm::NaClBitcodeRecord::RecordVector(RzSize, 'R');
// TODO(tlively): Handle all allocation functions
// In order to instrument the code correctly, the .pexe must not have had its
// symbols stripped.
using string_map = std::unordered_map<std::string, std::string>;
const string_map FuncSubstitutions = {{"malloc", "__asan_malloc"},
{"free", "__asan_free"}};
} // end of anonymous namespace
// Create redzones around all global variables, ensuring that the initializer
......@@ -113,16 +122,39 @@ ASanInstrumentation::createRz(VariableDeclarationList *List,
return Rz;
}
void ASanInstrumentation::instrumentCall(LoweringContext &Context,
InstCall *Instr) {
auto *CallTarget =
llvm::dyn_cast<ConstantRelocatable>(Instr->getCallTarget());
if (CallTarget == nullptr)
return;
std::string TargetName = CallTarget->getName().toStringOrEmpty();
auto Subst = FuncSubstitutions.find(TargetName);
if (Subst == FuncSubstitutions.end())
return;
std::string SubName = Subst->second;
Constant *NewFunc = Ctx->getConstantExternSym(Ctx->getGlobalString(SubName));
auto *NewCall =
InstCall::create(Context.getNode()->getCfg(), Instr->getNumArgs(),
Instr->getDest(), NewFunc, Instr->isTailcall());
for (SizeT I = 0, Args = Instr->getNumArgs(); I < Args; ++I)
NewCall->addArg(Instr->getArg(I));
Context.insert(NewCall);
Instr->setDeleted();
}
void ASanInstrumentation::instrumentLoad(LoweringContext &Context,
const InstLoad *Inst) {
instrumentAccess(Context, Inst->getSourceAddress(),
typeWidthInBytes(Inst->getDest()->getType()));
InstLoad *Instr) {
instrumentAccess(Context, Instr->getSourceAddress(),
typeWidthInBytes(Instr->getDest()->getType()));
}
void ASanInstrumentation::instrumentStore(LoweringContext &Context,
const InstStore *Inst) {
instrumentAccess(Context, Inst->getAddr(),
typeWidthInBytes(Inst->getData()->getType()));
InstStore *Instr) {
instrumentAccess(Context, Instr->getAddr(),
typeWidthInBytes(Instr->getData()->getType()));
}
// TODO(tlively): Take size of access into account as well
......
......@@ -40,9 +40,9 @@ private:
VariableDeclaration *RzArray,
SizeT &RzArraySize,
VariableDeclaration *Global);
void instrumentLoad(LoweringContext &Context, const InstLoad *Inst) override;
void instrumentStore(LoweringContext &Context,
const InstStore *Inst) override;
void instrumentCall(LoweringContext &Context, InstCall *Instr) override;
void instrumentLoad(LoweringContext &Context, InstLoad *Instr) override;
void instrumentStore(LoweringContext &Context, InstStore *Instr) override;
void instrumentAccess(LoweringContext &Context, Operand *Op, SizeT Size);
void instrumentStart(Cfg *Func) override;
bool DidInsertRedZones = false;
......
......@@ -29,6 +29,8 @@ void Instrumentation::instrumentFunc(Cfg *Func) {
assert(Func);
assert(!Func->getNodes().empty());
// TODO(tlively): More selectively instrument functions so that shadow memory
// represents user accessibility more and library accessibility less.
LoweringContext Context;
Context.init(Func->getNodes().front());
instrumentFuncStart(Context);
......@@ -42,7 +44,8 @@ void Instrumentation::instrumentFunc(Cfg *Func) {
}
}
if (Func->getFunctionName().toStringOrEmpty() == "_start")
std::string FuncName = Func->getFunctionName().toStringOrEmpty();
if (FuncName == "_start")
instrumentStart(Func);
}
......
......@@ -48,27 +48,27 @@ private:
void instrumentInst(LoweringContext &Context);
virtual void instrumentFuncStart(LoweringContext &) {}
virtual void instrumentAlloca(LoweringContext &, const class InstAlloca *) {}
virtual void instrumentArithmetic(LoweringContext &,
const class InstArithmetic *) {}
virtual void instrumentBr(LoweringContext &, const class InstBr *) {}
virtual void instrumentCall(LoweringContext &, const class InstCall *) {}
virtual void instrumentCast(LoweringContext &, const class InstCast *) {}
virtual void instrumentArithmetic(LoweringContext &, class InstArithmetic *) {
}
virtual void instrumentBr(LoweringContext &, class InstBr *) {}
virtual void instrumentCall(LoweringContext &, class InstCall *) {}
virtual void instrumentCast(LoweringContext &, class InstCast *) {}
virtual void instrumentExtractElement(LoweringContext &,
const class InstExtractElement *) {}
virtual void instrumentFcmp(LoweringContext &, const class InstFcmp *) {}
virtual void instrumentIcmp(LoweringContext &, const class InstIcmp *) {}
class InstExtractElement *) {}
virtual void instrumentFcmp(LoweringContext &, class InstFcmp *) {}
virtual void instrumentIcmp(LoweringContext &, class InstIcmp *) {}
virtual void instrumentInsertElement(LoweringContext &,
const class InstInsertElement *) {}
class InstInsertElement *) {}
virtual void instrumentIntrinsicCall(LoweringContext &,
const class InstIntrinsicCall *) {}
virtual void instrumentLoad(LoweringContext &, const class InstLoad *) {}
virtual void instrumentPhi(LoweringContext &, const class InstPhi *) {}
virtual void instrumentRet(LoweringContext &, const class InstRet *) {}
virtual void instrumentSelect(LoweringContext &, const class InstSelect *) {}
virtual void instrumentStore(LoweringContext &, const class InstStore *) {}
virtual void instrumentSwitch(LoweringContext &, const class InstSwitch *) {}
class InstIntrinsicCall *) {}
virtual void instrumentLoad(LoweringContext &, class InstLoad *) {}
virtual void instrumentPhi(LoweringContext &, class InstPhi *) {}
virtual void instrumentRet(LoweringContext &, class InstRet *) {}
virtual void instrumentSelect(LoweringContext &, class InstSelect *) {}
virtual void instrumentStore(LoweringContext &, class InstStore *) {}
virtual void instrumentSwitch(LoweringContext &, class InstSwitch *) {}
virtual void instrumentUnreachable(LoweringContext &,
const class InstUnreachable *) {}
class InstUnreachable *) {}
virtual void instrumentStart(Cfg *) {}
virtual void instrumentLocalVars(Cfg *) {}
......
; Test that calls to malloc() and free() are replaced
; REQUIRES: allow_dump
; RUN: %p2i -i %s --args -verbose=inst -threads=0 -fsanitize-address \
; RUN: --allow-externally-defined-symbols | FileCheck --check-prefix=DUMP %s
declare external i32 @malloc(i32)
declare external void @free(i32)
define internal void @func() {
%ptr = call i32 @malloc(i32 42)
call void @free(i32 %ptr)
ret void
}
; DUMP-LABEL: ================ Instrumented CFG ================
; DUMP-NEXT: define internal void @func() {
; DUMP-NEXT: __0:
; DUMP-NEXT: %ptr = call i32 @__asan_malloc(i32 42)
; DUMP-NEXT: call void @__asan_free(i32 %ptr)
; DUMP-NEXT: ret void
; DUMP-NEXT: }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment