Commit f0f80654 by Thomas Lively

implemented wrapper script to replace calls to calloc()

parent f6c41e46
......@@ -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
......
#!/usr/bin/env python2
import sz_driver
if __name__ == '__main__':
sz_driver.run(is_cpp=True)
#!/usr/bin/env python2
import sz_driver
if __name__ == '__main__':
sz_driver.run(is_cpp=False)
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
void *__asan_dummy_calloc(size_t nmemb, size_t size) {
return calloc(nmemb, size);
}
#ifdef __cplusplus
} // extern "C"
#endif
import os
import shutil
import subprocess
import sys
import tempfile
from utils import FindBaseNaCl, shellcmd
def subsToMacros(subs, src):
macros = ['#include <stddef.h>',
'#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)
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main(void) {
void *buf = calloc(14, sizeof(int));
strcpy(buf, "Hello, world!");
printf("%s\n", buf);
free(buf);
}
int main(void) { not_defined(); }
; 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: }
; 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment