Skip to content

Commit 4e8ad61

Browse files
vargazkripken
authored andcommitted
Fix the stack pointer in wasm backend+threads mode (#8811)
* Set STACK_MAX properly in the wasm backend, it should point to the lowest address since the wasm stack grows downwards. * Fix the stack overflow test to work with the wasm backend.
1 parent 9f8c224 commit 4e8ad61

7 files changed

+117
-8
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -414,3 +414,4 @@ a license to everyone to use it as detailed in LICENSE.)
414414
* Ferris Kwaijtaal <[email protected]>
415415
* Konstantin Podsvirov <[email protected]>
416416
* Eduardo Bart <[email protected]>
417+
* Zoltan Varga <[email protected]> (copyright owned by Microsoft, Inc.)

src/library_pthread.js

+12-3
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,8 @@ var LibraryPThread = {
414414
{{{ makeSetValue('tlsMemory', 'i*4', 0, 'i32') }}};
415415
}
416416

417+
var stackHigh = threadParams.stackBase + threadParams.stackSize;
418+
417419
var pthread = PThread.pthreads[threadParams.pthread_ptr] = { // Create a pthread info object to represent this thread.
418420
worker: worker,
419421
stackBase: threadParams.stackBase,
@@ -433,8 +435,8 @@ var LibraryPThread = {
433435

434436
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.attr }}}) >> 2, threadParams.stackSize);
435437
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.stack_size }}}) >> 2, threadParams.stackSize);
436-
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.stack }}}) >> 2, threadParams.stackBase);
437-
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.attr }}} + 8) >> 2, threadParams.stackBase);
438+
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.stack }}}) >> 2, stackHigh);
439+
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.attr }}} + 8) >> 2, stackHigh);
438440
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.attr }}} + 12) >> 2, threadParams.detached);
439441
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.attr }}} + 20) >> 2, threadParams.schedPolicy);
440442
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.attr }}} + 24) >> 2, threadParams.schedPrio);
@@ -1169,8 +1171,15 @@ var LibraryPThread = {
11691171

11701172
#if MODULARIZE
11711173
$establishStackSpaceInJsModule: function(stackBase, stackMax) {
1172-
STACK_BASE = STACKTOP = stackBase;
1174+
STACK_BASE = stackBase;
1175+
#if WASM_BACKEND
1176+
// The stack grows downwards
1177+
STACKTOP = stackMax;
1178+
STACK_MAX = stackBase;
1179+
#else
1180+
STACKTOP = stackBase;
11731181
STACK_MAX = stackMax;
1182+
#endif
11741183
},
11751184
#endif
11761185
};

src/runtime_stack_check.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,26 @@
22
// Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode.
33
function writeStackCookie() {
44
assert((STACK_MAX & 3) == 0);
5+
#if WASM_BACKEND
6+
// The stack grows downwards
7+
HEAPU32[(STACK_MAX >> 2)+1] = 0x02135467;
8+
HEAPU32[(STACK_MAX >> 2)+2] = 0x89BACDFE;
9+
#else
510
HEAPU32[(STACK_MAX >> 2)-1] = 0x02135467;
611
HEAPU32[(STACK_MAX >> 2)-2] = 0x89BACDFE;
12+
#endif
713
}
814

