From 3f97afb1675218f1a4cd7f30fc4faa86fcbeea03 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Thu, 7 Jul 2016 14:56:21 -0700 Subject: [PATCH] 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 . --- Makefile.standalone | 2 +- runtime/szrt_asan.c | 36 +++++++++++++++++----------- src/IceASanInstrumentation.cpp | 13 ++++++++--- src/IceASanInstrumentation.h | 1 + src/IceInstrumentation.cpp | 11 +++++---- src/IceInstrumentation.h | 1 + tests_lit/asan_tests/blacklist.ll | 49 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 91 insertions(+), 22 deletions(-) create mode 100644 tests_lit/asan_tests/blacklist.ll diff --git a/Makefile.standalone b/Makefile.standalone index 316479890..dce5e8e4c 100644 --- a/Makefile.standalone +++ b/Makefile.standalone @@ -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) diff --git a/runtime/szrt_asan.c b/runtime/szrt_asan.c index a406b9323..f8608bb44 100644 --- a/runtime/szrt_asan.c +++ b/runtime/szrt_asan.c @@ -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)); diff --git a/src/IceASanInstrumentation.cpp b/src/IceASanInstrumentation.cpp index a7551a896..6374ce8c6 100644 --- a/src/IceASanInstrumentation.cpp +++ b/src/IceASanInstrumentation.cpp @@ -24,6 +24,7 @@ #include #include +#include #include 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; +using string_set = std::unordered_set; +// 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 *, 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(); } diff --git a/src/IceASanInstrumentation.h b/src/IceASanInstrumentation.h index 68d0bf3c0..c9095fa52 100644 --- a/src/IceASanInstrumentation.h +++ b/src/IceASanInstrumentation.h @@ -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; diff --git a/src/IceInstrumentation.cpp b/src/IceInstrumentation.cpp index c911a0b44..0cd218abc 100644 --- a/src/IceInstrumentation.cpp +++ b/src/IceInstrumentation.cpp @@ -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 diff --git a/src/IceInstrumentation.h b/src/IceInstrumentation.h index 3a185426c..32979634a 100644 --- a/src/IceInstrumentation.h +++ b/src/IceInstrumentation.h @@ -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 *) { diff --git a/tests_lit/asan_tests/blacklist.ll b/tests_lit/asan_tests/blacklist.ll new file mode 100644 index 000000000..9a82d9dda --- /dev/null +++ b/tests_lit/asan_tests/blacklist.ll @@ -0,0 +1,49 @@ +; 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: } -- 2.11.0