[libc-commits] [libc] Supporting stack protectors in linux x86-64 (PR #66456)
via libc-commits
libc-commits at lists.llvm.org
Wed Oct 4 05:50:13 PDT 2023
https://github.com/tnv01 updated https://github.com/llvm/llvm-project/pull/66456
>From f018d9f5573939b7bb2186c066392ed7635873d5 Mon Sep 17 00:00:00 2001
From: Tanay Vakharia <tvakharia at google.com>
Date: Wed, 13 Sep 2023 03:47:00 +0000
Subject: [PATCH 1/2] Adding a stack protector for linux x86-64.
Setting %fs:0x28 to a random value which is used as the stack protector cookie.
Differential Revision: https://reviews.llvm.org/D159508
---
libc/startup/linux/x86_64/CMakeLists.txt | 1 +
libc/startup/linux/x86_64/start.cpp | 17 ++++-
.../integration/src/unistd/CMakeLists.txt | 23 +++++++
.../src/unistd/stack_smashing_test.cpp | 68 +++++++++++++++++++
4 files changed, 108 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 75c8296ee9e676c..1ea16e994ab3eb5 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.memcpy_implementation
libc.src.unistd.environ
diff --git a/libc/startup/linux/x86_64/start.cpp b/libc/startup/linux/x86_64/start.cpp
index d4f2f49928ababf..780726ff8f53d1a 100644
--- 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 @@ 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 +81,16 @@ void init_tls(TLSDescriptor &tls_descriptor) {
__llvm_libc::inline_memcpy(reinterpret_cast<char *>(tlsAddr),
reinterpret_cast<const char *>(app.tls.address),
app.tls.init_size);
+ uintptr_t *stackGuardAddr = reinterpret_cast<uintptr_t *>(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<ssize_t>(
+ SYS_getrandom, reinterpret_cast<long>(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
index 72fdb8fc7e6b018..10aac212af355ea 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 000000000000000..c0eb8927f180587
--- /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 <errno.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+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<char *>(__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;
+}
>From 64cd61dd6d76ba41800fb924318b275cdba5ea8e Mon Sep 17 00:00:00 2001
From: Tanay Vakharia <tvakharia at google.com>
Date: Wed, 4 Oct 2023 12:47:25 +0000
Subject: [PATCH 2/2] Updating the tests based on the comments and adding a
failure message to __stack_chk_fail()
---
libc/startup/linux/x86_64/start.cpp | 6 +++++-
libc/test/integration/src/unistd/stack_smashing_test.cpp | 4 ++--
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/libc/startup/linux/x86_64/start.cpp b/libc/startup/linux/x86_64/start.cpp
index 780726ff8f53d1a..c9fd906f623ce8b 100644
--- a/libc/startup/linux/x86_64/start.cpp
+++ b/libc/startup/linux/x86_64/start.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#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"
@@ -24,7 +25,10 @@
extern "C" int main(int, char **, char **);
-extern "C" void __stack_chk_fail() { __llvm_libc::abort(); }
+extern "C" void __stack_chk_fail() {
+ __llvm_libc::write_to_stderr("stack smashing detected");
+ __llvm_libc::abort();
+}
namespace __llvm_libc {
diff --git a/libc/test/integration/src/unistd/stack_smashing_test.cpp b/libc/test/integration/src/unistd/stack_smashing_test.cpp
index c0eb8927f180587..f5bc935464a9b85 100644
--- a/libc/test/integration/src/unistd/stack_smashing_test.cpp
+++ b/libc/test/integration/src/unistd/stack_smashing_test.cpp
@@ -28,7 +28,7 @@ void no_stack_smashing_normal_exit() {
// Child process
char foo[30];
for (int i = 0; i < 30; i++)
- foo[i] = 42;
+ foo[i] = (foo[i] != 42) ? 42 : 24;
return;
}
ASSERT_TRUE(pid > 0);
@@ -48,7 +48,7 @@ void stack_smashing_abort() {
char *cur_ptr = &foo[0];
// Corrupt the stack
while (cur_ptr != frame_ptr) {
- *cur_ptr = 42;
+ *cur_ptr = (*cur_ptr != 42) ? 42 : 24;
cur_ptr++;
}
return;
More information about the libc-commits
mailing list