915
function checkStackCookie() {
10-
if (HEAPU32[(STACK_MAX >> 2)-1] != 0x02135467 || HEAPU32[(STACK_MAX >> 2)-2] != 0x89BACDFE) {
11-
abort('Stack overflow! Stack cookie has been overwritten, expected hex dwords 0x89BACDFE and 0x02135467, but received 0x' + HEAPU32[(STACK_MAX >> 2)-2].toString(16) + ' ' + HEAPU32[(STACK_MAX >> 2)-1].toString(16));
16+
#if WASM_BACKEND
17+
var cookie1 = HEAPU32[(STACK_MAX >> 2)+1];
18+
var cookie2 = HEAPU32[(STACK_MAX >> 2)+2];
19+
#else
20+
var cookie1 = HEAPU32[(STACK_MAX >> 2)-1];
21+
var cookie2 = HEAPU32[(STACK_MAX >> 2)-2];
22+
#endif
23+
if (cookie1 != 0x02135467 || cookie2 != 0x89BACDFE) {
24+
abort('Stack overflow! Stack cookie has been overwritten, expected hex dwords 0x89BACDFE and 0x02135467, but received 0x' + cookie2.toString(16) + ' ' + cookie1.toString(16));
1225
}
1326
// Also test the global address 0 for integrity.
1427
if (HEAP32[0] !== 0x63736d65 /* 'emsc' */) abort('Runtime error: The application has corrupted its heap memory area (address zero)!');

src/support.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -966,7 +966,7 @@ GLOBAL_BASE = alignMemory(GLOBAL_BASE, {{{ MAX_GLOBAL_ALIGN || 1 }}});
966966
// The wasm backend path does not have a way to set the stack max, so we can
967967
// just implement this function in a trivial way
968968
function establishStackSpace(base, max) {
969-
stackRestore(base);
969+
stackRestore(max);
970970
}
971971

972972
// JS library code refers to Atomics in the manner used from asm.js, provide

src/worker.js

+13-2
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,25 @@ this.onmessage = function(e) {
175175
selfThreadId = e.data.selfThreadId;
176176
parentThreadId = e.data.parentThreadId;
177177
// Establish the stack frame for this thread in global scope
178-
{{{ makeAsmExportAndGlobalAssignTargetInPthread('STACK_BASE') }}} = {{{ makeAsmExportAndGlobalAssignTargetInPthread('STACKTOP') }}} = e.data.stackBase;
179-
{{{ makeAsmExportAndGlobalAssignTargetInPthread('STACK_MAX') }}} = STACK_BASE + e.data.stackSize;
178+
#if WASM_BACKEND
179+
// The stack grows downwards
180+
var max = e.data.stackBase;
181+
var top = e.data.stackBase + e.data.stackSize;
182+
#else
183+
var max = e.data.stackBase + e.data.stackSize;
184+
var top = e.data.stackBase;
185+
#endif
186+
{{{ makeAsmExportAndGlobalAssignTargetInPthread('STACK_BASE') }}} = e.data.stackBase;
187+
{{{ makeAsmExportAndGlobalAssignTargetInPthread('STACKTOP') }}} = top;
188+
{{{ makeAsmExportAndGlobalAssignTargetInPthread('STACK_MAX') }}} = max;
180189
#if ASSERTIONS
181190
assert(threadInfoStruct);
182191
assert(selfThreadId);
183192
assert(parentThreadId);
184193
assert(STACK_BASE != 0);
194+
#if !WASM_BACKEND
185195
assert(STACK_MAX > STACK_BASE);
196+
#endif
186197
#endif
187198
// Call inside asm.js/wasm module to set up the stack frame for this pthread in asm.js/wasm module scope
188199
Module['establishStackSpace'](e.data.stackBase, e.data.stackBase + e.data.stackSize);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2015 The Emscripten Authors. All rights reserved.
2+
// Emscripten is available under two separate licenses, the MIT license and the
3+
// University of Illinois/NCSA Open Source License. Both these licenses can be
4+
// found in the LICENSE file.
5+
6+
#include <stdio.h>
7+
#include <stdlib.h>
8+
#include <pthread.h>
9+
#include <unistd.h>
10+
#include <emscripten.h>
11+
#include <emscripten/threading.h>
12+
#include <assert.h>
13+
14+
void *ThreadMain(void *arg)
15+
{
16+
pthread_attr_t attr;
17+
int rc;
18+
void *stbase;
19+
size_t stsize;
20+
int dummy, result;
21+
22+
rc = pthread_attr_init(&attr);
23+
assert(rc == 0);
24+
25+
rc = pthread_getattr_np(pthread_self(), &attr);
26+
assert(rc == 0);
27+
28+
rc = pthread_attr_getstack(&attr, &stbase, &stsize);
29+
assert(rc == 0);
30+
31+
//printf ("%p %p %p\n", stbase, (char*)stbase + stsize, &dummy);
32+
33+
if (&dummy < stbase)
34+
result = 1;
35+
else if ((char*)&dummy > (char*)stbase + stsize)
36+
result = 2;
37+
else
38+
result = 0;
39+
40+
pthread_exit((void*)result);
41+
}
42+
43+
int main()
44+
{
45+
if (!emscripten_has_threading_support())
46+
{
47+
#ifdef REPORT_RESULT
48+
REPORT_RESULT(0);
49+
#endif
50+
printf("Skipped: Threading is not supported.\n");
51+
return 0;
52+
}
53+
54+
pthread_t thread;
55+
pthread_attr_t attr;
56+
int rc, result;
57+
58+
pthread_attr_init(&attr);
59+
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
60+
rc = pthread_create(&thread, &attr, ThreadMain, NULL);
61+
assert(rc == 0);
62+
pthread_attr_destroy(&attr);
63+
64+
rc = pthread_join(thread, (void**)&result);
65+
assert(rc == 0);
66+
67+
#ifdef REPORT_RESULT
68+
REPORT_RESULT(result);
69+
#endif
70+
return result;
71+
}

tests/test_browser.py

+4
Original file line numberDiff line numberDiff line change
@@ -3658,6 +3658,10 @@ def test_pthread_mutex(self):
36583658
for arg in [[], ['-DSPINLOCK_TEST']]:
36593659
self.btest(path_from_root('tests', 'pthread', 'test_pthread_mutex.cpp'), expected='50', args=['-s', 'TOTAL_MEMORY=64MB', '-O3', '-s', 'USE_PTHREADS=1', '-s', 'PTHREAD_POOL_SIZE=8'] + arg)
36603660

3661+
@requires_threads
3662+
def test_pthread_attr_getstack(self):
3663+
self.btest(path_from_root('tests', 'pthread', 'test_pthread_attr_getstack.cpp'), expected='0', args=['-s', 'USE_PTHREADS=1', '-s', 'PTHREAD_POOL_SIZE=2'])
3664+
36613665
# Test that memory allocation is thread-safe.
36623666
@requires_threads
36633667
def test_pthread_malloc(self):

0 commit comments

Comments
 (0)