2 * Copyright (c) 2023 Alexey Dobriyan <adobriyan@gmail.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 * Test that userspace stack is NX. Requires linking with -Wl,-z,noexecstack
18 * because I don't want to bother with PT_GNU_STACK detection.
20 * Fill the stack with INT3's and then try to execute some of them:
21 * SIGSEGV -- good, SIGTRAP -- bad.
23 * Regular stack is completely overwritten before testing.
24 * Test doesn't exit SIGSEGV handler after first fault at INT3.
35 #include <sys/resource.h>
38 #define PAGE_SIZE 4096
41 * This is memset(rsp, 0xcc, -1); but down.
42 * It will SIGSEGV when bottom of the stack is reached.
43 * Byte-size access is important! (see rdi tweak in the signal handler).
45 void make_stack1(void);
47 ".pushsection .text\n"
48 ".globl make_stack1\n"
55 #elif defined __i386__
65 ".type make_stack1,@function\n"
66 ".size make_stack1,.-make_stack1\n"
71 * memset(p, 0xcc, -1);
72 * It will SIGSEGV when top of the stack is reached.
74 void make_stack2(uint64_t p
);
76 ".pushsection .text\n"
77 ".globl make_stack2\n"
83 #elif defined __i386__
92 ".type make_stack2,@function\n"
93 ".size make_stack2,.-make_stack2\n"
97 static volatile int test_state
= 0;
98 static volatile unsigned long stack_min_addr
;
100 #if defined __amd64__
103 #define RIP_STRING "rip"
104 #elif defined __i386__
107 #define RIP_STRING "eip"
112 static void sigsegv(int _
, siginfo_t
*__
, void *uc_
)
115 * Some Linux versions didn't clear DF before entering signal
116 * handler. make_stack1() doesn't have a chance to clear DF
117 * either so we clear it by hand here.
119 asm volatile ("cld" ::: "memory");
121 ucontext_t
*uc
= uc_
;
123 if (test_state
== 0) {
124 /* Stack is faulted and cleared from RSP to the lowest address. */
125 stack_min_addr
= ++uc
->uc_mcontext
.gregs
[RDI
];
127 printf("stack min %lx\n", stack_min_addr
);
129 uc
->uc_mcontext
.gregs
[RIP
] = (uintptr_t)&make_stack2
;
131 } else if (test_state
== 1) {
132 /* Stack has been cleared from top to bottom. */
133 unsigned long stack_max_addr
= uc
->uc_mcontext
.gregs
[RDI
];
135 printf("stack max %lx\n", stack_max_addr
);
137 /* Start faulting pages on stack and see what happens. */
138 uc
->uc_mcontext
.gregs
[RIP
] = stack_max_addr
- PAGE_SIZE
;
140 } else if (test_state
== 2) {
141 /* Stack page is NX -- good, test next page. */
142 uc
->uc_mcontext
.gregs
[RIP
] -= PAGE_SIZE
;
143 if (uc
->uc_mcontext
.gregs
[RIP
] == stack_min_addr
) {
144 /* One more SIGSEGV and test ends. */
148 printf("PASS\tAll stack pages are NX\n");
153 static void sigtrap(int _
, siginfo_t
*__
, void *uc_
)
155 const ucontext_t
*uc
= uc_
;
156 unsigned long rip
= uc
->uc_mcontext
.gregs
[RIP
];
157 printf("FAIL\texecutable page on the stack: " RIP_STRING
" %lx\n", rip
);
164 struct sigaction act
= {};
165 sigemptyset(&act
.sa_mask
);
166 act
.sa_flags
= SA_SIGINFO
;
167 act
.sa_sigaction
= &sigsegv
;
168 int rv
= sigaction(SIGSEGV
, &act
, NULL
);
172 struct sigaction act
= {};
173 sigemptyset(&act
.sa_mask
);
174 act
.sa_flags
= SA_SIGINFO
;
175 act
.sa_sigaction
= &sigtrap
;
176 int rv
= sigaction(SIGTRAP
, &act
, NULL
);
181 int rv
= getrlimit(RLIMIT_STACK
, &rlim
);
183 /* Cap stack at time-honored 8 MiB value. */
184 rlim
.rlim_max
= rlim
.rlim_cur
;
185 if (rlim
.rlim_max
> 8 * 1024 * 1024) {
186 rlim
.rlim_max
= 8 * 1024 * 1024;
188 rv
= setrlimit(RLIMIT_STACK
, &rlim
);
193 * We don't know now much stack SIGSEGV handler uses.
194 * Bump this by 1 page every time someone complains,
195 * or rewrite it in assembly.
197 const size_t len
= SIGSTKSZ
;
198 void *p
= mmap(NULL
, len
, PROT_READ
|PROT_WRITE
, MAP_PRIVATE
|MAP_ANONYMOUS
, -1, 0);
199 assert(p
!= MAP_FAILED
);
203 int rv
= sigaltstack(&ss
, NULL
);
208 * Unreachable, but if _this_ INT3 is ever reached, it's a bug somewhere.
209 * Fold it into main SIGTRAP pathway.