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 .
diff --git a/Makefile.standalone b/Makefile.standalone index 3164798..dce5e8e 100644 --- a/Makefile.standalone +++ b/Makefile.standalone
@@ -527,7 +527,7 @@ @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 a406b93..f8608bb 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 @@ // 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 @@ 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_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 @@ 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 @@ 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 a7551a8..6374ce8 100644 --- a/src/IceASanInstrumentation.cpp +++ b/src/IceASanInstrumentation.cpp
@@ -24,6 +24,7 @@ #include <sstream> #include <unordered_map> +#include <unordered_set> #include <vector> namespace Ice { @@ -37,12 +38,14 @@ 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 @@ 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 @@ } // 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 68d0bf3..c9095fa 100644 --- a/src/IceASanInstrumentation.h +++ b/src/IceASanInstrumentation.h
@@ -42,6 +42,7 @@ 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 c911a0b..0cd218a 100644 --- a/src/IceInstrumentation.cpp +++ b/src/IceInstrumentation.cpp
@@ -29,17 +29,18 @@ 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 3a18542..3297963 100644 --- a/src/IceInstrumentation.h +++ b/src/IceInstrumentation.h
@@ -48,6 +48,7 @@ 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 0000000..9a82d9d --- /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: }