Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
092d8b0
Use emscripten_builtin_malloc/free in select library code
quantum5 May 30, 2019
d417ff3
Make emscripten_builtin_* strong since we need them to be the original
quantum5 May 30, 2019
789679d
Fix and parameterize emmalloc test
quantum5 May 30, 2019
9e95a56
Make interception build on emscripten
quantum5 May 29, 2019
e4e778b
Implement LSan linking
quantum5 May 29, 2019
79fba27
Some LSan work
quantum5 May 29, 2019
69ff180
Working global/stack leak detection
quantum5 May 31, 2019
1dee165
Use emscripten_builtin_malloc/free for stack trace functionality
quantum5 May 31, 2019
39dfd17
Show LSan stack trace
quantum5 May 31, 2019
5cbb27c
Export __data_end and __heap_base when using lsan
quantum5 Jun 3, 2019
132f846
Make LSan not cache its own allocator cache
quantum5 Jun 4, 2019
8a07112
Fix library dependencies
quantum5 Jun 4, 2019
6b99edd
Fix fastcomp dlfcn tests
quantum5 Jun 4, 2019
ee7642e
Make test_dlmalloc work by defining dl* as weak again
quantum5 Jun 4, 2019
eed8741
Don't try to install signal handlers on emscripten
quantum5 Jun 5, 2019
e87da4c
Implement LSan suppressions
quantum5 Jun 5, 2019
cb02ce7
Fix sdl2-mixer fastcomp build
quantum5 Jun 5, 2019
9bba5d9
Run original dlmalloc on fastcomp
quantum5 Jun 5, 2019
145f8a5
Keep using _malloc and _free on fastcomp
quantum5 Jun 5, 2019
0cdff24
Update metadce expectations
quantum5 Jun 5, 2019
1ea4015
Fix various problems
quantum5 Jun 5, 2019
636141a
Fix memalign not found
quantum5 Jun 5, 2019
85d5076
Fix metadce tests
quantum5 Jun 5, 2019
1dc762c
Make embuilder build lsan
quantum5 Jun 7, 2019
fffba0a
Update ChangeLog.md
quantum5 Jun 7, 2019
f5e53cb
Fix LSan treating everything as leaks
quantum5 Jun 7, 2019
5c18aa1
Add LSan tests
quantum5 Jun 7, 2019
641793c
Do not use source map beyond function boundaries
quantum5 Jun 7, 2019
250dd2f
LSan is just a link time thing, no need to optimize source in test
quantum5 Jun 7, 2019
f1105d9
Add C++ operator new tests for LSan
quantum5 Jun 8, 2019
7d8817c
LSan does not yet support threads
quantum5 Jun 10, 2019
c6cd0ee
Add lsan unit test mode
quantum5 Jun 10, 2019
ec8c875
Merge remote-tracking branch 'origin/incoming' into lsan
quantum5 Jun 18, 2019
cf03713
fix metadce tests
quantum5 Jun 18, 2019
e80236d
Disable multithread builds for LSan
quantum5 Jun 18, 2019
664fd84
Fix CI tests
quantum5 Jun 18, 2019
46fb41b
Merge remote-tracking branch 'origin/incoming' into lsan
quantum5 Jun 18, 2019
1a926d1
Fix comments as requested
quantum5 Jun 18, 2019
c1a473c
flake8
quantum5 Jun 18, 2019
acd5e65
Merge remote-tracking branch 'origin/incoming' into lsan
quantum5 Jun 18, 2019
980b7dd
Preemptively address review comments
quantum5 Jun 20, 2019
f0352e2
Load LSan options from Module with emscripten_builtin_malloc/free
quantum5 Jun 20, 2019
6233f42
Make UBSan load options from Module for consistency
quantum5 Jun 20, 2019
415e18a
Fix UBSan stack trace tests
quantum5 Jun 24, 2019
d1e68ea
Fix metadce tests
quantum5 Jun 24, 2019
601f36f
Fix other.test_binaryen_names
quantum5 Jun 24, 2019
1860653
Only use _emscripten_builtin_memalign/free with LSan
quantum5 Jun 24, 2019
973666a
Fix metadce tests more
quantum5 Jun 24, 2019
3bf391e
Use self.assertTrue in do_smart_test
quantum5 Jun 24, 2019
21da2e7
metadce tests are evil
quantum5 Jun 24, 2019
7208374
Fix metadce tests harder
quantum5 Jun 25, 2019
01f5f6f
Fix munmap to not dlfree unless mmap used dlmalloc
quantum5 Jun 25, 2019
fc4328b
Merge remote-tracking branch 'origin/incoming' into lsan
quantum5 Jun 25, 2019
6e24d6c
Merge remote-tracking branch 'origin/incoming' into lsan
quantum5 Jun 26, 2019
e2d6125
Add comment for mmap/munmap changes
quantum5 Jun 26, 2019
087c2d0
Remove old workaround in emmalloc.cpp
quantum5 Jun 26, 2019
fb3a713
Move builtin memory allocation logic into one place
quantum5 Jun 26, 2019
f738e8c
Fix metadce tests
quantum5 Jun 26, 2019
0b958c0
Fix mmap/munmap dependecy issue
quantum5 Jun 26, 2019
0c08b66
Don't use a builtin malloc in test_emmalloc_*
quantum5 Jun 26, 2019
5939bd3
Fix metadce tests
quantum5 Jun 26, 2019
35493fd
Fix more metadce tests
quantum5 Jun 26, 2019
84f264e
Fix no FS test
quantum5 Jun 26, 2019
eaf0d74
Fix even more metadce
quantum5 Jun 26, 2019
f4e1a5e
Simplify do_smart_test
quantum5 Jun 27, 2019
d4dc4c1
Remove TESTING_EMMALLOC
quantum5 Jun 27, 2019
a0c81e5
Add some comments about StopTheWorld
quantum5 Jun 27, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ See docs/process.md for how version tagging works.
Current Trunk
-------------

