Commit 3f97afb1 by Thomas Lively

Blacklisted instrumenting _Balloc.

Increases number of spec2k tests that run successfully with ASan from 2 to 6. BUG=https://bugs.chromium.org/p/nativeclient/issues/detail?id=4374 R=kschimpf@google.com Review URL: https://codereview.chromium.org/2128383002 .
parent d1e97776
......@@ -527,7 +527,7 @@ runtime.is.built: $(RT_SRC) pydir/build-runtime.py
@echo ================ Building Subzero runtime ================
./pydir/build-runtime.py -v --pnacl-root $(PNACL_TOOLCHAIN_ROOT)
check-lit: $(OBJDIR)/pnacl-sz make_symlink
check-lit: $(OBJDIR)/pnacl-sz make_symlink runtime
PNACL_BIN_PATH=$(PNACL_BIN_PATH) \
$(LLVM_SRC_PATH)/utils/lit/lit.py -sv $(CHECK_LIT_TESTS) \
$(FORCEASM_LIT_TEST_EXCLUDES) $(FORCEASM_LIT_PARAM)
......
......@@ -28,6 +28,7 @@
#define RZ_SIZE (32)
#define SHADOW_SCALE_LOG2 (3)
#define SHADOW_SCALE ((size_t)1 << SHADOW_SCALE_LOG2)
#define DEBUG (0)
// Assuming 48 bit address space on 64 bit systems
#define SHADOW_LENGTH_64 (1u << (48 - SHADOW_SCALE_LOG2))
......@@ -44,6 +45,15 @@
#define POISON_VAL (-1)
#if DEBUG
#define DUMP(args...) \
do { \
printf(args); \
} while (false);
#else // !DEBUG
#define DUMP(args...)
#endif // DEBUG
static char *shadow_offset = NULL;
static void __asan_error(char *, int);
......@@ -65,13 +75,13 @@ static void __asan_error(char *ptr, int size) {
// check only the first byte of each word unless strict
static void __asan_check(char *ptr, int size, bool strict) {
assert(strict || (uintptr_t)ptr % WORD_SIZE == 0);
printf("%s check %d bytes at %p\n", (strict) ? "strict" : "loose", size, ptr);
DUMP("%s check %d bytes at %p\n", (strict) ? "strict" : "loose", size, ptr);
char *end = ptr + size;
int step = (strict) ? 1 : WORD_SIZE;
for (char *cur = ptr; cur < end; cur += step) {
char shadow = *(char *)MEM2SHADOW(cur);
printf("checking %p against %p with shadow %d\n", cur, MEM2SHADOW(cur),
shadow);
DUMP("checking %p against %p with shadow %d\n", cur, MEM2SHADOW(cur),
shadow);
if (shadow != 0 && (shadow < 0 || SHADOW_OFFSET(cur) >= shadow)) {
__asan_error(ptr, size);
}
......@@ -104,23 +114,23 @@ void __asan_init(int n_rzs, void **rzs, int *rz_sizes) {
if (shadow_offset == NULL)
fprintf(stderr, "unable to allocate shadow memory\n");
else
printf("set up shadow memory at %p\n", shadow_offset);
DUMP("set up shadow memory at %p\n", shadow_offset);
if (mprotect(MEM2SHADOW(shadow_offset), length >> SHADOW_SCALE_LOG2,
PROT_NONE))
fprintf(stderr, "could not protect bad region\n");
else
printf("protected bad region\n");
DUMP("protected bad region\n");
// poison global redzones
printf("poisioning %d global redzones\n", n_rzs);
DUMP("poisioning %d global redzones\n", n_rzs);
for (int i = 0; i < n_rzs; i++) {
printf("(%d) poisoning redzone of size %d at %p\n", i, rz_sizes[i], rzs[i]);
DUMP("(%d) poisoning redzone of size %d at %p\n", i, rz_sizes[i], rzs[i]);
__asan_poison(rzs[i], rz_sizes[i]);
}
}
void *__asan_malloc(size_t size) {
printf("malloc() called with size %d\n", size);
DUMP("malloc() called with size %d\n", size);
size_t padding =
(IS_SHADOW_ALIGNED(size)) ? 0 : SHADOW_SCALE - SHADOW_OFFSET(size);
size_t rz_left_size = RZ_SIZE;
......@@ -144,7 +154,7 @@ void *__asan_malloc(size_t size) {
}
void __asan_free(char *ptr) {
printf("free() called on %p\n", ptr);
DUMP("free() called on %p\n", ptr);
void *rz_left = ptr - RZ_SIZE;
void *rz_right = *(void **)rz_left;
size_t rz_right_size = *(size_t *)rz_right;
......@@ -158,8 +168,8 @@ void __asan_poison(char *ptr, int size) {
assert(IS_SHADOW_ALIGNED(end));
// redzones should be no greater than RZ_SIZE + RZ_SIZE-1 for alignment
assert(size < 2 * RZ_SIZE);
printf("poison %d bytes at %p: %p - %p\n", size, ptr, MEM2SHADOW(ptr),
MEM2SHADOW(end));
DUMP("poison %d bytes at %p: %p - %p\n", size, ptr, MEM2SHADOW(ptr),
MEM2SHADOW(end));
size_t offset = SHADOW_OFFSET(ptr);
*(char *)MEM2SHADOW(ptr) = (offset == 0) ? POISON_VAL : offset;
ptr += SHADOW_OFFSET(size);
......@@ -173,8 +183,8 @@ void __asan_unpoison(char *ptr, int size) {
char *end = ptr + size;
assert(IS_SHADOW_ALIGNED(end));
assert(size < 2 * RZ_SIZE);
printf("unpoison %d bytes at %p: %p - %p\n", size, ptr, MEM2SHADOW(ptr),
MEM2SHADOW(end));
DUMP("unpoison %d bytes at %p: %p - %p\n", size, ptr, MEM2SHADOW(ptr),
MEM2SHADOW(end));
*(char *)MEM2SHADOW(ptr) = 0;
ptr += SHADOW_OFFSET(size);
assert(IS_SHADOW_ALIGNED(ptr));
......
......@@ -24,6 +24,7 @@
#include <sstream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace Ice {
......@@ -37,12 +38,14 @@ constexpr const char *RzSizesName = "__$rz_sizes";
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>;
using string_set = std::unordered_set<std::string>;
// TODO(tlively): Handle all allocation functions
const string_map FuncSubstitutions = {{"malloc", "__asan_malloc"},
{"free", "__asan_free"}};
const string_set FuncBlackList = {"_Balloc"};
llvm::NaClBitcodeRecord::RecordVector sizeToByteVec(SizeT Size) {
llvm::NaClBitcodeRecord::RecordVector SizeContents;
......@@ -58,6 +61,11 @@ llvm::NaClBitcodeRecord::RecordVector sizeToByteVec(SizeT Size) {
ICE_TLS_DEFINE_FIELD(std::vector<InstCall *> *, ASanInstrumentation,
LocalDtors);
bool ASanInstrumentation::isInstrumentable(Cfg *Func) {
std::string FuncName = Func->getFunctionName().toStringOrEmpty();
return FuncName == "" || FuncBlackList.count(FuncName) == 0;
}
// Create redzones around all global variables, ensuring that the initializer
// types of the redzones and their associated globals match so that they are
// laid out together in memory.
......@@ -333,8 +341,7 @@ void ASanInstrumentation::instrumentStart(Cfg *Func) {
}
// TODO(tlively): make this more efficient with swap idiom
void ASanInstrumentation::finishFunc(Cfg *Func) {
(void)Func;
void ASanInstrumentation::finishFunc(Cfg *) {
ICE_TLS_GET_FIELD(LocalDtors)->clear();
}
......
......@@ -42,6 +42,7 @@ public:
private:
std::string nextRzName();
bool isInstrumentable(Cfg *Func) override;
void instrumentFuncStart(LoweringContext &Context) override;
void instrumentCall(LoweringContext &Context, InstCall *Instr) override;
void instrumentRet(LoweringContext &Context, InstRet *Instr) override;
......
......@@ -29,17 +29,18 @@ 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.
bool DidInstrumentStart = false;
if (!isInstrumentable(Func))
return;
bool DidInstrumentEntry = false;
LoweringContext Context;
Context.init(Func->getNodes().front());
for (CfgNode *Node : Func->getNodes()) {
Context.init(Node);
while (!Context.atEnd()) {
if (!DidInstrumentStart) {
if (!DidInstrumentEntry) {
instrumentFuncStart(Context);
DidInstrumentStart = true;
DidInstrumentEntry = true;
}
instrumentInst(Context);
// go to next undeleted instruction
......
......@@ -48,6 +48,7 @@ protected:
virtual void instrumentInst(LoweringContext &Context);
private:
virtual bool isInstrumentable(Cfg *) { return true; }
virtual void instrumentFuncStart(LoweringContext &) {}
virtual void instrumentAlloca(LoweringContext &, class InstAlloca *) {}
virtual void instrumentArithmetic(LoweringContext &, class InstArithmetic *) {
......
; Test to ensure that blacklisted functions are not instrumented and others are.
; 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)
; A black listed function
define internal void @_Balloc() {
%local = alloca i8, i32 4, align 4
%heapvar = call i32 @malloc(i32 42)
call void @free(i32 %heapvar)
ret void
}
; DUMP-LABEL: ================ Instrumented CFG ================
; DUMP-NEXT: define internal void @_Balloc() {
; DUMP-NEXT: __0:
; DUMP-NEXT: %local = alloca i8, i32 4, align 4
; DUMP-NEXT: %heapvar = call i32 @malloc(i32 42)
; DUMP-NEXT: call void @free(i32 %heapvar)
; DUMP-NEXT: ret void
; DUMP-NEXT: }
; A non black listed function
define internal void @func() {
%local = alloca i8, i32 4, align 4
%heapvar = call i32 @malloc(i32 42)
call void @free(i32 %heapvar)
ret void
}
; DUMP-LABEL: ================ Instrumented CFG ================
; DUMP-NEXT: define internal void @func() {
; DUMP-NEXT: __0:
; DUMP-NEXT: %local = alloca i8, i32 64, align 8
; DUMP-NEXT: %__$rz1 = alloca i8, i32 32, align 8
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz1, i32 32)
; DUMP-NEXT: %__$rz0 = add i32 %local, 4
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz0, i32 60)
; DUMP-NEXT: %heapvar = call i32 @__asan_malloc(i32 42)
; DUMP-NEXT: call void @__asan_free(i32 %heapvar)
; DUMP-NEXT: call void @__asan_unpoison(i32 %__$rz0, i32 60)
; DUMP-NEXT: call void @__asan_unpoison(i32 %__$rz1, i32 32)
; DUMP-NEXT: ret void
; DUMP-NEXT: }
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