Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mark pthread create/free functions as __noleakcheck #15108

Merged
merged 1 commit into from Sep 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Expand Up @@ -369,7 +369,7 @@ jobs:
steps:
- run-tests:
# also add a few asan tests
test_targets: "wasm2 asan.test_embind* asan.test_abort_on_exceptions asan.test_ubsan_full_left_shift_fsanitize_integer asan.test_pthread* asan.test_dyncall_specific_minimal_runtime asan.test_async_hello lsan.test_stdio_locking"
test_targets: "wasm2 asan.test_embind* asan.test_abort_on_exceptions asan.test_ubsan_full_left_shift_fsanitize_integer asan.test_pthread* asan.test_dyncall_specific_minimal_runtime asan.test_async_hello lsan.test_stdio_locking lsan.test_pthread_create"
test-wasm3:
executor: bionic
steps:
Expand Down
49 changes: 32 additions & 17 deletions src/library_pthread.js
Expand Up @@ -9,6 +9,7 @@ var LibraryPThread = {
$PThread__deps: ['_emscripten_thread_init',
'emscripten_futex_wake', '$killThread',
'$cancelThread', '$cleanupThread',
'$freeThreadData',
'exit',
#if !MINIMAL_RUNTIME
'$handleException',
Expand Down Expand Up @@ -150,21 +151,6 @@ var LibraryPThread = {
}
PThread.unusedWorkers = [];
},
freeThreadData: function(pthread) {
if (!pthread) return;
if (pthread.threadInfoStruct) {
#if PTHREADS_PROFILING
var profilerBlock = {{{ makeGetValue('pthread.threadInfoStruct', C_STRUCTS.pthread.profilerBlock, 'i32') }}};
{{{ makeSetValue('pthread.threadInfoStruct', C_STRUCTS.pthread.profilerBlock, 0, 'i32') }}};
_free(profilerBlock);
#endif
_free(pthread.threadInfoStruct);
}
pthread.threadInfoStruct = 0;
if (pthread.allocatedOwnStack && pthread.stackBase) _free(pthread.stackBase);
pthread.stackBase = 0;
if (pthread.worker) pthread.worker.pthread = null;
},
returnWorkerToPool: function(worker) {
// We don't want to run main thread queued calls here, since we are doing
// some operations that leave the worker queue in an invalid state until
Expand All @@ -178,7 +164,7 @@ var LibraryPThread = {
PThread.unusedWorkers.push(worker);
PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker), 1);
// Not a running Worker anymore
PThread.freeThreadData(worker.pthread);
freeThreadData(worker.pthread);
// Detach the worker from the pthread object, and return it to the
// worker pool as an unused worker.
worker.pthread = undefined;
Expand Down Expand Up @@ -450,6 +436,27 @@ var LibraryPThread = {
}
},

$freeThreadData__noleakcheck: true,
$freeThreadData: function(pthread) {
#if ASSERTIONS
assert(!ENVIRONMENT_IS_PTHREAD, 'Internal Error! freeThreadData() can only ever be called from main application thread!');
#endif
if (!pthread) return;
if (pthread.threadInfoStruct) {
#if PTHREADS_PROFILING
var profilerBlock = {{{ makeGetValue('pthread.threadInfoStruct', C_STRUCTS.pthread.profilerBlock, 'i32') }}};
{{{ makeSetValue('pthread.threadInfoStruct', C_STRUCTS.pthread.profilerBlock, 0, 'i32') }}};
_free(profilerBlock);
#endif
_free(pthread.threadInfoStruct);
}
pthread.threadInfoStruct = 0;
if (pthread.allocatedOwnStack && pthread.stackBase) _free(pthread.stackBase);
pthread.stackBase = 0;
if (pthread.worker) pthread.worker.pthread = null;
},

$killThread__desp: ['$freeThreadData'],
$killThread: function(pthread_ptr) {
#if ASSERTIONS
assert(!ENVIRONMENT_IS_PTHREAD, 'Internal Error! killThread() can only ever be called from main application thread!');
Expand All @@ -459,7 +466,7 @@ var LibraryPThread = {
var pthread = PThread.pthreads[pthread_ptr];
delete PThread.pthreads[pthread_ptr];
pthread.worker.terminate();
PThread.freeThreadData(pthread);
freeThreadData(pthread);
// The worker was completely nuked (not just the pthread execution it was hosting), so remove it from running workers
// but don't put it back to the pool.
PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(pthread.worker), 1); // Not a running Worker anymore.
Expand Down Expand Up @@ -632,6 +639,14 @@ var LibraryPThread = {
PThread.threadInit();
},

// ASan wraps the emscripten_builtin_pthread_create call in
// __lsan::ScopedInterceptorDisabler. Unfortunately, that only disables it on
// the thread that made the call. __pthread_create_js gets proxied to the
// main thread, where LSan is not disabled. This makes it necessary for us to
// disable LSan here (using __noleakcheck), so that it does not detect
// pthread's internal allocations as leaks. If/when we remove all the
// allocations from __pthread_create_js we could also remove this.
__pthread_create_js__noleakcheck: true,
__pthread_create_js__sig: 'iiiii',
__pthread_create_js__deps: ['$spawnThread', 'pthread_self', 'memalign', 'emscripten_sync_run_in_main_thread_4'],
__pthread_create_js: function(pthread_ptr, attr, start_routine, arg) {
Expand Down
20 changes: 0 additions & 20 deletions system/lib/pthread/library_pthread.c
Expand Up @@ -147,33 +147,13 @@ extern double emscripten_receive_on_main_thread_js(int functionIndex, int numCal
extern int _emscripten_notify_thread_queue(pthread_t targetThreadId, pthread_t mainThreadId);
extern int __pthread_create_js(struct pthread *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

#if defined(__has_feature)
#if __has_feature(leak_sanitizer) || __has_feature(address_sanitizer)
#define HAS_SANITIZER
#include <sanitizer/lsan_interface.h>
#endif
#endif

static void _do_call(em_queued_call* q) {
// C function pointer
assert(EM_FUNC_SIG_NUM_FUNC_ARGUMENTS(q->functionEnum) <= EM_QUEUED_CALL_MAX_ARGS);
switch (q->functionEnum) {
case EM_PROXIED_PTHREAD_CREATE:
#ifdef HAS_SANITIZER
// ASan wraps the emscripten_builtin_pthread_create call in __lsan::ScopedInterceptorDisabler.
// Unfortunately, that only disables it on the thread that made the call.
// This is sufficient on the main thread.
// On non-main threads, pthread_create gets proxied to the main thread, where LSan is not
// disabled. This makes it necessary for us to disable LSan here, so that it does not detect
// pthread's internal allocations as leaks.
// If/when we remove all the allocations from __pthread_create_js we could also remove this.
__lsan_disable();
#endif
q->returnValue.i =
__pthread_create_js(q->args[0].vp, q->args[1].vp, q->args[2].vp, q->args[3].vp);
#ifdef HAS_SANITIZER
__lsan_enable();
#endif
break;
case EM_PROXIED_CREATE_CONTEXT:
q->returnValue.i = emscripten_webgl_create_context(q->args[0].cp, q->args[1].vp);
Expand Down