Skip to content

Commit 28245b4

Browse files
authored
[libc] Add x86-64 stack protector support.
1 parent eb1905d commit 28245b4

File tree

4 files changed

+112
-1
lines changed

4 files changed

+112
-1
lines changed

libc/startup/linux/x86_64/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_startup_object(
1010
libc.src.__support.threads.thread
1111
libc.src.__support.OSUtil.osutil
1212
libc.src.stdlib.exit
13+
libc.src.stdlib.abort
1314
libc.src.stdlib.atexit
1415
libc.src.string.memory_utils.inline_memcpy
1516
libc.src.unistd.environ

libc/startup/linux/x86_64/start.cpp

+20-1
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "config/linux/app.h"
10+
#include "src/__support/OSUtil/io.h"
1011
#include "src/__support/OSUtil/syscall.h"
1112
#include "src/__support/threads/thread.h"
13+
#include "src/stdlib/abort.h"
1214
#include "src/stdlib/atexit.h"
1315
#include "src/stdlib/exit.h"
1416
#include "src/string/memory_utils/inline_memcpy.h"
@@ -23,6 +25,11 @@
2325

2426
extern "C" int main(int, char **, char **);
2527

28+
extern "C" void __stack_chk_fail() {
29+
LIBC_NAMESPACE::write_to_stderr("stack smashing detected");
30+
LIBC_NAMESPACE::abort();
31+
}
32+
2633
namespace LIBC_NAMESPACE {
2734

2835
#ifdef SYS_mmap2
@@ -54,7 +61,9 @@ void init_tls(TLSDescriptor &tls_descriptor) {
5461
// Per the x86_64 TLS ABI, the entry pointed to by the thread pointer is the
5562
// address of the TLS block. So, we add more size to accomodate this address
5663
// entry.
57-
uintptr_t tlsSizeWithAddr = tlsSize + sizeof(uintptr_t);
64+
// We also need to include space for the stack canary. The canary is at
65+
// offset 0x28 (40) and is of size uintptr_t.
66+
uintptr_t tlsSizeWithAddr = tlsSize + sizeof(uintptr_t) + 40;
5867

5968
// We cannot call the mmap function here as the functions set errno on
6069
// failure. Since errno is implemented via a thread local variable, we cannot
@@ -76,6 +85,16 @@ void init_tls(TLSDescriptor &tls_descriptor) {
7685
LIBC_NAMESPACE::inline_memcpy(reinterpret_cast<char *>(tlsAddr),
7786
reinterpret_cast<const char *>(app.tls.address),
7887
app.tls.init_size);
88+
uintptr_t *stackGuardAddr = reinterpret_cast<uintptr_t *>(endPtr + 40);
89+
// Setting the stack guard to a random value.
90+
// We cannot call the get_random function here as the function sets errno on
91+
// failure. Since errno is implemented via a thread local variable, we cannot
92+
// use errno before TLS is setup.
93+
ssize_t stackGuardRetVal = LIBC_NAMESPACE::syscall_impl<ssize_t>(
94+
SYS_getrandom, reinterpret_cast<long>(stackGuardAddr), sizeof(uint64_t),
95+
0);
96+
if (stackGuardRetVal < 0)
97+
LIBC_NAMESPACE::syscall_impl(SYS_exit, 1);
7998

8099
tls_descriptor = {tlsSizeWithAddr, uintptr_t(tlsAddr), endPtr};
81100
return;

libc/test/integration/src/unistd/CMakeLists.txt

+23
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,29 @@ add_integration_test(
3333
libc.src.unistd.fork
3434
)
3535

36+
if((${LIBC_TARGET_OS} STREQUAL "linux") AND (${LIBC_TARGET_ARCHITECTURE_IS_X86}))
37+
add_integration_test(
38+
stack_smashing_test
39+
SUITE
40+
unistd-integration-tests
41+
SRCS
42+
stack_smashing_test.cpp
43+
DEPENDS
44+
libc.include.errno
45+
libc.include.signal
46+
libc.include.sys_wait
47+
libc.include.unistd
48+
libc.src.pthread.pthread_atfork
49+
libc.src.signal.raise
50+
libc.src.sys.wait.wait
51+
libc.src.sys.wait.wait4
52+
libc.src.sys.wait.waitpid
53+
libc.src.unistd.fork
54+
COMPILE_OPTIONS
55+
-fstack-protector-all
56+
)
57+
endif()
58+
3659
add_executable(
3760
libc_execv_test_normal_exit
3861
EXCLUDE_FROM_ALL
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//===--- Stack smashing test to check stack canary set up ----------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/__support/CPP/string.h"
10+
#include "src/__support/OSUtil/io.h"
11+
#include "src/pthread/pthread_atfork.h"
12+
#include "src/signal/raise.h"
13+
#include "src/sys/wait/wait.h"
14+
#include "src/sys/wait/wait4.h"
15+
#include "src/sys/wait/waitpid.h"
16+
#include "src/unistd/fork.h"
17+
18+
#include "test/IntegrationTest/test.h"
19+
20+
#include <errno.h>
21+
#include <signal.h>
22+
#include <sys/wait.h>
23+
#include <unistd.h>
24+
25+
void no_stack_smashing_normal_exit() {
26+
pid_t pid = LIBC_NAMESPACE::fork();
27+
if (pid == 0) {
28+
// Child process
29+
char foo[30];
30+
for (int i = 0; i < 30; i++)
31+
foo[i] = (foo[i] != 42) ? 42 : 24;
32+
return;
33+
}
34+
ASSERT_TRUE(pid > 0);
35+
int status;
36+
pid_t cpid = LIBC_NAMESPACE::wait(&status);
37+
ASSERT_TRUE(cpid > 0);
38+
ASSERT_EQ(cpid, pid);
39+
ASSERT_TRUE(WIFEXITED(status));
40+
}
41+
42+
void stack_smashing_abort() {
43+
pid_t pid = LIBC_NAMESPACE::fork();
44+
if (pid == 0) {
45+
// Child process
46+
char foo[30];
47+
char *frame_ptr = static_cast<char *>(__builtin_frame_address(0));
48+
char *cur_ptr = &foo[0];
49+
// Corrupt the stack
50+
while (cur_ptr != frame_ptr) {
51+
*cur_ptr = (*cur_ptr != 42) ? 42 : 24;
52+
cur_ptr++;
53+
}
54+
return;
55+
}
56+
ASSERT_TRUE(pid > 0);
57+
int status;
58+
pid_t cpid = LIBC_NAMESPACE::wait(&status);
59+
ASSERT_TRUE(cpid > 0);
60+
ASSERT_EQ(cpid, pid);
61+
ASSERT_TRUE(WTERMSIG(status) == SIGABRT);
62+
}
63+
64+
TEST_MAIN(int argc, char **argv, char **envp) {
65+
no_stack_smashing_normal_exit();
66+
stack_smashing_abort();
67+
return 0;
68+
}

0 commit comments

Comments
 (0)