-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[libc] fix issues around stack protector #74567
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[libc] fix issues around stack protector #74567
Conversation
If a function is declared with stack-protector, the compiler may try to load the TLS. However, inside the certain runtime functions, TLS may not be available. This patch disables stack protectors for certain routines to fix the problem. Closes llvm#74487.
@llvm/pr-subscribers-libc Author: Schrodinger ZHU Yifan (SchrodingerZhu) ChangesIf a function is declared with stack-protector, the compiler may try to load the TLS. However, inside the certain runtime functions, TLS may not be available. This patch disables stack protectors for certain routines to fix the problem. Closes #74487. Full diff: https://github.com/llvm/llvm-project/pull/74567.diff 6 Files Affected:
diff --git a/libc/src/__support/OSUtil/linux/quick_exit.h b/libc/src/__support/OSUtil/linux/quick_exit.h
index c461d5f8b8fcb..43d9f82784109 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,9 @@
namespace LIBC_NAMESPACE {
-LIBC_INLINE void quick_exit(int status) {
+// mark as no_stack_protector since TLS can be torn down before calling
+// quick_exit
+__attribute__((no_stack_protector)) 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/CMakeLists.txt b/libc/startup/linux/aarch64/CMakeLists.txt
index b47db8eb5d23f..ccd9bbcb9b63d 100644
--- a/libc/startup/linux/aarch64/CMakeLists.txt
+++ b/libc/startup/linux/aarch64/CMakeLists.txt
@@ -13,6 +13,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.
)
diff --git a/libc/startup/linux/aarch64/start.cpp b/libc/startup/linux/aarch64/start.cpp
index b5c426866b56d..858995203d9b2 100644
--- a/libc/startup/linux/aarch64/start.cpp
+++ b/libc/startup/linux/aarch64/start.cpp
@@ -184,7 +184,7 @@ __attribute__((noinline)) static void do_start() {
app.tls.align = phdr->p_align;
}
- LIBC_NAMESPACE::TLSDescriptor tls;
+ static LIBC_NAMESPACE::TLSDescriptor tls;
LIBC_NAMESPACE::init_tls(tls);
if (tls.size != 0)
LIBC_NAMESPACE::set_thread_ptr(tls.tp);
@@ -193,6 +193,8 @@ __attribute__((noinline)) static void do_start() {
LIBC_NAMESPACE::main_thread_attrib.atexit_callback_mgr =
LIBC_NAMESPACE::internal::get_thread_atexit_callback_mgr();
+ 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 +210,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 bf04be5ad14ad..8bd76631f402c 100644
--- a/libc/startup/linux/riscv/start.cpp
+++ b/libc/startup/linux/riscv/start.cpp
@@ -187,7 +187,7 @@ __attribute__((noinline)) static void do_start() {
app.tls.align = phdr->p_align;
}
- LIBC_NAMESPACE::TLSDescriptor tls;
+ static LIBC_NAMESPACE::TLSDescriptor tls;
LIBC_NAMESPACE::init_tls(tls);
if (tls.size != 0)
LIBC_NAMESPACE::set_thread_ptr(tls.tp);
@@ -196,6 +196,8 @@ __attribute__((noinline)) static void do_start() {
LIBC_NAMESPACE::main_thread_attrib.atexit_callback_mgr =
LIBC_NAMESPACE::internal::get_thread_atexit_callback_mgr();
+ 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 +213,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 076c0c3e444f5..aac5a0626a176 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 bc1b4f0487f31..96031723e009a 100644
--- a/libc/startup/linux/x86_64/start.cpp
+++ b/libc/startup/linux/x86_64/start.cpp
@@ -222,7 +222,7 @@ extern "C" void _start() {
app.tls.align = phdr->p_align;
}
- LIBC_NAMESPACE::TLSDescriptor tls;
+ 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);
@@ -231,6 +231,8 @@ extern "C" void _start() {
LIBC_NAMESPACE::main_thread_attrib.atexit_callback_mgr =
LIBC_NAMESPACE::internal::get_thread_atexit_callback_mgr();
+ 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 +248,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);
}
|
I thought certain platforms have a global vs thread local stack canary? |
Could you please give a link to the specifications? I wish I can add links in the comment and gate the attributes accordingly. |
@nickdesaulniers it does not seem to be arch dependent on linux: https://github.com/bminor/musl/blob/f314e133929b6379eccc632bef32eaebb66a7335/src/env/__stack_chk_fail.c#L21C6-L21C6 Update |
I updated the patch to disable stack protector explicitly for |
This is only from observation; my prior work on the Linux kernel involved getting additional functionality added to clang for the kernel's more exotic stack protector implementations. The kernel doesn't share the same ABI as userspace, so it can break its own internal ABI whenever, which allows for it to do better than the ABI constrained userspace. I'd guess maybe the Itanium x86 psABI, ARM ARM, and RISCV psABI might have specific details, but I don't know. I've only observed behavior by playing with the compiler. |
I think we are doing the right thing. For documentation, I suggest us reading
and all its default/override implementations. |
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.