From: Thomas Lively Date: Thu, 14 Jul 2016 21:29:59 +0000 (-0700) Subject: implemented wrapper script to replace calls to calloc() X-Git-Tag: android-x86-7.1-r1~148^2~181 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=f0f80654e8bafb18d795c3c77f590ee885ea753a;p=android-x86%2Fexternal-swiftshader.git implemented wrapper script to replace calls to calloc() BUG=https://bugs.chromium.org/p/nativeclient/issues/detail?id=4374 R=kschimpf@google.com, stichnot@chromium.org Review URL: https://codereview.chromium.org/2145213002 . --- diff --git a/docs/ASAN.rst b/docs/ASAN.rst index d815817c0..c51c905e0 100644 --- a/docs/ASAN.rst +++ b/docs/ASAN.rst @@ -10,14 +10,21 @@ used in production. In Subzero, AddressSanitizer depends on being able to find and instrument calls to various functions such as malloc() and free(), and as such the .pexe file -being translated must not have had those symbols stripped. Subzero will not -complain if it is told to translate a .pexe file with its symbols stripped, but -it will not be able to find calls to malloc() and free(), so AddressSanitizer -will not work correctly in the final executable. +being translated must not have had those symbols stripped or inlined. Subzero +will not complain if it is told to translate a .pexe file with its symbols +stripped, but it will not be able to find calls to malloc(), calloc(), free(), +etc., so AddressSanitizer will not work correctly in the final executable. + +Furthermore, pnacl-clang automatically inlines some calls to calloc(), +even with inlining turned off, so we provide wrapper scripts, +sz-clang.py and sz-clang++.py, that normally just pass their arguments +through to pnacl-clang or pnacl-clang++, but add instrumentation to +replace calls to calloc() at the source level if they are passed +-fsanitize-address. These are the steps to compile hello.c to an instrumented object file:: - pnacl-clang -o hello.nonfinal.pexe hello.c + sz-clang.py -fsanitize-address -o hello.nonfinal.pexe hello.c pnacl-finalize --no-strip-syms -o hello.pexe hello.nonfinal.pexe pnacl-sz -fsanitize-address -filetype=obj -o hello.o hello.pexe diff --git a/pydir/sz-clang++.py b/pydir/sz-clang++.py new file mode 100755 index 000000000..121dd5ae5 --- /dev/null +++ b/pydir/sz-clang++.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python2 + +import sz_driver + +if __name__ == '__main__': + sz_driver.run(is_cpp=True) diff --git a/pydir/sz-clang.py b/pydir/sz-clang.py new file mode 100755 index 000000000..7b93ea046 --- /dev/null +++ b/pydir/sz-clang.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python2 + +import sz_driver + +if __name__ == '__main__': + sz_driver.run(is_cpp=False) diff --git a/pydir/sz_clang_dummies.c b/pydir/sz_clang_dummies.c new file mode 100644 index 000000000..d0abc94cb --- /dev/null +++ b/pydir/sz_clang_dummies.c @@ -0,0 +1,13 @@ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void *__asan_dummy_calloc(size_t nmemb, size_t size) { + return calloc(nmemb, size); +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/pydir/sz_driver.py b/pydir/sz_driver.py new file mode 100644 index 000000000..f27d678c1 --- /dev/null +++ b/pydir/sz_driver.py @@ -0,0 +1,88 @@ +import os +import shutil +import subprocess +import sys +import tempfile + +from utils import FindBaseNaCl, shellcmd + +def subsToMacros(subs, src): + macros = ['#include ', + '#ifdef __cplusplus', + 'extern "C" {', + '#endif'] + for func in subs: + args = [('{atype} a{num}').format(atype=atype, num=i) for + i, atype in enumerate(subs[func]['sig'][1:])] + macros.append(( + '{ftype} {name}({args});' + ).format(ftype=subs[func]['sig'][0], + name=subs[func]['sub'], + args=', '.join(args))) + macros.append(( + '#define {func}(args...) ({sub}(args))' + ).format(func=func, sub=subs[func]['sub'])) + macros += ['#ifdef __cplusplus', + '} // extern "C"', + '#endif', + '#line 1 "{src}"'.format(src=src)] + return '\n'.join(macros) + '\n' + +def run(is_cpp): + """Passes its arguments directly to pnacl-clang. + + If -fsanitize-address is specified, extra information is passed to + pnacl-clang to ensure that later instrumentation in pnacl-sz can be + performed. For example, clang automatically inlines many memory allocation + functions, so this script will redefine them at compile time to make sure + they can be correctly instrumented by pnacl-sz. + """ + pnacl_root = FindBaseNaCl() + dummy_subs = {'calloc': {'sig': ['void *', 'size_t', 'size_t'], + 'sub': '__asan_dummy_calloc'}, + '_calloc': {'sig': ['void *', 'size_t', 'size_t'], + 'sub': '__asan_dummy_calloc'}} + subs_src = ( + '{root}/toolchain_build/src/subzero/pydir/sz_clang_dummies.c' + ).format(root=pnacl_root) + clang = ( + '{root}/toolchain/linux_x86/pnacl_newlib_raw/bin/pnacl-clang{pp}' + ).format(root=pnacl_root, pp='++' if is_cpp else '') + args = sys.argv + args[0] = clang + tmp_dir = '' + if '-fsanitize-address' in args: + args.remove('-fsanitize-address') + include_dirs = set() + tmp_dir = tempfile.mkdtemp() + for i, arg in enumerate(args[1:], 1): + if not os.path.isfile(arg): + continue + src = os.path.basename(arg) + ext = os.path.splitext(arg)[1] + if ext in ['.c', '.cc', '.cpp']: + include_dirs |= {os.path.dirname(arg)} + dest_name = os.path.join(tmp_dir, src) + with open(dest_name, 'w') as dest: + dest.write(subsToMacros(dummy_subs, arg)) + with open(arg) as src: + for line in src: + dest.write(line) + args[i] = dest_name + # If linking (not single file compilation) then add dummy definitions + if not ('-o' in args and + ('-c' in args or '-S' in args or '-E' in args)): + args.append(subs_src) + for d in include_dirs: + args.append('-iquote {d}'.format(d=d)) + if '-fno-inline' not in args: + args.append('-fno-inline') + err_code = 0 + try: + shellcmd(args, echo=True) + except subprocess.CalledProcessError as e: + print e.output + err_code = e.returncode + if tmp_dir != '': + shutil.rmtree(tmp_dir) + exit(err_code) diff --git a/tests_lit/asan_tests/Input/calloc.c b/tests_lit/asan_tests/Input/calloc.c new file mode 100644 index 000000000..16dd6d7ac --- /dev/null +++ b/tests_lit/asan_tests/Input/calloc.c @@ -0,0 +1,10 @@ +#include +#include +#include + +int main(void) { + void *buf = calloc(14, sizeof(int)); + strcpy(buf, "Hello, world!"); + printf("%s\n", buf); + free(buf); +} diff --git a/tests_lit/asan_tests/Input/calloc_err.c b/tests_lit/asan_tests/Input/calloc_err.c new file mode 100644 index 000000000..3dbf902fe --- /dev/null +++ b/tests_lit/asan_tests/Input/calloc_err.c @@ -0,0 +1 @@ +int main(void) { not_defined(); } diff --git a/tests_lit/asan_tests/calloc.ll b/tests_lit/asan_tests/calloc.ll new file mode 100644 index 000000000..a6d7c4c6c --- /dev/null +++ b/tests_lit/asan_tests/calloc.ll @@ -0,0 +1,14 @@ +; Test that sz-clang.py and sz-clang++.py successfully replace calls to calloc + +; RUN: %S/../../pydir/sz-clang.py -fsanitize-address %S/Input/calloc.c -E \ +; RUN: | FileCheck %s + +; RUN: %S/../../pydir/sz-clang++.py -fsanitize-address %S/Input/calloc.c -E \ +; RUN: | FileCheck %s + +; CHECK-LABEL: int main(void) { +; CHECK-NEXT: void *buf = (__asan_dummy_calloc(14, sizeof(int))); +; CHECK-NEXT: strcpy(buf, "Hello, world!"); +; CHECK-NEXT: printf("%s\n", buf); +; CHECK-NEXT: free(buf); +; CHECK-NEXT: } diff --git a/tests_lit/asan_tests/calloc_err.ll b/tests_lit/asan_tests/calloc_err.ll new file mode 100644 index 000000000..f492dbb1b --- /dev/null +++ b/tests_lit/asan_tests/calloc_err.ll @@ -0,0 +1,11 @@ +; Test that errors in source files are transparently reported by +; sz-clang.py and sz-clang++.py + +; RUN: not %S/../../pydir/sz-clang.py -fsanitize-address %S/Input/calloc_err.c \ +; RUN: 2>&1 | FileCheck %s + +; RUN: not %S/../../pydir/sz-clang\+\+.py -fsanitize-address \ +; RUN: %S/Input/calloc_err.c 2>&1 | FileCheck %s + +; CHECK-LABEL: Input/calloc_err.c:1:18: warning: +; CHECK-SAME: implicit declaration of function 'not_defined' is invalid in C99