diff --git a/libc/startup/linux/x86_64/CMakeLists.txt b/libc/startup/linux/x86_64/CMakeLists.txt --- a/libc/startup/linux/x86_64/CMakeLists.txt +++ b/libc/startup/linux/x86_64/CMakeLists.txt @@ -10,6 +10,7 @@ libc.src.__support.threads.thread libc.src.__support.OSUtil.osutil libc.src.stdlib.exit + libc.src.stdlib.abort libc.src.stdlib.atexit libc.src.string.memory_utils.memcpy_implementation libc.src.unistd.environ diff --git a/libc/startup/linux/x86_64/start.cpp b/libc/startup/linux/x86_64/start.cpp --- a/libc/startup/linux/x86_64/start.cpp +++ b/libc/startup/linux/x86_64/start.cpp @@ -9,6 +9,7 @@ #include "config/linux/app.h" #include "src/__support/OSUtil/syscall.h" #include "src/__support/threads/thread.h" +#include "src/stdlib/abort.h" #include "src/stdlib/atexit.h" #include "src/stdlib/exit.h" #include "src/string/memory_utils/memcpy_implementations.h" @@ -23,6 +24,8 @@ extern "C" int main(int, char **, char **); +extern "C" void __stack_chk_fail() { __llvm_libc::abort(); } + namespace __llvm_libc { #ifdef SYS_mmap2 @@ -54,7 +57,9 @@ // Per the x86_64 TLS ABI, the entry pointed to by the thread pointer is the // address of the TLS block. So, we add more size to accomodate this address // entry. - uintptr_t tlsSizeWithAddr = tlsSize + sizeof(uintptr_t); + // We also need to include space for the stack canary. The canary is at + // offset 0x28 (40) and is of size uintptr_t. + uintptr_t tlsSizeWithAddr = tlsSize + sizeof(uintptr_t) + 40; // We cannot call the mmap function here as the functions set errno on // failure. Since errno is implemented via a thread local variable, we cannot @@ -76,6 +81,16 @@ __llvm_libc::inline_memcpy(reinterpret_cast(tlsAddr), reinterpret_cast(app.tls.address), app.tls.init_size); + uintptr_t *stackGuardAddr = reinterpret_cast(endPtr + 40); + // Setting the stack guard to a random value. + // We cannot call the get_random function here as the function sets errno on + // failure. Since errno is implemented via a thread local variable, we cannot + // use errno before TLS is setup. + ssize_t stackGuardRetVal = __llvm_libc::syscall_impl( + SYS_getrandom, reinterpret_cast(stackGuardAddr), sizeof(uint64_t), + 0); + if (stackGuardRetVal < 0) + __llvm_libc::syscall_impl(SYS_exit, 1); tls_descriptor = {tlsSizeWithAddr, uintptr_t(tlsAddr), endPtr}; return; diff --git a/libc/test/integration/src/unistd/CMakeLists.txt b/libc/test/integration/src/unistd/CMakeLists.txt --- a/libc/test/integration/src/unistd/CMakeLists.txt +++ b/libc/test/integration/src/unistd/CMakeLists.txt @@ -33,6 +33,29 @@ libc.src.unistd.fork ) +if((${LIBC_TARGET_OS} STREQUAL "linux") AND (${LIBC_TARGET_ARCHITECTURE_IS_X86})) + add_integration_test( + stack_smashing_test + SUITE + unistd-integration-tests + SRCS + stack_smashing_test.cpp + DEPENDS + libc.include.errno + libc.include.signal + libc.include.sys_wait + libc.include.unistd + libc.src.pthread.pthread_atfork + libc.src.signal.raise + libc.src.sys.wait.wait + libc.src.sys.wait.wait4 + libc.src.sys.wait.waitpid + libc.src.unistd.fork + COMPILE_OPTIONS + -fstack-protector-all + ) +endif() + add_executable( libc_execv_test_normal_exit EXCLUDE_FROM_ALL diff --git a/libc/test/integration/src/unistd/stack_smashing_test.cpp b/libc/test/integration/src/unistd/stack_smashing_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/integration/src/unistd/stack_smashing_test.cpp @@ -0,0 +1,68 @@ +//===--- Stack smashing test to check stack canary set up ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/CPP/string.h" +#include "src/__support/OSUtil/io.h" +#include "src/pthread/pthread_atfork.h" +#include "src/signal/raise.h" +#include "src/sys/wait/wait.h" +#include "src/sys/wait/wait4.h" +#include "src/sys/wait/waitpid.h" +#include "src/unistd/fork.h" + +#include "test/IntegrationTest/test.h" + +#include +#include +#include +#include + +void no_stack_smashing_normal_exit() { + pid_t pid = __llvm_libc::fork(); + if (pid == 0) { + // Child process + char foo[30]; + for (int i = 0; i < 30; i++) + foo[i] = 42; + return; + } + ASSERT_TRUE(pid > 0); + int status; + pid_t cpid = __llvm_libc::wait(&status); + ASSERT_TRUE(cpid > 0); + ASSERT_EQ(cpid, pid); + ASSERT_TRUE(WIFEXITED(status)); +} + +void stack_smashing_abort() { + pid_t pid = __llvm_libc::fork(); + if (pid == 0) { + // Child process + char foo[30]; + char *frame_ptr = static_cast(__builtin_frame_address(0)); + char *cur_ptr = &foo[0]; + // Corrupt the stack + while (cur_ptr != frame_ptr) { + *cur_ptr = 42; + cur_ptr++; + } + return; + } + ASSERT_TRUE(pid > 0); + int status; + pid_t cpid = __llvm_libc::wait(&status); + ASSERT_TRUE(cpid > 0); + ASSERT_EQ(cpid, pid); + ASSERT_TRUE(WTERMSIG(status) == SIGABRT); +} + +TEST_MAIN(int argc, char **argv, char **envp) { + no_stack_smashing_normal_exit(); + stack_smashing_abort(); + return 0; +}