- Add support for standalone [leak sanitizer](https://clang.llvm.org/docs/LeakSanitizer.html). (#8711)

v1.38.37: 06/26/2019
--------------------

- Set ENV['LANG'] following the user's preferred language (HTTP Accept-Language / navigator.languages[0])
- `emscripten_run_script_string` now returns C `NULL` instead of the string `null`
or `undefined` when the result of the `eval` is JavaScript `null` or `undefined`.
Expand Down
7 changes: 7 additions & 0 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1479,6 +1479,13 @@ def check(input_file):
else:
shared.Settings.UBSAN_RUNTIME = 2

if 'leak' in sanitize:
shared.Settings.USE_LSAN = 1
shared.Settings.EXIT_RUNTIME = 1

if shared.Settings.USE_PTHREADS:
exit_with_error('LSan currently does not support threads')

if sanitize and '-g4' in args:
shared.Settings.LOAD_SOURCE_MAP = 1

Expand Down
6 changes: 3 additions & 3 deletions src/deps_info.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"arc4random": ["rand"],
"freopen": ["free"],
"munmap": ["free"],
"munmap": ["emscripten_builtin_free", "free"],
"getenv": ["malloc", "free", "_get_environ"],
"clearenv": ["_get_environ"],
"setenv": ["_get_environ"],
Expand All @@ -19,7 +19,7 @@
"realloc": ["malloc", "free"],
"getlogin": ["malloc", "_get_environ"],
"tmpnam": ["malloc"],
"mmap": ["memalign"],
"mmap": ["emscripten_builtin_memalign", "memalign"],
"realpath": ["malloc"],
"strerror": ["malloc"],
"__ctype_b_loc": ["malloc"],
Expand Down Expand Up @@ -89,6 +89,6 @@
"stringToNewUTF8": ["malloc"],
"_embind_register_std_string": ["malloc", "free"],
"_embind_register_std_wstring": ["malloc", "free"],
"__syscall192": ["memalign"]
"__syscall192": ["emscripten_builtin_memalign"]
}

47 changes: 41 additions & 6 deletions src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -4522,7 +4522,7 @@ LibraryManager.library = {
},

// Look up the function name from our stack frame cache with our PC representation.
emscripten_pc_get_function__deps: ['$UNWIND_CACHE'],
emscripten_pc_get_function__deps: ['$UNWIND_CACHE', 'emscripten_with_builtin_malloc'],
emscripten_pc_get_function: function (pc) {
var frame = UNWIND_CACHE[pc];
if (!frame) return 0;
Expand All @@ -4536,8 +4536,10 @@ LibraryManager.library = {
}
if (!name) return 0;

if (_emscripten_pc_get_function.ret) _free(_emscripten_pc_get_function.ret);
_emscripten_pc_get_function.ret = allocateUTF8(name);
_emscripten_with_builtin_malloc(function () {
if (_emscripten_pc_get_function.ret) _free(_emscripten_pc_get_function.ret);
_emscripten_pc_get_function.ret = allocateUTF8(name);
});
return _emscripten_pc_get_function.ret;
},

