aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSchrodinger ZHU Yifan <yifanzhu@rochester.edu>2023-12-12 15:31:51 -0500
committerGitHub <noreply@github.com>2023-12-12 12:31:51 -0800
commit3568521e3d2d09d12bd0c476d0376de165b19178 (patch)
treea80334dce385db3cecfaeaf3d09424a8b1e58988
parent7f54070194f552e0c5de7543ad05e9ea163461ba (diff)
downloadllvm-3568521e3d2d09d12bd0c476d0376de165b19178.zip
llvm-3568521e3d2d09d12bd0c476d0376de165b19178.tar.gz
llvm-3568521e3d2d09d12bd0c476d0376de165b19178.tar.bz2
[libc] fix issues around stack protector (#74567)
If a function is declared with stack-protector, the compiler may try to load the TLS. However, inside certain runtime functions, TLS may not be available. This patch disables stack protectors for such routines to fix the problem. Closes #74487.
-rw-r--r--libc/src/__support/OSUtil/linux/quick_exit.h10
-rw-r--r--libc/startup/linux/aarch64/start.cpp14
-rw-r--r--libc/startup/linux/riscv/start.cpp14
-rw-r--r--libc/startup/linux/x86_64/CMakeLists.txt1
-rw-r--r--libc/startup/linux/x86_64/start.cpp14
5 files changed, 33 insertions, 20 deletions
diff --git a/libc/src/__support/OSUtil/linux/quick_exit.h b/libc/src/__support/OSUtil/linux/quick_exit.h
index c461d5f..4323955 100644
--- a/libc/src/__support/OSUtil/linux/quick_exit.h
+++ b/libc/src/__support/OSUtil/linux/quick_exit.h
@@ -9,7 +9,7 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_QUICK_EXIT_H
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_QUICK_EXIT_H
-#include "syscall.h" // For internal syscall function.
+#include "syscall.h" // For internal syscall function.
#include "src/__support/common.h"
@@ -17,7 +17,13 @@
namespace LIBC_NAMESPACE {
-LIBC_INLINE void quick_exit(int status) {
+// mark as no_stack_protector for x86 since TLS can be torn down before calling
+// quick_exit so that the stack protector canary cannot be loaded.
+#ifdef LIBC_TARGET_ARCH_IS_X86
+__attribute__((no_stack_protector))
+#endif
+LIBC_INLINE void
+quick_exit(int status) {
for (;;) {
LIBC_NAMESPACE::syscall_impl<long>(SYS_exit_group, status);
LIBC_NAMESPACE::syscall_impl<long>(SYS_exit, status);
diff --git a/libc/startup/linux/aarch64/start.cpp b/libc/startup/linux/aarch64/start.cpp
index b5c4268..c3e20eb 100644
--- a/libc/startup/linux/aarch64/start.cpp
+++ b/libc/startup/linux/aarch64/start.cpp
@@ -184,7 +184,9 @@ __attribute__((noinline)) static void do_start() {
app.tls.align = phdr->p_align;
}
- LIBC_NAMESPACE::TLSDescriptor tls;
+ // This descriptor has to be static since its cleanup function cannot
+ // capture the context.
+ static LIBC_NAMESPACE::TLSDescriptor tls;
LIBC_NAMESPACE::init_tls(tls);
if (tls.size != 0)
LIBC_NAMESPACE::set_thread_ptr(tls.tp);
@@ -192,7 +194,11 @@ __attribute__((noinline)) static void do_start() {
LIBC_NAMESPACE::self.attrib = &LIBC_NAMESPACE::main_thread_attrib;
LIBC_NAMESPACE::main_thread_attrib.atexit_callback_mgr =
LIBC_NAMESPACE::internal::get_thread_atexit_callback_mgr();
-
+ // We register the cleanup_tls function to be the last atexit callback to be
+ // invoked. It will tear down the TLS. Other callbacks may depend on TLS (such
+ // as the stack protector canary).
+ LIBC_NAMESPACE::atexit(
+ []() { LIBC_NAMESPACE::cleanup_tls(tls.tp, tls.size); });
// We want the fini array callbacks to be run after other atexit
// callbacks are run. So, we register them before running the init
// array callbacks as they can potentially register their own atexit
@@ -208,10 +214,6 @@ __attribute__((noinline)) static void do_start() {
reinterpret_cast<char **>(app.args->argv),
reinterpret_cast<char **>(env_ptr));
- // TODO: TLS cleanup should be done after all other atexit callbacks
- // are run. So, register a cleanup callback for it with atexit before
- // everything else.
- LIBC_NAMESPACE::cleanup_tls(tls.addr, tls.size);
LIBC_NAMESPACE::exit(retval);
}
diff --git a/libc/startup/linux/riscv/start.cpp b/libc/startup/linux/riscv/start.cpp
index bf04be5..4d37662 100644
--- a/libc/startup/linux/riscv/start.cpp
+++ b/libc/startup/linux/riscv/start.cpp
@@ -187,7 +187,9 @@ __attribute__((noinline)) static void do_start() {
app.tls.align = phdr->p_align;
}
- LIBC_NAMESPACE::TLSDescriptor tls;
+ // This descriptor has to be static since its cleanup function cannot
+ // capture the context.
+ static LIBC_NAMESPACE::TLSDescriptor tls;
LIBC_NAMESPACE::init_tls(tls);
if (tls.size != 0)
LIBC_NAMESPACE::set_thread_ptr(tls.tp);
@@ -195,7 +197,11 @@ __attribute__((noinline)) static void do_start() {
LIBC_NAMESPACE::self.attrib = &LIBC_NAMESPACE::main_thread_attrib;
LIBC_NAMESPACE::main_thread_attrib.atexit_callback_mgr =
LIBC_NAMESPACE::internal::get_thread_atexit_callback_mgr();
-
+ // We register the cleanup_tls function to be the last atexit callback to be
+ // invoked. It will tear down the TLS. Other callbacks may depend on TLS (such
+ // as the stack protector canary).
+ LIBC_NAMESPACE::atexit(
+ []() { LIBC_NAMESPACE::cleanup_tls(tls.tp, tls.size); });
// We want the fini array callbacks to be run after other atexit
// callbacks are run. So, we register them before running the init
// array callbacks as they can potentially register their own atexit
@@ -211,10 +217,6 @@ __attribute__((noinline)) static void do_start() {
reinterpret_cast<char **>(app.args->argv),
reinterpret_cast<char **>(env_ptr));
- // TODO: TLS cleanup should be done after all other atexit callbacks
- // are run. So, register a cleanup callback for it with atexit before
- // everything else.
- LIBC_NAMESPACE::cleanup_tls(tls.addr, tls.size);
LIBC_NAMESPACE::exit(retval);
}
diff --git a/libc/startup/linux/x86_64/CMakeLists.txt b/libc/startup/linux/x86_64/CMakeLists.txt
index 076c0c3..aac5a06 100644
--- a/libc/startup/linux/x86_64/CMakeLists.txt
+++ b/libc/startup/linux/x86_64/CMakeLists.txt
@@ -15,6 +15,7 @@ add_startup_object(
libc.src.string.memory_utils.inline_memcpy
libc.src.unistd.environ
COMPILE_OPTIONS
+ -fno-stack-protector
-fno-omit-frame-pointer
-ffreestanding # To avoid compiler warnings about calling the main function.
-fno-builtin
diff --git a/libc/startup/linux/x86_64/start.cpp b/libc/startup/linux/x86_64/start.cpp
index bc1b4f0..496105d 100644
--- a/libc/startup/linux/x86_64/start.cpp
+++ b/libc/startup/linux/x86_64/start.cpp
@@ -222,7 +222,9 @@ extern "C" void _start() {
app.tls.align = phdr->p_align;
}
- LIBC_NAMESPACE::TLSDescriptor tls;
+ // This descriptor has to be static since its cleanup function cannot
+ // capture the context.
+ static LIBC_NAMESPACE::TLSDescriptor tls;
LIBC_NAMESPACE::init_tls(tls);
if (tls.size != 0 && !LIBC_NAMESPACE::set_thread_ptr(tls.tp))
LIBC_NAMESPACE::syscall_impl<long>(SYS_exit, 1);
@@ -230,7 +232,11 @@ extern "C" void _start() {
LIBC_NAMESPACE::self.attrib = &LIBC_NAMESPACE::main_thread_attrib;
LIBC_NAMESPACE::main_thread_attrib.atexit_callback_mgr =
LIBC_NAMESPACE::internal::get_thread_atexit_callback_mgr();
-
+ // We register the cleanup_tls function to be the last atexit callback to be
+ // invoked. It will tear down the TLS. Other callbacks may depend on TLS (such
+ // as the stack protector canary).
+ LIBC_NAMESPACE::atexit(
+ []() { LIBC_NAMESPACE::cleanup_tls(tls.tp, tls.size); });
// We want the fini array callbacks to be run after other atexit
// callbacks are run. So, we register them before running the init
// array callbacks as they can potentially register their own atexit
@@ -246,9 +252,5 @@ extern "C" void _start() {
reinterpret_cast<char **>(app.args->argv),
reinterpret_cast<char **>(env_ptr));
- // TODO: TLS cleanup should be done after all other atexit callbacks
- // are run. So, register a cleanup callback for it with atexit before
- // everything else.
- LIBC_NAMESPACE::cleanup_tls(tls.addr, tls.size);
LIBC_NAMESPACE::exit(retval);
}