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 @@ ...@@ -15,7 +15,8 @@
/// ///
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include <stdio.h> #include <stddef.h>
#include <stdlib.h>
// TODO(tlively): Define and implement this library // TODO(tlively): Define and implement this library
void __asan_init(void) { void __asan_init(void) {
...@@ -24,6 +25,16 @@ void __asan_init(void) { ...@@ -24,6 +25,16 @@ void __asan_init(void) {
} }
void __asan_check(void *addr, int size) { 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; 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 @@ ...@@ -22,6 +22,7 @@
#include "IceTypes.h" #include "IceTypes.h"
#include <sstream> #include <sstream>
#include <unordered_map>
namespace Ice { namespace Ice {
...@@ -30,6 +31,14 @@ constexpr SizeT RzSize = 32; ...@@ -30,6 +31,14 @@ constexpr SizeT RzSize = 32;
const std::string RzPrefix = "__$rz"; const std::string RzPrefix = "__$rz";
const llvm::NaClBitcodeRecord::RecordVector RzContents = const llvm::NaClBitcodeRecord::RecordVector RzContents =
llvm::NaClBitcodeRecord::RecordVector(RzSize, 'R'); 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 } // end of anonymous namespace
// Create redzones around all global variables, ensuring that the initializer // Create redzones around all global variables, ensuring that the initializer
...@@ -113,16 +122,39 @@ ASanInstrumentation::createRz(VariableDeclarationList *List, ...@@ -113,16 +122,39 @@ ASanInstrumentation::createRz(VariableDeclarationList *List,
return Rz; 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, void ASanInstrumentation::instrumentLoad(LoweringContext &Context,
const InstLoad *Inst) { InstLoad *Instr) {
instrumentAccess(Context, Inst->getSourceAddress(), instrumentAccess(Context, Instr->getSourceAddress(),
typeWidthInBytes(Inst->getDest()->getType())); typeWidthInBytes(Instr->getDest()->getType()));
} }
void ASanInstrumentation::instrumentStore(LoweringContext &Context, void ASanInstrumentation::instrumentStore(LoweringContext &Context,
const InstStore *Inst) { InstStore *Instr) {
instrumentAccess(Context, Inst->getAddr(), instrumentAccess(Context, Instr->getAddr(),
typeWidthInBytes(Inst->getData()->getType())); typeWidthInBytes(Instr->getData()->getType()));
} }
// TODO(tlively): Take size of access into account as well // TODO(tlively): Take size of access into account as well
......
...@@ -40,9 +40,9 @@ private: ...@@ -40,9 +40,9 @@ private:
VariableDeclaration *RzArray, VariableDeclaration *RzArray,
SizeT &RzArraySize, SizeT &RzArraySize,
VariableDeclaration *Global); VariableDeclaration *Global);
void instrumentLoad(LoweringContext &Context, const InstLoad *Inst) override; void instrumentCall(LoweringContext &Context, InstCall *Instr) override;
void instrumentStore(LoweringContext &Context, void instrumentLoad(LoweringContext &Context, InstLoad *Instr) override;
const InstStore *Inst) override; void instrumentStore(LoweringContext &Context, InstStore *Instr) override;
void instrumentAccess(LoweringContext &Context, Operand *Op, SizeT Size); void instrumentAccess(LoweringContext &Context, Operand *Op, SizeT Size);
void instrumentStart(Cfg *Func) override; void instrumentStart(Cfg *Func) override;
bool DidInsertRedZones = false; bool DidInsertRedZones = false;
......
...@@ -29,6 +29,8 @@ void Instrumentation::instrumentFunc(Cfg *Func) { ...@@ -29,6 +29,8 @@ void Instrumentation::instrumentFunc(Cfg *Func) {
assert(Func); assert(Func);
assert(!Func->getNodes().empty()); assert(!Func->getNodes().empty());
// TODO(tlively): More selectively instrument functions so that shadow memory
// represents user accessibility more and library accessibility less.
LoweringContext Context; LoweringContext Context;
Context.init(Func->getNodes().front()); Context.init(Func->getNodes().front());
instrumentFuncStart(Context); instrumentFuncStart(Context);
...@@ -42,7 +44,8 @@ void Instrumentation::instrumentFunc(Cfg *Func) { ...@@ -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); instrumentStart(Func);
} }
......
...@@ -48,27 +48,27 @@ private: ...@@ -48,27 +48,27 @@ private:
void instrumentInst(LoweringContext &Context); void instrumentInst(LoweringContext &Context);
virtual void instrumentFuncStart(LoweringContext &) {} virtual void instrumentFuncStart(LoweringContext &) {}
virtual void instrumentAlloca(LoweringContext &, const class InstAlloca *) {} virtual void instrumentAlloca(LoweringContext &, const class InstAlloca *) {}
virtual void instrumentArithmetic(LoweringContext &, virtual void instrumentArithmetic(LoweringContext &, class InstArithmetic *) {
const class InstArithmetic *) {} }
virtual void instrumentBr(LoweringContext &, const class InstBr *) {} virtual void instrumentBr(LoweringContext &, class InstBr *) {}
virtual void instrumentCall(LoweringContext &, const class InstCall *) {} virtual void instrumentCall(LoweringContext &, class InstCall *) {}
virtual void instrumentCast(LoweringContext &, const class InstCast *) {} virtual void instrumentCast(LoweringContext &, class InstCast *) {}
virtual void instrumentExtractElement(LoweringContext &, virtual void instrumentExtractElement(LoweringContext &,
const class InstExtractElement *) {} class InstExtractElement *) {}
virtual void instrumentFcmp(LoweringContext &, const class InstFcmp *) {} virtual void instrumentFcmp(LoweringContext &, class InstFcmp *) {}
virtual void instrumentIcmp(LoweringContext &, const class InstIcmp *) {} virtual void instrumentIcmp(LoweringContext &, class InstIcmp *) {}
virtual void instrumentInsertElement(LoweringContext &, virtual void instrumentInsertElement(LoweringContext &,
const class InstInsertElement *) {} class InstInsertElement *) {}
virtual void instrumentIntrinsicCall(LoweringContext &, virtual void instrumentIntrinsicCall(LoweringContext &,
const class InstIntrinsicCall *) {} class InstIntrinsicCall *) {}
virtual void instrumentLoad(LoweringContext &, const class InstLoad *) {} virtual void instrumentLoad(LoweringContext &, class InstLoad *) {}
virtual void instrumentPhi(LoweringContext &, const class InstPhi *) {} virtual void instrumentPhi(LoweringContext &, class InstPhi *) {}
virtual void instrumentRet(LoweringContext &, const class InstRet *) {} virtual void instrumentRet(LoweringContext &, class InstRet *) {}
virtual void instrumentSelect(LoweringContext &, const class InstSelect *) {} virtual void instrumentSelect(LoweringContext &, class InstSelect *) {}
virtual void instrumentStore(LoweringContext &, const class InstStore *) {} virtual void instrumentStore(LoweringContext &, class InstStore *) {}
virtual void instrumentSwitch(LoweringContext &, const class InstSwitch *) {} virtual void instrumentSwitch(LoweringContext &, class InstSwitch *) {}
virtual void instrumentUnreachable(LoweringContext &, virtual void instrumentUnreachable(LoweringContext &,
const class InstUnreachable *) {} class InstUnreachable *) {}
virtual void instrumentStart(Cfg *) {} virtual void instrumentStart(Cfg *) {}
virtual void instrumentLocalVars(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