Expand Down Expand Up @@ -4573,13 +4575,15 @@ LibraryManager.library = {
},

// Look up the file name from our stack frame cache with our PC representation.
emscripten_pc_get_file__deps: ['emscripten_pc_get_source_js'],
emscripten_pc_get_file__deps: ['emscripten_pc_get_source_js', 'emscripten_with_builtin_malloc'],
emscripten_pc_get_file: function (pc) {
var result = _emscripten_pc_get_source_js(pc);
if (!result) return 0;

if (_emscripten_pc_get_file.ret) _free(_emscripten_pc_get_file.ret);
_emscripten_pc_get_file.ret = allocateUTF8(result.file);
_emscripten_with_builtin_malloc(function () {
if (_emscripten_pc_get_file.ret) _free(_emscripten_pc_get_file.ret);
_emscripten_pc_get_file.ret = allocateUTF8(result.file);
});
return _emscripten_pc_get_file.ret;
},

Expand All @@ -4601,6 +4605,37 @@ LibraryManager.library = {
return stringToUTF8(wasmBinaryFile, buf, length);
},

emscripten_with_builtin_malloc__deps: ['emscripten_builtin_malloc', 'emscripten_builtin_free', 'emscripten_builtin_memalign'],
emscripten_with_builtin_malloc: function (func) {
var prev_malloc = _malloc;
var prev_memalign = _memalign;
var prev_free = _free;
_malloc = _emscripten_builtin_malloc;
_memalign = _emscripten_builtin_memalign;
_free = _emscripten_builtin_free;
try {
return func();
} finally {
_malloc = prev_malloc;
_memalign = prev_memalign;
_free = prev_free;
}
},

emscripten_builtin_mmap2__deps: ['emscripten_with_builtin_malloc', '_emscripten_syscall_mmap2'],
emscripten_builtin_mmap2: function (addr, len, prot, flags, fd, off) {
return _emscripten_with_builtin_malloc(function () {
return __emscripten_syscall_mmap2(addr, len, prot, flags, fd, off);
});
},

emscripten_builtin_munmap__deps: ['emscripten_with_builtin_malloc', '_emscripten_syscall_munmap'],
emscripten_builtin_munmap: function (addr, len) {
return _emscripten_with_builtin_malloc(function () {
return __emscripten_syscall_munmap(addr, len);
});
},

emscripten_get_stack_top: function() {
return STACKTOP;
},
Expand Down
4 changes: 4 additions & 0 deletions src/library_pthread_stub.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ var LibraryPThreadStub = {
{{{ makeSetValue('stacksize', '0', 'TOTAL_STACK', 'i32') }}};
return 0;
},
pthread_attr_getdetachstate: function(attr, detachstate) {
/* int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); */
return 0;
},

pthread_setcancelstate: function() { return 0; },
pthread_setcanceltype: function() { return 0; },
Expand Down
105 changes: 62 additions & 43 deletions src/library_syscall.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,64 @@ var SyscallsLibrary = {
}
},

_emscripten_syscall_mmap2__deps: ['memalign', 'memset', '$SYSCALLS',
#if FILESYSTEM && SYSCALLS_REQUIRE_FILESYSTEM
'$FS',
#endif
],
_emscripten_syscall_mmap2: function(addr, len, prot, flags, fd, off) {
off <<= 12; // undo pgoffset
var ptr;
var allocated = false;

// addr argument must be page aligned if MAP_FIXED flag is set.
if ((flags & {{{ cDefine('MAP_FIXED') }}}) !== 0 && (addr % PAGE_SIZE) !== 0) {
return -{{{ cDefine('EINVAL') }}};
}

// MAP_ANONYMOUS (aka MAP_ANON) isn't actually defined by POSIX spec,
// but it is widely used way to allocate memory pages on Linux, BSD and Mac.
// In this case fd argument is ignored.
if ((flags & {{{ cDefine('MAP_ANONYMOUS') }}}) !== 0) {
ptr = _memalign(PAGE_SIZE, len);
if (!ptr) return -{{{ cDefine('ENOMEM') }}};
_memset(ptr, 0, len);
allocated = true;
} else {
var info = FS.getStream(fd);
if (!info) return -{{{ cDefine('EBADF') }}};
var res = FS.mmap(info, HEAPU8, addr, len, off, prot, flags);
ptr = res.ptr;
allocated = res.allocated;
}
SYSCALLS.mappings[ptr] = { malloc: ptr, len: len, allocated: allocated, fd: fd, flags: flags };
return ptr;
},

