From fc25245984f346a5620998f02f93ad9b85dae80b Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Mon, 13 Jul 2026 16:00:19 -0400 Subject: [PATCH] libc: align wasm32 pthread entry stacks Align the stack passed to kernel_clone to the 16-byte boundary required by Wasm code generation. This keeps stack-based variadic arguments intact when a new pthread begins. Add a local Sortix regression that formats the SQLite temporary-file pattern from fresh pthreads. The kernel_clone interface and ABI layout are unchanged. --- .../src/thread/wasm32posix/clone.c | 10 ++- .../pthread/pthread_stack_snprintf_varargs.c | 67 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 tests/sortix/os-test-local/basic/pthread/pthread_stack_snprintf_varargs.c diff --git a/libc/musl-overlay/src/thread/wasm32posix/clone.c b/libc/musl-overlay/src/thread/wasm32posix/clone.c index bd6c45f58d..6e1475e48a 100644 --- a/libc/musl-overlay/src/thread/wasm32posix/clone.c +++ b/libc/musl-overlay/src/thread/wasm32posix/clone.c @@ -27,9 +27,17 @@ int __clone(int (*fn)(void *), void *stack, int flags, void *arg, ...) int *ctid = __builtin_va_arg(ap, int *); __builtin_va_end(ap); + /* + * pthread_create places its start_args object on the child stack, but only + * realigns the resulting stack pointer to pointer width. Wasm codegen + * assumes a 16-byte-aligned __stack_pointer at function entry; starting a + * thread at a 4-byte residue corrupts stack-based varargs such as %llx%c. + */ + uintptr_t stack_ptr = (uintptr_t)stack & ~(uintptr_t)15; + return kernel_clone( (uint32_t)(uintptr_t)fn, - (uint32_t)(uintptr_t)stack, + (uint32_t)stack_ptr, (uint32_t)flags, (uint32_t)(uintptr_t)arg, (uint32_t)(uintptr_t)ptid, diff --git a/tests/sortix/os-test-local/basic/pthread/pthread_stack_snprintf_varargs.c b/tests/sortix/os-test-local/basic/pthread/pthread_stack_snprintf_varargs.c new file mode 100644 index 0000000000..fa4ea05162 --- /dev/null +++ b/tests/sortix/os-test-local/basic/pthread/pthread_stack_snprintf_varargs.c @@ -0,0 +1,67 @@ +/* Test pthread initial stack alignment for variadic calls. */ + +#include +#include +#include + +#include "../basic.h" + +struct test_case { + unsigned long long value; + const char* expected; +}; + +static const struct test_case test_cases[] = { + { 0x333c7d7900000000ULL, "/tmp/etilqs_333c7d7900000000" }, + { 0x17887ec000000000ULL, "/tmp/etilqs_17887ec000000000" }, + { 0x09f49ef200000000ULL, "/tmp/etilqs_9f49ef200000000" }, +}; + +static void check_format(const struct test_case* test) +{ + char buffer[80]; + memset(buffer, 0x5a, sizeof(buffer)); + + int ret = snprintf(buffer, sizeof(buffer), "%s/etilqs_%llx%c", + "/tmp", test->value, 0); + size_t expected_len = strlen(test->expected); + if ( ret != (int) expected_len + 1 ) + errx(1, "snprintf returned %d, expected %zu", ret, + expected_len + 1); + if ( strlen(buffer) != expected_len ) + errx(1, "snprintf wrote visible length %zu, expected %zu", + strlen(buffer), expected_len); + if ( strcmp(buffer, test->expected) != 0 ) + errx(1, "snprintf wrote '%s', expected '%s'", buffer, + test->expected); + if ( buffer[expected_len] != '\0' || buffer[expected_len + 1] != '\0' ) + errx(1, "snprintf did not write the %%c NUL and terminator"); +} + +static void* start(void* arg) +{ + check_format((const struct test_case*) arg); + return NULL; +} + +int main(void) +{ + for ( size_t i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++ ) + { + pthread_t thread; + int errnum = pthread_create(&thread, NULL, start, + (void*) &test_cases[i]); + if ( errnum ) + { + errno = errnum; + err(1, "pthread_create"); + } + errnum = pthread_join(thread, NULL); + if ( errnum ) + { + errno = errnum; + err(1, "pthread_join"); + } + } + return 0; +}