From 23bbc3e1308b8d6dd399c91e2a91286bb7615616 Mon Sep 17 00:00:00 2001 From: Tanay Vakharia Date: Wed, 4 Oct 2023 20:50:53 +0000 Subject: [PATCH 1/2] [libc] Add x86-64 stack protector support. --- libc/startup/linux/x86_64/CMakeLists.txt | 1 + libc/startup/linux/x86_64/start.cpp | 21 +++++- .../integration/src/unistd/CMakeLists.txt | 23 +++++++ .../src/unistd/stack_smashing_test.cpp | 68 +++++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 libc/test/integration/src/unistd/stack_smashing_test.cpp diff --git a/libc/startup/linux/x86_64/CMakeLists.txt b/libc/startup/linux/x86_64/CMakeLists.txt index 40f01836c1313..076c0c3e444f5 100644 --- a/libc/startup/linux/x86_64/CMakeLists.txt +++ b/libc/startup/linux/x86_64/CMakeLists.txt @@ -10,6 +10,7 @@ add_startup_object( 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.inline_memcpy libc.src.unistd.environ diff --git a/libc/startup/linux/x86_64/start.cpp b/libc/startup/linux/x86_64/start.cpp index 4b46a4f4a7b08..e791326c97418 100644 --- a/libc/startup/linux/x86_64/start.cpp +++ b/libc/startup/linux/x86_64/start.cpp @@ -7,8 +7,10 @@ //===----------------------------------------------------------------------===// #include "config/linux/app.h" +#include "src/__support/OSUtil/io.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/inline_memcpy.h" @@ -23,6 +25,11 @@ extern "C" int main(int, char **, char **); +extern "C" void __stack_chk_fail() { + __llvm_libc::write_to_stderr("stack smashing detected"); + __llvm_libc::abort(); +} + namespace LIBC_NAMESPACE { #ifdef SYS_mmap2 @@ -54,7 +61,9 @@ void init_tls(TLSDescriptor &tls_descriptor) { // 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 +85,16 @@ void init_tls(TLSDescriptor &tls_descriptor) { LIBC_NAMESPACE::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) + LIBC_NAMESPACE::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 index 72fdb8fc7e6b0..10aac212af355 100644 --- a/libc/test/integration/src/unistd/CMakeLists.txt +++ b/libc/test/integration/src/unistd/CMakeLists.txt @@ -33,6 +33,29 @@ add_integration_test( 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 index 0000000000000..f5bc935464a9b --- /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] = (foo[i] != 42) ? 42 : 24; + 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 = (*cur_ptr != 42) ? 42 : 24; + 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; +} From cea53af54a7c2c4e71eb804632d7765942181b3f Mon Sep 17 00:00:00 2001 From: Tanay Vakharia Date: Wed, 4 Oct 2023 21:08:41 +0000 Subject: [PATCH 2/2] [libc] :%s/__llvm_libc/LIBC_NAMESPACE/g --- libc/startup/linux/x86_64/start.cpp | 6 +++--- libc/test/integration/src/unistd/stack_smashing_test.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libc/startup/linux/x86_64/start.cpp b/libc/startup/linux/x86_64/start.cpp index e791326c97418..af95d2702ded9 100644 --- a/libc/startup/linux/x86_64/start.cpp +++ b/libc/startup/linux/x86_64/start.cpp @@ -26,8 +26,8 @@ extern "C" int main(int, char **, char **); extern "C" void __stack_chk_fail() { - __llvm_libc::write_to_stderr("stack smashing detected"); - __llvm_libc::abort(); + LIBC_NAMESPACE::write_to_stderr("stack smashing detected"); + LIBC_NAMESPACE::abort(); } namespace LIBC_NAMESPACE { @@ -90,7 +90,7 @@ void init_tls(TLSDescriptor &tls_descriptor) { // 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( + ssize_t stackGuardRetVal = LIBC_NAMESPACE::syscall_impl( SYS_getrandom, reinterpret_cast(stackGuardAddr), sizeof(uint64_t), 0); if (stackGuardRetVal < 0) diff --git a/libc/test/integration/src/unistd/stack_smashing_test.cpp b/libc/test/integration/src/unistd/stack_smashing_test.cpp index f5bc935464a9b..89fc53dac506a 100644 --- a/libc/test/integration/src/unistd/stack_smashing_test.cpp +++ b/libc/test/integration/src/unistd/stack_smashing_test.cpp @@ -23,7 +23,7 @@ #include void no_stack_smashing_normal_exit() { - pid_t pid = __llvm_libc::fork(); + pid_t pid = LIBC_NAMESPACE::fork(); if (pid == 0) { // Child process char foo[30]; @@ -33,14 +33,14 @@ void no_stack_smashing_normal_exit() { } ASSERT_TRUE(pid > 0); int status; - pid_t cpid = __llvm_libc::wait(&status); + pid_t cpid = LIBC_NAMESPACE::wait(&status); ASSERT_TRUE(cpid > 0); ASSERT_EQ(cpid, pid); ASSERT_TRUE(WIFEXITED(status)); } void stack_smashing_abort() { - pid_t pid = __llvm_libc::fork(); + pid_t pid = LIBC_NAMESPACE::fork(); if (pid == 0) { // Child process char foo[30]; @@ -55,7 +55,7 @@ void stack_smashing_abort() { } ASSERT_TRUE(pid > 0); int status; - pid_t cpid = __llvm_libc::wait(&status); + pid_t cpid = LIBC_NAMESPACE::wait(&status); ASSERT_TRUE(cpid > 0); ASSERT_EQ(cpid, pid); ASSERT_TRUE(WTERMSIG(status) == SIGABRT);