_emscripten_syscall_munmap__deps: ['$SYSCALLS',
#if FILESYSTEM && SYSCALLS_REQUIRE_FILESYSTEM
'$FS',
#endif
],
_emscripten_syscall_munmap: function(addr, len) {
if (addr == {{{ cDefine('MAP_FAILED') }}} || len == 0) {
return -{{{ cDefine('EINVAL') }}};
}
// TODO: support unmmap'ing parts of allocations
var info = SYSCALLS.mappings[addr];
if (!info) return 0;
if (len === info.len) {
var stream = FS.getStream(info.fd);
SYSCALLS.doMsync(addr, stream, len, info.flags)
FS.munmap(stream);
SYSCALLS.mappings[addr] = null;
if (info.allocated) {
_free(info.malloc);
}
}
return 0;
},

__syscall1: function(which, varargs) { // exit
var status = SYSCALLS.get();
exit(status);
Expand Down Expand Up @@ -444,24 +502,10 @@ var SyscallsLibrary = {
var path = SYSCALLS.getStr(), buf = SYSCALLS.get(), bufsize = SYSCALLS.get();
return SYSCALLS.doReadlink(path, buf, bufsize);
},
__syscall91__deps: ['_emscripten_syscall_munmap'],
__syscall91: function(which, varargs) { // munmap
var addr = SYSCALLS.get(), len = SYSCALLS.get();
if (addr == {{{ cDefine('MAP_FAILED') }}} || len == 0) {
return -{{{ cDefine('EINVAL') }}};
}
// TODO: support unmmap'ing parts of allocations
var info = SYSCALLS.mappings[addr];
if (!info) return 0;
if (len === info.len) {
var stream = FS.getStream(info.fd);
SYSCALLS.doMsync(addr, stream, len, info.flags)
FS.munmap(stream);
SYSCALLS.mappings[addr] = null;
if (info.allocated) {
_free(info.malloc);
}
}
return 0;
return __emscripten_syscall_munmap(addr, len);
},
__syscall94: function(which, varargs) { // fchmod
var fd = SYSCALLS.get(), mode = SYSCALLS.get();
Expand Down Expand Up @@ -996,35 +1040,10 @@ var SyscallsLibrary = {
{{{ makeSetValue('rlim', C_STRUCTS.rlimit.rlim_max + 4, '-1', 'i32') }}}; // RLIM_INFINITY
return 0; // just report no limits
},
__syscall192__deps: ['memalign'],
__syscall192__deps: ['_emscripten_syscall_mmap2'],
__syscall192: function(which, varargs) { // mmap2
var addr = SYSCALLS.get(), len = SYSCALLS.get(), prot = SYSCALLS.get(), flags = SYSCALLS.get(), fd = SYSCALLS.get(), off = SYSCALLS.get()
off <<= 12; // undo pgoffset
var ptr;
var allocated = false;

// addr argument must be page aligned if MAP_FIXED flag is set.
if ((flags & {{{ cDefine('MAP_FIXED') }}}) !== 0 && (addr % PAGE_SIZE) !== 0) {
return -{{{ cDefine('EINVAL') }}};
}

// MAP_ANONYMOUS (aka MAP_ANON) isn't actually defined by POSIX spec,
// but it is widely used way to allocate memory pages on Linux, BSD and Mac.
// In this case fd argument is ignored.
if ((flags & {{{ cDefine('MAP_ANONYMOUS') }}}) !== 0) {
ptr = _memalign(PAGE_SIZE, len);
if (!ptr) return -{{{ cDefine('ENOMEM') }}};
_memset(ptr, 0, len);
allocated = true;
} else {
var info = FS.getStream(fd);
if (!info) return -{{{ cDefine('EBADF') }}};
var res = FS.mmap(info, HEAPU8, addr, len, off, prot, flags);
ptr = res.ptr;
allocated = res.allocated;
}
SYSCALLS.mappings[ptr] = { malloc: ptr, len: len, allocated: allocated, fd: fd, flags: flags };
return ptr;
return __emscripten_syscall_mmap2(addr, len, prot, flags, fd, off);
},
__syscall193: function(which, varargs) { // truncate64
var path = SYSCALLS.getStr(), zero = SYSCALLS.getZero(), length = SYSCALLS.get64();
Expand Down
4 changes: 4 additions & 0 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -1460,6 +1460,10 @@ var WASM2JS = 0;
// -fsanitize=undefined. To use minimal runtime, also pass `-fsanitize-minimal-runtime`.
var UBSAN_RUNTIME = 0;

// Whether we should link in LSan's runtime library. This is intended to be used invoked`
// by -fsanitize=leak instead of used directly.
var USE_LSAN = 0;

// Whether we should load the WASM source map at runtime.
// This is enabled automatically when using -g4 with sanitizers.
var LOAD_SOURCE_MAP = 0;
Expand Down
11 changes: 9 additions & 2 deletions src/source_map_support.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,16 @@ function WasmSourceMap(sourceMap) {
}

WasmSourceMap.prototype.lookup = function (offset) {
var info = this.mapping[this.normalizeOffset(offset)];
if (!info)
var normalized = this.normalizeOffset(offset);
#if 'emscripten_generate_pc' in addedLibraryItems
if (!wasmOffsetConverter.isSameFunc(offset, normalized)) {
return null;
}
#endif
var info = this.mapping[normalized];
if (!info) {
return null;
}
return {
source: this.sources[info.source],
line: info.line,
Expand Down
27 changes: 25 additions & 2 deletions src/wasm_offset_converter.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ function WasmOffsetConverter(wasmBytes) {
// WASM stack trace format.
//
// v8 bug: https://crbug.com/v8/9172
// We may be able to remove this code once the fix makes its way into all
// commonly used versions of node.
//
// This code is also used to check if the candidate source map offset is
// actually part of the same function as the offset we are looking for.

// current byte offset into the WASM binary, as we parse it
// the first section starts at offset 8
Expand All @@ -21,6 +22,7 @@ function WasmOffsetConverter(wasmBytes) {

// map from function index to byte offset in WASM binary
this.map = {};
this.func_starts = [];

function unsignedLEB128() {
// consumes an unsigned LEB128 integer starting at `offset`.
Expand Down Expand Up @@ -82,6 +84,7 @@ function WasmOffsetConverter(wasmBytes) {
while (count-- > 0) {
var size = unsignedLEB128();
this.map[funcidx++] = offset;
this.func_starts.push(offset);
offset += size;
}
return;
Expand All @@ -93,3 +96,23 @@ function WasmOffsetConverter(wasmBytes) {
WasmOffsetConverter.prototype.convert = function (funcidx, offset) {
return this.map[funcidx] + offset;
}

WasmOffsetConverter.prototype.isSameFunc = function (offset1, offset2) {
var array = this.func_starts;
function bisect(offset) {
var lo = 0;
var hi = array.length;
var mid;

while (lo < hi) {
mid = Math.floor((lo + hi) / 2);
if (array[mid] > offset) {
hi = mid;
} else {
lo = mid + 1;
}
}
return lo;
}
return bisect(offset1) == bisect(offset2);
}
7 changes: 4 additions & 3 deletions system/lib/compiler-rt/lib/interception/interception.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

#if !SANITIZER_LINUX && !SANITIZER_FREEBSD && !SANITIZER_MAC && \
!SANITIZER_NETBSD && !SANITIZER_OPENBSD && !SANITIZER_WINDOWS && \
!SANITIZER_FUCHSIA && !SANITIZER_RTEMS && !SANITIZER_SOLARIS
!SANITIZER_FUCHSIA && !SANITIZER_RTEMS && !SANITIZER_SOLARIS && \
!SANITIZER_EMSCRIPTEN
# error "Interception doesn't work on this operating system."
#endif

Expand Down Expand Up @@ -131,7 +132,7 @@ const interpose_substitution substitution_##func_name[] \
extern "C" ret_type func(__VA_ARGS__);
# define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \
extern "C" __declspec(dllimport) ret_type __stdcall func(__VA_ARGS__);
#elif SANITIZER_RTEMS
#elif SANITIZER_RTEMS || SANITIZER_EMSCRIPTEN
# define WRAP(x) x
# define WRAPPER_NAME(x) #x
# define INTERCEPTOR_ATTRIBUTE
Expand Down Expand Up @@ -276,7 +277,7 @@ typedef unsigned long uptr; // NOLINT
#define INCLUDED_FROM_INTERCEPTION_LIB

#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \
SANITIZER_OPENBSD || SANITIZER_SOLARIS
SANITIZER_OPENBSD || SANITIZER_SOLARIS || SANITIZER_EMSCRIPTEN

# include "interception_linux.h"
# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func)
Expand Down
Loading