From 28245b4ecb2baa74ab97320d6bf4ddcfe3a43429 Mon Sep 17 00:00:00 2001 From: tnv01 <119359509+tnv01@users.noreply.github.com> Date: Wed, 4 Oct 2023 14:18:23 -0700 Subject: [PATCH] [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 40f01836c131..076c0c3e444f 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 4b46a4f4a7b0..af95d2702ded 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() { + LIBC_NAMESPACE::write_to_stderr("stack smashing detected"); + LIBC_NAMESPACE::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 = LIBC_NAMESPACE::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 72fdb8fc7e6b..10aac212af35 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 000000000000..89fc53dac506 --- /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 = LIBC_NAMESPACE::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 = LIBC_NAMESPACE::wait(&status); + ASSERT_TRUE(cpid > 0); + ASSERT_EQ(cpid, pid); + ASSERT_TRUE(WIFEXITED(status)); +} + +void stack_smashing_abort() { + pid_t pid = LIBC_NAMESPACE::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 = LIBC_NAMESPACE::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; +}