Skip to content

Commit 3568521

Browse files
[libc] fix issues around stack protector (llvm#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 llvm#74487.
1 parent 7f54070 commit 3568521

File tree

5 files changed

+33
-20
lines changed

5 files changed

+33
-20
lines changed

libc/src/__support/OSUtil/linux/quick_exit.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,21 @@
99
#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_QUICK_EXIT_H
1010
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_QUICK_EXIT_H
1111

12-
#include "syscall.h" // For internal syscall function.
12+
#include "syscall.h" // For internal syscall function.
1313

1414
#include "src/__support/common.h"
1515

1616
#include <sys/syscall.h> // For syscall numbers.
1717

1818
namespace LIBC_NAMESPACE {
1919

20-
LIBC_INLINE void quick_exit(int status) {
20+
// mark as no_stack_protector for x86 since TLS can be torn down before calling
21+
// quick_exit so that the stack protector canary cannot be loaded.
22+
#ifdef LIBC_TARGET_ARCH_IS_X86
23+
__attribute__((no_stack_protector))
24+
#endif
25+
LIBC_INLINE void
26+
quick_exit(int status) {
2127
for (;;) {
2228
LIBC_NAMESPACE::syscall_impl<long>(SYS_exit_group, status);
2329
LIBC_NAMESPACE::syscall_impl<long>(SYS_exit, status);

libc/startup/linux/aarch64/start.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -184,15 +184,21 @@ __attribute__((noinline)) static void do_start() {
184184
app.tls.align = phdr->p_align;
185185
}
186186

187-
LIBC_NAMESPACE::TLSDescriptor tls;
187+
// This descriptor has to be static since its cleanup function cannot
188+
// capture the context.
189+
static LIBC_NAMESPACE::TLSDescriptor tls;
188190
LIBC_NAMESPACE::init_tls(tls);
189191
if (tls.size != 0)
190192
LIBC_NAMESPACE::set_thread_ptr(tls.tp);
191193

192194
LIBC_NAMESPACE::self.attrib = &LIBC_NAMESPACE::main_thread_attrib;
193195
LIBC_NAMESPACE::main_thread_attrib.atexit_callback_mgr =
194196
LIBC_NAMESPACE::internal::get_thread_atexit_callback_mgr();
195-
197+
// We register the cleanup_tls function to be the last atexit callback to be
198+
// invoked. It will tear down the TLS. Other callbacks may depend on TLS (such
199+
// as the stack protector canary).
200+
LIBC_NAMESPACE::atexit(
201+
[]() { LIBC_NAMESPACE::cleanup_tls(tls.tp, tls.size); });
196202
// We want the fini array callbacks to be run after other atexit
197203
// callbacks are run. So, we register them before running the init
198204
// array callbacks as they can potentially register their own atexit
@@ -208,10 +214,6 @@ __attribute__((noinline)) static void do_start() {
208214
reinterpret_cast<char **>(app.args->argv),
209215
reinterpret_cast<char **>(env_ptr));
210216

211-
// TODO: TLS cleanup should be done after all other atexit callbacks
212-
// are run. So, register a cleanup callback for it with atexit before
213-
// everything else.
214-
LIBC_NAMESPACE::cleanup_tls(tls.addr, tls.size);
215217
LIBC_NAMESPACE::exit(retval);
216218
}
217219

libc/startup/linux/riscv/start.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,15 +187,21 @@ __attribute__((noinline)) static void do_start() {
187187
app.tls.align = phdr->p_align;
188188
}
189189

190-
LIBC_NAMESPACE::TLSDescriptor tls;
190+
// This descriptor has to be static since its cleanup function cannot
191+
// capture the context.
192+
static LIBC_NAMESPACE::TLSDescriptor tls;
191193
LIBC_NAMESPACE::init_tls(tls);
192194
if (tls.size != 0)
193195
LIBC_NAMESPACE::set_thread_ptr(tls.tp);
194196

195197
LIBC_NAMESPACE::self.attrib = &LIBC_NAMESPACE::main_thread_attrib;
196198
LIBC_NAMESPACE::main_thread_attrib.atexit_callback_mgr =
197199
LIBC_NAMESPACE::internal::get_thread_atexit_callback_mgr();
198-
200+
// We register the cleanup_tls function to be the last atexit callback to be
201+
// invoked. It will tear down the TLS. Other callbacks may depend on TLS (such
202+
// as the stack protector canary).
203+
LIBC_NAMESPACE::atexit(
204+
[]() { LIBC_NAMESPACE::cleanup_tls(tls.tp, tls.size); });
199205
// We want the fini array callbacks to be run after other atexit
200206
// callbacks are run. So, we register them before running the init
201207
// array callbacks as they can potentially register their own atexit
@@ -211,10 +217,6 @@ __attribute__((noinline)) static void do_start() {
211217
reinterpret_cast<char **>(app.args->argv),
212218
reinterpret_cast<char **>(env_ptr));
213219

214-
// TODO: TLS cleanup should be done after all other atexit callbacks
215-
// are run. So, register a cleanup callback for it with atexit before
216-
// everything else.
217-
LIBC_NAMESPACE::cleanup_tls(tls.addr, tls.size);
218220
LIBC_NAMESPACE::exit(retval);
219221
}
220222

libc/startup/linux/x86_64/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ add_startup_object(
1515
libc.src.string.memory_utils.inline_memcpy
1616
libc.src.unistd.environ
1717
COMPILE_OPTIONS
18+
-fno-stack-protector
1819
-fno-omit-frame-pointer
1920
-ffreestanding # To avoid compiler warnings about calling the main function.
2021
-fno-builtin

libc/startup/linux/x86_64/start.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -222,15 +222,21 @@ extern "C" void _start() {
222222
app.tls.align = phdr->p_align;
223223
}
224224

225-
LIBC_NAMESPACE::TLSDescriptor tls;
225+
// This descriptor has to be static since its cleanup function cannot
226+
// capture the context.
227+
static LIBC_NAMESPACE::TLSDescriptor tls;
226228
LIBC_NAMESPACE::init_tls(tls);
227229
if (tls.size != 0 && !LIBC_NAMESPACE::set_thread_ptr(tls.tp))
228230
LIBC_NAMESPACE::syscall_impl<long>(SYS_exit, 1);
229231

230232
LIBC_NAMESPACE::self.attrib = &LIBC_NAMESPACE::main_thread_attrib;
231233
LIBC_NAMESPACE::main_thread_attrib.atexit_callback_mgr =
232234
LIBC_NAMESPACE::internal::get_thread_atexit_callback_mgr();
233-
235+
// We register the cleanup_tls function to be the last atexit callback to be
236+
// invoked. It will tear down the TLS. Other callbacks may depend on TLS (such
237+
// as the stack protector canary).
238+
LIBC_NAMESPACE::atexit(
239+
[]() { LIBC_NAMESPACE::cleanup_tls(tls.tp, tls.size); });
234240
// We want the fini array callbacks to be run after other atexit
235241
// callbacks are run. So, we register them before running the init
236242
// array callbacks as they can potentially register their own atexit
@@ -246,9 +252,5 @@ extern "C" void _start() {
246252
reinterpret_cast<char **>(app.args->argv),
247253
reinterpret_cast<char **>(env_ptr));
248254

249-
// TODO: TLS cleanup should be done after all other atexit callbacks
250-
// are run. So, register a cleanup callback for it with atexit before
251-
// everything else.
252-
LIBC_NAMESPACE::cleanup_tls(tls.addr, tls.size);
253255
LIBC_NAMESPACE::exit(retval);
254256
}

0 commit comments

Comments
 (0)