├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── config ├── __init__.py ├── instances.py ├── packages.py └── targets.py ├── deltatags-test ├── .gitignore ├── Makefile ├── deltatags.h ├── fault_handler.h ├── runtests.sh ├── test_arith_geps.c ├── test_arith_neg.c ├── test_exec.c ├── test_fork.c ├── test_global_string_free.cpp ├── test_implicitcast.c ├── test_iovec.c ├── test_libptr_copyfromarg.c ├── test_libptr_copyfromarg_cpp.cpp ├── test_libptr_ptrdiff.c ├── test_libptr_retsize.c ├── test_libptr_strlen.c ├── test_libptr_strtok.c ├── test_mask.c ├── test_memintrinsics.c ├── test_nullptr.c ├── test_old.c ├── test_oob.c └── test_simple.c ├── llvm-passes ├── AddressSpace.cpp ├── AddressSpace.h ├── CheckAddressSpace.cpp ├── DebugNegArith.cpp ├── DeltaTagAlloc.cpp ├── DeltaTagProp.cpp ├── GlobalOpt.cpp ├── GlobalOpt.h ├── LibPtrRet.h ├── MagicTags.cpp ├── Makefile ├── MaskPointers.cpp ├── ReinterpretedPointers.cpp ├── ReinterpretedPointers.h ├── ReplaceAddressTakenMalloc.cpp ├── RuntimeStats.cpp ├── SafeAllocs.cpp ├── SafeAllocs.h ├── SafeAllocsOld.cpp ├── SafeAllocsOld.h ├── TagGlobals.cpp ├── TagGlobalsConst.cpp ├── TagGlobalsConst.h └── UboundBranch.cpp ├── patches ├── spec2006-gcc.patch ├── spec2006-h264ref-sizetagprop-BCBP.patch ├── spec2006-perlbench.patch └── spec2006-soplex.patch ├── runtime ├── Makefile ├── addrspace.h ├── debug.c ├── libptrret.c ├── mask-wrappers.c ├── mask.c ├── runtimestats.c └── source-instrumentation.h └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *__pycache__ 2 | *.pyc 3 | *~ 4 | *.swp 5 | build 6 | results/ 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "infra"] 2 | path = infra 3 | url = git@github.com:vusec/instrumentation-infra.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Vrije Universiteit Amsterdam: 2 | Instrumentation infrastructure: Taddeus Kroes 3 | Delta Pointers: Taddeus Kroes & Koen Koning 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this software except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 4 | This repository contains the source code for the EuroSys'18 paper 5 | "Delta Pointers: Buffer Overflow Checks Without the Checks" by Taddeus Kroes, 6 | Koen Koning (shared first authors), Erik van der Kouwe, Herbert Bos and 7 | Cristiano Giuffrida. The paper is available for download 8 | [here](https://www.vusec.net/download/?t=papers/delta-pointers_eurosys18.pdf). 9 | 10 | Delta Pointers protect programs against buffer overflows by means of 11 | instrumentation inserted by the compiler. The inserted instrumentation inserts 12 | and updates a so-called *delta tag* into each pointer, which records two pieces 13 | of information: the distance of the pointer to the end of its corresponding 14 | memory object in the virtual address space, and a single overflow bit that 15 | indicates whether the pointer is out-of-bounds. The state of the overflow bit 16 | is managed efficiently by arithmetic operations, without the use of any 17 | additional branches or memory accesses. Upon dereference, the distance is taken 18 | out of the pointer using a bitwise AND operation, but the overflow bit is left 19 | intact. When set, this bit will cause the dereferenced pointer to become 20 | *non-canonical*, in turn causing a general protection fault in the processor. This way, out-of-bounds pointers are automatically rejected by the hardware. 21 | 22 | 23 | Building and running instrumented programs 24 | ========================================== 25 | 26 | This repository only contains code for LLVM passes and a small runtime library. 27 | We use an external [infrastructure](https://github.com/vusec/instrumentation-infra) 28 | library to plug these passes into existing build systems like that of SPEC. 29 | 30 | First, make sure the infrastructure is up-to-date (in case you did not so a 31 | recursive clone of this repo): 32 | 33 | $ git submodule update --init 34 | 35 | The infrastructure's only hard dependency is Python 3.5. It downloads and 36 | builds most tools and libraries needed to build LLVM, but those in turn have 37 | some dependencies. On a clean Ubuntu 16.04 installation, this is what you need: 38 | 39 | $ sudo apt-get install bison build-essential gettext git pkg-config python ssh 40 | 41 | For nicer command-line usage, install the following python packages (optional): 42 | 43 | $ pip3 install --user coloredlogs argcomplete 44 | 45 | `argcomplete` enables command-line argument completion, but it needs to be 46 | activated first (optional): 47 | 48 | $ eval "$(register-python-argcomplete --complete-arguments -o nospace -o default -- setup.py)" 49 | 50 | Building/running benchmarks and dependencies is done with `setup.py` which 51 | knows about dependencies and build scripts. The following command builds all 52 | dependencies, the Delta Pointers passes and runtime, and some small test 53 | programs. It then runs the test programs, both without (the `clang-lto` baseline 54 | instance) and with our instrumentation (the `deltatags` instance): 55 | 56 | $ ./setup.py run --build deltatags-test clang-lto deltatags 57 | 58 | This will take a long time, so go get some coffee and plug in your laptop 59 | battery. The output should look somewhat like [this](https://pastebin.com/raw/pviqa8CG). 60 | For `clang-lto`, the tests that do a buffer overflow should fail because they 61 | expect an error to be raised. For `deltatags`, all test should succeed. 62 | 63 | To run SPEC-CPU2006, you will need to provide your own copy of the source. You 64 | can do so by modifying `source` and `source_type` on the relevant lines in 65 | `setup.py`. See the relevant 66 | [documentation](https://github.com/vusec/instrumentation-infra/README.md) for 67 | details. After configuring `setup.py`, build and run the benchmark suite like 68 | this: 69 | 70 | $ ./setup.py run --build spec2006 deltatags --test 71 | 72 | This will build and run all 19 C/C++ benchmarks with Delta Pointers 73 | instrumentation, using the 'test' workload. To run the baseline, use 74 | `clang-lto` instead of `deltatags`. Results will be in the SPEC installation 75 | directory `build/targets/spec2006/install`. For a complete list of run options, 76 | consult: 77 | 78 | $ ./setup.py run --help 79 | $ ./setup.py run spec2006 --help 80 | 81 | 82 | Instrumenting your own program 83 | ============================== 84 | 85 | To run instrumentation on your own programs, you can either extract the 86 | relevant parts (`llvm-passes/` and `runtime/`) and put them into your own 87 | repository, or you can define your own *target*. See `target.py` for an 88 | example. 89 | 90 | 91 | Repo organization 92 | ================= 93 | 94 | The source consists of several components: 95 | 96 | - `llvm-passes/` LLVM passes that instrument a program at compile time. This 97 | is the core of our work. 98 | 99 | - `runtime/` A runtime library containing helper functions called by our 100 | instrumentation. `dep.py` informs the setup script how to build the 101 | runtime. 102 | 103 | - `patches/` Some patches for the SPEC-CPU2006 benchmark suite, making it 104 | compatible with tagged pointers. 105 | 106 | - `deltatags-test` A collection of toy programs that test different parts of 107 | the Delta Pointers implementation. `config/targets.py` informs the setup 108 | script how to build/run these programs. 109 | 110 | - `infra` An external repository that facilitates program instrumentation for 111 | common benchmarks in systems research (developed in conjunction with Delta 112 | Pointers). The interface into this framework is `setup.py`. 113 | 114 | - `setup.py` is the main tool to build/run stuff with. This is where you 115 | register new benchmarks (or 'targets') and hook in any custom passes of 116 | your own. The script has descriptive usage messages for all subcommands. 117 | 118 | - `config/instances.py` informs the setup script how to build programs with 119 | Delta Pointers instrumentation. If you want to add custom instrumentation 120 | passes, this is the place to do it. 121 | -------------------------------------------------------------------------------- /config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vusec/deltapointers/5f22ff660ffd3cceb2b1cfc44fbc764f553e57b9/config/__init__.py -------------------------------------------------------------------------------- /config/instances.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import infra 3 | from infra.packages import LLVM, BuiltinLLVMPasses, LLVMPasses, LibShrink 4 | from .packages import LibDeltaTags 5 | 6 | 7 | class DeltaTags(infra.Instance): 8 | addrspace_bits = 32 9 | llvm_version = '3.8.0' 10 | llvm_patches = ['gold-plugins', 'statsfilter'] 11 | debug = False # toggle for debug symbols 12 | 13 | rootdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 14 | doxygen_flags = [ 15 | #'-DLLVM_ENABLE_DOXYGEN=On', 16 | #'-DLLVM_DOXYGEN_SVG=On', 17 | #'-DLLVM_INSTALL_DOXYGEN_HTML_DIR=%s/build/doxygen' % rootdir 18 | ] 19 | llvm = LLVM(version=llvm_version, compiler_rt=False, 20 | patches=llvm_patches, build_flags=doxygen_flags) 21 | llvm_passes = LLVMPasses(llvm, rootdir + '/llvm-passes', 'deltatags', 22 | use_builtins=True) 23 | libshrink = LibShrink(addrspace_bits, debug=debug) 24 | libdeltatags = LibDeltaTags(llvm_passes, addrspace_bits, overflow_bit=True, 25 | runtime_stats=False, debug=debug) 26 | 27 | def __init__(self, name, overflow_check, optimizer): 28 | self.name = name 29 | self.overflow_check = overflow_check 30 | self.optimizer = optimizer 31 | 32 | def dependencies(self): 33 | yield self.llvm 34 | yield self.llvm_passes 35 | yield self.libshrink 36 | yield self.libdeltatags 37 | 38 | def configure(self, ctx): 39 | # helper libraries 40 | self.llvm.configure(ctx) 41 | self.llvm_passes.configure(ctx) 42 | self.libshrink.configure(ctx, static=True) 43 | self.libdeltatags.configure(ctx) 44 | 45 | if self.debug: 46 | ctx.cflags += ['-O0', '-ggdb'] 47 | ctx.cxxflags += ['-O0', '-ggdb'] 48 | LLVM.add_plugin_flags(ctx, '-disable-opt') 49 | else: 50 | # note: link-time optimizations break some programs (perlbench, 51 | # gcc) if our instrumentation runs and -O2 was not passed at 52 | # compile time 53 | ctx.cflags += ['-O2'] 54 | ctx.cxxflags += ['-O2'] 55 | 56 | def add_stats_pass(name, *args): 57 | LLVM.add_plugin_flags(ctx, name, '-stats-only=' + name, *args) 58 | 59 | # prepare initalizations of globals so that the next passes only have to 60 | # operate on instructions (rather than constantexprs) 61 | add_stats_pass('-defer-global-init') 62 | add_stats_pass('-expand-const-global-users') 63 | 64 | # make sure all calls to allocation functions are direct 65 | add_stats_pass('-replace-address-taken-malloc') 66 | 67 | # do some analysis for optimizations 68 | if self.optimizer == 'old': 69 | add_stats_pass('-safe-allocs-old') 70 | elif self.optimizer == 'new': 71 | # simplify loops to ease analysis 72 | LLVM.add_plugin_flags(ctx, '-loop-simplify') 73 | add_stats_pass('-safe-allocs') 74 | 75 | # find integers that contain pointer values and thus need to be masked 76 | add_stats_pass('-find-reinterpreted-pointers') 77 | 78 | # tag heap/stack/global allocations 79 | add_stats_pass('-deltatags-alloc', 80 | '-address-space-bits=%d' % self.addrspace_bits) 81 | 82 | # propagate size tags on ptr arith and libc calls 83 | add_stats_pass('-deltatags-prop', 84 | '-deltatags-check-overflow=' + self.overflow_check) 85 | 86 | # mask pointers at dereferences / libcalls 87 | add_stats_pass('-mask-pointers', 88 | '-mask-pointers-ignore-list=strtok') 89 | 90 | # undo loop simplification changes 91 | if self.optimizer == 'new': 92 | LLVM.add_plugin_flags(ctx, '-simplifycfg') 93 | 94 | # dump IR for debugging 95 | LLVM.add_plugin_flags(ctx, '-dump-ir') 96 | 97 | # inline statically linked helpers 98 | LLVM.add_plugin_flags(ctx, '-custominline') 99 | 100 | def prepare_run(self, ctx): 101 | assert 'target_run_wrapper' not in ctx 102 | ctx.target_run_wrapper = self.libshrink.run_wrapper(ctx) 103 | 104 | @classmethod 105 | def make_instances(cls): 106 | # cls(name, overflow_check, optimizer) 107 | yield cls('deltatags-noopt', 'none', None) 108 | yield cls('deltatags', 'none', 'old') 109 | yield cls('deltatags-satarith', 'satarith', 'old') 110 | yield cls('deltatags-newopt', 'none', 'new') 111 | -------------------------------------------------------------------------------- /config/packages.py: -------------------------------------------------------------------------------- 1 | import os 2 | from infra import Package 3 | from infra.packages import LibShrink, LLVM 4 | from infra.util import run 5 | 6 | 7 | def strbool(b): 8 | return 'true' if b else 'false' 9 | 10 | 11 | class LibDeltaTags(Package): 12 | def __init__(self, llvm_passes, addrspace_bits, overflow_bit, 13 | runtime_stats=False, debug=False): 14 | self.llvm_passes = llvm_passes 15 | self.addrspace_bits = addrspace_bits 16 | self.overflow_bit = overflow_bit 17 | self.runtime_stats = runtime_stats 18 | self.debug = debug 19 | 20 | def dependencies(self): 21 | yield self.llvm_passes.llvm 22 | curdir = os.path.dirname(os.path.abspath(__file__)) 23 | yield LibShrink(self.addrspace_bits) 24 | 25 | def ident(self): 26 | return 'libdeltatags-%d%s' % (self.addrspace_bits, 27 | '-overflow-bit' if self.overflow_bit else '') 28 | 29 | def fetch(self, ctx): 30 | os.symlink(os.path.join(ctx.paths.root, 'runtime'), 'src') 31 | 32 | def build(self, ctx): 33 | os.makedirs('obj', exist_ok=True) 34 | self.run_make(ctx, '-j%d' % ctx.jobs) 35 | 36 | def install(self, ctx): 37 | pass 38 | 39 | def run_make(self, ctx, *args): 40 | os.chdir(self.path(ctx, 'src')) 41 | env = { 42 | 'OBJDIR': self.path(ctx, 'obj'), 43 | 'LLVM_VERSION': self.llvm_passes.llvm.version, 44 | 'ADDRSPACE_BITS': str(self.addrspace_bits), 45 | 'OVERFLOW_BIT': strbool(self.overflow_bit), 46 | 'RUNTIME_STATS': strbool(self.runtime_stats), 47 | 'DEBUG': strbool(self.debug) 48 | } 49 | return run(ctx, ['make', *args], env=env) 50 | 51 | def is_fetched(self, ctx): 52 | return os.path.exists('src') 53 | 54 | def is_built(self, ctx): 55 | return os.path.exists('obj/libdeltatags.a') 56 | 57 | def is_installed(self, ctx): 58 | return self.is_built(ctx) 59 | 60 | def configure(self, ctx): 61 | # undef symbols to make sure the pass can find them 62 | exposed_functions = [ 63 | 'strsize_nullsafe', 'strtok', 'strtok_ubound', 'rts_gep', 64 | 'rts_load', 'rts_store', 'check_neg_arith', 'mask_pointer_bzhi', 65 | 'mask_pointer_pext_reg', 'mask_pointer_pext_glob', 'execv_mask', 66 | 'execvp_mask', 'execvpe_mask', 'execve_mask', 'writev_mask', 67 | 'is_oob', '_tag_pointer', '_mask_pointer', '_tag_of', '_take_tag', 68 | '_ptr_arith' 69 | ] 70 | ctx.ldflags += ['-u__noinstrument_' + fn for fn in exposed_functions] 71 | 72 | # link static library 73 | ctx.ldflags += ['-L' + self.path(ctx, 'obj'), '-Wl,-whole-archive', 74 | '-l:libdeltatags.a', '-Wl,-no-whole-archive'] 75 | cflags = ['-DDELTAPOINTERS', '-I' + self.path(ctx, 'src')] 76 | cflags += self.llvm_passes.runtime_cflags(ctx) 77 | ctx.cflags += cflags 78 | ctx.cxxflags += cflags 79 | 80 | # pass overflow-bit option to instrumentation pass 81 | LLVM.add_plugin_flags(ctx, '-overflow-bit=' + strbool(self.overflow_bit)) 82 | -------------------------------------------------------------------------------- /config/targets.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | from infra import Target 4 | from infra.util import run, qjoin 5 | from .instances import DeltaTags 6 | 7 | 8 | class DeltaTagsTest(Target): 9 | name = 'deltatags-test' 10 | 11 | def __init__(self): 12 | pass 13 | 14 | def is_fetched(self, ctx): 15 | return os.path.exists('src') 16 | 17 | def fetch(self, ctx): 18 | os.symlink(os.path.join(ctx.paths.root, 'deltatags-test'), 'src') 19 | 20 | def build(self, ctx, instance): 21 | self.run_make(ctx, instance, '--always-make') 22 | 23 | def link(self, ctx, instance): 24 | pass 25 | 26 | def binary_paths(self, ctx, instance): 27 | return self.run_make(ctx, instance, 'bins').stdout.split() 28 | 29 | def run_make(self, ctx, instance, *args): 30 | os.chdir(self.path(ctx, 'src')) 31 | env = { 32 | 'TARGETDIR': self.path(ctx, instance.name), 33 | 'LLVM_VERSION': DeltaTags.llvm.version, 34 | 'CC': ctx.cc, 35 | 'CXX': ctx.cxx, 36 | 'CFLAGS': qjoin(ctx.cflags), 37 | 'CXXFLAGS': qjoin(ctx.cxxflags), 38 | 'LDFLAGS': qjoin(ctx.ldflags) 39 | } 40 | return run(ctx, ['make', *args], env=env) 41 | 42 | def add_run_args(self, parser): 43 | parser.add_argument('args', nargs=argparse.REMAINDER, 44 | help='arguments to pass to run script') 45 | 46 | def run(self, ctx, instance): 47 | os.chdir('src') 48 | wrap = ctx.get('target_run_wrapper', '') 49 | run(ctx, ['bash', 'runtests.sh', instance.name, wrap, *ctx.args.args], 50 | teeout=True, allow_error=True) 51 | -------------------------------------------------------------------------------- /deltatags-test/.gitignore: -------------------------------------------------------------------------------- 1 | prelink-* 2 | build* 3 | rts_* 4 | debugnegarith.out 5 | *.ll 6 | -------------------------------------------------------------------------------- /deltatags-test/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean cleaner 2 | 3 | ifndef TARGETDIR 4 | ifdef INSTANCE 5 | TARGETDIR := ../build/targets/deltatags-test/$(INSTANCE) 6 | else 7 | $(error TARGETDIR is undefined) 8 | endif 9 | endif 10 | 11 | OBJDIR := $(TARGETDIR)/obj 12 | OBJDIR_CPP := $(TARGETDIR)/obj-cpp 13 | BINDIR := $(TARGETDIR)/bin 14 | 15 | SRCS_C := $(wildcard test_*.c) 16 | SRCS_CPP := $(wildcard test_*.cpp) 17 | OBJS_C := $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS_C)) 18 | OBJS_CPP := $(patsubst %.cpp,$(OBJDIR_CPP)/%.o,$(SRCS_CPP)) 19 | BINS := $(patsubst %.c,$(BINDIR)/%,$(SRCS_C)) $(patsubst %.cpp,$(BINDIR)/%,$(SRCS_CPP)) 20 | 21 | LLVM_VERSION := 3.8.0 22 | PKG_CONFIG := python3 ../setup.py pkg-config 23 | INCLUDES = `$(PKG_CONFIG) llvm-passes-builtin-$(LLVM_VERSION) --runtime-cflags` 24 | CFLAGS += -g -O0 25 | CXXFLAGS := $(CFLAGS) -std=c++11 26 | 27 | all: $(BINS) 28 | 29 | $(BINDIR)/%: $(OBJDIR)/%.o | $(BINDIR) 30 | $(CC) $(LDFLAGS) -o $@ $^ 31 | $(BINDIR)/%: $(OBJDIR_CPP)/%.o | $(BINDIR) 32 | $(CXX) $(LDFLAGS) -o $@ $^ 33 | 34 | $(OBJDIR)/%.o: %.c | $(OBJDIR) 35 | $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< 36 | $(OBJDIR_CPP)/%.o: %.cpp | $(OBJDIR_CPP) 37 | $(CXX) $(INCLUDES) $(CXXFLAGS) -c -o $@ $< 38 | 39 | .PRECIOUS: $(OBJS_C) $(OBJS_CPP) 40 | 41 | clean: 42 | rm -f $(BINS) $(OBJS_C) $(OBJS_CPP) *.ll 43 | 44 | cleaner: clean 45 | rm -rf prelink-* build* rts_* 46 | 47 | .PHONY: bins 48 | bins: 49 | @echo $(BINS) 50 | 51 | $(OBJDIR) $(OBJDIR_CPP) $(BINDIR): 52 | mkdir -p $@ 53 | -------------------------------------------------------------------------------- /deltatags-test/deltatags.h: -------------------------------------------------------------------------------- 1 | #ifndef _DELTATAGS_H 2 | #define _DELTATAGS_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "noinstrument.h" /* for NOINSTRUMENT macro */ 13 | #include "dump-ir-helper.h" /* for DEBUG_MODULE_NAME macro */ 14 | 15 | // Uncommente if testing ubound-branch pass 16 | //#define TESTING_UBOUND_BRANCH 17 | 18 | #define ADDRSPACE_BITS 32 19 | #define ADDRSPACE_MASK ((1ULL << ADDRSPACE_BITS) - 1) 20 | 21 | /* assert doesn't work properly with shrinkaddrspace (has ptr to old stack in 22 | * __assert_fail_base()). 23 | * 24 | * Use inline asm to exit with error, so optimizer cannot catch 25 | * on to the fact that, if the condition does not hold, we do a no-return exit() 26 | * call, causing the optimizer to assume all statements in the assert as true, 27 | * and throwing away a bunch of code because of it (e.g., &a == &b causes the 28 | * entire remainder of function to use that alias). 29 | */ 30 | #define assert(cond) \ 31 | do { \ 32 | if (!(cond)) { \ 33 | fprintf(stderr, "Assertion failed: " #cond "\n"); \ 34 | asm volatile ("mov $231, %rax\n\t" /* sys_exit_group */ \ 35 | "mov $134, %rdi\n\t" /* error code */ \ 36 | "syscall\n\t"); \ 37 | } \ 38 | } while (0) 39 | 40 | #define leak_ptr(x) NOINSTRUMENT(leak_ptr)(x) 41 | #define ptr_get_size(x) NOINSTRUMENT(ptr_get_size)(x) 42 | 43 | __attribute__((noinline)) static uintptr_t NOINSTRUMENT(leak_ptr)(void *p) { 44 | uintptr_t tmp; 45 | asm volatile ("mov %1, %0\n\t" : "=r"(tmp) : "r"(p)); 46 | return tmp; 47 | } 48 | 49 | __attribute__((noinline)) static size_t NOINSTRUMENT(ptr_get_size)(void *p) { 50 | uintptr_t raw = leak_ptr(p); 51 | #ifdef TESTING_UBOUND_BRANCH 52 | return (raw >> ADDRSPACE_BITS) - (raw & ADDRSPACE_MASK); 53 | #else 54 | uintptr_t meta = (uintptr_t)(raw >> ADDRSPACE_BITS); 55 | return (size_t)(meta ? (-meta << (ADDRSPACE_BITS + 1) >> (ADDRSPACE_BITS + 1)) : 0); 56 | #endif 57 | } 58 | 59 | 60 | #ifdef __cplusplus 61 | } 62 | #endif 63 | 64 | #endif /* _DELTATAGS_H */ 65 | -------------------------------------------------------------------------------- /deltatags-test/fault_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef _FAULT_HANDLER_H 2 | #define _FAULT_HANDLER_H 3 | 4 | #define _GNU_SOURCE 5 | #include 6 | #include 7 | #include 8 | 9 | static int expect_fault = 0; 10 | static jmp_buf chkp; 11 | 12 | static void test_sig_handler(int sig, siginfo_t *si, void *ptr); 13 | 14 | __attribute__((noinline)) 15 | static void install_sig_handler(void) 16 | { 17 | struct sigaction sa; 18 | sa.sa_flags = SA_SIGINFO | SA_RESETHAND; 19 | sigemptyset(&sa.sa_mask); 20 | sa.sa_sigaction = test_sig_handler; 21 | sigaction(SIGSEGV, &sa, NULL); 22 | sigaction(SIGBUS, &sa, NULL); 23 | sigaction(SIGILL, &sa, NULL); 24 | } 25 | 26 | static void test_sig_handler(int sig, siginfo_t *si, void *ptr) 27 | { 28 | if (!expect_fault) 29 | exit(1); 30 | 31 | expect_fault = 0; 32 | install_sig_handler(); 33 | siglongjmp(chkp, 1); 34 | } 35 | 36 | #define checkpoint(x) \ 37 | do { \ 38 | expect_fault = 1; \ 39 | if (sigsetjmp(chkp, 1)) \ 40 | goto x; \ 41 | } while (0) 42 | 43 | #endif /* _FAULT_HANDLER_H */ 44 | -------------------------------------------------------------------------------- /deltatags-test/runtests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | [ $# -gt 2 -o $# -lt 1 ] && { echo "Usage: $0 [config-name] [run-wrapper]"; exit 1; } 5 | 6 | instance="" 7 | run_wrapper="" 8 | [ $# -ge 1 ] && instance="$1" 9 | [ $# -eq 2 ] && run_wrapper="$2" 10 | 11 | # Determine directory that script is in, so it can be invoked from anywhere. 12 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 13 | cd "$DIR" 14 | 15 | fails=0 16 | bins=`make -s bins INSTANCE="$instance"` 17 | for b in $bins; do 18 | if [ ! -f "$b" ]; then 19 | echo "ERROR: Binary $b not found in build dir!" 20 | exit 1 21 | fi 22 | 23 | bname=`basename "$b"` 24 | outfile="${b}.out" 25 | 26 | export RTS_OUTFILE_BASE="rts_${bname}_" 27 | 28 | if ! eval "$run_wrapper $b" &> "$outfile"; then 29 | echo "[FAIL] $bname" 30 | fails=$((fails + 1)) 31 | else 32 | echo "[ OK ] $bname" 33 | rm "$outfile" 34 | fi 35 | done 36 | 37 | if [ $fails -gt 0 ]; then 38 | echo 39 | echo "$fails benchmark(s) failed. See binary directory for output files" 40 | exit 1; 41 | fi 42 | -------------------------------------------------------------------------------- /deltatags-test/test_arith_geps.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "deltatags.h" 7 | 8 | DEBUG_MODULE_NAME("test_arith_geps"); 9 | 10 | struct a { 11 | int a1, a2; 12 | void *a3, *a4; 13 | }; 14 | 15 | struct b { 16 | int b1; 17 | struct a b2[16]; 18 | }; 19 | 20 | int main(int argc, char **argv) 21 | { 22 | struct b s[8]; 23 | 24 | memset(s, 'A', sizeof(s)); 25 | 26 | void **indexed = &s[2].b2[5].a3; 27 | printf("s: %p size: %zu\n", s, sizeof(s)); 28 | printf("&s[2].b2[5].a3: %p: %p\n", indexed, *indexed); 29 | printf("Supposed size: %zu, metadata size: %zu\n", 30 | sizeof(s) - ((char*)indexed - (char*)s), ptr_get_size(indexed)); 31 | assert(sizeof(s) - ((char*)indexed - (char*)s) == ptr_get_size(indexed)); 32 | 33 | 34 | void **indexed2 = &s[2].b2[argc].a3; 35 | printf("Using argc now: %d\n", argc); 36 | printf("&s[2].b2[%d].a3: %p: %p\n", argc, indexed2, *indexed2); 37 | printf("Supposed size: %zu, metadata size: %zu\n", 38 | sizeof(s) - ((char*)indexed2 - (char*)s), ptr_get_size(indexed2)); 39 | assert(sizeof(s) - ((char*)indexed2 - (char*)s) == ptr_get_size(indexed2)); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /deltatags-test/test_arith_neg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "deltatags.h" 7 | 8 | DEBUG_MODULE_NAME("test_arith_neg"); 9 | 10 | int main(int argc, char **argv) 11 | { 12 | int foo[16]; 13 | memset(foo, 'A', sizeof(foo)); 14 | 15 | int *midway = (int*)leak_ptr(&foo[8]); 16 | 17 | printf(" foo: %p metasize: %zu\n", foo, ptr_get_size(foo)); 18 | printf("midway: %p metasize: %zu\n", midway, ptr_get_size(midway)); 19 | printf("foo[8]: %x midway[0]: %x\n", foo[8], midway[0]); 20 | assert(&foo[8] == &midway[0]); 21 | assert(ptr_get_size(midway) == sizeof(foo) / 2); 22 | 23 | int *sub = &midway[-7]; 24 | printf("&foo[1]: %p, &sub[0]: %p\n", &foo[1], &sub[0]); 25 | printf("correct size: %zu, sub metasize: %zu\n", ptr_get_size(&foo[1]), ptr_get_size(sub)); 26 | assert(&foo[1] == sub); 27 | assert(ptr_get_size(&foo[1]) == ptr_get_size(sub)); 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /deltatags-test/test_exec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "deltatags.h" 5 | 6 | DEBUG_MODULE_NAME("test_exec"); 7 | 8 | int main(int argc, char **argv, char **envp) 9 | { 10 | if (argc == 2) { 11 | printf("%s child reporting in\n", argv[0]); 12 | return 0; 13 | } 14 | char *newargv[] = {argv[0], "dummy", NULL}; 15 | execve(argv[0], newargv, envp); 16 | perror("execve"); 17 | return -1; 18 | } 19 | -------------------------------------------------------------------------------- /deltatags-test/test_fork.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "deltatags.h" 7 | 8 | DEBUG_MODULE_NAME("test_fork"); 9 | 10 | int main(void) { 11 | int pid; 12 | 13 | pid = fork(); 14 | if (pid < 0) { 15 | perror("fork"); 16 | exit(1); 17 | } else if (pid == 0) { 18 | /* Child */ 19 | printf("Print from child\n"); 20 | } else { 21 | /* Parent */ 22 | int status, retpid; 23 | printf("Print from parent, child pid: %d\n", pid); 24 | retpid = waitpid(pid, &status, 0); 25 | printf("Waitpid: pid: %d, exited: %d, exitstatus: %d\n", retpid, WIFEXITED(status), WEXITSTATUS(status)); 26 | assert(retpid == pid); 27 | assert(WIFEXITED(status)); 28 | assert(WEXITSTATUS(status) == 0); 29 | } 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /deltatags-test/test_global_string_free.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "deltatags.h" 4 | 5 | DEBUG_MODULE_NAME("test_global_string_free"); 6 | 7 | std::string s; 8 | 9 | int main(void) 10 | { 11 | printf("s @ %p\n", &s); 12 | s = "foo"; 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /deltatags-test/test_implicitcast.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "deltatags.h" 11 | 12 | DEBUG_MODULE_NAME("test_implicitcast"); 13 | 14 | /* We intentionally don't include header files for open and read, so that they 15 | * are declared as i32 @open(...) and then casted to the correct type when used. 16 | */ 17 | 18 | int main(int argc, char **argv) 19 | { 20 | char *a = malloc(10); 21 | int fd = open("/dev/zero", 0); /* O_RDONLY */ 22 | printf("fd = %d\n", fd); 23 | assert(fd > 0); 24 | size_t r = read(fd, a, 10); 25 | printf("reading 10 bytes, got %zu\n ", r); 26 | assert(r == 10); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /deltatags-test/test_iovec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "deltatags.h" 9 | 10 | DEBUG_MODULE_NAME("test_iovec"); 11 | 12 | int main(int argc, char **argv) 13 | { 14 | struct iovec iov[3]; 15 | 16 | int fd = open("/dev/null", O_WRONLY); 17 | if (fd == -1) { 18 | perror("open"); 19 | return 1; 20 | } 21 | 22 | iov[0].iov_base = "Hello, "; 23 | iov[0].iov_len = strlen(iov[0].iov_base); 24 | iov[1].iov_base = "World"; 25 | iov[1].iov_len = strlen(iov[1].iov_base); 26 | iov[2].iov_base = "!\n"; 27 | iov[2].iov_len = strlen(iov[2].iov_base); 28 | size_t expected_size = strlen("Hello, World!\n"); 29 | 30 | ssize_t ret = writev(fd, iov, 3); 31 | printf("writev: %zd\n", ret); 32 | 33 | if (ret == -1) { 34 | perror("writev"); 35 | return 2; 36 | } else if (ret != expected_size) { 37 | fprintf(stderr, "writev returned %zd, expected %zu\n", ret, expected_size); 38 | return 3; 39 | } 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /deltatags-test/test_libptr_copyfromarg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "deltatags.h" 6 | 7 | DEBUG_MODULE_NAME("test_libptr_copyfromarg"); 8 | 9 | int main(void) 10 | { 11 | char buf[512]; 12 | 13 | char *str = getcwd(buf, sizeof(buf)); 14 | printf("getcwd: '%s'\n", str); 15 | printf("real size: %zu, input metasize: %zu, output metasize: %zu\n", sizeof(buf), ptr_get_size(buf), ptr_get_size(str)); 16 | assert(sizeof(buf) == ptr_get_size(buf)); 17 | assert(sizeof(buf) == ptr_get_size(str)); 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /deltatags-test/test_libptr_copyfromarg_cpp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "deltatags.h" 7 | 8 | DEBUG_MODULE_NAME("test_libptr_copyfromarg_cpp"); 9 | 10 | int main(void) 11 | { 12 | std::ofstream file; 13 | file.open("/dev/null", std::ios::binary); 14 | std::ostream *t = &(file << 1); 15 | 16 | printf("file: %p, t: %p\n", &file, t); 17 | printf("real size: %zu, input metasize: %zu, output metasize: %zu\n", sizeof(file), ptr_get_size(&file), ptr_get_size(t)); 18 | assert(sizeof(file) == ptr_get_size(&file)); 19 | assert(sizeof(file) == ptr_get_size(t)); 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /deltatags-test/test_libptr_ptrdiff.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "deltatags.h" 7 | 8 | DEBUG_MODULE_NAME("test_libptr_ptrdiff"); 9 | 10 | int main(void) 11 | { 12 | char haystack[] = "this is a test sentence"; 13 | char needle_str[] = "test"; 14 | char needle_str_not[] = "foobar"; 15 | 16 | 17 | char *strstr_ret = strstr(haystack, needle_str); 18 | printf("strstr: '%s' @ %p\n supposed size: %zu, output metasize: %zu\n", 19 | strstr_ret, strstr_ret, sizeof(haystack) - (strstr_ret - haystack), 20 | ptr_get_size(strstr_ret)); 21 | assert(sizeof(haystack) - (strstr_ret - haystack) == ptr_get_size(strstr_ret)); 22 | 23 | char *strstr_ret_not = strstr(haystack, needle_str_not); 24 | printf("strstr notfound: %p\n output metasize: %zu\n", 25 | strstr_ret_not, ptr_get_size(strstr_ret_not)); 26 | assert(strstr_ret_not == NULL); 27 | 28 | char *memchr_ret = memchr(haystack, 'a', sizeof(haystack)); 29 | printf("memchr: '%s' @ %p\n supposed size: %zu, output metasize: %zu\n", 30 | memchr_ret, memchr_ret, sizeof(haystack) - (memchr_ret - haystack), 31 | ptr_get_size(memchr_ret)); 32 | assert(sizeof(haystack) - (memchr_ret - haystack) == ptr_get_size(memchr_ret)); 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /deltatags-test/test_libptr_retsize.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "deltatags.h" 8 | 9 | DEBUG_MODULE_NAME("test_libptr_retsize"); 10 | 11 | int main(void) 12 | { 13 | const unsigned short **table = __ctype_b_loc(); 14 | printf("__ctype_b_loc: supposed size: %zu, output metasize: %zu\n", 15 | sizeof(*table), ptr_get_size(table)); 16 | assert(sizeof(*table) == ptr_get_size(table)); 17 | 18 | time_t rawtime; 19 | time(&rawtime); 20 | struct tm *tm = gmtime(&rawtime); 21 | 22 | printf("gmtime: supposed size: %zu, output metasize: %zu\n", 23 | sizeof(*tm), ptr_get_size(tm)); 24 | assert(sizeof(*tm) == ptr_get_size(tm)); 25 | 26 | FILE *f = fopen("/etc/passwd", "r"); 27 | printf("fopen: supposed size: %zu, output metasize: %zu\n", 28 | sizeof(*f), ptr_get_size(f)); 29 | assert(sizeof(*f) == ptr_get_size(f)); 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /deltatags-test/test_libptr_strlen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "deltatags.h" 8 | 9 | DEBUG_MODULE_NAME("test_libptr_strlen"); 10 | 11 | int main(void) 12 | { 13 | time_t rawtime; 14 | time(&rawtime); 15 | 16 | char *str = ctime(&rawtime); 17 | printf("ctime: '%s'\n", str); 18 | printf("ctime size: %zu, metadata size: %zu\n", strlen(str) + 1, ptr_get_size(str)); 19 | assert(strlen(str) + 1 == ptr_get_size(str)); 20 | 21 | unsetenv("ENVVAR_THAT_DOES_NOT_EXIST"); 22 | char *env = getenv("ENVVAR_THAT_DOES_NOT_EXIST"); 23 | printf("getenv: %p\n", env); 24 | printf("getenv metadata size: %zu\n", ptr_get_size(env)); 25 | assert(env == NULL); 26 | assert(ptr_get_size(env) == 0); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /deltatags-test/test_libptr_strtok.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "deltatags.h" 7 | 8 | DEBUG_MODULE_NAME("test_libptr_strtok"); 9 | 10 | #define check_tok(tok, correct_str, base) \ 11 | do { \ 12 | printf("strtok: '%s' @ %p, supposed size: %zu, output metasize: %zu\n",\ 13 | tok, tok, sizeof(base) - (tok - base), ptr_get_size(tok)); \ 14 | assert(!strcmp(tok, correct_str)); \ 15 | assert(sizeof(base) - (tok - base) == ptr_get_size(tok)); \ 16 | } while (0) 17 | 18 | int main(void) 19 | { 20 | char haystack[] = "this is a test"; 21 | char haystack2[] = "second haystack"; 22 | char *tok; 23 | 24 | tok = strtok(haystack, " "); 25 | check_tok(tok, "this", haystack); 26 | tok = strtok(NULL, " "); 27 | check_tok(tok, "is", haystack); 28 | tok = strtok(NULL, " "); 29 | check_tok(tok, "a", haystack); 30 | tok = strtok(NULL, " "); 31 | check_tok(tok, "test", haystack); 32 | 33 | tok = strtok(haystack2, " "); 34 | check_tok(tok, "second", haystack2); 35 | tok = strtok(NULL, " "); 36 | check_tok(tok, "haystack", haystack2); 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /deltatags-test/test_mask.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "deltatags.h" 5 | 6 | DEBUG_MODULE_NAME("test_mask"); 7 | 8 | int main(int argc, char **argv) 9 | { 10 | if (argc != 2) { 11 | fprintf(stderr, "Usage: %s N\n", argv[0]); 12 | return 0; // so that the testcase succeeds 13 | } 14 | 15 | long long i, n = atoi(argv[1]); 16 | 17 | if (n > 0x7fffffff) { 18 | fprintf(stderr, "max N is %lld\n", 0x7fffffffLL); 19 | return 1; 20 | } 21 | 22 | volatile char *a = malloc(n * sizeof (char)); 23 | if (!a) { 24 | perror("malloc"); 25 | return 1; 26 | } 27 | 28 | struct timeval tv_start, tv_end; 29 | if (gettimeofday(&tv_start, NULL) < 0) { 30 | perror("gettimeofday"); 31 | return 1; 32 | } 33 | 34 | for (i = 0; i < n; i++) { 35 | a[i] = (char)(argc * i); 36 | } 37 | 38 | if (gettimeofday(&tv_end, NULL) < 0) { 39 | perror("gettimeofday"); 40 | return 1; 41 | } 42 | double diff = (double)(tv_end.tv_sec - tv_start.tv_sec) + 43 | (double)(tv_end.tv_usec - tv_start.tv_usec) / 1e6; 44 | printf("%lld iterations took %g seconds\n", n, diff); 45 | 46 | free((void*)a); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /deltatags-test/test_memintrinsics.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "deltatags.h" 11 | 12 | DEBUG_MODULE_NAME("test_memintrinsics"); 13 | 14 | int expect_fault = 0; 15 | jmp_buf chkp; 16 | 17 | void test_sig_handler(int sig, siginfo_t *si, void *ptr); 18 | 19 | void install_sig_handler(void) 20 | { 21 | struct sigaction sa; 22 | sa.sa_flags = SA_SIGINFO | SA_RESETHAND; 23 | sigemptyset(&sa.sa_mask); 24 | sa.sa_sigaction = test_sig_handler; 25 | sigaction(SIGSEGV, &sa, NULL); 26 | sigaction(SIGBUS, &sa, NULL); 27 | sigaction(SIGILL, &sa, NULL); 28 | } 29 | 30 | void test_sig_handler(int sig, siginfo_t *si, void *ptr) 31 | { 32 | if (!expect_fault) 33 | exit(1); 34 | 35 | expect_fault = 0; 36 | install_sig_handler(); 37 | siglongjmp(chkp, 1); 38 | } 39 | 40 | #define checkpoint(x) \ 41 | do { \ 42 | expect_fault = 1; \ 43 | if (sigsetjmp(chkp, 1)) \ 44 | goto x; \ 45 | } while (0) 46 | 47 | 48 | int main(int argc, char **argv) 49 | { 50 | install_sig_handler(); 51 | 52 | #define USE(var) dprintf(null, "var " #var ": %lx\n", (unsigned long)var) 53 | int null = open("/dev/null", O_WRONLY); 54 | 55 | char *a = malloc(10); 56 | char *b = malloc(1000); 57 | char *c = malloc(1001); 58 | int ret; 59 | 60 | /************ 61 | * memcpy * 62 | ************/ 63 | 64 | printf("memcpy(a, argv, 10) (OK)\n"); 65 | memcpy(a, argv, 10); 66 | USE(a); 67 | 68 | printf("memcpy(a, argv, 11) (small, should fault on dest!)\n"); 69 | checkpoint(cont1); 70 | memcpy(a, argv, 11); 71 | assert(!"err 1"); 72 | USE(a); 73 | 74 | cont1: 75 | printf("memcpy(b, c, 1001) (should fault on dest!)\n"); 76 | checkpoint(cont2); 77 | memcpy(b, c, 1001); 78 | assert(!"err 2"); 79 | 80 | cont2: 81 | printf("memcpy(c, b, 1001) (should fault on src!)\n"); 82 | checkpoint(cont3); 83 | memcpy(c, b, 1001); 84 | assert(!"err 3"); 85 | 86 | cont3: 87 | /************ 88 | * memset * 89 | ************/ 90 | 91 | printf("memset(c, 2, 1001) (OK)\n"); 92 | memset(c, 2, 1001); 93 | 94 | printf("memset(b, 4, 1001) (should fault)\n"); 95 | checkpoint(cont4); 96 | memset(b, 4, 1001); 97 | assert(!"err 4"); 98 | 99 | cont4: 100 | /************* 101 | * memmove * 102 | *************/ 103 | 104 | printf("memmove(b, c, 1000) (OK)\n"); 105 | memmove(b, c, 1000); 106 | 107 | printf("memmove(b, c, 1001) (fault dest)\n"); 108 | checkpoint(cont5); 109 | memmove(b, c, 1001); 110 | assert(!"err 5"); 111 | 112 | cont5: 113 | printf("memmove(c, b, 1001) (fault src)\n"); 114 | checkpoint(cont6); 115 | memmove(c, b, 1001); 116 | assert(!"err 6"); 117 | 118 | cont6: 119 | /************ 120 | * memcmp * 121 | ************/ 122 | 123 | printf("memcmp(b, c, 1000) (OK) %p %p\n", b, c); 124 | ret = memcmp(b, c, 1000); 125 | USE(ret); 126 | 127 | printf("memcmp(b, c, 1001) (fault arg1)\n"); 128 | checkpoint(cont7); 129 | ret = memcmp(b, c, 1001); 130 | assert(!"err 7"); 131 | USE(ret); 132 | cont7: 133 | printf("memcmp(c, b, 1001) (fault arg2)\n"); 134 | checkpoint(cont8); 135 | ret = memcmp(c, b, 1001); 136 | assert(!"err 8"); 137 | USE(ret); 138 | 139 | 140 | cont8: 141 | printf("Succes!\n"); 142 | /* Our stack is actually kinda broken here, return 0 does odd stuff. */ 143 | exit(0); 144 | } 145 | -------------------------------------------------------------------------------- /deltatags-test/test_nullptr.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "deltatags.h" 4 | #include "fault_handler.h" 5 | 6 | DEBUG_MODULE_NAME("test_nullptr"); 7 | 8 | static unsigned int glob = 0xdeadbeef; 9 | 10 | int main(int argc, char **argv) 11 | { 12 | install_sig_handler(); 13 | 14 | char *exploitable = NULL; 15 | unsigned int *ptr = &glob; 16 | 17 | printf("NULL[offset] dereference, should fault\n"); 18 | checkpoint(end); 19 | unsigned int leaked_glob = *(unsigned int*)&exploitable[(unsigned long long)ptr]; 20 | assert(leaked_glob == 0xdeadbeef); 21 | assert(!"value of glob was leaked"); 22 | 23 | end: 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /deltatags-test/test_old.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "deltatags.h" 7 | 8 | DEBUG_MODULE_NAME("test_old"); 9 | 10 | /* Make compiler play nice for simple code. */ 11 | #define USED(x) \ 12 | __asm__ __volatile__("" :: "r"(x)) 13 | #define CLOBBER(x) \ 14 | __asm__ __volatile__(\ 15 | "mov %1, %0\n\t" \ 16 | :"=r"(x) \ 17 | : "r"(x)) 18 | 19 | struct mystruct_t { 20 | const char *format; 21 | union { 22 | int ival; 23 | double dval; 24 | } value; 25 | }; 26 | 27 | extern char **environ; 28 | 29 | char glob[100] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque bibendum leo eu purus pretium amet."; 30 | 31 | static int zero[100] = {0}; 32 | 33 | __attribute__ ((noinline)) void *foo_do(size_t size) 34 | { 35 | char *tmp = malloc(size); 36 | memset(tmp, 0, size); 37 | return tmp; 38 | } 39 | 40 | __attribute__((noinline)) void takes_struct_by_val(struct mystruct_t s) 41 | { 42 | printf(s.format, s.value.ival); 43 | memcpy((char*)&s, zero, sizeof (struct mystruct_t)); 44 | } 45 | 46 | __attribute__((noinline)) void takes_struct_by_ref(struct mystruct_t *s) 47 | { 48 | printf(s->format, s->value.dval); 49 | memcpy((char*)s, zero, sizeof (struct mystruct_t)); 50 | } 51 | 52 | int main(int argc, char **argv) 53 | { 54 | int *a = malloc(1000 * sizeof(int)); 55 | char *b = calloc(1000, 1); 56 | char *c = foo_do(1000); 57 | char *d = malloc(argc + sizeof(struct mystruct_t)); 58 | char *e = foo_do(argc); 59 | char *f = calloc(argc, 8); 60 | int *g; 61 | int h[1000]; 62 | 63 | struct mystruct_t struct_a = {"I haz int: %d\n", 1}; 64 | struct mystruct_t struct_b = {"I haz int too: %d\n", 2}; 65 | 66 | USED(c); 67 | USED(d); 68 | USED(e); 69 | USED(f); 70 | b[0] = 'A'; 71 | b[1] = 'B'; 72 | printf("b: %p %s\n", b, b); 73 | printf("array spans %p - %p (inclusive)\n", &a[0], &a[999]); 74 | a[999] = 42; 75 | //a[1000] = 43; /* crash */ 76 | *a = 1; 77 | a++; 78 | *a = 2; 79 | g = a; 80 | a += 100; 81 | *a = 3; 82 | CLOBBER(a); 83 | printf("%p %d %d %d\n", a, *a, *g, a[-100]); 84 | a += 1000; 85 | CLOBBER(a); 86 | printf("%p\n", a); 87 | a -= 102; /* a[999] */ 88 | printf("%p %d\n", a, *a); 89 | CLOBBER(a); 90 | a = (int*)((char*)a + 3); 91 | printf("%p %d\n", a, *a); 92 | //CLOBBER(h); // FIXME 93 | h[50] = 50; 94 | //CLOBBER(h); // FIXME 95 | printf("%p %d\n", &h[50], h[50]); 96 | 97 | takes_struct_by_val(struct_a); 98 | takes_struct_by_val(struct_b); 99 | takes_struct_by_ref(&struct_a); 100 | takes_struct_by_ref(&struct_b); 101 | takes_struct_by_ref((struct mystruct_t*)d); 102 | 103 | printf("environ[0]: %s\n", environ[0]); 104 | printf("glob: %s\n", glob); 105 | 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /deltatags-test/test_oob.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "deltatags.h" 11 | 12 | DEBUG_MODULE_NAME("test_oob"); 13 | 14 | int expect_fault = 0; 15 | jmp_buf chkp; 16 | 17 | void test_sig_handler(int sig, siginfo_t *si, void *ptr); 18 | 19 | void install_sig_handler(void) 20 | { 21 | struct sigaction sa; 22 | sa.sa_flags = SA_SIGINFO | SA_RESETHAND; 23 | sigemptyset(&sa.sa_mask); 24 | sa.sa_sigaction = test_sig_handler; 25 | sigaction(SIGSEGV, &sa, NULL); 26 | sigaction(SIGBUS, &sa, NULL); 27 | sigaction(SIGILL, &sa, NULL); 28 | } 29 | 30 | void test_sig_handler(int sig, siginfo_t *si, void *ptr) 31 | { 32 | if (!expect_fault) 33 | exit(1); 34 | 35 | expect_fault = 0; 36 | install_sig_handler(); 37 | siglongjmp(chkp, 1); 38 | } 39 | 40 | #define checkpoint(x) \ 41 | do { \ 42 | expect_fault = 1; \ 43 | if (sigsetjmp(chkp, 1)) \ 44 | goto x; \ 45 | } while (0) 46 | 47 | 48 | int main(int argc, char **argv) 49 | { 50 | install_sig_handler(); 51 | 52 | #define USE(var) dprintf(null, "var " #var ": %lx\n", (unsigned long)var) 53 | int null = open("/dev/null", O_WRONLY); 54 | 55 | volatile char *a = malloc(10); 56 | 57 | printf("a[9] = 1; (OK)\n"); 58 | a[9] = 1; 59 | 60 | printf("read a[9]; (OK)\n"); 61 | USE(a[9]); 62 | 63 | printf("a[10] = 1; (should fail)\n"); 64 | checkpoint(cont1); 65 | a[10] = 1; 66 | assert(!"err 1"); 67 | 68 | cont1: 69 | printf("read a[10] (should fail)\n"); 70 | checkpoint(cont2); 71 | USE(a[10]); 72 | assert(!"err 2"); 73 | 74 | cont2: 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /deltatags-test/test_simple.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "deltatags.h" 7 | 8 | DEBUG_MODULE_NAME("test_simple"); 9 | 10 | int main(int argc, char **argv) 11 | { 12 | char *a = malloc(1); 13 | 14 | printf("a: %p\n", a); 15 | size_t size = ptr_get_size(a); 16 | printf("a raw: %p, size %zu %zx\n", (char*)leak_ptr(a), size, size); 17 | assert(ptr_get_size(a) == 1); 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /llvm-passes/AddressSpace.cpp: -------------------------------------------------------------------------------- 1 | #include "AddressSpace.h" 2 | 3 | using namespace llvm; 4 | 5 | cl::opt AddressSpaceBits("address-space-bits", 6 | cl::desc("Number of possible (lower) non-zero bits in a pointer (used for pointer masking)"), 7 | cl::init(64)); 8 | 9 | cl::opt PointerBits("pointer-bits", 10 | cl::desc("Total number of bits in a pointer (architectural)"), 11 | cl::init(64)); 12 | 13 | cl::opt OverflowBit("overflow-bit", 14 | cl::desc("Reserve most significant pointer bit for overflow on pointer arithmetic (implies 64-bit masking)"), 15 | cl::init(false)); 16 | 17 | unsigned getAddressSpaceBits() { 18 | if (AddressSpaceBits.getNumOccurrences() == 0) { 19 | llvm::errs() << "Error: number of address space bits unspecified\n"; 20 | exit(1); 21 | } 22 | return AddressSpaceBits; 23 | } 24 | 25 | unsigned long long getAddressSpaceMask(bool MayPreserveOverflowBit) { 26 | unsigned long long Mask = (unsigned long long)(-1LL) >> (PointerBits - getAddressSpaceBits()); 27 | if (OverflowBit && MayPreserveOverflowBit) 28 | Mask |= getOverflowMask(); 29 | return Mask; 30 | } 31 | 32 | unsigned long long getOverflowMask() { 33 | return 1ULL << (PointerBits - 1); 34 | } 35 | -------------------------------------------------------------------------------- /llvm-passes/AddressSpace.h: -------------------------------------------------------------------------------- 1 | #ifndef ADDRESS_SPACE_H 2 | #define ADDRESS_SPACE_H 3 | 4 | #include 5 | #include // because fails on LLVM 3.8.0 6 | #include 7 | 8 | extern llvm::cl::opt AddressSpaceBits; 9 | extern llvm::cl::opt PointerBits; /* FIXME: better to get this from datalayout */ 10 | extern llvm::cl::opt OverflowBit; 11 | 12 | unsigned getAddressSpaceBits(); 13 | unsigned long long getAddressSpaceMask(bool MayPreserveOverflowBit=false); 14 | unsigned long long getOverflowMask(); 15 | 16 | inline bool isPtrIntTy(llvm::Type *Ty) { 17 | return Ty->isIntegerTy(PointerBits); 18 | } 19 | 20 | inline llvm::IntegerType *getPtrIntTy(llvm::LLVMContext &C) { 21 | return llvm::Type::getIntNTy(C, PointerBits); 22 | } 23 | 24 | #define TAG_SHIFT (getAddressSpaceBits()) 25 | #define TAG_BITS (PointerBits - getAddressSpaceBits()) 26 | #define TAG_MASK_LOW ((1ULL << TAG_BITS) - 1) 27 | #define TAG_MASK_HIGH (TAG_MASK_LOW << TAG_SHIFT) 28 | 29 | #define BOUND_SHIFT (TAG_SHIFT) 30 | #define BOUND_BITS (OverflowBit ? (TAG_BITS - 1) : TAG_BITS) 31 | #define BOUND_MASK_LOW ((1ULL << BOUND_BITS) - 1) 32 | #define BOUND_MASK_HIGH (BOUND_MASK_LOW << BOUND_SHIFT) 33 | 34 | #define ALLOWED_OOB_BYTES 0 35 | 36 | #endif /* !ADDRESS_SPACE_H */ 37 | -------------------------------------------------------------------------------- /llvm-passes/CheckAddressSpace.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Check if all dereferenced pointers are within the address space limits 3 | * enforced by shrinkaddrspace (this pass basically tests if shrinkaddrspace 4 | * works correctly). 5 | * 6 | * We could aso check the addresses of globals here, but those are just 7 | * wherever you put the data sections so you can also check that with readelf. 8 | */ 9 | 10 | #define DEBUG_TYPE "check-address-space" 11 | 12 | #include "builtin/Common.h" 13 | #include "builtin/CustomFunctionPass.h" 14 | #include "AddressSpace.h" 15 | #include "SafeAllocs.h" 16 | #include "SafeAllocsOld.h" 17 | #include "ReinterpretedPointers.h" 18 | 19 | using namespace llvm; 20 | 21 | class CheckAddrSpace : public CustomFunctionPass { 22 | public: 23 | static char ID; 24 | CheckAddrSpace() : CustomFunctionPass(ID) {} 25 | 26 | virtual void getAnalysisUsage(AnalysisUsage &AU) const; 27 | 28 | private: 29 | Module *M; 30 | Function *CheckFunc; 31 | 32 | bool runOnFunction(Function &F) override; 33 | bool initializeModule(Module &M) override; 34 | 35 | void instrumentCallAlloc(CallSite *CS); 36 | void instrumentCallExt(CallSite *CS); 37 | void instrumentCallExtWrap(CallSite *CS); 38 | void instrumentCallByval(CallSite *CS); 39 | void instrumentCallExtNestedPtrs(CallSite *CS); 40 | void instrumentCmpPtr(CmpInst *ins); 41 | void instrumentPtrInt(Instruction *ins); 42 | void instrumentMemAccess(Instruction *ins); 43 | void instrumentGlobals(Module &M); 44 | 45 | void checkPointer(Value *V, IRBuilder<> &B); 46 | void checkPointerArgs(CallSite *CS); 47 | void checkNestedPointers(Value *val, CompositeType *elTy, 48 | std::vector &indices, IRBuilder<> &B); 49 | }; 50 | 51 | void CheckAddrSpace::getAnalysisUsage(AnalysisUsage &AU) const { 52 | AU.addPreserved(); 53 | AU.addPreserved(); 54 | AU.addPreserved(); 55 | AU.addUsedIfAvailable(); 56 | } 57 | 58 | char CheckAddrSpace::ID = 0; 59 | static RegisterPass X("check-address-space", 60 | "Check pointers for nonzero upper bits (to test addrspace shrinking)"); 61 | 62 | STATISTIC(NChecks, "Number of pointer checks inserted"); 63 | 64 | void CheckAddrSpace::checkPointer(Value *V, IRBuilder<> &B) { 65 | assert(CheckFunc); 66 | if (V->getType()->isPointerTy()) 67 | V = B.CreatePtrToInt(V, B.getInt64Ty(), Twine(V->getName()) + "_as_int"); 68 | assert(isPtrIntTy(V->getType())); 69 | B.CreateCall(CheckFunc, {V, B.getInt64(++NChecks)}); 70 | } 71 | 72 | void CheckAddrSpace::checkPointerArgs(CallSite *CS) { 73 | IRBuilder<> B(CS->getInstruction()); 74 | 75 | for (unsigned i = 0, n = CS->getNumArgOperands(); i < n; i++) { 76 | Value *arg = CS->getArgOperand(i); 77 | if (arg->getType()->isPointerTy()) 78 | checkPointer(arg, B); 79 | } 80 | } 81 | 82 | /* 83 | * Mask pointers to external functions. 84 | */ 85 | void CheckAddrSpace::instrumentCallExt(CallSite *CS) { 86 | Function *F = CS->getCalledFunction(); 87 | 88 | /* XXX inline asm should actually be masked, but currently our tests rely on 89 | * these semantics. For for instance nginx breaks with this. */ 90 | if (CS->isInlineAsm()) 91 | return; 92 | 93 | /* Indirect external calls are handled differently (wrapped in new function 94 | * that does the masking) */ 95 | if (!F) 96 | return; 97 | 98 | if (!F->isDeclaration()) /* not external */ 99 | return; 100 | 101 | // FIXME: use subclasses of CallInst here 102 | if (F->getName().startswith("llvm.eh.") || 103 | F->getName().startswith("llvm.dbg.") || 104 | F->getName().startswith("llvm.lifetime.")) 105 | return; 106 | 107 | checkPointerArgs(CS); 108 | } 109 | 110 | void CheckAddrSpace::instrumentCallExtWrap(CallSite *CS) { 111 | Function *F = CS->getCalledFunction(); 112 | if (CS->isInlineAsm() || !F) 113 | return; 114 | 115 | if (F->getName() != "_E__pr_info" && /* sphinx3 vfprintf wrapper */ 116 | F->getName() != "_ZN12pov_frontend13MessageOutput6PrintfEiPKcz" && /* povray vsnprintf wrapper */ 117 | F->getName() != "_ZN8pov_base16TextStreamBuffer6printfEPKcz" && /* povray vsnprintf wrapper */ 118 | F->getName() != "_ZN3pov10Debug_InfoEPKcz" && /* povray vsnprintf wrapper */ 119 | F->getName() != "_ZN6cEnvir9printfmsgEPKcz") /* omnetpp vsprintf wrapper */ 120 | return; 121 | 122 | checkPointerArgs(CS); 123 | } 124 | 125 | void CheckAddrSpace::instrumentCallByval(CallSite *CS) { 126 | Function *F = CS->getCalledFunction(); 127 | if (CS->isInlineAsm()) /* XXX inline asm should actually be masked? */ 128 | return; 129 | if (F && F->isDeclaration()) /* external functions are handled above */ 130 | return; 131 | 132 | IRBuilder<> B(CS->getInstruction()); 133 | 134 | for (unsigned i = 0, n = CS->getNumArgOperands(); i < n; i++) { 135 | Value *arg = CS->getArgOperand(i); 136 | if (arg->getType()->isPointerTy() && CS->paramHasAttr(i + 1, Attribute::ByVal)) 137 | checkPointer(arg, B); 138 | } 139 | } 140 | 141 | void CheckAddrSpace::checkNestedPointers(Value *val, CompositeType *elTy, 142 | std::vector &indices, IRBuilder<> &B) { 143 | unsigned n = elTy->isStructTy() ? 144 | cast(elTy)->getNumElements() : 145 | cast(elTy)->getNumElements(); 146 | 147 | for (unsigned i = 0; i < n; i++) { 148 | Type *ty = elTy->getTypeAtIndex(i); 149 | 150 | if (ty->isPointerTy()) { 151 | DEBUG_LINE("masking nested pointer of type " << *ty << " in value: " << *val); 152 | indices.push_back(B.getInt32(i)); 153 | Value *ptr = B.CreateInBoundsGEP(val, indices); 154 | indices.pop_back(); 155 | checkPointer(B.CreateLoad(ptr), B); 156 | } 157 | else if (ty->isAggregateType()) { 158 | indices.push_back(B.getInt32(i)); 159 | checkNestedPointers(val, cast(ty), indices, B); 160 | indices.pop_back(); 161 | } 162 | } 163 | } 164 | 165 | void CheckAddrSpace::instrumentCallExtNestedPtrs(CallSite *CS) { 166 | static std::map> whitelist = { 167 | /* inlined std::list::push_back */ 168 | {"_ZNSt8__detail15_List_node_base7_M_hookEPS0_", {1}}, 169 | /* inlined std::string += std::string */ 170 | {"_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcm", {0}} 171 | }; 172 | 173 | Function *F = CS->getCalledFunction(); 174 | if (!F) 175 | return; 176 | 177 | auto it = whitelist.find(F->getName()); 178 | if (it == whitelist.end()) 179 | return; 180 | 181 | assert(F->isDeclaration()); 182 | IRBuilder<> B(CS->getInstruction()); 183 | std::vector indices = {B.getInt64(0)}; 184 | 185 | for (unsigned i : it->second) { 186 | Value *arg = CS->getArgOperand(i); 187 | Type *elTy = cast(arg->getType())->getElementType(); 188 | assert(elTy->isAggregateType()); 189 | DEBUG_LINE("check nested pointers in arg " << i << " of:" << *CS->getInstruction()); 190 | checkNestedPointers(arg, cast(elTy), indices, B); 191 | } 192 | } 193 | 194 | void CheckAddrSpace::instrumentCmpPtr(CmpInst *ins) { 195 | Value *arg1 = ins->getOperand(0); 196 | Value *arg2 = ins->getOperand(1); 197 | assert(arg1->getType()->isPointerTy() == arg2->getType()->isPointerTy()); 198 | if (!arg1->getType()->isPointerTy()) 199 | return; 200 | 201 | IRBuilder<> B(ins); 202 | checkPointer(arg1, B); 203 | checkPointer(arg2, B); 204 | } 205 | 206 | /* 207 | * Mask out metadata bits in pointers when a pointer is accessed. It does not 208 | * mask out the overflow bit, so out-of-bound accesses will cause a fault. 209 | */ 210 | void CheckAddrSpace::instrumentMemAccess(Instruction *ins) { 211 | int ptrOperand = isa(ins) ? 1 : 0; 212 | Value *ptr = ins->getOperand(ptrOperand); 213 | // TODO: don't mask if isa(ptrOperand) 214 | 215 | IRBuilder<> B(ins); 216 | checkPointer(ptr, B); 217 | 218 | /* Also mask writes of pointers to externs (e.g., environ). */ 219 | /* TODO: we don't have to mask the ptr above if global value */ 220 | GlobalVariable *gv = dyn_cast(ptr->stripPointerCasts()); 221 | if (isa(ins) && gv && !gv->hasInitializer() && gv->getType()->isPointerTy()) 222 | checkPointer(ins->getOperand(0), B); 223 | } 224 | 225 | bool CheckAddrSpace::runOnFunction(Function &F) { 226 | if (F.getName() == "initialize_global_metapointers") 227 | return false; 228 | 229 | ReinterpretedPointers *ReintPtrs = getAnalysisIfAvailable(); 230 | std::vector MemoryAccesses; 231 | 232 | for (Instruction &I : instructions(F)) { 233 | if (isa(I) || isa(I)) { 234 | MemoryAccesses.push_back(&I); 235 | } 236 | if (ReintPtrs && ReintPtrs->hasNullTagUsers(&I)) { 237 | instrumentPtrInt(&I); 238 | } 239 | } 240 | 241 | for (Instruction &I : instructions(F)) { 242 | if (isa(I) || isa(I)) { 243 | CallSite CS(&I); 244 | instrumentCallExt(&CS); 245 | instrumentCallExtWrap(&CS); 246 | instrumentCallByval(&CS); 247 | instrumentCallExtNestedPtrs(&CS); 248 | } 249 | else ifcast(CmpInst, Cmp, &I) { 250 | instrumentCmpPtr(Cmp); 251 | } 252 | } 253 | 254 | for (Instruction *Access : MemoryAccesses) 255 | instrumentMemAccess(Access); 256 | 257 | return true; 258 | } 259 | 260 | void CheckAddrSpace::instrumentPtrInt(Instruction *I) { 261 | IRBuilder<> B(getInsertPointAfter(I)); 262 | checkPointer(I, B); 263 | } 264 | 265 | static Function *createCheckFunc(Module &M) { 266 | LLVMContext &Ctx = M.getContext(); 267 | Type *VoidTy = Type::getVoidTy(Ctx); 268 | Type *i32Ty = Type::getInt32Ty(Ctx); 269 | Type *i64Ty = Type::getInt64Ty(Ctx); 270 | Type *i8PtrTy = Type::getInt8Ty(Ctx)->getPointerTo(); 271 | 272 | FunctionType *PrintfTy = FunctionType::get(i32Ty, i8PtrTy, true); 273 | Function *Printf = cast(M.getOrInsertFunction("printf", PrintfTy)); 274 | 275 | Type *ArgTypes[] = {i64Ty, i64Ty}; 276 | FunctionType *FnTy = FunctionType::get(VoidTy, ArgTypes, false); 277 | Function *F = createNoInstrumentFunction(M, FnTy, "checkptr", true); 278 | 279 | BasicBlock *Entry = BasicBlock::Create(F->getContext(), "entry", F); 280 | BasicBlock *Trap = BasicBlock::Create(F->getContext(), "trap", F); 281 | BasicBlock *Exit = BasicBlock::Create(F->getContext(), "exit", F); 282 | 283 | auto it = F->getArgumentList().begin(); 284 | Value *PtrInt = &*it++; 285 | Value *CheckID = &*it; 286 | 287 | IRBuilder<> B(Entry); 288 | Value *Cond = B.CreateICmpUGT(PtrInt, B.getInt64(getAddressSpaceMask())); 289 | B.CreateCondBr(Cond, Trap, Exit); 290 | 291 | B.SetInsertPoint(Trap); 292 | Value *Format = B.CreateGlobalStringPtr( 293 | "nonzero upper bits in pointer %llx (check %llu)\n", 294 | "checkptr_error"); 295 | Value *PrintfArgs[] = {Format, PtrInt, CheckID}; 296 | B.CreateCall(Printf, PrintfArgs); 297 | B.CreateCall(Intrinsic::getDeclaration(&M, Intrinsic::trap)); 298 | B.CreateUnreachable(); 299 | 300 | B.SetInsertPoint(Exit); 301 | B.CreateRetVoid(); 302 | 303 | return F; 304 | } 305 | 306 | bool CheckAddrSpace::initializeModule(Module &M) { 307 | CheckFunc = createCheckFunc(M); 308 | return true; 309 | } 310 | -------------------------------------------------------------------------------- /llvm-passes/DebugNegArith.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define DEBUG_TYPE "debugnegarith" 4 | 5 | #include "builtin/Common.h" 6 | #include "builtin/CustomFunctionPass.h" 7 | 8 | using namespace llvm; 9 | 10 | struct DebugNegArith : public CustomFunctionPass { 11 | static char ID; 12 | DebugNegArith() : CustomFunctionPass(ID) {} 13 | 14 | private: 15 | const DataLayout *DL; 16 | Function *CheckFunc; 17 | 18 | bool initializeModule(Module &M) override; 19 | bool runOnFunction(Function &F) override; 20 | 21 | void instrGep(GetElementPtrInst *Gep); 22 | }; 23 | 24 | char DebugNegArith::ID = 0; 25 | static RegisterPass X("debug-neg-arith", 26 | "Insert run-time checks to see if negative arith happens on non-tagged pointers"); 27 | 28 | STATISTIC(NumGeps, "Number of GEPs"); 29 | STATISTIC(NumConstPosGeps, "Number of dynamically-indexed GEPs"); 30 | STATISTIC(NumConstNegGeps, "Number of constant-negative GEPs"); 31 | STATISTIC(NumDynGeps, "Number of constant-positive GEPs"); 32 | STATISTIC(NumZeroGeps, "Number of VTable GEPs"); 33 | STATISTIC(NumVtableGeps, "Number of zero GEPs"); 34 | 35 | bool DebugNegArith::initializeModule(Module &M) { 36 | DL = &M.getDataLayout(); 37 | CheckFunc = getNoInstrumentFunction(M, "check_neg_arith"); 38 | return false; 39 | } 40 | 41 | void DebugNegArith::instrGep(GetElementPtrInst *Gep) { 42 | NumGeps++; 43 | 44 | if (Gep->hasAllZeroIndices()) { 45 | NumZeroGeps++; 46 | return; 47 | } 48 | 49 | /* deltatags ignores vtable-related geps already */ 50 | Value *SrcPtr = Gep->getPointerOperand(); 51 | if (SrcPtr->hasName() && SrcPtr->getName().startswith("vtable")) { 52 | NumVtableGeps++; 53 | return; 54 | } 55 | if (Gep->getNumIndices() == 1) { 56 | Value *FirstOp = Gep->getOperand(1); 57 | if (FirstOp->hasName() && 58 | FirstOp->getName().startswith("vbase.offset")) { 59 | NumVtableGeps++; 60 | return; 61 | } 62 | } 63 | 64 | APInt ConstOffset(64, 0); 65 | if (Gep->accumulateConstantOffset(*DL, ConstOffset)) { 66 | if (ConstOffset.sgt(0)) { 67 | NumConstPosGeps++; 68 | return; 69 | } 70 | NumConstNegGeps++; 71 | } else 72 | NumDynGeps++; 73 | 74 | IRBuilder<> B(getInsertPointAfter(Gep)); 75 | Value *Offset = EmitGEPOffset(&B, *DL, Gep); 76 | Value *Ptr = B.CreateBitCast(Gep->getPointerOperand(), CheckFunc->getFunctionType()->getParamType(1)); 77 | B.CreateCall(CheckFunc, { B.getInt64(NumGeps), Ptr, Offset, B.getInt32(Gep->hasAllConstantIndices()) } ); 78 | } 79 | 80 | bool DebugNegArith::runOnFunction(Function &F) { 81 | for (inst_iterator II = inst_begin(&F), E = inst_end(&F); II != E; ++II) { 82 | Instruction *I = &*II; 83 | ifcast(GetElementPtrInst, Gep, I) { 84 | instrGep(Gep); 85 | } 86 | } 87 | 88 | return true; 89 | } 90 | -------------------------------------------------------------------------------- /llvm-passes/DeltaTagAlloc.cpp: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "deltatags-alloc" 2 | 3 | #include "builtin/Common.h" 4 | #include "builtin/CustomFunctionPass.h" 5 | #include "AddressSpace.h" 6 | #include "builtin/Allocation.h" 7 | #include "builtin/SizeofTypes.h" 8 | #include "TagGlobalsConst.h" 9 | #include "SafeAllocs.h" 10 | #include "SafeAllocsOld.h" 11 | #include "ReinterpretedPointers.h" 12 | 13 | using namespace llvm; 14 | 15 | struct DeltaTagAlloc : public CustomFunctionPass { 16 | static char ID; 17 | DeltaTagAlloc() : CustomFunctionPass(ID) {} 18 | 19 | void getAnalysisUsage(AnalysisUsage &AU) const override; 20 | 21 | private: 22 | const DataLayout *DL; 23 | SafeAllocsBase *SafeAlloc; 24 | SizeofTypes *SizeofAnalysis; 25 | 26 | bool runOnFunction(Function &F) override; 27 | bool initializeModule(Module &M) override; 28 | 29 | bool instrumentGlobals(Module &M); 30 | void instrumentAllocation(AllocationSite &AS); 31 | uint64_t derefALignmentBytes(Type *Ty); 32 | Type *getAllocatedElementType(AllocationSite &AS); 33 | Constant *getNullPtr(PointerType *Ty); 34 | }; 35 | 36 | char DeltaTagAlloc::ID = 0; 37 | static RegisterPass X("deltatags-alloc", 38 | "Encode object size in high bits of pointers for bounds checking"); 39 | 40 | static cl::opt OptGlobal("deltatags-global", 41 | cl::desc("Tag globals"), 42 | cl::init(true)); 43 | 44 | static cl::opt OptHeap("deltatags-heap", 45 | cl::desc("Tag heap allocations"), 46 | cl::init(true)); 47 | 48 | static cl::opt OptStack("deltatags-stack", 49 | cl::desc("Tag stack allocations"), 50 | cl::init(true)); 51 | 52 | static cl::opt OptOverinit("deltatags-overinit", 53 | cl::desc("Add (allocsize - 1) initial offset to size tag"), 54 | cl::init(false)); 55 | 56 | static cl::opt OptReplaceNull("deltatags-nullptr", 57 | cl::desc("Instrument the NULL pointer with a size tag that always overflows on arith"), 58 | cl::init(true)); 59 | 60 | STATISTIC(NStack, "Number of tagged stack variables"); 61 | STATISTIC(NHeap, "Number of tagged heap allocations"); 62 | STATISTIC(NGlobal, "Number of tagged globals"); 63 | STATISTIC(NNullPtr, "Number of NULL pointer operands replaced"); 64 | 65 | void DeltaTagAlloc::getAnalysisUsage(AnalysisUsage &AU) const { 66 | AU.setPreservesCFG(); 67 | AU.addPreserved(); 68 | AU.addPreserved(); 69 | AU.addPreserved(); 70 | AU.addPreserved(); 71 | AU.addUsedIfAvailable(); 72 | AU.addUsedIfAvailable(); 73 | AU.addUsedIfAvailable(); 74 | } 75 | 76 | bool DeltaTagAlloc::instrumentGlobals(Module &M) { 77 | if (!OptGlobal) 78 | return false; 79 | 80 | for (GlobalVariable &GV : M.globals()) { 81 | if (!canTagGlobal(GV)) 82 | continue; 83 | 84 | if (SafeAlloc && !SafeAlloc->needsTag(&GV)) 85 | continue; 86 | 87 | Type *Ty = GV.getType()->getPointerElementType(); 88 | uint64_t MaxByteOffset = DL->getTypeStoreSize(Ty); 89 | if (OptOverinit) 90 | MaxByteOffset -= derefALignmentBytes(Ty); 91 | MaxByteOffset += ALLOWED_OOB_BYTES; 92 | uint64_t Tag = -MaxByteOffset & BOUND_MASK_LOW; 93 | tagGlobal(GV, Tag); 94 | ++NGlobal; 95 | } 96 | 97 | return NGlobal > 0; 98 | } 99 | 100 | bool DeltaTagAlloc::initializeModule(Module &M) { 101 | DL = &M.getDataLayout(); 102 | if (!(SafeAlloc = getAnalysisIfAvailable())) 103 | SafeAlloc = getAnalysisIfAvailable(); 104 | SizeofAnalysis = getAnalysisIfAvailable(); 105 | return instrumentGlobals(M); 106 | } 107 | 108 | /* 109 | * Mask out any metadata from nested allocation functions 110 | */ 111 | static Value *maskMallocWrapper(IRBuilder<> &B, AllocationSite &AS) { 112 | if (!AS.isHeapAllocation() || !AS.IsWrapped) 113 | return AS.Allocation; 114 | 115 | Value *Ptr = AS.Allocation; 116 | std::vector Users(Ptr->user_begin(), Ptr->user_end()); 117 | 118 | std::string Prefix = Ptr->hasName() ? Ptr->getName().str() + "." : ""; 119 | Value *PtrInt = B.CreatePtrToInt(Ptr, getPtrIntTy(Ptr->getContext()), Prefix + "int"); 120 | Value *Masked = B.CreateAnd(PtrInt, getAddressSpaceMask(), Prefix + "mask"); 121 | Value *NewPtr = B.CreateIntToPtr(Masked, Ptr->getType(), Prefix + "unwrapped"); 122 | 123 | for (User *U : Users) 124 | U->replaceUsesOfWith(Ptr, NewPtr); 125 | 126 | return NewPtr; 127 | } 128 | 129 | /* 130 | * Put the inverse size in the upper bits of an allocated pointer, and replace 131 | * all occurences of this value with the instrumented pointer. IRBuilder will 132 | * do const prop on size, often yielding a single `or` instruction. 133 | */ 134 | void DeltaTagAlloc::instrumentAllocation(AllocationSite &AS) { 135 | if (AS.isStackAllocation() && !OptStack) 136 | return; 137 | 138 | if (AS.isHeapAllocation() && !OptHeap) 139 | return; 140 | 141 | IRBuilder<> B(getInsertPointAfter(AS.Allocation)); 142 | Value *Ptr = maskMallocWrapper(B, AS); 143 | 144 | if (SafeAlloc && !SafeAlloc->needsTag(AS.Allocation)) 145 | return; 146 | 147 | std::vector Users(Ptr->user_begin(), Ptr->user_end()); 148 | 149 | Value *Size = AS.instrumentWithByteSize(B, *DL); 150 | IntegerType *SizeTy = cast(Size->getType()); 151 | 152 | if (OptOverinit) { 153 | if (uint64_t AlignBytes = derefALignmentBytes(getAllocatedElementType(AS))) 154 | Size = B.CreateSub(Size, ConstantInt::get(SizeTy, AlignBytes)); 155 | } 156 | 157 | if (ALLOWED_OOB_BYTES) 158 | Size = B.CreateAdd(Size, ConstantInt::get(SizeTy, ALLOWED_OOB_BYTES)); 159 | 160 | // XXX add debug mode that inserts an assertion on the size limit? 161 | Value *InvSz = B.CreateAnd(B.CreateNeg(Size), BOUND_MASK_LOW); 162 | Value *SizeMask = B.CreateShl(InvSz, BOUND_SHIFT); 163 | 164 | Value *PtrInt = B.CreatePtrToInt(Ptr, B.getInt64Ty()); 165 | Value *Tagged = B.CreateOr(PtrInt, SizeMask); 166 | Value *NewPtr = B.CreateIntToPtr(Tagged, Ptr->getType(), Twine(Ptr->getName()) + ".tagged"); 167 | 168 | for (User *U : Users) 169 | U->replaceUsesOfWith(Ptr, NewPtr); 170 | 171 | if (AS.isStackAllocation()) ++NStack; else ++NHeap; 172 | } 173 | 174 | bool DeltaTagAlloc::runOnFunction(Function &F) { 175 | unsigned long long Nold = NStack + NHeap; 176 | 177 | for (Instruction &I : instructions(F)) { 178 | AllocationSite AS; 179 | if (isAllocation(&I, AS)) 180 | instrumentAllocation(AS); 181 | 182 | if (OptReplaceNull) { 183 | // Skip libcalls (indirect calls may be libcalls as well, ignore 184 | // those for now and hope they are never called with NULL args) 185 | if (isa(I) || isa(I)) { 186 | Function *F = CallSite(&I).getCalledFunction(); 187 | if (F && F->isDeclaration()) 188 | continue; 189 | } 190 | 191 | for (Use &U : I.operands()) { 192 | ifcast(ConstantPointerNull, NullPtr, U.get()) { 193 | U.set(getNullPtr(NullPtr->getType())); 194 | NNullPtr++; 195 | } 196 | } 197 | } 198 | } 199 | 200 | return NStack + NHeap > Nold; 201 | } 202 | 203 | uint64_t DeltaTagAlloc::derefALignmentBytes(Type *Ty) { 204 | if (isa(Ty) || isa(Ty)) 205 | return derefALignmentBytes(cast(Ty)->getElementType()); 206 | 207 | ifcast(StructType, StructTy, Ty) 208 | return derefALignmentBytes(StructTy->getElementType(StructTy->getNumElements() - 1)); 209 | 210 | assert(Ty->isSized()); 211 | return DL->getTypeStoreSize(Ty) - 1; 212 | } 213 | 214 | Type *DeltaTagAlloc::getAllocatedElementType(AllocationSite &AS) { 215 | // Stack allocations are trivial, the alloca encodes the type 216 | if (AS.isStackAllocation()) { 217 | Type *Ty = cast(AS.Allocation)->getAllocatedType(); 218 | 219 | // In its great wisdom, the LLVM god decides to allocate structs that 220 | // fit within an int type as that int and then cast it, causing our 221 | // offset to become 1 and thus failing struct member accesses. 222 | // Catch that case here by recognizing the pattern: 223 | // %tmpcast = bitcast cast (alloca int) to %struct.X* 224 | if (Ty->isIntegerTy()) { 225 | int IsCast = 0; 226 | 227 | for (User *U : AS.Allocation->users()) { 228 | ifncast(BitCastInst, BC, U) 229 | continue; 230 | if (!isa(BC->getDestTy())) 231 | continue; 232 | 233 | Type *DstTy = BC->getDestTy()->getPointerElementType(); 234 | 235 | // In case of multiple casts, pick the smallest destination type 236 | if (DL->getTypeStoreSize(DstTy) < DL->getTypeStoreSize(Ty)) { 237 | Ty = DstTy; 238 | IsCast = (BC->hasName() && BC->getName().startswith("tmpcast")) ? 2 : 1; 239 | } 240 | } 241 | 242 | if (IsCast == 1) 243 | DEBUG_LINE("alloca cast: " << *AS.Allocation << " (smaller type: " << *Ty << ")"); 244 | else if (IsCast == 2) 245 | DEBUG_LINE("fake alloca: " << *AS.Allocation << " (real type: " << *Ty << ")"); 246 | } 247 | 248 | return Ty; 249 | } 250 | 251 | // malloc-like calls may use sizeof(elementty) at source level, use that 252 | // annotation here 253 | if (SizeofAnalysis) { 254 | // Note: for arrays, this returns the element type, NOT the array type 255 | if (Type *SizeofTy = SizeofAnalysis->getSizeofType(AS.Allocation)) 256 | return SizeofTy; 257 | } 258 | 259 | return Type::getInt8Ty(AS.Allocation->getContext()); 260 | } 261 | 262 | Constant *DeltaTagAlloc::getNullPtr(PointerType *Ty) { 263 | IntegerType *IntTy = IntegerType::get(Ty->getContext(), PointerBits); 264 | ConstantInt *IntVal = ConstantInt::get(IntTy, BOUND_MASK_HIGH); 265 | return ConstantExpr::getIntToPtr(IntVal, Ty); 266 | } 267 | -------------------------------------------------------------------------------- /llvm-passes/GlobalOpt.cpp: -------------------------------------------------------------------------------- 1 | #include "builtin/Common.h" 2 | #include "GlobalOpt.h" 3 | 4 | using namespace llvm; 5 | 6 | /* 7 | * Copied from llvm/lib/Transforms/IPO/GlobalOpt.cpp 8 | */ 9 | 10 | /// C may have non-instruction users. Can all of those users be turned into 11 | /// instructions? 12 | bool allNonInstructionUsersCanBeMadeInstructions(Constant *C) { 13 | // We don't do this exhaustively. The most common pattern that we really need 14 | // to care about is a constant GEP or constant bitcast - so just looking 15 | // through one single ConstantExpr. 16 | // 17 | // The set of constants that this function returns true for must be able to be 18 | // handled by makeAllConstantUsesInstructions. 19 | for (auto *U : C->users()) { 20 | if (isa(U)) 21 | // A phi node using a global as a const, cannot be replaced trivially 22 | // because the insert point needs to be the corresponding predecessor 23 | return false; 24 | if (isa(U)) 25 | continue; 26 | if (!isa(U)) 27 | // Non instruction, non-constantexpr user; cannot convert this. 28 | return false; 29 | for (auto *UU : U->users()) { 30 | if (!isa(UU)) 31 | // A constantexpr used by another constant. We don't try and recurse any 32 | // further but just bail out at this point. 33 | return false; 34 | if (isa(UU)) 35 | // because the insert point needs to be the corresponding predecessor 36 | return false; 37 | } 38 | } 39 | 40 | return true; 41 | } 42 | 43 | /// C may have non-instruction users, and 44 | /// allNonInstructionUsersCanBeMadeInstructions has returned true. Convert the 45 | /// non-instruction users to instructions. 46 | void makeAllConstantUsesInstructions(Constant *C) { 47 | SmallVector Users; 48 | for (auto *U : C->users()) { 49 | if (isa(U)) 50 | Users.push_back(cast(U)); 51 | else 52 | // We should never get here; allNonInstructionUsersCanBeMadeInstructions 53 | // should not have returned true for C. 54 | assert( 55 | isa(U) && 56 | "Can't transform non-constantexpr non-instruction to instruction!"); 57 | } 58 | 59 | SmallVector UUsers; 60 | for (auto *U : Users) { 61 | UUsers.clear(); 62 | for (auto *UU : U->users()) 63 | UUsers.push_back(UU); 64 | for (auto *UU : UUsers) { 65 | Instruction *UI = cast(UU); 66 | Instruction *NewU = U->getAsInstruction(); 67 | NewU->insertBefore(UI); 68 | UI->replaceUsesOfWith(U, NewU); 69 | } 70 | U->dropAllReferences(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /llvm-passes/GlobalOpt.h: -------------------------------------------------------------------------------- 1 | #ifndef GLOBAL_OPT_UTILS_H 2 | #define GLOBAL_OPT_UTILS_H 3 | 4 | #include 5 | 6 | bool allNonInstructionUsersCanBeMadeInstructions(llvm::Constant *C); 7 | void makeAllConstantUsesInstructions(llvm::Constant *C); 8 | 9 | #endif /* !GLOBAL_OPT_UTILS_H */ 10 | -------------------------------------------------------------------------------- /llvm-passes/LibPtrRet.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_PTR_RET_H 2 | #define LIB_PTR_RET_H 3 | 4 | enum LibPtr { 5 | None, 6 | Ignore, 7 | CopyFromArg, 8 | PtrDiff, 9 | RetSizeStatic, 10 | Strlen, 11 | Strtok, 12 | }; 13 | 14 | enum LibPtr getLibPtrType(Function *F, int *dat); 15 | 16 | #endif /* !LIB_PTR_RET_H */ 17 | -------------------------------------------------------------------------------- /llvm-passes/MagicTags.cpp: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "magic-tags" 2 | 3 | #include "builtin/Common.h" 4 | #include "builtin/CustomFunctionPass.h" 5 | #include "AddressSpace.h" 6 | #include "builtin/Allocation.h" 7 | #include "TagGlobalsConst.h" 8 | 9 | using namespace llvm; 10 | 11 | static cl::opt MagicValue("magic-value", 12 | cl::desc("Magic metadata value to put in high bits of pointers " 13 | "(will be left-shifted by address-space-bits)"), 14 | cl::init(0xdeadbeefdeadbeefULL)); 15 | 16 | static cl::opt AllowZeroMagic("allow-zero-magic", 17 | cl::desc("Do not require all pointers to have the magic metadata when checking at masking"), 18 | cl::init(false)); 19 | 20 | struct MagicTags : public CustomFunctionPass { 21 | static char ID; 22 | MagicTags() : CustomFunctionPass(ID) {} 23 | 24 | private: 25 | bool initializeModule(Module &M) override; 26 | bool runOnFunction(Function &F) override; 27 | 28 | bool instrumentGlobals(Module &M); 29 | void instrumentAllocation(AllocationSite &AS); 30 | Function *createCheckFunc(Module &M); 31 | }; 32 | 33 | char MagicTags::ID = 0; 34 | static RegisterPass X("magic-tags", 35 | "Tag pointers with magic metadata (run before -mask-pointers)"); 36 | 37 | STATISTIC(NStack, "Number of tagged stack variables"); 38 | STATISTIC(NHeap, "Number of tagged heap allocations"); 39 | STATISTIC(NGlobal, "Number of tagged globals"); 40 | 41 | bool MagicTags::instrumentGlobals(Module &M) { 42 | for (GlobalVariable &GV : M.globals()) { 43 | if (!canTagGlobal(GV)) 44 | continue; 45 | 46 | tagGlobal(GV, MagicValue); 47 | ++NGlobal; 48 | } 49 | 50 | return NGlobal > 0; 51 | } 52 | 53 | bool MagicTags::initializeModule(Module &M) { 54 | uint64_t Magic = (MagicValue << AddressSpaceBits) >> AddressSpaceBits; 55 | DEBUG_LINE("Tagging pointers with magic value 0x" << hex(Magic)); 56 | 57 | assert(getNoInstrumentFunction(M, "checkmagic", true) == nullptr); 58 | createCheckFunc(M); 59 | 60 | instrumentGlobals(M); 61 | 62 | return true; 63 | } 64 | 65 | void MagicTags::instrumentAllocation(AllocationSite &AS) { 66 | Instruction *Ptr = AS.Allocation; 67 | IRBuilder<> B(getInsertPointAfter(Ptr)); 68 | SmallVector Users(Ptr->user_begin(), Ptr->user_end()); 69 | 70 | Value *MagicMask = B.getIntN(PointerBits, MagicValue << AddressSpaceBits); 71 | Value *PtrInt = B.CreatePtrToInt(Ptr, B.getIntNTy(PointerBits), "ptrint"); 72 | Value *Masked = B.CreateOr(PtrInt, MagicMask, "magicmask"); 73 | std::string Name = (Ptr->hasName() ? Ptr->getName().str() : "_anon") + ".magic"; 74 | Value *MagicPtr = B.CreateIntToPtr(Masked, Ptr->getType(), Name); 75 | 76 | for (User *U : Users) 77 | U->replaceUsesOfWith(Ptr, MagicPtr); 78 | } 79 | 80 | bool MagicTags::runOnFunction(Function &F) { 81 | unsigned long long Nold = NStack + NHeap; 82 | 83 | for (inst_iterator II = inst_begin(&F), E = inst_end(&F); II != E; ++II) { 84 | Instruction *I = &*II; 85 | AllocationSite AS; 86 | 87 | if (isAllocation(I, AS)) { 88 | instrumentAllocation(AS); 89 | if (AS.isStackAllocation()) 90 | ++NStack; 91 | else 92 | ++NHeap; 93 | } 94 | } 95 | 96 | return NStack + NHeap > Nold; 97 | } 98 | 99 | Function *MagicTags::createCheckFunc(Module &M) { 100 | LLVMContext &C = M.getContext(); 101 | Type *VoidTy = Type::getVoidTy(C); 102 | Type *i32Ty = Type::getInt32Ty(C); 103 | Type *i64Ty = Type::getInt64Ty(C); 104 | Type *i8PtrTy = Type::getInt8Ty(C)->getPointerTo(); 105 | Type *PtrIntTy = getPtrIntTy(C); 106 | 107 | Type *PrintfArgTypes[] = {i32Ty, i8PtrTy}; 108 | FunctionType *PrintfTy = FunctionType::get(i32Ty, PrintfArgTypes, true); 109 | Function *Printf = cast(M.getOrInsertFunction("dprintf", PrintfTy)); 110 | 111 | Type *ArgTypes[] = {PtrIntTy, i64Ty}; 112 | FunctionType *FnTy = FunctionType::get(VoidTy, ArgTypes, false); 113 | Function *F = createNoInstrumentFunction(M, FnTy, "checkmagic", true); 114 | F->addFnAttr(Attribute::AlwaysInline); 115 | 116 | BasicBlock *Entry = BasicBlock::Create(F->getContext(), "entry", F); 117 | BasicBlock *Trap = BasicBlock::Create(C, "trap", F); 118 | BasicBlock *Exit = BasicBlock::Create(C, "exit", F); 119 | 120 | auto it = F->getArgumentList().begin(); 121 | Value *PtrInt = &*it++; 122 | Value *CheckID = &*it; 123 | 124 | IRBuilder<> B(Entry); 125 | Value *HighBits = B.CreateLShr(PtrInt, AddressSpaceBits, "highbits"); 126 | 127 | if (AllowZeroMagic) { 128 | BasicBlock *NonZero = BasicBlock::Create(C, "nonzero", F, Trap); 129 | Value *Cond1 = B.CreateICmpNE(HighBits, B.getIntN(PointerBits, 0)); 130 | B.CreateCondBr(Cond1, NonZero, Exit); 131 | B.SetInsertPoint(NonZero); 132 | } 133 | 134 | unsigned long long Magic = (MagicValue << AddressSpaceBits) >> AddressSpaceBits; 135 | Value *Cond2 = B.CreateICmpNE(HighBits, B.getIntN(PointerBits, Magic)); 136 | B.CreateCondBr(Cond2, Trap, Exit); 137 | 138 | B.SetInsertPoint(Trap); 139 | Value *Format = B.CreateGlobalStringPtr( 140 | "high bits %llx in pointer %llx do not match magic value (check %llu)\n", 141 | NOINSTRUMENT_PREFIX "checkmagic_error"); 142 | Value *Fd = B.getInt32(2); 143 | Value *PrintfArgs[] = {Fd, Format, HighBits, PtrInt, CheckID}; 144 | B.CreateCall(Printf, PrintfArgs); 145 | B.CreateCall(Intrinsic::getDeclaration(&M, Intrinsic::trap)); 146 | B.CreateUnreachable(); 147 | //B.CreateBr(Exit); 148 | 149 | B.SetInsertPoint(Exit); 150 | B.CreateRetVoid(); 151 | 152 | return F; 153 | } 154 | -------------------------------------------------------------------------------- /llvm-passes/Makefile: -------------------------------------------------------------------------------- 1 | BUILD_SUFFIX = deltatags 2 | LLVM_VERSION = 3.8.0 3 | SETUP_SCRIPT = ../setup.py 4 | include ../infra/infra//packages/llvm_passes/Makefile 5 | -------------------------------------------------------------------------------- /llvm-passes/MaskPointers.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define DEBUG_TYPE "mask-pointers" 4 | 5 | #include "builtin/Common.h" 6 | #include "builtin/CustomFunctionPass.h" 7 | #include "AddressSpace.h" 8 | #include "GlobalOpt.h" 9 | #include "SafeAllocs.h" 10 | #include "SafeAllocsOld.h" 11 | #include "ReinterpretedPointers.h" 12 | 13 | //#define USE_MASK_HELPER 14 | 15 | using namespace llvm; 16 | 17 | static cl::list FunctionIgnoreList("mask-pointers-ignore-list", 18 | cl::desc("List of function names to not mask pointers in"), 19 | cl::ZeroOrMore); 20 | 21 | static cl::opt UseMaskHelper("mask-pointers-helper", 22 | cl::desc("Name of helper function for masking"), 23 | cl::value_desc("function_name")); 24 | 25 | struct MaskPointers : public CustomFunctionPass { 26 | static char ID; 27 | MaskPointers() : CustomFunctionPass(ID) {} 28 | 29 | virtual void getAnalysisUsage(AnalysisUsage &AU) const; 30 | 31 | private: 32 | struct MaskEntry : public std::pair { 33 | MaskEntry(Instruction *User, unsigned Idx, bool ForceMask) { 34 | first = User; 35 | int i = static_cast(Idx); 36 | second = ForceMask ? -i - 1 : i; 37 | } 38 | 39 | Instruction *User() { return first; } 40 | unsigned OperandIndex() { 41 | return static_cast(second < 0 ? -second - 1 : second); 42 | } 43 | bool ForceMaskOverflowBit() { return second < 0; } 44 | 45 | Instruction *User() const { return first; } 46 | unsigned OperandIndex() const { 47 | return static_cast(second < 0 ? -second - 1 : second); 48 | } 49 | bool ForceMaskOverflowBit() const { return second < 0; } 50 | }; 51 | 52 | Function *CheckFunc; 53 | Function *MaskPtrFunc; 54 | unsigned long long NChecks; 55 | MapVector> Masks; // ptrint -> [ins] 56 | SafeAllocsBase *SafeAlloc; 57 | ReinterpretedPointers *ReintPtrs; 58 | 59 | bool initializeModule(Module &M) override; 60 | bool runOnFunction(Function &F) override; 61 | bool finalizeModule(Module &M) override; 62 | 63 | bool instrumentGlobals(Module &M); 64 | void instrumentArgs(Function *F); 65 | void instrumentCallExt(CallSite *CS); 66 | void instrumentCallExtWrap(CallSite *CS); 67 | void instrumentCallByval(CallSite *CS); 68 | void instrumentCallExtNestedPtrs(CallSite *CS); 69 | void instrumentCallSafeArgs(CallSite *CS); 70 | void instrumentCmpPtr(CmpInst *I); 71 | void instrumentMemAccess(Instruction *I); 72 | void instrumentNullTagUsers(Instruction *PtrInt); 73 | 74 | void setMasks(Instruction *I, Value *Ptr, unsigned Idx, bool ForceMaskOverflowBit); 75 | Value *maskPointer(Value *Ptr, IRBuilder<> &B, bool ForceMaskOverflowBit=false); 76 | void maskPointerArgs(CallSite *CS); 77 | void maskNestedPointers(Value *V, CompositeType *EltTy, 78 | SmallVector &indices, IRBuilder<> &B); 79 | }; 80 | 81 | char MaskPointers::ID = 0; 82 | static RegisterPass X("mask-pointers", 83 | "Mask high bits of pointers at loads and stores (SFI)"); 84 | 85 | STATISTIC(NMasks, "Number of pointers masked"); 86 | STATISTIC(NSkippedSafe, "Number of masks prevented by static analysis"); 87 | STATISTIC(NSkippedTags, "Number of masks prevented by directly using the allocation without tag"); 88 | 89 | void MaskPointers::getAnalysisUsage(AnalysisUsage &AU) const { 90 | AU.addPreserved(); 91 | AU.addPreserved(); 92 | AU.addPreserved(); 93 | AU.addUsedIfAvailable(); 94 | AU.addUsedIfAvailable(); 95 | AU.addUsedIfAvailable(); 96 | } 97 | 98 | bool MaskPointers::initializeModule(Module &M) { 99 | CheckFunc = getNoInstrumentFunction(M, "checkmagic", true); 100 | 101 | if (UseMaskHelper.getNumOccurrences()) { 102 | MaskPtrFunc = getNoInstrumentFunction(M, UseMaskHelper); 103 | MaskPtrFunc->addFnAttr(Attribute::AlwaysInline); 104 | } else { 105 | MaskPtrFunc = nullptr; 106 | } 107 | 108 | NChecks = 0; 109 | Masks.clear(); 110 | if (!(SafeAlloc = getAnalysisIfAvailable())) 111 | SafeAlloc = getAnalysisIfAvailable(); 112 | ReintPtrs = getAnalysisIfAvailable(); 113 | return instrumentGlobals(M); 114 | } 115 | 116 | bool MaskPointers::finalizeModule(Module &M) { 117 | // TODO: move loop-invariant masks out of loops 118 | 119 | // Apply all masks while applying as few masks as possible 120 | IRBuilder<> B(M.getContext()); 121 | 122 | for (auto &MI : Masks) { 123 | Value * const Ptr = MI.first; 124 | 125 | // TODO: only mask once for each pointer 126 | for (const MaskEntry &ME : MI.second) { 127 | assert(ME.User()->getOperand(ME.OperandIndex()) == Ptr); 128 | B.SetInsertPoint(ME.User()); 129 | Value *Masked = maskPointer(Ptr, B, ME.ForceMaskOverflowBit()); 130 | ME.User()->setOperand(ME.OperandIndex(), Masked); 131 | } 132 | } 133 | 134 | return NMasks > 0; 135 | } 136 | 137 | void MaskPointers::setMasks(Instruction *I, Value *Ptr, unsigned Idx, bool ForceMaskOverflowBit) { 138 | // Don't mask safe pointers 139 | if (SafeAlloc && !SafeAlloc->needsMask(I, Ptr)) { 140 | ++NSkippedSafe; 141 | return; 142 | } 143 | 144 | // TODO: optimization: don't mask in comparison with NULL (since null 145 | // pointers don't have metadata) 146 | 147 | MaskEntry Entry(I, Idx, ForceMaskOverflowBit); 148 | auto MI = Masks.find(Ptr); 149 | if (MI == Masks.end()) { 150 | SmallSetVector Vec; 151 | Vec.insert(Entry); 152 | Masks[Ptr] = Vec; 153 | } else { 154 | // Check for duplicates 155 | // XXX: this is possibly inefficient 156 | for (const MaskEntry &ME : MI->second) { 157 | if (ME.User() == I && ME.OperandIndex() == Idx) { 158 | // Choose ForceMask=true over false, assuming that calls to 159 | // setmasks with ForceMask=true are certain that it is safe not 160 | // to preserve the overflow bit 161 | if (!ME.ForceMaskOverflowBit() && ForceMaskOverflowBit) { 162 | MI->second.remove(ME); 163 | break; 164 | } else { 165 | return; 166 | } 167 | } 168 | } 169 | 170 | MI->second.insert(Entry); 171 | } 172 | } 173 | 174 | static Value *getAllocationFromTag(Value *Ptr) { 175 | Ptr = Ptr->stripPointerCasts(); 176 | 177 | ifcast(IntToPtrInst, Cast, Ptr) { 178 | ifcast(BinaryOperator, Tag, Cast->getOperand(0)) { 179 | if (Tag->getOpcode() == Instruction::Or) { 180 | ifcast(ConstantInt, Mask, Tag->getOperand(1)) { 181 | if (Mask->getZExtValue() > getAddressSpaceMask()) { 182 | ifcast(PtrToIntInst, PtrInt, Tag->getOperand(0)) 183 | return PtrInt->getOperand(0); 184 | } 185 | } 186 | } 187 | } 188 | } 189 | else ifcast(ConstantExpr, Cast, Ptr) { 190 | if (Cast->getOpcode() == Instruction::IntToPtr) { 191 | ifcast(ConstantExpr, Tag, Cast->getOperand(0)) { 192 | if (Tag->getOpcode() == Instruction::Or) { 193 | ifcast(ConstantExpr, Allocation, Tag->getOperand(0)) { 194 | if (Allocation->getOpcode() == Instruction::PtrToInt) { 195 | ifcast(GlobalVariable, GV, Allocation->getOperand(0)) 196 | return GV; 197 | } 198 | } 199 | } 200 | } 201 | } 202 | } 203 | 204 | return nullptr; 205 | } 206 | 207 | Value *MaskPointers::maskPointer(Value *Ptr, IRBuilder<> &B, bool ForceMaskOverflowBit) { 208 | // Don't mask safe pointers 209 | if (SafeAlloc && !SafeAlloc->hasTag(Ptr)) { 210 | ++NSkippedSafe; 211 | return Ptr; 212 | } 213 | 214 | // Instead of doing alloc -> tag -> mask, use the alloc directly 215 | if (Value *Allocation = getAllocationFromTag(Ptr)) { 216 | ++NSkippedTags; 217 | return B.CreateBitCast(Allocation, Ptr->getType(), Ptr->getName()); 218 | } 219 | 220 | ++NMasks; 221 | 222 | bool IsInt = isPtrIntTy(Ptr->getType()); 223 | std::string Prefix = Ptr->hasName() ? Ptr->getName().str() + "." : ""; 224 | bool Preserve = OverflowBit && !ForceMaskOverflowBit; 225 | 226 | if (MaskPtrFunc) { 227 | // Inlined helper 228 | if (IsInt) { 229 | if (CheckFunc) 230 | B.CreateCall(CheckFunc, {Ptr, B.getInt64(++NChecks)}); 231 | return B.CreateCall(MaskPtrFunc, {Ptr, B.getInt1(Preserve)}, "masked"); 232 | } 233 | Value *AsInt = B.CreatePtrToInt(Ptr, B.getIntNTy(PointerBits), Prefix + "int"); 234 | if (CheckFunc) 235 | B.CreateCall(CheckFunc, {AsInt, B.getInt64(++NChecks)}); 236 | Value *MaskedInt = B.CreateCall(MaskPtrFunc, {AsInt, B.getInt1(Preserve)}, "mask"); 237 | return B.CreateIntToPtr(MaskedInt, Ptr->getType(), Prefix + "masked"); 238 | } else { 239 | // TODO: dont do ptrtoint if the source is an inttoptr cast 240 | 241 | // AND with constant mask and leave opts up to the compiler backend 242 | unsigned long long Mask = getAddressSpaceMask(Preserve); 243 | if (IsInt) { 244 | if (CheckFunc) 245 | B.CreateCall(CheckFunc, {Ptr, B.getInt64(++NChecks)}); 246 | return B.CreateAnd(Ptr, Mask, Prefix + "masked"); 247 | } 248 | Value *AsInt = B.CreatePtrToInt(Ptr, B.getIntNTy(PointerBits), Prefix + "int"); 249 | if (CheckFunc) 250 | B.CreateCall(CheckFunc, {AsInt, B.getInt64(++NChecks)}); 251 | Value *MaskedInt = B.CreateAnd(AsInt, Mask, Prefix + "mask"); 252 | return B.CreateIntToPtr(MaskedInt, Ptr->getType(), Prefix + "masked"); 253 | } 254 | 255 | #if 0 256 | // cmov 257 | Constant *Zero = B.getInt64(0); 258 | Value *AsInt = B.CreatePtrToInt(Ptr, B.getIntNTy(PointerBits), Prefix + "int"); 259 | if (CheckFunc) 260 | B.CreateCall(CheckFunc, {AsInt, B.getInt64(++NChecks)}); 261 | Value *Cond = B.CreateICmpSLT(AsInt, Zero, Prefix + "cmp"); 262 | Value *DerefPtr = B.CreateSelect(Cond, Zero, AsInt, Prefix + "derefptr"); 263 | Value *MaskedInt = B.CreateAnd(DerefPtr, getAddressSpaceMask(), Prefix + "mask"); 264 | return B.CreateIntToPtr(MaskedInt, Ptr->getType(), Prefix + "masked"); 265 | 266 | // other cmov 267 | Constant *Zero = B.getInt64(0); 268 | Value *AsInt = B.CreatePtrToInt(Ptr, B.getIntNTy(PointerBits), Prefix + "int"); 269 | if (CheckFunc) 270 | B.CreateCall(CheckFunc, {AsInt, B.getInt64(++NChecks)}); 271 | Value *Cond = B.CreateICmpSLT(AsInt, Zero, Prefix + "cmp"); 272 | Value *MaskedInt = B.CreateAnd(AsInt, getAddressSpaceMask(), Prefix + "mask"); 273 | Value *AsPtr = B.CreateIntToPtr(MaskedInt, Ptr->getType(), Prefix + "masked"); 274 | Value *Null = B.CreateLoad(ConstantPointerNull::get(cast(Ptr->getType())->getPointerTo())); 275 | return B.CreateSelect(Cond, Null, AsPtr, Prefix + "derefptr"); 276 | #endif 277 | } 278 | 279 | void MaskPointers::maskPointerArgs(CallSite *CS) { 280 | Instruction *I = CS->getInstruction(); 281 | 282 | for (unsigned i = 0, n = CS->getNumArgOperands(); i < n; i++) { 283 | Value *Arg = CS->getArgOperand(i); 284 | if (Arg->getType()->isPointerTy()) { 285 | // Preserve overflow bit so that libcalls cannot be used to 286 | // dereference an OOB pointer 287 | setMasks(I, Arg, i, false); 288 | } 289 | } 290 | } 291 | 292 | static bool hasPointerArg(Function *F) { 293 | FunctionType *FT = F->getFunctionType(); 294 | for (unsigned i = 0, n = FT->getNumParams(); i < n; i++) { 295 | Type *type = FT->getParamType(i); 296 | if (type->isPointerTy()) 297 | return true; 298 | } 299 | return false; 300 | } 301 | 302 | bool isIgnoredFunctionCall(Function *F) { 303 | std::string FuncName = F->getName().str(); 304 | for (auto IgnoredFunction : FunctionIgnoreList) 305 | if (IgnoredFunction == FuncName) 306 | return true; 307 | return false; 308 | } 309 | 310 | /* 311 | * Mask pointers to external functions. 312 | * TODO: don't do this if we can determine it's not eg heap based. 313 | */ 314 | void MaskPointers::instrumentCallExt(CallSite *CS) { 315 | // Don't use getCalledFunction() directly because implicit declarations of 316 | // functions will result in inline bitcasts of function. 317 | Value *V = CS->getCalledValue()->stripPointerCasts(); 318 | Function *F = dyn_cast(V); 319 | 320 | // We currently rely on non-masked asm arguments for our tests, but for 321 | // instance nginx needs pointers to inline asm masked. 322 | if (CS->isInlineAsm()) { 323 | maskPointerArgs(CS); 324 | return; 325 | } 326 | 327 | if (!F) /* XXX indirect calls? */ 328 | return; 329 | if (!F->isDeclaration() && !F->isDeclarationForLinker()) /* not external */ 330 | return; 331 | if (isIgnoredFunctionCall(F)) 332 | return; 333 | 334 | if (F->isIntrinsic() && hasPointerArg(F)) { 335 | switch (F->getIntrinsicID()) { 336 | case Intrinsic::dbg_declare: 337 | case Intrinsic::dbg_value: 338 | case Intrinsic::lifetime_start: 339 | case Intrinsic::lifetime_end: 340 | case Intrinsic::invariant_start: 341 | case Intrinsic::invariant_end: 342 | case Intrinsic::eh_typeid_for: 343 | case Intrinsic::eh_return_i32: 344 | case Intrinsic::eh_return_i64: 345 | case Intrinsic::eh_sjlj_functioncontext: 346 | case Intrinsic::eh_sjlj_setjmp: 347 | case Intrinsic::eh_sjlj_longjmp: 348 | return; /* No masking */ 349 | case Intrinsic::memcpy: 350 | case Intrinsic::memmove: 351 | case Intrinsic::memset: 352 | case Intrinsic::vastart: 353 | case Intrinsic::vacopy: 354 | case Intrinsic::vaend: 355 | break; /* Continue with masking */ 356 | default: 357 | errs() << "Unhandled intrinsic that takes pointer: " << *F << "\n"; 358 | break; /* Do mask to be sure. */ 359 | } 360 | } 361 | 362 | // Some functions need dynamic logic to mask nested pointers in args, e.g. execve 363 | if (Function *WrapFunc = getNoInstrumentFunction(*F->getParent(), F->getName().str() + "_mask", true)) { 364 | cast(CS->getInstruction())->setCalledFunction(WrapFunc); 365 | return; 366 | } 367 | 368 | maskPointerArgs(CS); 369 | } 370 | 371 | /* 372 | * Mask pointers passed as arguments to wrapper functions for external 373 | * functions, that we cannot detect in the wrapper itself. This is mostly the 374 | * cast for wrappers of varargs functions where the wrapper just passes a 375 | * va_list. 376 | */ 377 | void MaskPointers::instrumentCallExtWrap(CallSite *CS) { 378 | Function *F = CS->getCalledFunction(); 379 | if (CS->isInlineAsm() || !F) 380 | return; 381 | 382 | if (F->getName() != "_E__pr_info" && /* sphinx3 vfprintf wrapper */ 383 | F->getName() != "_ZN12pov_frontend13MessageOutput6PrintfEiPKcz" && /* povray vsnprintf wrapper */ 384 | F->getName() != "_ZN8pov_base16TextStreamBuffer6printfEPKcz" && /* povray vsnprintf wrapper */ 385 | F->getName() != "_ZN3pov10Debug_InfoEPKcz" && /* povray vsnprintf wrapper */ 386 | F->getName() != "_ZN3pov25POVMSUtil_SetFormatStringEP9POVMSDatajPKcz" && /* povray vsprintf wrapper */ 387 | F->getName() != "_ZN6cEnvir9printfmsgEPKcz" && /* omnetpp vsprintf wrapper */ 388 | F->getName() != "PerlIO_printf" && /* perl vprintf wrapper */ 389 | F->getName() != "PerlIO_stdoutf") /* perl vprintf wrapper */ 390 | return; 391 | 392 | maskPointerArgs(CS); 393 | } 394 | 395 | void MaskPointers::instrumentCallByval(CallSite *CS) { 396 | Function *F = CS->getCalledFunction(); 397 | if (CS->isInlineAsm()) /* XXX inline asm should actually be masked? */ 398 | return; 399 | if (F && F->isDeclaration()) /* external functions are handled above */ 400 | return; 401 | 402 | Instruction *I = CS->getInstruction(); 403 | 404 | for (unsigned i = 0, n = CS->getNumArgOperands(); i < n; i++) { 405 | Value *Arg = CS->getArgOperand(i); 406 | if (Arg->getType()->isPointerTy() && CS->paramHasAttr(i + 1, Attribute::ByVal)) 407 | setMasks(I, Arg, i, true); 408 | } 409 | } 410 | 411 | void MaskPointers::maskNestedPointers(Value *V, CompositeType *EltTy, 412 | SmallVector &indices, IRBuilder<> &B) { 413 | unsigned n = EltTy->isStructTy() ? 414 | cast(EltTy)->getNumElements() : 415 | cast(EltTy)->getNumElements(); 416 | 417 | for (unsigned i = 0; i < n; i++) { 418 | Type *Ty = EltTy->getTypeAtIndex(i); 419 | 420 | if (Ty->isPointerTy()) { 421 | DEBUG_LINE("masking nested pointer of type " << *Ty << " in value: " << *V); 422 | indices.push_back(B.getInt32(i)); 423 | Value *Ptr = B.CreateInBoundsGEP(V, indices, "nestedgep"); 424 | indices.pop_back(); 425 | Value *MaskedPtr = maskPointer(Ptr, B); 426 | Value *Load = B.CreateLoad(MaskedPtr, "nestedval"); 427 | Value *Masked = maskPointer(Load, B); 428 | B.CreateStore(Masked, MaskedPtr); 429 | } 430 | else if (Ty->isAggregateType()) { 431 | indices.push_back(B.getInt32(i)); 432 | maskNestedPointers(V, cast(Ty), indices, B); 433 | indices.pop_back(); 434 | } 435 | } 436 | } 437 | 438 | void MaskPointers::instrumentCallExtNestedPtrs(CallSite *CS) { 439 | static std::map> whitelist = { 440 | /* inlined std::list::push_back */ 441 | {"_ZNSt8__detail15_List_node_base7_M_hookEPS0_", {1}}, 442 | /* inlined std::string += std::string */ 443 | {"_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcm", {0}} 444 | }; 445 | // TODO: more generic: look for mangled std::list and push_back in fn name 446 | 447 | Function *F = CS->getCalledFunction(); 448 | if (!F) 449 | return; 450 | 451 | auto it = whitelist.find(F->getName()); 452 | if (it == whitelist.end()) 453 | return; 454 | 455 | assert(F->isDeclaration()); 456 | IRBuilder<> B(CS->getInstruction()); 457 | SmallVector indices = {B.getInt64(0)}; 458 | 459 | for (unsigned i : it->second) { 460 | Value *arg = CS->getArgOperand(i); 461 | Type *EltTy = cast(arg->getType())->getElementType(); 462 | assert(EltTy->isAggregateType()); 463 | DEBUG_LINE("mask nested pointers in arg " << i << " of:" << *CS->getInstruction()); 464 | maskNestedPointers(arg, cast(EltTy), indices, B); 465 | } 466 | } 467 | 468 | void MaskPointers::instrumentCallSafeArgs(CallSite *CS) { 469 | if (!SafeAlloc) 470 | return; 471 | 472 | Function *F = CS->getCalledFunction(); 473 | if (!F || F->isDeclaration()) 474 | return; 475 | 476 | Instruction *Call = CS->getInstruction(); 477 | unsigned Idx = 0; 478 | 479 | for (Argument &Arg : F->args()) { 480 | Value *Param = CS->getArgument(Idx); 481 | if (!SafeAlloc->hasTag(&Arg) && SafeAlloc->hasTag(Param)) { 482 | // No need to preserve overflow bit, since all uses are safe 483 | setMasks(Call, Param, Idx, true); 484 | } 485 | Idx++; 486 | } 487 | } 488 | 489 | void MaskPointers::instrumentCmpPtr(CmpInst *I) { 490 | Value *Arg1 = I->getOperand(0); 491 | if (!Arg1->getType()->isPointerTy()) 492 | return; 493 | 494 | Value *Arg2 = I->getOperand(1); 495 | assert(Arg2->getType()->isPointerTy()); 496 | 497 | setMasks(I, Arg1, 0, true); 498 | setMasks(I, Arg2, 1, true); 499 | } 500 | 501 | /* 502 | * Mask out metadata bits in pointers when a pointer is accessed. It does not 503 | * mask out the overflow bit, so out-of-bound accesses will cause a fault. 504 | */ 505 | void MaskPointers::instrumentMemAccess(Instruction *I) { 506 | int PtrOperand = isa(I) ? 1 : 0; 507 | Value *Ptr = I->getOperand(PtrOperand); 508 | setMasks(I, Ptr, PtrOperand, false); 509 | 510 | /* Also mask writes of pointers to externs (e.g., environ). */ 511 | /* TODO: we don't have to mask the ptr above if global value */ 512 | ifcast(StoreInst, SI, I) { 513 | ifcast(GlobalVariable, GV, Ptr->stripPointerCasts()) { 514 | if (!GV->hasInitializer() && GV->getType()->isPointerTy()) 515 | setMasks(I, SI->getValueOperand(), 0, true); 516 | } 517 | } 518 | } 519 | 520 | /* 521 | * Create masks for ptrints and replace uses in users that need masking with 522 | * the masked value. 523 | */ 524 | void MaskPointers::instrumentNullTagUsers(Instruction *PtrInt) { 525 | assert(ReintPtrs); 526 | 527 | for (Instruction *User : ReintPtrs->getNullTagUsers(PtrInt)) { 528 | unsigned Idx = static_cast(getOperandNo(User, PtrInt)); 529 | setMasks(User, PtrInt, Idx, true); 530 | } 531 | } 532 | 533 | /* 534 | * Mask safe uses of unsafe function arguments. 535 | */ 536 | void MaskPointers::instrumentArgs(Function *F) { 537 | if (!SafeAlloc) 538 | return; 539 | 540 | DenseMap> ArgMasks; 541 | 542 | for (Argument &Arg : F->args()) { 543 | if (!Arg.getType()->isPointerTy()) 544 | continue; 545 | 546 | if (!SafeAlloc->hasTag(&Arg)) 547 | continue; 548 | 549 | for (User *U : Arg.users()) { 550 | if (!SafeAlloc->hasTag(U)) 551 | ArgMasks.FindAndConstruct(&Arg).second.push_back(U); 552 | } 553 | } 554 | 555 | if (ArgMasks.empty()) 556 | return; 557 | 558 | IRBuilder<> B(&*F->getEntryBlock().getFirstInsertionPt()); 559 | 560 | for (auto P : ArgMasks) { 561 | Argument *Arg = P.first; 562 | // Force overflow bit masking here, reasoning similar to that of safe 563 | // parameters at callsites (see finalizeModule) 564 | Value *Masked = maskPointer(Arg, B, true); 565 | 566 | for (User *U : P.second) 567 | U->replaceUsesOfWith(Arg, Masked); 568 | } 569 | } 570 | 571 | bool MaskPointers::runOnFunction(Function &F) { 572 | SmallVector MemoryAccesses, PtrInts; 573 | 574 | instrumentArgs(&F); 575 | 576 | for (inst_iterator II = inst_begin(&F), E = inst_end(&F); II != E; ++II) { 577 | Instruction *I = &*II; 578 | 579 | if (isa(I) || isa(I)) 580 | MemoryAccesses.push_back(I); 581 | // TODO: fence, cmpxchg, atomicrmw 582 | 583 | if (ReintPtrs && ReintPtrs->hasNullTagUsers(I)) 584 | PtrInts.push_back(I); 585 | } 586 | 587 | for (inst_iterator II = inst_begin(&F), E = inst_end(&F); II != E; ++II) { 588 | Instruction *I = &*II; 589 | if (isa(I) || isa(I)) { 590 | CallSite CS(I); 591 | instrumentCallExt(&CS); 592 | instrumentCallExtWrap(&CS); 593 | instrumentCallByval(&CS); 594 | instrumentCallExtNestedPtrs(&CS); 595 | instrumentCallSafeArgs(&CS); 596 | } 597 | else ifcast(CmpInst, Cmp, I) { 598 | instrumentCmpPtr(Cmp); 599 | } 600 | else if (SafeAlloc && !SafeAlloc->hasTag(I)) { 601 | ifcast(BitCastInst, BC, I) { 602 | if (BC->getSrcTy()->isPointerTy()) { 603 | Value *Base = I->getOperand(0); 604 | if (SafeAlloc->hasTag(Base)) 605 | setMasks(I, Base, 0, true); 606 | } 607 | } 608 | else if (isa(I)) { 609 | Value *Base = I->getOperand(0); 610 | if (SafeAlloc->hasTag(Base)) 611 | setMasks(I, Base, 0, true); 612 | } 613 | } 614 | } 615 | 616 | for (Instruction *PtrInt : PtrInts) 617 | instrumentNullTagUsers(PtrInt); 618 | 619 | for (Instruction *Access : MemoryAccesses) 620 | instrumentMemAccess(Access); 621 | 622 | return true; // TODO: return false if nothing actually instruments 623 | } 624 | 625 | bool MaskPointers::instrumentGlobals(Module &M) { 626 | if (!getNoInstrumentFunction(M, "initialize_tagged_globals", true)) 627 | return false; 628 | 629 | IRBuilder<> B(M.getContext()); 630 | bool Changed = false; 631 | 632 | for (GlobalVariable &TaggedGV : M.globals()) { 633 | if (!TaggedGV.hasName() || !TaggedGV.getName().startswith("tagged.")) 634 | continue; 635 | 636 | GlobalVariable *GV = M.getNamedGlobal(TaggedGV.getName().substr(7)); 637 | 638 | /* All uses are probably constantexprs after optimizations, replace 639 | * them with instructions so we can replace them with loads later on */ 640 | if (!allNonInstructionUsersCanBeMadeInstructions(GV)) { 641 | if (GV->hasName()) { 642 | DEBUG_LINE("Warning: cannot replace all references to global @" 643 | << GV->getName() << ", skipping its instrumentation"); 644 | } else { 645 | DEBUG_LINE("Warning: cannot replace all references to nameless " 646 | << "global, skipping its instrumentation:\n" << *GV); 647 | } 648 | continue; 649 | } 650 | makeAllConstantUsesInstructions(GV); 651 | 652 | std::vector Users(GV->user_begin(), GV->user_end()); 653 | for (User *U : Users) { 654 | Instruction *I = cast(U); 655 | Function *Parent = I->getParent()->getParent(); 656 | 657 | if (shouldInstrument(Parent)) { 658 | B.SetInsertPoint(I); 659 | Value *TaggedPtr = B.CreateLoad(&TaggedGV, GV->getName()); 660 | I->replaceUsesOfWith(GV, maskPointer(TaggedPtr, B)); 661 | // TODO: one load per function 662 | } 663 | } 664 | 665 | Changed = true; 666 | } 667 | 668 | return Changed; 669 | } 670 | -------------------------------------------------------------------------------- /llvm-passes/ReinterpretedPointers.cpp: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "find-reinterpreted-pointers" 2 | 3 | #include "builtin/Common.h" 4 | #include "AddressSpace.h" 5 | #include "ReinterpretedPointers.h" 6 | 7 | using namespace llvm; 8 | 9 | typedef SetVector PtrIntListT; 10 | 11 | char ReinterpretedPointers::ID = 0; 12 | static RegisterPass X("find-reinterpreted-pointers", 13 | "Find pointers that are cast to integers and identify any uses that " 14 | "need masking of metadata tags", 15 | false, true); 16 | 17 | void ReinterpretedPointers::getAnalysisUsage(AnalysisUsage &AU) const { 18 | AU.setPreservesAll(); 19 | } 20 | 21 | static inline bool isAnd(Instruction *I) { 22 | return I->isBinaryOp() && I->getOpcode() == Instruction::And; 23 | } 24 | 25 | /* 26 | * A pointer mask preserves the high bits if if the mask is constant and has 27 | * all ones in the upper (metadata) bits. 28 | */ 29 | static bool andDefinitelyPreservesHighBits(Instruction *I, Instruction *PtrOp) { 30 | unsigned OtherOpIndex = I->getOperand(0) == PtrOp ? 1 : 0; 31 | ifcast(ConstantInt, Mask, I->getOperand(OtherOpIndex)) 32 | return Mask->getZExtValue() >= ~getAddressSpaceMask(); 33 | return false; 34 | } 35 | 36 | /* 37 | * A pointer mask zeroes the high bits if if the mask is constant and has all 38 | * zeroes in the upper (metadata) bits. 39 | */ 40 | static bool andDefinitelyZeroesHighBits(Instruction *I, Instruction *PtrOp) { 41 | unsigned OtherOpIndex = I->getOperand(0) == PtrOp ? 1 : 0; 42 | ifcast(ConstantInt, Mask, I->getOperand(OtherOpIndex)) 43 | return Mask->getZExtValue() <= getAddressSpaceMask(); 44 | return false; 45 | } 46 | 47 | /* 48 | * Check if an instruction needs to mask metadata bits of a ptrint. 49 | */ 50 | static bool breaksWithPointerTag(Instruction *I, Instruction *PtrInt) { 51 | /* Trivial: 52 | * - cmp, sub, add, xor, rem, div, gep, sh[rl], switch, ret, itofp, insert 53 | * (xors are sometimes used to create uglygeps) 54 | * - external/asm/intrinsic call */ 55 | switch (I->getOpcode()) { 56 | case Instruction::ICmp: 57 | case Instruction::Sub: 58 | case Instruction::Add: 59 | case Instruction::Xor: 60 | case Instruction::URem: 61 | case Instruction::SRem: // gcc 62 | case Instruction::Mul: // perlbench, h264ref 63 | case Instruction::SDiv: // omnetpp 64 | case Instruction::UDiv: 65 | case Instruction::GetElementPtr: 66 | case Instruction::LShr: 67 | case Instruction::AShr: 68 | case Instruction::Shl: 69 | case Instruction::Or: // xalancbmk (TraverseSchema.cpp:2658) 70 | case Instruction::Switch: 71 | case Instruction::SIToFP: // omnetpp 72 | case Instruction::UIToFP: // perl FIXME: wtf... should we mask this? 73 | case Instruction::InsertElement: // dealII 74 | case Instruction::InsertValue: // xalancbmk 75 | return true; 76 | } 77 | 78 | ifcast(CallInst, CI, I) { 79 | Function *F = CI->getCalledFunction(); 80 | return !F || F->isIntrinsic() || F->isDeclaration(); 81 | } 82 | 83 | ifcast(InvokeInst, IV, I) { 84 | Function *F = IV->getCalledFunction(); 85 | return !F || F->isDeclaration(); 86 | } 87 | 88 | /* Non-trivial, these need more analysis: 89 | * - and: if the mask may not preserve metadata bits */ 90 | if (isAnd(I)) 91 | return !andDefinitelyPreservesHighBits(I, PtrInt); 92 | 93 | return false; 94 | } 95 | 96 | /* 97 | * Identify uses of ptrints that definitely do not need masking: 98 | * - store, inttoptr, bitcast (int -> ptr) 99 | * - internal call: don't know if causes problems, just print a warning for now 100 | * - and: if the mask already zeroes the metapointer 101 | * - trunc: if the destination type fits within the address space bits 102 | */ 103 | static bool doesNotBreakWithPointerTag(Instruction *I, Instruction *PtrInt) { 104 | if (isa(I) || isa(I)) 105 | return true; 106 | 107 | ifcast(BitCastInst, BC, I) { 108 | assert(isPtrIntTy(BC->getSrcTy())); 109 | 110 | if (!BC->getDestTy()->isPointerTy()) { 111 | DEBUG_LINE("Warning: bitcast user of ptrint might need masking:"); 112 | DEBUG_LINE(" ptrint: " << *PtrInt); 113 | DEBUG_LINE(" bitcast:" << *BC); 114 | return true; 115 | } 116 | 117 | return BC->getDestTy()->isPointerTy(); 118 | } 119 | 120 | ifcast(CallInst, CI, I) { 121 | Function *F = CI->getCalledFunction(); 122 | if (!F || F->isIntrinsic() || F->isDeclaration()) 123 | return false; 124 | 125 | //DEBUG_LINE("Warning: call to internal function might need to mask ptrint:"); 126 | //DEBUG_LINE(" ptrint:" << *PtrInt); 127 | //DEBUG_LINE(" call: " << *CI); 128 | return true; 129 | } 130 | 131 | ifcast(InvokeInst, IV, I) { 132 | Function *F = IV->getCalledFunction(); 133 | if (!F || F->isDeclaration()) 134 | return false; 135 | 136 | //DEBUG_LINE("Warning: invoke of internal method might need to mask ptrint:"); 137 | //DEBUG_LINE(" ptrint:" << *PtrInt); 138 | //DEBUG_LINE(" call: " << *IV); 139 | return true; 140 | } 141 | 142 | if (isAnd(I)) 143 | return andDefinitelyZeroesHighBits(I, PtrInt); 144 | 145 | ifcast(TruncInst, TI, I) { 146 | IntegerType *DestTy = cast(TI->getDestTy()); 147 | return DestTy->getBitWidth() <= AddressSpaceBits; 148 | } 149 | 150 | if (isa(I)) 151 | return true; 152 | 153 | return false; 154 | } 155 | 156 | static bool propagatesPointerTag(Instruction *I, Instruction *Ptr) { 157 | if (isa(I) || isa(I)) 158 | return true; 159 | 160 | if (isAnd(I)) 161 | return andDefinitelyPreservesHighBits(I, Ptr); 162 | 163 | return false; 164 | } 165 | 166 | /* 167 | * Collect users for a ptrint source that need masking of metadata bits. 168 | * Recursively follow phi nodes, selects and metadata-preserving pointer masks. 169 | * We whitelist all uses that definitely need or don't need masking and error 170 | * if we encounter something unexpected. 171 | */ 172 | static void findNullTagUsers(Instruction *I, SmallVectorImpl &NullTagUsers) { 173 | for (User *U : I->users()) { 174 | ifncast(Instruction, UI, U) { 175 | continue; 176 | } 177 | else if (propagatesPointerTag(UI, I) || doesNotBreakWithPointerTag(UI, I)) { 178 | continue; 179 | } 180 | else if (breaksWithPointerTag(UI, I)) { 181 | NullTagUsers.push_back(UI); 182 | } 183 | else { 184 | errs() << "Error: found use of ptrint in "; 185 | errs() << I->getParent()->getParent()->getName(); 186 | errs() << " and not sure if should mask.\n"; 187 | errs() << " ptrint:" << *I << "\n"; 188 | errs() << " user: " << *UI << "\n"; 189 | exit(1); 190 | } 191 | } 192 | } 193 | 194 | static bool isPointerPointerBitCast(Value *V) { 195 | std::vector Origins; 196 | 197 | ifcast(PHINode, PN, V) 198 | collectPHIOrigins(PN, Origins); 199 | else 200 | Origins.push_back(V); 201 | 202 | for (Value *Origin : Origins) { 203 | ifcast(BitCastOperator, BC, Origin) { 204 | assert(isPtrIntTy(cast(BC->getDestTy())->getElementType())); 205 | PointerType *SrcTy = dyn_cast(BC->getSrcTy()); 206 | assert(SrcTy); 207 | if (SrcTy->getElementType()->isPointerTy()) 208 | return true; 209 | } 210 | } 211 | 212 | return false; 213 | } 214 | 215 | static bool isUsedAsPointer(Instruction *I, PtrIntListT &PtrInts) { 216 | for (User *U : I->users()) { 217 | Instruction *UI = dyn_cast(U); 218 | if (!UI) 219 | continue; 220 | 221 | if (isa(UI)) { 222 | return true; 223 | } else ifcast(StoreInst, SI, UI) { 224 | return isPointerPointerBitCast(SI->getPointerOperand()); 225 | } else if (isa(UI)) { 226 | return PtrInts.count(UI) > 0; 227 | } else if (UI->isBinaryOp() && UI->getOpcode() == Instruction::Sub) { 228 | Instruction *Other = dyn_cast(otherOperand(UI, I)); 229 | return PtrInts.count(UI) > 0 || 230 | (Other && PtrInts.count(Other) > 0) || 231 | UI->getName().startswith("sub.ptr.sub"); 232 | } 233 | } 234 | 235 | return false; 236 | } 237 | 238 | void ReinterpretedPointers::addNullTagUser(Instruction *PtrInt, Instruction *User) { 239 | assert(isPtrIntTy(PtrInt->getType())); 240 | 241 | auto it = NullTagUsers.find(PtrInt); 242 | 243 | if (it == NullTagUsers.end()) { 244 | UserListT NullTagUserList; 245 | NullTagUserList.push_back(User); 246 | NullTagUsers[PtrInt] = NullTagUserList; 247 | //NullTagUsers[PtrInt] = std::move(NullTagUserList); 248 | } else { 249 | for (Instruction *Existing : it->second) { 250 | if (Existing == User) 251 | return; 252 | } 253 | it->second.push_back(User); 254 | } 255 | } 256 | 257 | /* 258 | * Use Type Based Alias Analysis results to see if a load inst loads a pointer 259 | * as an integer. 260 | * See also: http://releases.llvm.org/3.8.0/docs/LangRef.html#tbaa-metadata 261 | */ 262 | static Possibility loadsPointerAsInt(LoadInst *LI) { 263 | if (MDNode *TBAA = LI->getMetadata("tbaa")) { 264 | MDNode *TypeOp = cast(TBAA->getOperand(1)); 265 | MDString *TypeName = cast(TypeOp->getOperand(0)); 266 | return TypeName->getString() == "any pointer" ? Yes : No; 267 | } 268 | 269 | // Without TBAA info we should be conservative and always investigate the 270 | // integer (assuming it could fit a pointer). 271 | return isPtrIntTy(LI->getType()) ? Maybe : No; 272 | } 273 | 274 | static bool isPtrVecTy(Type *Ty) { 275 | VectorType *VecTy = dyn_cast(Ty); 276 | return VecTy && isPtrIntTy(VecTy->getElementType()); 277 | } 278 | 279 | bool ReinterpretedPointers::runOnFunction(Function &F) { 280 | PtrIntListT PtrInts, PtrVecs; 281 | SmallSetVector IntLoads; 282 | 283 | // First find trivial cases in a first pass 284 | foreach_func_inst(&F, I) { 285 | if (isPtrIntTy(I->getType())) { 286 | if (isa(I)) { 287 | PtrInts.insert(I); 288 | } 289 | else ifcast(BitCastInst, BC, I) { 290 | if (BC->getSrcTy()->isPointerTy()) 291 | PtrInts.insert(I); 292 | } 293 | else ifcast(LoadInst, LI, I) { 294 | switch (loadsPointerAsInt(LI)) { 295 | case Yes: PtrInts.insert(I); break; 296 | case Maybe: IntLoads.insert(I); break; 297 | case No: break; 298 | } 299 | } 300 | } 301 | else ifcast(LoadInst, LI, I) { 302 | if (isPtrVecTy(I->getType()) && loadsPointerAsInt(LI) == Yes) 303 | PtrVecs.insert(I); 304 | } 305 | } 306 | 307 | // Look at users of uncertain ptrints (loads) to see if they are used as a 308 | // pointer. Reiterate until no more pointers are found (since 309 | // isUsedAsPointer uses the tagged ptrints). Propagate ptrint tags to phi 310 | // nodes, selects and pointer masks. 311 | size_t OldSize; 312 | do { 313 | OldSize = PtrInts.size() + PtrVecs.size(); 314 | 315 | foreach_func_inst(&F, I) { 316 | Type *Ty = I->getType(); 317 | 318 | if (isPtrIntTy(Ty)) { 319 | if (PtrInts.count(I) > 0) { 320 | foreach(Instruction, UI, I->users()) { 321 | if (isa(UI) || isa(UI)) { 322 | PtrInts.insert(UI); 323 | } 324 | else if (isAnd(UI) && andDefinitelyPreservesHighBits(UI, I)) { 325 | PtrInts.insert(UI); 326 | } 327 | } 328 | } 329 | else if (isa(I) && IntLoads.count(I) > 0 && 330 | isUsedAsPointer(I, PtrInts)) { 331 | PtrInts.insert(I); 332 | IntLoads.remove(I); 333 | } 334 | } 335 | else if (PtrVecs.count(I) > 0) { 336 | foreach(Instruction, UI, I->users()) { 337 | if (isa(UI) || 338 | isa(UI) || 339 | isa(UI) || 340 | isa(UI) || 341 | UI->isBinaryOp()) { 342 | PtrVecs.insert(UI); 343 | } 344 | else if (isa(UI)) { 345 | PtrInts.insert(UI); 346 | } 347 | else ifcast(BitCastInst, BC, UI) { 348 | if (!BC->getDestTy()->isIntegerTy(128)) { 349 | errs() << "Warning: unexpected bitcast of ptrvec in " << F.getName() << ":\n"; 350 | errs() << " ptrvec: " << *I << "\n"; 351 | errs() << " bitcast:" << *BC << "\n"; 352 | } 353 | if (BC->getDestTy()->isIntegerTy()) 354 | PtrVecs.insert(UI); 355 | } 356 | else if (isa(UI)) { 357 | if (isPtrIntTy(UI->getType())) { 358 | PtrInts.insert(UI); 359 | } else { 360 | assert(isa(UI->getType())); 361 | PtrVecs.insert(UI); 362 | } 363 | } 364 | else if (!isa(UI) && 365 | !isa(UI)) { 366 | errs() << "unexpected use of ptrvec in " << F.getName() << ":\n"; 367 | errs() << " ptrvec:" << *I << "\n"; 368 | errs() << " user: " << *UI << "\n"; 369 | exit(1); 370 | } 371 | } 372 | } 373 | } 374 | } while (PtrInts.size() + PtrVecs.size() != OldSize); 375 | 376 | // Tag users for all ptrints that need masking 377 | SmallVector PtrIntNullTagUsers; 378 | 379 | for (Instruction *PtrInt : PtrInts) { 380 | PtrIntNullTagUsers.clear(); 381 | findNullTagUsers(PtrInt, PtrIntNullTagUsers); 382 | 383 | for (Instruction *User : PtrIntNullTagUsers) 384 | addNullTagUser(PtrInt, User); 385 | } 386 | 387 | // Emit warnings for loads that could not be determined to be pointers 388 | for (Instruction *LI : IntLoads) { 389 | PtrIntNullTagUsers.clear(); 390 | findNullTagUsers(LI, PtrIntNullTagUsers); 391 | 392 | if (!PtrIntNullTagUsers.empty()) { 393 | DEBUG_LINE("Warning: possible pointer load in " << F.getName() << 394 | " has users that would need masking"); 395 | DEBUG_LINE(" load:" << *LI); 396 | for (Instruction *User : PtrIntNullTagUsers) 397 | DEBUG_LINE(" user:" << *User); 398 | } 399 | } 400 | 401 | return false; 402 | } 403 | -------------------------------------------------------------------------------- /llvm-passes/ReinterpretedPointers.h: -------------------------------------------------------------------------------- 1 | #ifndef _REINTERPRETED_POINTERS_H 2 | #define _REINTERPRETED_POINTERS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "builtin/CustomFunctionPass.h" 11 | 12 | using namespace llvm; 13 | 14 | struct ReinterpretedPointers : public CustomFunctionPass { 15 | typedef TinyPtrVector UserListT; 16 | 17 | static char ID; 18 | ReinterpretedPointers() : CustomFunctionPass(ID) {}; 19 | 20 | bool hasNullTagUsers(Instruction *I) { 21 | return NullTagUsers.count(I) > 0; 22 | } 23 | 24 | const UserListT &getNullTagUsers(Instruction *I) { 25 | return NullTagUsers[I]; 26 | } 27 | 28 | private: 29 | DenseMap NullTagUsers; 30 | 31 | bool runOnFunction(Function &F) override; 32 | void getAnalysisUsage(AnalysisUsage &AU) const override; 33 | void addNullTagUser(Instruction *PtrInt, Instruction *User); 34 | }; 35 | 36 | #endif /* _REINTERPRETED_POINTERS_H */ 37 | -------------------------------------------------------------------------------- /llvm-passes/ReplaceAddressTakenMalloc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "llvm/Support/FileSystem.h" 8 | 9 | #define DEBUG_TYPE "replace-address-taken-malloc" 10 | 11 | #include "builtin/Common.h" 12 | #include "builtin/Allocation.h" 13 | 14 | using namespace llvm; 15 | 16 | struct ReplaceAddressTakenMalloc : public ModulePass { 17 | static char ID; 18 | ReplaceAddressTakenMalloc() : ModulePass(ID) {} 19 | virtual bool runOnModule(Module &M); 20 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 21 | AU.setPreservesAll(); 22 | } 23 | }; 24 | 25 | char ReplaceAddressTakenMalloc::ID = 0; 26 | static RegisterPass X("replace-address-taken-malloc", 27 | "Add wrappers around address-taken malloc/free calls to make sure that " 28 | "all calls to allocation functions are direct"); 29 | 30 | STATISTIC(NWrappers, "Number of address-taken functions wrapped"); 31 | STATISTIC(NUses, "Number of function pointers replaced"); 32 | 33 | static Function *makeWrapper(Function *F) { 34 | Function *Wrapper = Function::Create(F->getFunctionType(), 35 | GlobalValue::InternalLinkage, 36 | Twine("__wrap.") + F->getName(), 37 | F->getParent()); 38 | IRBuilder<> B(BasicBlock::Create(F->getContext(), "entry", Wrapper)); 39 | 40 | SmallVector Args; 41 | for (Argument &Arg : Wrapper->args()) 42 | Args.push_back(&Arg); 43 | 44 | if (F->getReturnType()->isVoidTy()) { 45 | B.CreateCall(F, Args); 46 | B.CreateRetVoid(); 47 | } else { 48 | B.CreateRet(B.CreateCall(F, Args, "ret")); 49 | } 50 | 51 | return Wrapper; 52 | } 53 | 54 | bool ReplaceAddressTakenMalloc::runOnModule(Module &M) { 55 | SmallVector WrapFunctions; 56 | 57 | for (Function &F : M) { 58 | if (F.hasAddressTaken() && (isAllocationFunc(&F) || isFreeFunc(&F))) 59 | WrapFunctions.push_back(&F); 60 | } 61 | 62 | for (Function *F : WrapFunctions) { 63 | DEBUG_LINE("creating wrapper for " << F->getName()); 64 | ++NWrappers; 65 | 66 | Function *Wrapper = makeWrapper(F); 67 | const User *UU; 68 | 69 | while (F->hasAddressTaken(&UU)) { 70 | User *U = const_cast(UU); 71 | 72 | ifcast(ConstantExpr, Expr, U) { 73 | unsigned i = getOperandNo(Expr, F); 74 | Constant *Repl = Expr->getWithOperandReplaced(i, Wrapper); 75 | Expr->replaceAllUsesWith(Repl); 76 | Expr->destroyConstant(); 77 | NUses += Repl->getNumUses(); 78 | } else { 79 | U->replaceUsesOfWith(F, Wrapper); 80 | ++NUses; 81 | } 82 | } 83 | } 84 | 85 | return !WrapFunctions.empty(); 86 | } 87 | -------------------------------------------------------------------------------- /llvm-passes/RuntimeStats.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define DEBUG_TYPE "runtime-stats" 4 | 5 | #include "builtin/Common.h" 6 | #include "builtin/CustomFunctionPass.h" 7 | 8 | using namespace llvm; 9 | 10 | struct RuntimeStats : public CustomFunctionPass { 11 | static char ID; 12 | RuntimeStats() : CustomFunctionPass(ID) {} 13 | 14 | private: 15 | const DataLayout *DL; 16 | 17 | Function *RTSGEPFunc; 18 | Function *RTSLoadFunc; 19 | Function *RTSStoreFunc; 20 | Type *RTSPtrTy; 21 | 22 | bool initializeModule(Module &M) override; 23 | bool runOnFunction(Function &F) override; 24 | }; 25 | 26 | char RuntimeStats::ID = 0; 27 | static RegisterPass X("runtime-stats", 28 | "Instrument calls to run-time statistics counters"); 29 | 30 | bool RuntimeStats::initializeModule(Module &M) { 31 | DL = &M.getDataLayout(); 32 | RTSGEPFunc = getNoInstrumentFunction(M, "rts_gep"); 33 | RTSLoadFunc = getNoInstrumentFunction(M, "rts_load"); 34 | RTSStoreFunc = getNoInstrumentFunction(M, "rts_store"); 35 | RTSPtrTy = RTSLoadFunc->getFunctionType()->getParamType(0); 36 | return false; 37 | } 38 | 39 | 40 | bool RuntimeStats::runOnFunction(Function &F) { 41 | for (inst_iterator II = inst_begin(&F), E = inst_end(&F); II != E; ++II) { 42 | Instruction *I = &*II; 43 | 44 | IRBuilder<> B(I); 45 | ifcast(LoadInst, LI, I) { 46 | Value *Ptr = B.CreateBitCast(LI->getPointerOperand(), RTSPtrTy); 47 | B.CreateCall(RTSLoadFunc, { Ptr }); 48 | } else ifcast(StoreInst, SI, I) { 49 | Value *Ptr = B.CreateBitCast(SI->getPointerOperand(), RTSPtrTy); 50 | B.CreateCall(RTSStoreFunc, { Ptr }); 51 | } else ifcast(GetElementPtrInst, GEP, I) { 52 | Value *Offset = EmitGEPOffset(&B, *DL, GEP); 53 | Value *Ptr = B.CreateBitCast(GEP->getPointerOperand(), RTSPtrTy); 54 | B.CreateCall(RTSGEPFunc, { Ptr, Offset, B.getInt32(GEP->hasAllConstantIndices()) }); 55 | } 56 | } 57 | 58 | return true; 59 | } 60 | -------------------------------------------------------------------------------- /llvm-passes/SafeAllocs.h: -------------------------------------------------------------------------------- 1 | #ifndef SAFE_ALLOCS_H 2 | #define SAFE_ALLOCS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "builtin/Allocation.h" 18 | 19 | using namespace llvm; 20 | 21 | class SafeAllocsBase { 22 | public: 23 | virtual ~SafeAllocsBase() {} 24 | 25 | virtual bool needsTag(Value *Allocation) = 0; 26 | virtual bool needsMask(Instruction *I, Value *Operand) = 0; 27 | virtual bool needsPropagation(GetElementPtrInst *GEP) = 0; 28 | virtual GetElementPtrInst *getPreemptedOffset(GetElementPtrInst *GEP) = 0; 29 | 30 | // Aliases for readability 31 | inline bool hasTag(Value *Ptr) { return needsTag(Ptr); } 32 | inline bool needsPropagation(Instruction *I) { 33 | assert(isa(I) || isa(I)); 34 | return hasTag(I); 35 | } 36 | }; 37 | 38 | class SafeAllocs : public ModulePass, public SafeAllocsBase { 39 | public: 40 | static char ID; 41 | SafeAllocs() : ModulePass(ID) {} 42 | 43 | bool runOnModule(Module &M) override; 44 | void getAnalysisUsage(AnalysisUsage &AU) const override; 45 | 46 | bool needsTag(Value *Allocation) override; 47 | bool needsMask(Instruction *I, Value *Operand) override; 48 | bool needsPropagation(GetElementPtrInst *GEP) override; 49 | GetElementPtrInst *getPreemptedOffset(GetElementPtrInst *GEP) override; 50 | 51 | private: 52 | const DataLayout *DL; 53 | DominatorTree *DT; 54 | LoopInfo *LI; 55 | ScalarEvolution *SE; 56 | 57 | DenseSet SafeAllocations; 58 | DenseSet SafeGEPs; 59 | DenseSet> SafeMaskSites; 60 | DenseMap PreemptedArithOffsets; 61 | 62 | void setNoTag(Value *Allocation); 63 | void setNoMask(Instruction *I, Value *Operand); 64 | void setNoPropagation(GetElementPtrInst *GEP); 65 | void setPreemptedOffset(GetElementPtrInst *CheckGEP, 66 | GetElementPtrInst *OffsetGEP); 67 | 68 | void propagateAllocationBounds(Function &F, 69 | DenseMap &PointerBounds); 70 | void findSafeGEPs(Function &F, 71 | DenseMap &PointerBounds); 72 | void findSafeAllocations(Function &F); 73 | void preemptBoundChecks(Function &F); 74 | void findSafeGlobals(Module &M); 75 | void propagateSafeTags(); 76 | 77 | bool isNotDereferencedBeyondNBytes(Value *Ptr, 78 | const SCEV *DistanceToEndOfObject); 79 | bool findAllDereferencedBytes(Value *Ptr, 80 | SmallVectorImpl &DerefBytes); 81 | 82 | bool isNotDereferencedInLastLoopIteration( 83 | GetElementPtrInst *GEP, InductionDescriptor &D); 84 | const SCEV *addNoWrapFlags(const SCEV *V); 85 | const SCEV *getGEPOffsetSCEV(GetElementPtrInst *GEP, bool NoWrap=false); 86 | const SCEV *getSizeOfSCEV(Type *Ty); 87 | const SCEV *getPointerCastOrArithOffset(Instruction *UI, Value *I); 88 | const SCEV *addSCEVs(const SCEV *LHS, const SCEV *RHS); 89 | bool compareSCEVs(ICmpInst::Predicate Pred, const SCEV *LHS, const SCEV *RHS); 90 | bool compareSCEVs(ICmpInst::Predicate Pred, Value *LHS, Value *RHS); 91 | bool compareGEPs(ICmpInst::Predicate Pred, GetElementPtrInst *LHS, GetElementPtrInst *RHS); 92 | bool eliminateCommonOperandsForComparison(const SCEV *&A, const SCEV *&B); 93 | }; 94 | 95 | #endif /* !SAFE_ALLOCS_H */ 96 | -------------------------------------------------------------------------------- /llvm-passes/SafeAllocsOld.h: -------------------------------------------------------------------------------- 1 | #ifndef SAFE_ALLOCS_OLD_H 2 | #define SAFE_ALLOCS_OLD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "builtin/Allocation.h" 17 | #include "SafeAllocs.h" 18 | 19 | using namespace llvm; 20 | 21 | class SafeAllocsOld : public ModulePass, public SafeAllocsBase { 22 | public: 23 | static char ID; 24 | SafeAllocsOld() : ModulePass(ID) {} 25 | 26 | bool runOnModule(Module &M) override; 27 | void getAnalysisUsage(AnalysisUsage &AU) const override; 28 | 29 | // New API 30 | virtual bool needsTag(Value *Allocation) { 31 | return !isSafe(Allocation); 32 | } 33 | virtual bool needsMask(Instruction *I __attribute__((unused)), Value *Operand) { 34 | return !isSafe(Operand); 35 | } 36 | virtual bool needsPropagation(GetElementPtrInst *GEP) { 37 | return !isSafe(GEP); 38 | } 39 | virtual GetElementPtrInst *getPreemptedOffset(GetElementPtrInst *GEP) { 40 | return PreemptedArithOffsets.lookup(GEP); 41 | } 42 | 43 | private: 44 | struct BoundT; 45 | typedef DenseMap BoundsT; 46 | 47 | const DataLayout *DL; 48 | TargetLibraryInfo *TLI; 49 | LoopInfo *LI; 50 | ScalarEvolution *SE; 51 | DenseSet SafePointers; 52 | SmallSet, 16> Visited; 53 | DenseSet UnsafeDerefs; 54 | DenseMap PreemptedArithOffsets; 55 | 56 | // Old API 57 | bool isSafe(Value *Ptr); 58 | //bool hasPreemptedOffset(GetElementPtrInst *GEP, int64_t &Offset); 59 | 60 | bool isSafePtr(Value *ptr, uint64_t size); 61 | bool isUnsafeDeref(Instruction *I, BoundsT &PtrBounds, unsigned Depth); 62 | bool isPtrUseSafe(Instruction *U, Value *Ptr, BoundsT &PtrBounds, unsigned Depth); 63 | bool areAllPtrUsesSafe(Value *Ptr, BoundsT &PtrBounds, unsigned Depth); 64 | void setSafe(Instruction *I, Value *Ptr); 65 | bool hasHoistableIndices(GetElementPtrInst *GEP); 66 | void markIntermediateExpandedInstsAsSafe(SCEVExpander &Expander, Value *Expanded); 67 | void hoistConstGEPOffsetsFromLoops(Function &F, bool &Changed); 68 | bool hoistBoundCheckFromLoop(GetElementPtrInst *GEP); 69 | void checkArgument(Argument *Arg, bool &Changed); 70 | void checkAllocation(AllocationSite &AS, bool &Changed); 71 | void checkGlobals(Module &M); 72 | void preemptBoundChecks(BasicBlock &BB, bool &Changed); 73 | Value *maskPointer(Value *Ptr, IRBuilder<> &B, bool MayPreserveOverflowBit); 74 | }; 75 | 76 | #endif /* !SAFE_ALLOCS_OLD_H */ 77 | -------------------------------------------------------------------------------- /llvm-passes/TagGlobals.cpp: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "tag-globals" 2 | 3 | #include "builtin/Common.h" 4 | #include "AddressSpace.h" 5 | 6 | using namespace llvm; 7 | 8 | struct TagGlobals : public ModulePass { 9 | static char ID; 10 | TagGlobals() : ModulePass(ID) {} 11 | 12 | bool runOnModule(Module &M) override; 13 | }; 14 | 15 | char TagGlobals::ID = 0; 16 | static RegisterPass X("tag-globals", 17 | "Replace all internal globals with `global_tag(global) | global` " 18 | "(must implement global_tag in a later pass)"); 19 | 20 | STATISTIC(NTaggedGlobal, "Number of tagged globals"); 21 | 22 | /* 23 | * Collect global variables that can be tagged (anything except environ and 24 | * llvm.*). 25 | */ 26 | static void findGlobalsToTag(Module &M, std::vector &Globals) { 27 | for (GlobalVariable &GV : M.globals()) { 28 | assert(!GV.getName().startswith("tagged.") && "can only tag globals once"); 29 | 30 | if (isNoInstrument(&GV)) 31 | continue; 32 | 33 | /* Don't mask globals from libraries XXX right? */ 34 | if (!GV.hasInitializer()) 35 | continue; 36 | 37 | /* Ignore constants */ 38 | if (GV.isConstant()) 39 | continue; 40 | 41 | /* Ignore @environ bevause of getenv problems FIXME */ 42 | //if (GV.getName() == "environ") 43 | // continue; 44 | // Should be caught by !hasInitializer() above: 45 | assert(GV.getName() != "environ"); 46 | 47 | /* Ignore intrinsics like constructor lists */ 48 | if (GV.getName().startswith("llvm.")) 49 | continue; 50 | 51 | Globals.push_back(&GV); 52 | } 53 | } 54 | 55 | /* 56 | * Create constructor function that replaces all globals with pointers, and 57 | * initializes their metapointers. Insert it in the constructor list after 58 | * initialize_global_metadata if it exists, or at the start of the list 59 | * otherwise. If no constructor list exists, create it. 60 | */ 61 | static Function *createGlobalTagConstructor(Module &M) { 62 | LLVMContext &Ctx = M.getContext(); 63 | FunctionType *FnTy = FunctionType::get(Type::getVoidTy(Ctx), false); 64 | Function *F = createNoInstrumentFunction(M, FnTy, "initialize_tagged_globals", false); 65 | 66 | /* Add function to constructor list after @initialize_global_metadata */ 67 | GlobalVariable *OldGlobalCtors = M.getGlobalVariable("llvm.global_ctors"); 68 | std::vector Ctors; 69 | int index = 0; 70 | 71 | if (OldGlobalCtors) { 72 | assert(OldGlobalCtors->hasNUses(0)); 73 | OldGlobalCtors->setName("llvm.global_ctors_old"); 74 | 75 | ConstantArray *CA = cast(OldGlobalCtors->getInitializer()); 76 | int i = 0; 77 | 78 | for (Use &I : CA->operands()) { 79 | Ctors.push_back(cast(I.get())); 80 | 81 | if (ConstantStruct *Struct = dyn_cast(I.get())) { 82 | Function *Fn = cast(Struct->getAggregateElement(1)); 83 | if (Fn->getName() == "initialize_global_metadata") 84 | index = i + 1; 85 | } 86 | i++; 87 | } 88 | } 89 | 90 | if (index == 0) 91 | DEBUG_LINE("Inserting global initializer at start of constructor list"); 92 | else 93 | DEBUG_LINE("Inserting global initializer after initialize_global_metadata"); 94 | 95 | IntegerType *i32 = Type::getInt32Ty(Ctx); 96 | PointerType *i8Ptr = Type::getInt8Ty(Ctx)->getPointerTo(); 97 | StructType *StructTy = StructType::get(i32, F->getType(), i8Ptr, nullptr); 98 | Constant *StructMembers[] = { 99 | ConstantInt::getSigned(i32, -1), F, ConstantPointerNull::get(i8Ptr) 100 | }; 101 | Constant *NewEntry = ConstantStruct::get(StructTy, StructMembers); 102 | Ctors.insert(Ctors.begin() + index, NewEntry); 103 | 104 | ArrayType *CtorsTy = ArrayType::get(StructTy, Ctors.size()); 105 | new GlobalVariable(M, CtorsTy, false, GlobalValue::AppendingLinkage, 106 | ConstantArray::get(CtorsTy, Ctors), "llvm.global_ctors"); 107 | 108 | if (OldGlobalCtors) 109 | OldGlobalCtors->eraseFromParent(); 110 | 111 | return F; 112 | } 113 | 114 | bool TagGlobals::runOnModule(Module &M) { 115 | std::vector Globals; 116 | findGlobalsToTag(M, Globals); 117 | 118 | if (Globals.empty()) 119 | return false; 120 | 121 | IntegerType *PtrIntTy = getPtrIntTy(M.getContext()); 122 | FunctionType *FnTy = FunctionType::get(PtrIntTy, PtrIntTy, false); 123 | Function *TagGLobalFunc = createNoInstrumentFunction(M, FnTy, "global_tag", true); 124 | 125 | Function *F = createGlobalTagConstructor(M); 126 | IRBuilder<> B(BasicBlock::Create(F->getContext(), "entry", F)); 127 | 128 | for (GlobalVariable *GV : Globals) { 129 | PointerType *PtrTy = GV->getType(); 130 | GlobalVariable *TaggedGV = new GlobalVariable(M, PtrTy, false, 131 | GlobalValue::InternalLinkage, ConstantPointerNull::get(PtrTy), 132 | Twine("tagged.") + GV->getName()); 133 | 134 | Value *PtrInt = B.CreatePtrToInt(GV, PtrIntTy, "ptrint"); 135 | Value *Tag = B.CreateCall(TagGLobalFunc, PtrInt, "tag"); 136 | Value *HighBits = B.CreateShl(Tag, AddressSpaceBits, "highbits"); 137 | Value *TaggedPtrInt = B.CreateOr(PtrInt, HighBits, "taggedint"); 138 | Value *TaggedPtr = B.CreateIntToPtr(TaggedPtrInt, PtrTy, "tagged"); 139 | B.CreateStore(TaggedPtr, TaggedGV); 140 | ++NTaggedGlobal; 141 | } 142 | 143 | B.CreateRetVoid(); 144 | return true; 145 | } 146 | -------------------------------------------------------------------------------- /llvm-passes/TagGlobalsConst.cpp: -------------------------------------------------------------------------------- 1 | #include "builtin/Common.h" 2 | #include "TagGlobalsConst.h" 3 | #include "AddressSpace.h" 4 | 5 | using namespace llvm; 6 | 7 | bool canTagGlobal(GlobalVariable &GV) { 8 | if (GV.getName().startswith("llvm.")) 9 | return false; 10 | 11 | if (isNoInstrument(&GV)) 12 | return false; 13 | 14 | if (GV.getNumUses() == 0) 15 | return false; 16 | 17 | // Ignore vtables because those may be dereferenced by libc 18 | //if (GV.getName().startswith("_ZTV")) 19 | // return false; 20 | 21 | // Ignore typeinfo pointers 22 | if (GV.getName().startswith("_ZT")) 23 | return false; 24 | 25 | return true; 26 | } 27 | 28 | void tagGlobal(GlobalVariable &GV, uint64_t Tag) { 29 | tagGlobal(GV, ConstantInt::get(getPtrIntTy(GV.getContext()), Tag)); 30 | } 31 | 32 | void tagGlobal(GlobalVariable &GV, Constant *Tag) { 33 | SmallVector Users(GV.user_begin(), GV.user_end()); 34 | 35 | IntegerType *PtrIntTy = getPtrIntTy(GV.getContext()); 36 | Constant *GVInt = ConstantExpr::getPtrToInt(&GV, PtrIntTy); 37 | Constant *TagShifted = ConstantExpr::getShl(Tag, ConstantInt::get(PtrIntTy, AddressSpaceBits)); 38 | Constant *TaggedGVInt = ConstantExpr::getOr(GVInt, TagShifted); 39 | Constant *TaggedGV = ConstantExpr::getIntToPtr(TaggedGVInt, GV.getType()); 40 | 41 | for (User *U : Users) { 42 | // Ignore constantexprs, they should have been removed by 43 | // -expand-const-global-users 44 | ifncast(Instruction, UI, U) 45 | continue; 46 | 47 | // XXX commented this because it screwed with global tagging in ubound-masks. It 48 | // should be redundant anyway because of getAllocationFromTag in mask-pointers 49 | #if 0 50 | // Don't add metadata to loads/stores since we'll just have to mask 51 | // it out again 52 | if (isa(U)) 53 | continue; 54 | 55 | ifcast(StoreInst, SI, U) { 56 | if (SI->getPointerOperand() == &GV) 57 | continue; 58 | } 59 | #endif 60 | 61 | Function *F = UI->getParent()->getParent(); 62 | 63 | if (!shouldInstrument(F)) 64 | continue; 65 | 66 | // Skip static global constructors which would store tagged global 67 | // pointers in objects which are later dereferenced by free() 68 | if (F->hasName() && F->getName().startswith("_GLOBAL__sub_I_")) 69 | continue; 70 | 71 | U->replaceUsesOfWith(&GV, TaggedGV); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /llvm-passes/TagGlobalsConst.h: -------------------------------------------------------------------------------- 1 | #ifndef TAG_GLOBALS_CONST_H 2 | #define TAG_GLOBALS_CONST_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | bool canTagGlobal(llvm::GlobalVariable &GV); 9 | 10 | void tagGlobal(llvm::GlobalVariable &GV, uint64_t Tag); 11 | 12 | void tagGlobal(llvm::GlobalVariable &GV, llvm::Constant *Tag); 13 | 14 | #endif /* !TAG_GLOBALS_CONST_H */ 15 | -------------------------------------------------------------------------------- /llvm-passes/UboundBranch.cpp: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "ubound-branch" 2 | 3 | #include "builtin/Common.h" 4 | #include "builtin/CustomFunctionPass.h" 5 | #include "builtin/Allocation.h" 6 | #include "builtin/SizeofTypes.h" 7 | #include "AddressSpace.h" 8 | #include "TagGlobalsConst.h" 9 | #include "SafeAllocs.h" 10 | #include "SafeAllocsOld.h" 11 | #include "ReinterpretedPointers.h" 12 | #include "LibPtrRet.h" 13 | 14 | using namespace llvm; 15 | 16 | struct UboundBranch : public CustomFunctionPass { 17 | static char ID; 18 | UboundBranch() : CustomFunctionPass(ID) {} 19 | 20 | void getAnalysisUsage(AnalysisUsage &AU) const override; 21 | 22 | private: 23 | const DataLayout *DL; 24 | SafeAllocsBase *SafeAlloc; 25 | DenseMap ErrorBlocks; 26 | Function *IsOOBFunc; 27 | Function *TrapFunc; 28 | Function *StrBufSizeFunc; 29 | Function *NewStrtokFunc; 30 | 31 | bool runOnFunction(Function &F) override; 32 | bool initializeModule(Module &M) override; 33 | 34 | bool instrumentGlobals(Module &M); 35 | bool instrumentAllocation(AllocationSite &AS); 36 | bool instrumentDeref(Instruction *I); 37 | bool instrumentMemIntrinsic(Instruction *I); 38 | void insertCheck(Value *Ptr, Value *DerefSize, Instruction *InsertBefore); 39 | BasicBlock *getOrCreateErrorBlock(Function *F); 40 | bool propagatePtrMetadata(Instruction *I); 41 | bool instrumentPtrArith(GetElementPtrInst *GEP); 42 | Constant *getNullPtr(PointerType *Ty); 43 | }; 44 | 45 | char UboundBranch::ID = 0; 46 | static RegisterPass X("ubound-branch", 47 | "Insert bound checks with ubound pointer in high pointer bits"); 48 | 49 | static cl::opt OptGlobal("ubound-branch-global", 50 | cl::desc("Tag globals"), 51 | cl::init(true)); 52 | 53 | static cl::opt OptHeap("ubound-branch-heap", 54 | cl::desc("Tag heap allocations"), 55 | cl::init(true)); 56 | 57 | static cl::opt OptStack("ubound-branch-stack", 58 | cl::desc("Tag stack allocations"), 59 | cl::init(true)); 60 | 61 | static cl::opt OptReplaceNull("ubound-branch-nullptr", 62 | cl::desc("Instrument the NULL pointer with upper bound pointer 0x1"), 63 | cl::init(true)); 64 | 65 | static cl::opt OptMemIntrinsics("ubound-branch-mem-intrinsics", 66 | cl::desc("Enable checks on memory intrinsics (e.g., memcpy)"), 67 | cl::init(true)); 68 | 69 | STATISTIC(NStack, "Number of tagged stack variables"); 70 | STATISTIC(NHeap, "Number of tagged heap allocations"); 71 | STATISTIC(NGlobal, "Number of tagged globals"); 72 | STATISTIC(NChecks, "Number of bound checks at loads/stores"); 73 | STATISTIC(NLibCall, "Number of libcalls instrumented: total"); 74 | STATISTIC(NIgnore, "Number of libcalls instrumented: Ignore"); 75 | STATISTIC(NCopyFromArg, "Number of libcalls instrumented: CopyFromArg"); 76 | STATISTIC(NPtrDiff, "Number of libcalls instrumented: PtrDiff"); 77 | STATISTIC(NRetSizeStatic, "Number of libcalls instrumented: RetSizeStatic"); 78 | STATISTIC(NStrlen, "Number of libcalls instrumented: Strlen"); 79 | STATISTIC(NStrtok, "Number of libcalls instrumented: Strtok"); 80 | STATISTIC(NGep, "Number of pointer arithmetic instructions instrumented"); 81 | STATISTIC(NNullPtr, "Number of NULL pointer operands replaced"); 82 | STATISTIC(NMemIntrinsic, "Number of memory intrinsics instrumented"); 83 | 84 | void UboundBranch::getAnalysisUsage(AnalysisUsage &AU) const { 85 | AU.setPreservesCFG(); 86 | AU.addPreserved(); 87 | AU.addPreserved(); 88 | AU.addPreserved(); 89 | AU.addPreserved(); 90 | AU.addUsedIfAvailable(); 91 | AU.addUsedIfAvailable(); 92 | } 93 | 94 | bool UboundBranch::initializeModule(Module &M) { 95 | DL = &M.getDataLayout(); 96 | 97 | if (!(SafeAlloc = getAnalysisIfAvailable())) 98 | SafeAlloc = getAnalysisIfAvailable(); 99 | 100 | ErrorBlocks.clear(); 101 | TrapFunc = Intrinsic::getDeclaration(&M, Intrinsic::trap); 102 | IsOOBFunc = getNoInstrumentFunction(M, "is_oob"); 103 | StrBufSizeFunc = getNoInstrumentFunction(M, "strsize_nullsafe"); 104 | NewStrtokFunc = getNoInstrumentFunction(M, "strtok_ubound"); 105 | 106 | return instrumentGlobals(M); 107 | } 108 | 109 | bool UboundBranch::runOnFunction(Function &F) { 110 | bool Changed = false; 111 | SmallVector Geps; 112 | SmallVector Derefs, MemIntrinsics; 113 | 114 | for (Instruction &I : instructions(F)) { 115 | AllocationSite AS; 116 | 117 | if (isAllocation(&I, AS)) { 118 | Changed |= instrumentAllocation(AS); 119 | } 120 | else if (isa(I) || isa(I)) { 121 | Derefs.push_back(&I); 122 | } 123 | else ifcast(GetElementPtrInst, GEP, &I) { 124 | Geps.push_back(GEP); 125 | } 126 | else { 127 | Changed |= propagatePtrMetadata(&I); 128 | } 129 | 130 | if (OptMemIntrinsics) { 131 | ifcast(MemIntrinsic, MI, &I) { 132 | MemIntrinsics.push_back(MI); 133 | } 134 | else ifcast(CallInst, CI, &I) { 135 | Function *CF = CI->getCalledFunction(); 136 | if (CF && CF->hasName() && CF->getName() == "memcmp") 137 | MemIntrinsics.push_back(CI); 138 | } 139 | } 140 | 141 | if (OptReplaceNull) { 142 | // Skip libcalls (indirect calls may be libcalls as well, ignore 143 | // those for now and hope they are never called with NULL args) 144 | if (isa(I) || isa(I)) { 145 | Function *F = CallSite(&I).getCalledFunction(); 146 | if (F && F->isDeclaration()) 147 | continue; 148 | } 149 | 150 | for (Use &U : I.operands()) { 151 | ifcast(ConstantPointerNull, NullPtr, U.get()) { 152 | U.set(getNullPtr(NullPtr->getType())); 153 | NNullPtr++; 154 | } 155 | } 156 | } 157 | } 158 | 159 | for (Instruction *I : Derefs) 160 | Changed |= instrumentDeref(I); 161 | 162 | for (GetElementPtrInst *GEP : Geps) 163 | Changed |= instrumentPtrArith(GEP); 164 | 165 | for (Instruction *I : MemIntrinsics) 166 | Changed |= instrumentMemIntrinsic(I); 167 | 168 | return Changed; 169 | } 170 | 171 | bool UboundBranch::instrumentGlobals(Module &M) { 172 | if (!OptGlobal) 173 | return false; 174 | 175 | for (GlobalVariable &GV : M.globals()) { 176 | if (!canTagGlobal(GV)) 177 | continue; 178 | 179 | if (SafeAlloc && !SafeAlloc->needsTag(&GV)) 180 | continue; 181 | 182 | Type *Ty = GV.getType()->getPointerElementType(); 183 | uint64_t Size = DL->getTypeStoreSize(Ty); 184 | Size += ALLOWED_OOB_BYTES; 185 | 186 | IntegerType *PtrIntTy = getPtrIntTy(GV.getContext()); 187 | Constant *GVInt = ConstantExpr::getPtrToInt(&GV, PtrIntTy); 188 | Constant *SizeInt = ConstantInt::get(PtrIntTy, Size); 189 | Constant *EndPtr = ConstantExpr::getAdd(GVInt, SizeInt); 190 | 191 | tagGlobal(GV, EndPtr); 192 | ++NGlobal; 193 | } 194 | 195 | return NGlobal > 0; 196 | } 197 | 198 | /* 199 | * Mask out any metadata from nested allocation functions 200 | */ 201 | static Value *maskMallocWrapper(IRBuilder<> &B, AllocationSite &AS) { 202 | if (!AS.isHeapAllocation() || !AS.IsWrapped) 203 | return AS.Allocation; 204 | 205 | Value *Ptr = AS.Allocation; 206 | std::vector Users(Ptr->user_begin(), Ptr->user_end()); 207 | 208 | std::string Prefix = Ptr->hasName() ? Ptr->getName().str() + "." : ""; 209 | Value *PtrInt = B.CreatePtrToInt(Ptr, getPtrIntTy(Ptr->getContext()), Prefix + "int"); 210 | Value *Masked = B.CreateAnd(PtrInt, getAddressSpaceMask(), Prefix + "applymask"); 211 | Value *NewPtr = B.CreateIntToPtr(Masked, Ptr->getType(), Prefix + "unwrapped"); 212 | 213 | for (User *U : Users) 214 | U->replaceUsesOfWith(Ptr, NewPtr); 215 | 216 | return NewPtr; 217 | } 218 | 219 | /* 220 | * Put the inverse size in the upper bits of an allocated pointer, and replace 221 | * all occurences of this value with the instrumented pointer. IRBuilder will 222 | * do const prop on size, often yielding a single `or` instruction. 223 | */ 224 | bool UboundBranch::instrumentAllocation(AllocationSite &AS) { 225 | if (AS.isStackAllocation() && !OptStack) 226 | return false; 227 | 228 | if (AS.isHeapAllocation() && !OptHeap) 229 | return false; 230 | 231 | IRBuilder<> B(getInsertPointAfter(AS.Allocation)); 232 | Value *Ptr = maskMallocWrapper(B, AS); 233 | 234 | if (SafeAlloc && !SafeAlloc->needsTag(AS.Allocation)) 235 | return Ptr != AS.Allocation; 236 | 237 | std::vector Users(Ptr->user_begin(), Ptr->user_end()); 238 | 239 | Value *Size = AS.instrumentWithByteSize(B, *DL); 240 | IntegerType *SizeTy = cast(Size->getType()); 241 | 242 | if (ALLOWED_OOB_BYTES) 243 | Size = B.CreateAdd(Size, ConstantInt::get(SizeTy, ALLOWED_OOB_BYTES)); 244 | 245 | Value *PtrInt = B.CreatePtrToInt(Ptr, getPtrIntTy(Ptr->getContext())); 246 | std::string Prefix = Ptr->hasName() ? Ptr->getName().str() + "." : ""; 247 | Value *EndPtr = B.CreateAdd(PtrInt, Size); 248 | Value *TagShifted = B.CreateShl(EndPtr, AddressSpaceBits, Prefix + "tag"); 249 | Value *Tagged = B.CreateOr(PtrInt, TagShifted, Prefix + "applytag"); 250 | Value *NewPtr = B.CreateIntToPtr(Tagged, Ptr->getType(), Prefix + "tagged"); 251 | 252 | for (User *U : Users) 253 | U->replaceUsesOfWith(Ptr, NewPtr); 254 | 255 | if (AS.isStackAllocation()) ++NStack; else ++NHeap; 256 | return true; 257 | } 258 | 259 | bool UboundBranch::instrumentDeref(Instruction *I) { 260 | int PtrOperand = isa(I) ? 0 : 1; 261 | Value *Ptr = I->getOperand(PtrOperand); 262 | 263 | if (SafeAlloc && !SafeAlloc->needsMask(I, Ptr)) 264 | return false; 265 | 266 | Type *DerefTy = isa(I) ? I->getType() : 267 | cast(I)->getValueOperand()->getType(); 268 | uint64_t DerefSize = DL->getTypeStoreSize(DerefTy); 269 | 270 | insertCheck(Ptr, ConstantInt::get(Type::getInt64Ty(I->getContext()), DerefSize), I); 271 | return true; 272 | } 273 | 274 | bool UboundBranch::instrumentMemIntrinsic(Instruction *I) { 275 | Value *Length; 276 | Use *PtrArg1Use, *PtrArg2Use = NULL; 277 | 278 | ifcast(MemIntrinsic, MI, I) { 279 | Length = MI->getLength(); 280 | PtrArg1Use = &MI->getRawDestUse(); 281 | ifcast(MemTransferInst, MTI, MI) { 282 | PtrArg2Use = &MTI->getRawSourceUse(); 283 | } 284 | } 285 | else ifcast(CallInst, CI, I) { 286 | if (CI->getCalledFunction()->getName() == "memcmp") { 287 | Length = CI->getArgOperand(2); 288 | PtrArg1Use = &CI->getArgOperandUse(0); 289 | PtrArg2Use = &CI->getArgOperandUse(1); 290 | } 291 | else { 292 | LOG_LINE("Unhandled call: " << *CI); 293 | llvm_unreachable("unhandled call"); 294 | } 295 | } 296 | else { 297 | LOG_LINE("Unhandled intrinsic inst: " << *I); 298 | llvm_unreachable("unhandled inst"); 299 | } 300 | 301 | insertCheck(PtrArg1Use->get(), Length, I); 302 | 303 | if (PtrArg2Use) 304 | insertCheck(PtrArg2Use->get(), Length, I); 305 | 306 | ++NMemIntrinsic; 307 | return true; 308 | } 309 | 310 | void UboundBranch::insertCheck(Value *Ptr, Value *DerefSize, Instruction *InsertBefore) { 311 | IRBuilder<> B(InsertBefore); 312 | std::string Prefix = Ptr->hasName() ? Ptr->getName().str() + "." : ""; 313 | 314 | BasicBlock *BB = InsertBefore->getParent(); 315 | BasicBlock *Succ = BB->splitBasicBlock(B.GetInsertPoint(), Prefix + "fallthru"); 316 | cast(BB->getTerminator())->eraseFromParent(); 317 | B.SetInsertPoint(BB); 318 | //Value *PtrInt = B.CreatePtrToInt(Ptr, getPtrIntTy(I->getContext()), Prefix + "int"); 319 | //Value *EndPtr = B.CreateLShr(PtrInt, AddressSpaceBits, Prefix + "endptr"); 320 | //Value *MaskedPtr = B.CreateAnd(PtrInt, getAddressSpaceMask(), Prefix + "maskedptr"); 321 | //Value *DerefEndPtr = B.CreateAdd(MaskedPtr, DerefSize, Prefix + "derefendptr"); 322 | //Value *NullCheck = B.CreateICmpNE(EndPtr, B.getInt64(0), Prefix + "nullcheck"); 323 | //Value *BoundsCheck = B.CreateICmpUGT(DerefEndPtr, EndPtr, Prefix + "boundscheck"); 324 | //Value *IsOOB = B.CreateAnd(NullCheck, BoundsCheck, Prefix + "oob"); 325 | Value *Ptr8 = B.CreatePointerCast(Ptr, B.getInt8PtrTy(), Prefix + "voidptr"); 326 | Value *IsOOB = B.CreateCall(IsOOBFunc, {Ptr8, DerefSize}, Prefix + "oob"); 327 | B.CreateCondBr(IsOOB, getOrCreateErrorBlock(BB->getParent()), Succ); 328 | 329 | ++NChecks; 330 | } 331 | 332 | BasicBlock *UboundBranch::getOrCreateErrorBlock(Function *F) { 333 | auto it = ErrorBlocks.find(F); 334 | if (it != ErrorBlocks.end()) 335 | return it->second; 336 | 337 | BasicBlock *BB = BasicBlock::Create(F->getContext(), "oob_error", F); 338 | ErrorBlocks[F] = BB; 339 | 340 | IRBuilder<> B(BB); 341 | B.CreateCall(TrapFunc); 342 | B.CreateUnreachable(); 343 | 344 | return BB; 345 | } 346 | 347 | bool UboundBranch::propagatePtrMetadata(Instruction *I) { 348 | int arg; 349 | 350 | if (!isa(I) && !isa(I)) 351 | return false; 352 | 353 | CallSite CS(I); 354 | Function *F = CS.getCalledFunction(); 355 | 356 | if (!F || F->isIntrinsic() || !F->isDeclaration()) 357 | return false; 358 | 359 | if (SafeAlloc && !SafeAlloc->needsPropagation(I)) 360 | return false; 361 | 362 | enum LibPtr type = getLibPtrType(F, &arg); 363 | 364 | switch (type) { 365 | case LibPtr::Strlen: ++NStrlen; break; 366 | case LibPtr::Ignore: ++NIgnore; break; 367 | case LibPtr::RetSizeStatic: ++NRetSizeStatic; break; 368 | case LibPtr::CopyFromArg: ++NCopyFromArg; break; 369 | case LibPtr::PtrDiff: ++NPtrDiff; break; 370 | case LibPtr::Strtok: ++NStrtok; break; 371 | case LibPtr::None: break; 372 | } 373 | 374 | if (type == LibPtr::Ignore) 375 | return false; 376 | 377 | if (type == LibPtr::Strtok) { 378 | CS.setCalledFunction(NewStrtokFunc); 379 | ++NLibCall; 380 | return true; 381 | } 382 | else if (type == LibPtr::None) { 383 | /* Sanity check that it doesn't return pointer. */ 384 | if (F->getReturnType()->isPointerTy()) { 385 | LOG_LINE("Error: unhandled ext func that returns pointer: " << 386 | F->getName() << ": " << *F->getType()); 387 | exit(1); 388 | } 389 | return false; 390 | } 391 | 392 | IRBuilder<> B(getInsertPointAfter(I)); 393 | Value *Ptr = I; 394 | std::vector Users(Ptr->user_begin(), Ptr->user_end()); 395 | 396 | Value *PtrVal = B.CreatePtrToInt(Ptr, B.getInt64Ty()); 397 | Value *NewEndPtr; 398 | 399 | if (type == LibPtr::Strlen) { 400 | Value *StrBufSizeArgs[] = { Ptr }; 401 | Value *StrSize = B.CreateCall(StrBufSizeFunc, StrBufSizeArgs); 402 | if (ALLOWED_OOB_BYTES) { 403 | IntegerType *Ty = cast(StrSize->getType()); 404 | StrSize = B.CreateAdd(StrSize, ConstantInt::get(Ty, ALLOWED_OOB_BYTES)); 405 | } 406 | NewEndPtr = B.CreateShl(B.CreateAdd(PtrVal, StrSize), AddressSpaceBits); 407 | } 408 | else if (type == LibPtr::RetSizeStatic) { 409 | Type *RetTy = F->getReturnType()->getPointerElementType(); 410 | uint64_t Sz = DL->getTypeStoreSize(RetTy); 411 | NewEndPtr = B.CreateShl(B.CreateAdd(PtrVal, B.getInt64(Sz)), AddressSpaceBits); 412 | } 413 | else if (type == LibPtr::CopyFromArg || type == LibPtr::PtrDiff) { 414 | IntegerType *PtrIntTy = getPtrIntTy(I->getContext()); 415 | Value *OrigPtrVal = B.CreatePtrToInt(CS.getArgOperand(arg), PtrIntTy); 416 | NewEndPtr = B.CreateAnd(OrigPtrVal, BOUND_MASK_HIGH); 417 | } 418 | 419 | Value *NewPtr = B.CreateIntToPtr(B.CreateOr(PtrVal, NewEndPtr), Ptr->getType()); 420 | for (User *U : Users) 421 | U->replaceUsesOfWith(Ptr, NewPtr); 422 | 423 | ++NLibCall; 424 | return true; 425 | } 426 | 427 | bool UboundBranch::instrumentPtrArith(GetElementPtrInst *Ptr) { 428 | if (SafeAlloc && !SafeAlloc->hasTag(Ptr)) 429 | return false; 430 | 431 | std::vector Users(Ptr->user_begin(), Ptr->user_end()); 432 | 433 | std::string Prefix = Ptr->hasName() ? Ptr->getName().str() + "." : ""; 434 | Type *PtrIntTy = getPtrIntTy(Ptr->getContext()); 435 | IRBuilder<> B(getInsertPointAfter(Ptr)); 436 | Value *BaseInt = B.CreatePtrToInt(Ptr->getPointerOperand(), PtrIntTy, Prefix + "baseint"); 437 | Value *BaseTag = B.CreateAnd(BaseInt, BOUND_MASK_HIGH, Prefix + "basetag"); 438 | Value *PtrInt = B.CreatePtrToInt(Ptr, PtrIntTy, Prefix + "int"); 439 | Value *Truncated = B.CreateAnd(PtrInt, getAddressSpaceMask(), Prefix + "truncated"); 440 | Value *Retagged = B.CreateOr(Truncated, BaseTag, Prefix + "retagged"); 441 | Value *NewPtr = B.CreateIntToPtr(Retagged, Ptr->getType(), Prefix + "newptr"); 442 | 443 | for (User *U : Users) 444 | U->replaceUsesOfWith(Ptr, NewPtr); 445 | 446 | ++NGep; 447 | return true; 448 | } 449 | 450 | /* 451 | * Replace NULL pointer with 0x0000000100000000 so that the end pointer can 452 | * never be mapped, but a zero-check on the metadata will produce a nonzero 453 | * upper bound. 454 | */ 455 | Constant *UboundBranch::getNullPtr(PointerType *Ty) { 456 | IntegerType *IntTy = IntegerType::get(Ty->getContext(), PointerBits); 457 | ConstantInt *IntVal = ConstantInt::get(IntTy, 1ULL << AddressSpaceBits); 458 | return ConstantExpr::getIntToPtr(IntVal, Ty); 459 | } 460 | -------------------------------------------------------------------------------- /patches/spec2006-gcc.patch: -------------------------------------------------------------------------------- 1 | diff --git a/benchspec/CPU2006/403.gcc/src/c-common.c b/benchspec/CPU2006/403.gcc/src/c-common.c 2 | index f9e5930..1d2d88a 100644 3 | --- a/benchspec/CPU2006/403.gcc/src/c-common.c 4 | +++ b/benchspec/CPU2006/403.gcc/src/c-common.c 5 | @@ -39,6 +39,10 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA 6 | #include "target.h" 7 | cpp_reader *parse_in; /* Declared in c-lex.h. */ 8 | 9 | +#ifdef DELTAPOINTERS 10 | +# include "source-instrumentation.h" 11 | +#endif 12 | + 13 | #undef WCHAR_TYPE_SIZE 14 | #define WCHAR_TYPE_SIZE TYPE_PRECISION (wchar_type_node) 15 | 16 | @@ -3315,10 +3319,19 @@ case_compare (k1, k2) 17 | { 18 | /* Consider a NULL key (such as arises with a `default' label) to be 19 | smaller than anything else. */ 20 | +#ifdef DELTAPOINTERS 21 | + splay_tree_key k1m = _mask_pointer((void*)k1); 22 | + splay_tree_key k2m = _mask_pointer((void*)k2); 23 | + if (!k1m) 24 | + return k2m ? -1 : 0; 25 | + else if (!k2m) 26 | + return k1m ? 1 : 0; 27 | +#else 28 | if (!k1) 29 | return k2 ? -1 : 0; 30 | else if (!k2) 31 | return k1 ? 1 : 0; 32 | +#endif 33 | 34 | return tree_int_cst_compare ((tree) k1, (tree) k2); 35 | } 36 | diff --git a/benchspec/CPU2006/403.gcc/src/ggc-page.c b/benchspec/CPU2006/403.gcc/src/ggc-page.c 37 | index d93a509..a257790 100644 38 | --- a/benchspec/CPU2006/403.gcc/src/ggc-page.c 39 | +++ b/benchspec/CPU2006/403.gcc/src/ggc-page.c 40 | @@ -114,7 +114,14 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA 41 | #define GGC_DEBUG_LEVEL (0) 42 | 43 | #ifndef HOST_BITS_PER_PTR 44 | -#define HOST_BITS_PER_PTR HOST_BITS_PER_LONG 45 | +# ifdef DELTAPOINTERS 46 | +# if ADDRSPACE_BITS > 32 47 | +# error "at most 32 address space bits are allowed for gcc due to a source patch incompatibility" 48 | +# endif 49 | +# define HOST_BITS_PER_PTR ADDRSPACE_BITS 50 | +# else 51 | +# define HOST_BITS_PER_PTR HOST_BITS_PER_LONG 52 | +# endif 53 | #endif 54 | 55 | 56 | diff --git a/benchspec/CPU2006/403.gcc/src/obstack.c b/benchspec/CPU2006/403.gcc/src/obstack.c 57 | index 97a8fa8..1ec9344 100644 58 | --- a/benchspec/CPU2006/403.gcc/src/obstack.c 59 | +++ b/benchspec/CPU2006/403.gcc/src/obstack.c 60 | @@ -46,6 +46,9 @@ 61 | #endif 62 | #endif 63 | 64 | +#if defined(DELTAPOINTERS) && defined(ELIDE_CODE) 65 | +# undef ELIDE_CODE 66 | +#endif 67 | 68 | #ifndef ELIDE_CODE 69 | 70 | diff --git a/benchspec/CPU2006/403.gcc/src/obstack.h b/benchspec/CPU2006/403.gcc/src/obstack.h 71 | index f91c6e1..a9cf85c 100644 72 | --- a/benchspec/CPU2006/403.gcc/src/obstack.h 73 | +++ b/benchspec/CPU2006/403.gcc/src/obstack.h 74 | @@ -114,6 +114,10 @@ Summary: 75 | extern "C" { 76 | #endif 77 | 78 | +#ifdef DELTAPOINTERS 79 | +# include "source-instrumentation.h" 80 | +#endif 81 | + 82 | /* We use subtraction of (char *) 0 instead of casting to int 83 | because on word-addressable machines a simple cast to int 84 | may ignore the byte-within-word field of the pointer. */ 85 | @@ -465,12 +469,40 @@ __extension__ \ 86 | obstack_grow0 (__h, (where), (length)); \ 87 | obstack_finish (__h); }) 88 | 89 | +#ifdef DELTAPOINTERS 90 | +/* The alignment casts do a NULL pointer addition that overflows the tag, so we 91 | + * manually preserve the pointer tag. 92 | + * We could solve this in other ways as well, e.g. by not instrumenting null 93 | + * pointers, changing the cast macro, and assuming in our pass that pointer ANDs 94 | + * are always alignment and should thus preserve the tag. */ 95 | + 96 | /* The local variable is named __o1 to avoid a name conflict 97 | when obstack_blank is called. */ 98 | # define obstack_finish(OBSTACK) \ 99 | __extension__ \ 100 | ({ struct obstack *__o1 = (OBSTACK); \ 101 | void *value; \ 102 | + uintptr_t tag; \ 103 | + value = (void *) __o1->object_base; \ 104 | + if (__o1->next_free == value) \ 105 | + __o1->maybe_empty_object = 1; \ 106 | + tag = _tag_of(__o1->next_free); \ 107 | + __o1->next_free \ 108 | + = __INT_TO_PTR ((__PTR_TO_INT (__o1->next_free)+__o1->alignment_mask)\ 109 | + & ~ (__o1->alignment_mask)); \ 110 | + __o1->next_free = _tag_pointer(_mask_pointer(__o1->next_free), tag); \ 111 | + if (__o1->next_free - (char *)__o1->chunk \ 112 | + > __o1->chunk_limit - (char *)__o1->chunk) \ 113 | + __o1->next_free = __o1->chunk_limit; \ 114 | + __o1->object_base = __o1->next_free; \ 115 | + value; }) 116 | + 117 | +#else 118 | + 119 | +# define obstack_finish(OBSTACK) \ 120 | +__extension__ \ 121 | +({ struct obstack *__o1 = (OBSTACK); \ 122 | + void *value; \ 123 | value = (void *) __o1->object_base; \ 124 | if (__o1->next_free == value) \ 125 | __o1->maybe_empty_object = 1; \ 126 | @@ -483,6 +515,8 @@ __extension__ \ 127 | __o1->object_base = __o1->next_free; \ 128 | value; }) 129 | 130 | +#endif /* DELTAPOINTERS */ 131 | + 132 | # define obstack_free(OBSTACK, OBJ) \ 133 | __extension__ \ 134 | ({ struct obstack *__o = (OBSTACK); \ 135 | -------------------------------------------------------------------------------- /patches/spec2006-h264ref-sizetagprop-BCBP.patch: -------------------------------------------------------------------------------- 1 | diff --git a/benchspec/CPU2006/464.h264ref/src/biariencode.c b/benchspec/CPU2006/464.h264ref/src/biariencode.c 2 | index 28975c5..47ed75b 100644 3 | --- a/benchspec/CPU2006/464.h264ref/src/biariencode.c 4 | +++ b/benchspec/CPU2006/464.h264ref/src/biariencode.c 5 | @@ -320,6 +320,9 @@ void biari_encode_symbol_final(EncodingEnvironmentPtr eep, signed short symbol) 6 | } 7 | 8 | 9 | +#ifdef DELTAPOINTERS 10 | +#include "source-instrumentation.h" 11 | +#endif 12 | 13 | /*! 14 | ************************************************************************ 15 | @@ -331,6 +334,13 @@ void biari_init_context (BiContextTypePtr ctx, const int* ini) 16 | { 17 | int pstate; 18 | 19 | +#ifdef DELTAPOINTERS 20 | + /* Mask out ini manually, as for INIT_BCBP_I it will go out-of-bounds 21 | + * legitimately because NUM_BLOCK_TYPE (=10) is larger than the array it will 22 | + * index (8). */ 23 | + ini = (const int*)_mask_pointer(ini); 24 | +#endif 25 | + 26 | pstate = ((ini[0]* max(0, img->qp)) >> 4) + ini[1]; 27 | pstate = min (max ( 1, pstate), 126); 28 | 29 | -------------------------------------------------------------------------------- /patches/spec2006-perlbench.patch: -------------------------------------------------------------------------------- 1 | diff --git a/benchspec/CPU2006/400.perlbench/src/mg.c b/benchspec/CPU2006/400.perlbench/src/mg.c 2 | index 64e7781..2847cad 100644 3 | --- a/benchspec/CPU2006/400.perlbench/src/mg.c 4 | +++ b/benchspec/CPU2006/400.perlbench/src/mg.c 5 | @@ -1072,6 +1072,8 @@ Perl_magic_set_all_env(pTHX_ SV *sv, MAGIC *mg) 6 | return 0; 7 | } 8 | 9 | +#include "sizetags-mask.h" 10 | + 11 | int 12 | Perl_magic_clear_all_env(pTHX_ SV *sv, MAGIC *mg) 13 | { 14 | @@ -1093,14 +1095,14 @@ Perl_magic_clear_all_env(pTHX_ SV *sv, MAGIC *mg) 15 | I32 i; 16 | 17 | if (environ == PL_origenviron) 18 | - environ = (char**)safesysmalloc(sizeof(char*)); 19 | + environ = (char**)_MASK(safesysmalloc(sizeof(char*))); 20 | else 21 | for (i = 0; environ[i]; i++) 22 | safesysfree(environ[i]); 23 | } 24 | # endif /* PERL_USE_SAFE_PUTENV */ 25 | 26 | - environ[0] = Nullch; 27 | + environ[0] = _MASK(Nullch); 28 | } 29 | # endif /* USE_ENVIRON_ARRAY */ 30 | # endif /* PERL_IMPLICIT_SYS || WIN32 */ 31 | diff --git a/benchspec/CPU2006/400.perlbench/src/perl.c b/benchspec/CPU2006/400.perlbench/src/perl.c 32 | index 9085e57..be58c80 100644 33 | --- a/benchspec/CPU2006/400.perlbench/src/perl.c 34 | +++ b/benchspec/CPU2006/400.perlbench/src/perl.c 35 | @@ -4292,6 +4292,8 @@ S_set_caret_X(pTHX) { 36 | } 37 | } 38 | 39 | +#include "sizetags-mask.h" 40 | + 41 | STATIC void 42 | S_init_postdump_symbols(pTHX_ register int argc, register char **argv, register char **env) 43 | { 44 | @@ -4341,7 +4343,7 @@ S_init_postdump_symbols(pTHX_ register int argc, register char **argv, register 45 | # endif 46 | ) 47 | { 48 | - environ[0] = Nullch; 49 | + environ[0] = _MASK(Nullch); 50 | } 51 | if (env) { 52 | char** origenv = environ; 53 | diff --git a/benchspec/CPU2006/400.perlbench/src/perl.h b/benchspec/CPU2006/400.perlbench/src/perl.h 54 | index bb38b25..1f9fb54 100644 55 | --- a/benchspec/CPU2006/400.perlbench/src/perl.h 56 | +++ b/benchspec/CPU2006/400.perlbench/src/perl.h 57 | @@ -1414,7 +1414,10 @@ typedef UVTYPE UV; 58 | #endif 59 | #define NUM2PTR(any,d) (any)(PTRV)(d) 60 | #define PTR2IV(p) INT2PTR(IV,p) 61 | -#define PTR2UV(p) INT2PTR(UV,p) 62 | +#include "sizetags-mask.h" 63 | +// mask PTR2UV pointers which are passed to sprintf-like functions as ints to be 64 | +// used for pointer stringification (sprintf will just see an int) 65 | +#define PTR2UV(p) INT2PTR(UV,_MASK(p)) 66 | #define PTR2NV(p) NUM2PTR(NV,p) 67 | #if PTRSIZE == LONGSIZE 68 | # define PTR2ul(p) (unsigned long)(p) 69 | diff --git a/benchspec/CPU2006/400.perlbench/src/sizetags-mask.h b/benchspec/CPU2006/400.perlbench/src/sizetags-mask.h 70 | new file mode 100644 71 | index 0000000..eee377c 72 | --- /dev/null 73 | +++ b/benchspec/CPU2006/400.perlbench/src/sizetags-mask.h 74 | @@ -0,0 +1,11 @@ 75 | +#ifndef DELTAPOINTERS_MASK_H 76 | +#define DELTAPOINTERS_MASK_H 77 | + 78 | +#ifdef DELTAPOINTERS 79 | +# include "source-instrumentation.h" 80 | +# define _MASK(p) _mask_pointer(p) 81 | +#else 82 | +# define _MASK(p) (p) 83 | +#endif 84 | + 85 | +#endif // DELTAPOINTERS_MASK_H 86 | diff --git a/benchspec/CPU2006/400.perlbench/src/util.c b/benchspec/CPU2006/400.perlbench/src/util.c 87 | index fc959bb..dbda6ee 100644 88 | --- a/benchspec/CPU2006/400.perlbench/src/util.c 89 | +++ b/benchspec/CPU2006/400.perlbench/src/util.c 90 | @@ -1408,6 +1408,8 @@ Perl_vwarner(pTHX_ U32 err, const char* pat, va_list* args) 91 | Copy(val, s+(nlen+1), vlen, char); \ 92 | *(s+(nlen+1+vlen)) = '\0' 93 | 94 | +#include "sizetags-mask.h" 95 | + 96 | #ifdef USE_ENVIRON_ARRAY 97 | /* VMS' my_setenv() is in vms.c */ 98 | #if !defined(WIN32) && !defined(NETWARE) 99 | @@ -1432,13 +1434,13 @@ Perl_my_setenv(pTHX_ char *nam, char *val) 100 | 101 | /*SUPPRESS 530*/ 102 | for (max = i; environ[max]; max++) ; 103 | - tmpenv = (char**)safesysmalloc((max+2) * sizeof(char*)); 104 | + tmpenv = (char**)_MASK(safesysmalloc((max+2) * sizeof(char*))); 105 | for (j=0; j::move(delta); 18 | for (elem = last(); elem; elem = prev(elem)) 19 | if (elem != first()) 20 | +#ifdef DELTAPOINTERS 21 | + elem->prev() = reinterpret_cast( 22 | + UNSAFE_ARITH(elem->prev(), delta)); 23 | +#else 24 | elem->prev() = reinterpret_cast( 25 | reinterpret_cast(elem->prev()) + delta); 26 | +#endif 27 | } 28 | } 29 | 30 | diff --git a/benchspec/CPU2006/450.soplex/src/islist.h b/benchspec/CPU2006/450.soplex/src/islist.h 31 | index d8d2485..4c3f48a 100644 32 | --- a/benchspec/CPU2006/450.soplex/src/islist.h 33 | +++ b/benchspec/CPU2006/450.soplex/src/islist.h 34 | @@ -28,6 +28,10 @@ 35 | 36 | #include "message.h" 37 | 38 | +#ifdef DELTAPOINTERS 39 | +#include "source-instrumentation.h" 40 | +#endif 41 | + 42 | namespace soplex 43 | { 44 | /**@brief Elements for #IsList%s. 45 | @@ -356,11 +360,19 @@ public: 46 | if (the_first) 47 | { 48 | T* elem; 49 | +#ifdef DELTAPOINTERS 50 | + the_last = reinterpret_cast(UNSAFE_ARITH(the_last, delta)); 51 | + the_first = reinterpret_cast(UNSAFE_ARITH(the_first, delta)); 52 | + for (elem = first(); elem; elem = next(elem)) 53 | + if (elem != last()) 54 | + elem->next() = reinterpret_cast(UNSAFE_ARITH(elem->next(), delta)); 55 | +#else 56 | the_last = reinterpret_cast(reinterpret_cast(the_last) + delta); 57 | the_first = reinterpret_cast(reinterpret_cast(the_first) + delta); 58 | for (elem = first(); elem; elem = next(elem)) 59 | if (elem != last()) 60 | elem->next() = reinterpret_cast(reinterpret_cast(elem->next()) + delta); 61 | +#endif 62 | } 63 | } 64 | 65 | diff --git a/benchspec/CPU2006/450.soplex/src/svector.h b/benchspec/CPU2006/450.soplex/src/svector.h 66 | index c5f562c..2b8f076 100644 67 | --- a/benchspec/CPU2006/450.soplex/src/svector.h 68 | +++ b/benchspec/CPU2006/450.soplex/src/svector.h 69 | @@ -30,6 +30,10 @@ 70 | #include "spxdefines.h" 71 | #include "vector.h" 72 | 73 | +#ifdef DELTAPOINTERS 74 | +#include "source-instrumentation.h" 75 | +#endif 76 | + 77 | namespace soplex 78 | { 79 | /**@brief Sparse vectors. 80 | @@ -351,7 +355,13 @@ public: 81 | { 82 | assert(elmem != 0); 83 | elmem->val = 0; // for purify to shut up 84 | +#ifdef DELTAPOINTERS_UBOUND_BRANCH 85 | + m_elem = (Element*)_tag_pointer(&elmem[1], (uintptr_t)(&elmem[n])); 86 | +#elif defined(DELTAPOINTERS) 87 | + m_elem = (Element*)_tag_pointer(&elmem[1], -(n - 1) * sizeof (Element)); 88 | +#else 89 | m_elem = &(elmem[1]); 90 | +#endif 91 | set_size( 0 ); 92 | set_max ( n - 1 ); 93 | } 94 | diff --git a/benchspec/CPU2006/450.soplex/src/svset.cc b/benchspec/CPU2006/450.soplex/src/svset.cc 95 | index 3ea9872..66efead 100644 96 | --- a/benchspec/CPU2006/450.soplex/src/svset.cc 97 | +++ b/benchspec/CPU2006/450.soplex/src/svset.cc 98 | @@ -23,6 +23,10 @@ 99 | #include "svset.h" 100 | #include "message.h" 101 | 102 | +#ifdef DELTAPOINTERS 103 | +#include "source-instrumentation.h" 104 | +#endif 105 | + 106 | namespace soplex 107 | { 108 | 109 | @@ -262,7 +266,11 @@ void SVSet::memRemax(int newmax) 110 | { 111 | for (DLPSV* ps = list.first(); ps; ps = list.next(ps)) 112 | { 113 | +#ifdef DELTAPOINTERS 114 | + SVector::Element * info = reinterpret_cast(UNSAFE_ARITH(ps->mem(), delta)); 115 | +#else 116 | SVector::Element * info = reinterpret_cast(reinterpret_cast(ps->mem()) + delta); 117 | +#endif 118 | int sz = info->idx; 119 | int l_max = int( info->val ); 120 | assert(l_max >= sz ); 121 | @@ -339,9 +347,15 @@ SVSet& SVSet::operator=(const SVSet& rhs) 122 | { 123 | newps = & set[ rhs.number(ps) ]; 124 | list.append(newps); 125 | +#ifdef DELTAPOINTERS 126 | + newps->setMem(ps->max() + 1, 127 | + reinterpret_cast( 128 | + UNSAFE_ARITH(ps->mem(), delta))); 129 | +#else 130 | newps->setMem(ps->max() + 1, 131 | reinterpret_cast( 132 | reinterpret_cast(ps->mem()) + delta)); 133 | +#endif 134 | newps->set_size( ps->size() ); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /runtime/Makefile: -------------------------------------------------------------------------------- 1 | # local build config for debugging (overwritten in deps.py) 2 | OBJDIR ?= ./obj 3 | LLVM_VERSION ?= 3.8.0 4 | ADDRSPACE_BITS ?= 32 5 | RUNTIME_STATS ?= false 6 | OVERFLOW_BIT ?= false 7 | DEBUG ?= false 8 | 9 | #PATHCLANGINCLUDE ?= ../build/packages/llvm-3.8.0/install/lib/clang/3.8.0/include 10 | PKG_CONFIG := python3 ../setup.py pkg-config 11 | LLVM_PREFIX := `$(PKG_CONFIG) llvm-$(LLVM_VERSION) --prefix` 12 | PATHCLANGINCLUDE := $(LLVM_PREFIX)/lib/clang/$(LLVM_VERSION)/include 13 | BUILTIN_CFLAGS := `$(PKG_CONFIG) llvm-passes-builtin-$(LLVM_VERSION) --runtime-cflags` 14 | 15 | CC := $(LLVM_PREFIX)/bin/clang 16 | CFLAGS := -flto -fpic -Wall -Wextra -march=native -I$(PATHCLANGINCLUDE) \ 17 | -DADDRSPACE_BITS=$(ADDRSPACE_BITS) $(BUILTIN_CFLAGS) 18 | ifeq ($(OVERFLOW_BIT), true) 19 | CFLAGS += -DOVERFLOW_BIT 20 | endif 21 | ifeq ($(RUNTIME_STATS), true) 22 | CFLAGS += -DRUNTIME_STATS 23 | endif 24 | ifeq ($(DEBUG), true) 25 | CFLAGS += -DDEBUG 26 | endif 27 | 28 | LIB := libdeltatags.a 29 | OBJS := libptrret mask debug runtimestats mask-wrappers 30 | OBJS := $(patsubst %,$(OBJDIR)/%.o,$(OBJS)) 31 | 32 | .PHONY: all clean 33 | 34 | all: $(OBJDIR)/$(LIB) 35 | 36 | $(OBJDIR)/$(LIB): $(OBJS) 37 | ar rcs $@ $^ 38 | 39 | $(OBJDIR)/%.o: %.c addrspace.h | $(OBJDIR) 40 | $(CC) -c $(CFLAGS) -o $@ $< 41 | 42 | $(OBJDIR): 43 | mkdir -p $@ 44 | 45 | clean: 46 | rm -rf $(OBJDIR) 47 | -------------------------------------------------------------------------------- /runtime/addrspace.h: -------------------------------------------------------------------------------- 1 | #ifndef _ADDRSPACE_H 2 | #define _ADDRSPACE_H 3 | 4 | #ifndef ADDRSPACE_BITS 5 | # define ADDRSPACE_BITS (32) 6 | #endif 7 | 8 | #define POINTER_BITS (sizeof (void*) * 8) 9 | #define ADDRSPACE_MASK ((1ULL << ADDRSPACE_BITS) - 1) 10 | #define BOUND_SHIFT (ADDRSPACE_BITS) 11 | 12 | #ifdef OVERFLOW_BIT 13 | # define BOUND_BITS (POINTER_BITS - BOUND_SHIFT - 1) 14 | # define OVERFLOW_MASK (1ULL << (POINTER_BITS - 1)) 15 | #else 16 | # define BOUND_BITS (POINTER_BITS - BOUND_SHIFT) 17 | # define OVERFLOW_MASK (0ULL) 18 | #endif 19 | 20 | #define BOUND_MASK_LOW ((1ULL << BOUND_BITS) - 1) 21 | #define BOUND_MASK_HIGH (BOUND_MASK_LOW << BOUND_SHIFT) 22 | //#define PRESERVE_MASK (~BOUND_MASK_HIGH) 23 | #define PRESERVE_MASK (ADDRSPACE_MASK | OVERFLOW_MASK) 24 | 25 | #endif /* _ADDRSPACE_H */ 26 | -------------------------------------------------------------------------------- /runtime/debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "noinstrument.h" 9 | 10 | #ifdef DEBUG 11 | 12 | #define LOGPATH_ENVVAR "DEBUGNEGARITH_PATH" 13 | #define DEFAULT_LOGPATH "debugnegarith.out" 14 | 15 | int NOINSTRUMENT(outfile) = 2; 16 | 17 | void NOINSTRUMENT(check_neg_arith)(uint64_t gep_num, void *ptr, long offset, int is_constant_gep) { 18 | uintptr_t upper; 19 | if (offset >= 0) 20 | return; 21 | upper = (uintptr_t)ptr >> 32; 22 | if (upper < -1 * offset) 23 | dprintf(NOINSTRUMENT(outfile), "UNSAFE GEP %llu: %p + %ld (%d)\n", gep_num, ptr, offset, is_constant_gep); 24 | 25 | /* This is interesting but *a lot* */ 26 | #if 0 27 | else if (offset < 0 && !is_constant_gep) 28 | dprintf(NOINSTRUMENT(outfile), "DYN NEG SAFE GEP %llu: %p + %ld\n", gep_num, ptr, offset, is_constant_gep); 29 | #endif 30 | } 31 | 32 | #if 1 33 | __attribute__((constructor(1000))) 34 | void NOINSTRUMENT(init_debugnegarith)() { 35 | char *logpath = getenv(LOGPATH_ENVVAR); 36 | if (logpath == NULL) 37 | logpath = DEFAULT_LOGPATH; 38 | NOINSTRUMENT(outfile) = open(logpath, O_WRONLY | O_TRUNC | O_CREAT, 0644); 39 | } 40 | 41 | __attribute__((destructor)) 42 | void NOINSTRUMENT(finalize_debugnegarith)() { 43 | close(NOINSTRUMENT(outfile)); 44 | } 45 | #endif 46 | 47 | #endif /* DEBUG */ 48 | -------------------------------------------------------------------------------- /runtime/libptrret.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "noinstrument.h" 7 | #include "addrspace.h" 8 | 9 | /* Special version of strlen that determines size of buffer, i.e. strlen + 1 or 10 | * 0 if given buffer is NULL. */ 11 | size_t NOINSTRUMENT(strsize_nullsafe)(char *str) { 12 | return str ? strlen(str) + 1 : 0; 13 | } 14 | 15 | char *NOINSTRUMENT(strtok)(char *str, const char *delim) { 16 | static uintptr_t base_ptr; 17 | static size_t base_size; 18 | char *maskedptr = (char*)((uintptr_t)str & PRESERVE_MASK); 19 | char *maskeddelim = (char*)((uintptr_t)delim & PRESERVE_MASK); 20 | 21 | if (maskedptr) { 22 | base_ptr = (uintptr_t)maskedptr; 23 | base_size = (uintptr_t)str >> ADDRSPACE_BITS; 24 | } 25 | 26 | uintptr_t strtok_ret = (uintptr_t)strtok(maskedptr, maskeddelim); 27 | if (!strtok_ret) 28 | return NULL; 29 | 30 | /* If the base pointer had no metadata, don't add it not to break stuff */ 31 | if (!base_size) 32 | return (char*)strtok_ret; 33 | 34 | uintptr_t newsize = (base_size + (strtok_ret - base_ptr)) << ADDRSPACE_BITS; 35 | return (char*)(strtok_ret | newsize); 36 | } 37 | 38 | char *NOINSTRUMENT(strtok_ubound)(char *str, const char *delim) { 39 | static uintptr_t endptr; 40 | char *maskedptr = (char*)((uintptr_t)str & ADDRSPACE_MASK); 41 | char *maskeddelim = (char*)((uintptr_t)delim & ADDRSPACE_MASK); 42 | 43 | if (maskedptr) 44 | endptr = (uintptr_t)str & BOUND_MASK_HIGH; 45 | 46 | uintptr_t strtok_ret = (uintptr_t)strtok(maskedptr, maskeddelim); 47 | if (!strtok_ret) 48 | return endptr; // instrumented NULL pointer 49 | 50 | return (char*)(strtok_ret | endptr); 51 | } 52 | 53 | /* Fix program taking the address of strcmp and calling it indirectly later. 54 | * In particular, fixes 403.gcc's splay-tree cmp func. 55 | * Taken from musl. */ 56 | #ifdef strcmp 57 | #undef strcmp 58 | #endif 59 | int strcmp(const char *l, const char *r) 60 | { 61 | for (; *l==*r && *l; l++, r++); 62 | return *(unsigned char *)l - *(unsigned char *)r; 63 | } 64 | -------------------------------------------------------------------------------- /runtime/mask-wrappers.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "noinstrument.h" 10 | #include "addrspace.h" 11 | #include "source-instrumentation.h" 12 | 13 | #define NOINLINE __attribute__((noinline)) 14 | #define ALWAYS_INLINE __attribute__((always_inline)) 15 | #define MASKWITH(p, mask) ((typeof(p)) ((unsigned long long)(p) & mask)) 16 | #define MASK(p) MASKWITH(p, PRESERVE_MASK) 17 | #define MASK_EXEC_ARRAY(p) \ 18 | ((typeof(p)) NOINSTRUMENT(mask_exec_array)((char**)p)) 19 | #define unlikely(x) __builtin_expect(!!(x), 0) 20 | 21 | static char **NOINSTRUMENT(mask_exec_array)(char *arr[]) { 22 | arr = MASK(arr); 23 | 24 | int len = 0; 25 | while (MASKWITH(arr[len++], ADDRSPACE_MASK) != NULL); 26 | 27 | char **buf = (char**)malloc(len * sizeof (char*)); 28 | 29 | int i = 0; 30 | do { 31 | buf[i] = MASK(arr[i]); 32 | } while (buf[i++] != NULL); 33 | 34 | return buf; 35 | } 36 | 37 | static const struct iovec *NOINSTRUMENT(mask_iovec)(const struct iovec *iov, 38 | int iovcnt) { 39 | const struct iovec *iov_old = MASK(iov); 40 | struct iovec *iov_new = calloc(iovcnt, sizeof(struct iovec)); 41 | int i; 42 | for (i = 0; i < iovcnt; i++) { 43 | iov_new[i].iov_len = iov_old[i].iov_len; 44 | iov_new[i].iov_base = MASK(iov_old[i].iov_base); 45 | } 46 | return iov_new; 47 | } 48 | 49 | NOINLINE int NOINSTRUMENT(execv_mask)(const char *path, char *const argv[]) { 50 | return execv(MASK(path), MASK_EXEC_ARRAY(argv)); 51 | } 52 | 53 | NOINLINE int NOINSTRUMENT(execvp_mask)(const char *file, char *const argv[]) { 54 | return execvp(MASK(file), MASK_EXEC_ARRAY(argv)); 55 | } 56 | 57 | NOINLINE int NOINSTRUMENT(execvpe_mask)(const char *file, char *const argv[], 58 | char *const envp[]) { 59 | return execvpe(MASK(file), MASK_EXEC_ARRAY(argv), MASK_EXEC_ARRAY(envp)); 60 | } 61 | 62 | NOINLINE int NOINSTRUMENT(execve_mask)(const char *filename, char *const argv[], 63 | char *const envp[]) { 64 | return execve(MASK(filename), MASK_EXEC_ARRAY(argv), MASK_EXEC_ARRAY(envp)); 65 | } 66 | 67 | NOINLINE int NOINSTRUMENT(writev_mask)(int fd, const struct iovec *iov, 68 | int iovcnt) { 69 | const struct iovec *iov_masked = NOINSTRUMENT(mask_iovec)(iov, iovcnt); 70 | return writev(fd, iov_masked, iovcnt); 71 | } 72 | 73 | ALWAYS_INLINE bool NOINSTRUMENT(is_oob)(const void *ptr, uint64_t size) { 74 | uintptr_t ptrint = (uintptr_t)ptr & ADDRSPACE_MASK; 75 | uintptr_t ubnd = (uintptr_t)ptr >> ADDRSPACE_BITS; 76 | return ubnd != 0 && ptrint + size > ubnd; 77 | } 78 | 79 | /* 80 | * Source instrumentation implementations. These are NOINLINE to avoid inlining 81 | * by LTO, which would get rif of the noinstrument function names. Instead the 82 | * names start with "_inline_" (see definitions in source-instrumentation.h) 83 | * which is recognized by our custom inlining pass. 84 | */ 85 | 86 | NOINLINE void *_tag_pointer(const void *ptr, uintptr_t tag) { 87 | uintptr_t ptrint = (uintptr_t)ptr & ADDRSPACE_MASK; 88 | return (void*)(ptrint | ((tag & BOUND_MASK_LOW) << BOUND_SHIFT)); 89 | } 90 | 91 | NOINLINE void *_mask_pointer(const void *ptr) { 92 | return (void*)((uintptr_t)ptr & ADDRSPACE_MASK); 93 | } 94 | 95 | NOINLINE uintptr_t _tag_of(const void *ptr) { 96 | return (uintptr_t)ptr >> BOUND_SHIFT; 97 | } 98 | 99 | NOINLINE void *_copy_tag(const void *ptr, const void *tagptr) { 100 | uintptr_t addr = (uintptr_t)ptr & ADDRSPACE_MASK; 101 | uintptr_t tag = (uintptr_t)tagptr & BOUND_MASK_HIGH; 102 | return (void*)(addr | tag); 103 | } 104 | 105 | NOINLINE void *_ptr_arith(const void *ptr, int64_t delta) { 106 | //return _copy_tag((char*)ptr + delta, ptr); 107 | return (void*)((char*)ptr + delta); 108 | } 109 | 110 | NOINLINE uintptr_t _leak_pointer(const void *ptr) { 111 | return (uintptr_t)ptr; 112 | } 113 | 114 | NOINLINE uintptr_t _delta_tag(unsigned long long size) { 115 | return (1ULL << BOUND_BITS) - size; 116 | } 117 | -------------------------------------------------------------------------------- /runtime/mask.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "noinstrument.h" 5 | #include "addrspace.h" 6 | 7 | #define UNUSED __attribute__((unused)) 8 | #define ALWAYS_INLINE __attribute__((always_inline)) 9 | 10 | #ifdef __x86_64__ 11 | 12 | #include "x86intrin.h" 13 | 14 | #ifdef OVERFLOW_BIT 15 | 16 | ALWAYS_INLINE uintptr_t NOINSTRUMENT(mask_pointer_pext_reg)(uintptr_t ptr, bool preserve UNUSED) { 17 | return _pext_u64(ptr, PRESERVE_MASK); 18 | } 19 | 20 | volatile uint64_t NOINSTRUMENT(pext_mask) = PRESERVE_MASK; 21 | 22 | ALWAYS_INLINE uintptr_t NOINSTRUMENT(mask_pointer_pext_glob)(uintptr_t ptr, bool preserve UNUSED) { 23 | return _pext_u64(ptr, NOINSTRUMENT(pext_mask)); 24 | } 25 | 26 | #else /* OVERFLOW_BIT */ 27 | 28 | ALWAYS_INLINE uintptr_t NOINSTRUMENT(mask_pointer_bzhi)(uintptr_t ptr, bool preserve UNUSED) { 29 | return _bzhi_u64(ptr, ADDRSPACE_BITS); 30 | } 31 | 32 | #endif /* !OVERFLOW_BIT */ 33 | 34 | #endif /* __x86_64__ */ 35 | 36 | 37 | 38 | 39 | /* Alternative implementations of masking to test their performance. */ 40 | #if 0 41 | ALWAYS_INLINE uintptr_t NOINSTRUMENT(mask_pointer)(uintptr_t ptr, bool preserve) { 42 | return ptr & (preserve ? PRESERVE_MASK : ADDRSPACE_MASK); 43 | } 44 | 45 | uintptr_t NOINSTRUMENT(mask_pointer)(uintptr_t ptr, bool preserve) { 46 | if (preserve) { 47 | asm ("rol %0" : "+a"(ptr) ::); 48 | ptr &= ADDRSPACE_MASK; 49 | asm ("ror %0" : "+a"(ptr) ::); 50 | return ptr; 51 | } 52 | return ptr & ADDRSPACE_MASK; 53 | } 54 | 55 | static inline uintptr_t rot(uintptr_t input, unsigned bits) { 56 | return (input >> bits) | (input << ((sizeof (input) << 3) - bits)); 57 | } 58 | 59 | uintptr_t NOINSTRUMENT(mask_pointer)(uintptr_t ptr, bool preserve) { 60 | if (preserve) 61 | return rot(rot(ptr, 63) & ADDRSPACE_MASK, 1); 62 | return ptr & ADDRSPACE_MASK; 63 | } 64 | 65 | uintptr_t NOINSTRUMENT(mask_pointer)(uintptr_t ptr, bool preserve) { 66 | if (preserve) { 67 | asm ("test %%rax, %%rax\n\t" 68 | "cmovs %[zero], %%eax" 69 | : "+a"(ptr) 70 | : [zero] "r"(0) 71 | : "cc"); 72 | return ptr; 73 | } 74 | return ptr & ADDRSPACE_MASK; 75 | } 76 | 77 | uintptr_t NOINSTRUMENT(mask_pointer)(uintptr_t ptr, bool preserve) { 78 | if (preserve) { 79 | register uintptr_t tmp; 80 | asm ("movabs $0x80000000ffffffff, %[tmp]\n\t" 81 | "and %[tmp], %[ptr]" 82 | : [ptr] "+r"(ptr), [tmp] "=r"(tmp) 83 | : 84 | : "cc"); 85 | return ptr; 86 | } 87 | return ptr & ADDRSPACE_MASK; 88 | } 89 | 90 | uintptr_t NOINSTRUMENT(mask_pointer)(uintptr_t ptr, bool preserve) { 91 | if (preserve) { 92 | asm ("test %[ptr], %[ptr]\n\t" 93 | "js ." 94 | : 95 | : [ptr] "r"(ptr) 96 | : "cc"); 97 | return ptr; 98 | } 99 | return ptr & ADDRSPACE_MASK; 100 | } 101 | #endif 102 | -------------------------------------------------------------------------------- /runtime/runtimestats.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "noinstrument.h" 7 | 8 | #ifdef RUNTIME_STATS 9 | 10 | #define OUTFILE_BASE_ENVVAR "RTS_OUTFILE_BASE" 11 | #define OUTFILE_BASE_DEFAULT "rts_" 12 | 13 | unsigned long NOINSTRUMENT(stat_gep_total); 14 | unsigned long NOINSTRUMENT(stat_gep_dyn); 15 | unsigned long NOINSTRUMENT(stat_gep_dyn_neg); 16 | unsigned long NOINSTRUMENT(stat_gep_const_neg); 17 | unsigned long NOINSTRUMENT(stat_gep_nometa); 18 | 19 | unsigned long NOINSTRUMENT(stat_loads_total); 20 | unsigned long NOINSTRUMENT(stat_loads_nometa); 21 | unsigned long NOINSTRUMENT(stat_stores_total); 22 | unsigned long NOINSTRUMENT(stat_stores_nometa); 23 | 24 | 25 | __attribute__((constructor(1000))) 26 | void NOINSTRUMENT(init_rts)() { 27 | NOINSTRUMENT(stat_gep_total) = 0; 28 | NOINSTRUMENT(stat_gep_dyn) = 0; 29 | NOINSTRUMENT(stat_gep_dyn_neg) = 0; 30 | NOINSTRUMENT(stat_gep_const_neg) = 0; 31 | NOINSTRUMENT(stat_loads_total) = 0; 32 | NOINSTRUMENT(stat_loads_nometa) = 0; 33 | NOINSTRUMENT(stat_stores_total) = 0; 34 | NOINSTRUMENT(stat_stores_nometa) = 0; 35 | } 36 | 37 | __attribute__((destructor)) 38 | void NOINSTRUMENT(finalize_rts)() { 39 | char *outfile_base = getenv(OUTFILE_BASE_ENVVAR); 40 | if (outfile_base == NULL) 41 | outfile_base = OUTFILE_BASE_DEFAULT; 42 | 43 | /* Determine unused filename, as SPEC has multiple runs/inputsets. */ 44 | char outfile[255]; 45 | unsigned num = 0; 46 | do { 47 | snprintf(outfile, sizeof(outfile), "%s%d", outfile_base, num); 48 | num++; 49 | } while (access(outfile, F_OK) != -1); 50 | 51 | 52 | FILE *f = fopen(outfile, "w"); 53 | fprintf(f, "gep_total: %lu\n", NOINSTRUMENT(stat_gep_total)); 54 | fprintf(f, "gep_dyn: %lu\n", NOINSTRUMENT(stat_gep_dyn)); 55 | fprintf(f, "gep_dyn_neg: %lu\n", NOINSTRUMENT(stat_gep_dyn_neg)); 56 | fprintf(f, "gep_const_neg: %lu\n", NOINSTRUMENT(stat_gep_const_neg)); 57 | fprintf(f, "gep_nometa: %lu\n", NOINSTRUMENT(stat_gep_nometa)); 58 | 59 | fprintf(f, "loads_total: %lu\n", NOINSTRUMENT(stat_loads_total)); 60 | fprintf(f, "loads_nometa: %lu\n", NOINSTRUMENT(stat_loads_nometa)); 61 | fprintf(f, "stores_total: %lu\n", NOINSTRUMENT(stat_stores_total)); 62 | fprintf(f, "stores_nometa: %lu\n", NOINSTRUMENT(stat_stores_nometa)); 63 | fclose(f); 64 | } 65 | 66 | void NOINSTRUMENT(rts_gep)(void *ptr, int64_t off, int is_constant) { 67 | NOINSTRUMENT(stat_gep_total)++; 68 | if (!is_constant) 69 | NOINSTRUMENT(stat_gep_dyn)++; 70 | if (!is_constant && off < 0) 71 | NOINSTRUMENT(stat_gep_dyn_neg)++; 72 | if (is_constant && off < 0) 73 | NOINSTRUMENT(stat_gep_const_neg)++; 74 | 75 | if (((uintptr_t)ptr & (0xffffffffULL << 32)) == 0) 76 | NOINSTRUMENT(stat_gep_nometa)++; 77 | } 78 | 79 | void NOINSTRUMENT(rts_load)(void *ptr) { 80 | NOINSTRUMENT(stat_loads_total)++; 81 | 82 | if (((uintptr_t)ptr & (0xffffffffULL << 32)) == 0) 83 | NOINSTRUMENT(stat_loads_nometa)++; 84 | } 85 | 86 | void NOINSTRUMENT(rts_store)(void *ptr) { 87 | NOINSTRUMENT(stat_stores_total)++; 88 | 89 | if (((uintptr_t)ptr & (0xffffffffULL << 32)) == 0) 90 | NOINSTRUMENT(stat_stores_nometa)++; 91 | } 92 | 93 | #endif /* RUNTIME_STATS */ 94 | -------------------------------------------------------------------------------- /runtime/source-instrumentation.h: -------------------------------------------------------------------------------- 1 | #ifndef _SOURCE_INSTRUMENTATION_H 2 | #define _SOURCE_INSTRUMENTATION_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include "addrspace.h" 10 | #include "noinstrument.h" 11 | 12 | #define _tag_pointer NOINSTRUMENT(_inline_tag_pointer) 13 | #define _mask_pointer NOINSTRUMENT(_inline_mask_pointer) 14 | #define _tag_of NOINSTRUMENT(_inline_tag_of) 15 | #define _copy_tag NOINSTRUMENT(_inline_take_tag) 16 | #define _ptr_arith NOINSTRUMENT(_inline_ptr_arith) 17 | #define _leak_pointer NOINSTRUMENT(_inline_leak_pointer) 18 | #define _delta_tag NOINSTRUMENT(_inline_delta_tag) 19 | 20 | #define MASK_POINTER_TYPED(ty, p) ((ty*)_mask_pointer((void*)(p))) 21 | 22 | /* 23 | * Implementations for these are in mask-wrappers.c 24 | */ 25 | 26 | void *_tag_pointer(const void *ptr, uintptr_t tag); 27 | void *_mask_pointer(const void *ptr); 28 | uintptr_t _tag_of(const void *ptr); 29 | void *_copy_tag(const void *ptr, const void *tagptr); 30 | void *_ptr_arith(const void *ptr, int64_t delta); 31 | uintptr_t _leak_pointer(const void *ptr); 32 | uintptr_t _delta_tag(unsigned long long size); 33 | 34 | #ifdef DELTAPOINTERS_UBOUND_BRANCH 35 | # define UNSAFE_ARITH(ptr, delta) ({ \ 36 | uintptr_t _tag = _tag_of(ptr); \ 37 | _tag_pointer((char*)ptr + delta, _tag ? _tag + delta : 0UL); \ 38 | }) 39 | #elif defined(DELTAPOINTERS) 40 | # define UNSAFE_ARITH _ptr_arith 41 | #endif 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | 47 | #endif /* _SOURCE_INSTRUMENTATION_H */ 48 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # PYTHON_ARGCOMPLETE_OK 3 | import sys 4 | import os.path 5 | curdir = os.path.dirname(os.path.abspath(__file__)) 6 | sys.path.insert(0, os.path.join(curdir, 'infra')) 7 | 8 | import infra 9 | from config.instances import DeltaTags 10 | from config.targets import DeltaTagsTest 11 | 12 | setup = infra.Setup(__file__) 13 | 14 | setup.add_instance(infra.instances.Clang(DeltaTags.llvm)) 15 | setup.add_instance(infra.instances.ClangLTO(DeltaTags.llvm)) 16 | for instance in DeltaTags.make_instances(): 17 | setup.add_instance(instance) 18 | 19 | # microtest target for debugging 20 | setup.add_target(DeltaTagsTest()) 21 | 22 | # patched SPEC2006 23 | curdir = os.path.dirname(os.path.abspath(__file__)) 24 | patches = ['asan', 'dealII-stddef', 'omnetpp-invalid-ptrcheck'] 25 | for name in ('gcc', 'perlbench', 'soplex', 'h264ref-sizetagprop-BCBP'): 26 | patches.append('%s/patches/spec2006-%s.patch' % (curdir, name)) 27 | 28 | setup.add_target(infra.targets.SPEC2006( 29 | # see the following link for more options for source[_type] below: 30 | # http://instrumentation-infra.readthedocs.io/en/master/targets.html#infra.targets.SPEC2006 31 | source='spec-mount-dir', # SET THIS FOR SPEC 32 | source_type='mounted', # SET THIS FOR SPEC 33 | patches=patches 34 | )) 35 | 36 | if __name__ == '__main__': 37 | setup.main() 38 | --------------------------------------------------------------------------------