Created
June 11, 2018 23:12
-
-
Save scottt/9f770c3ce2a349f1b6576662a8d70c75 to your computer and use it in GitHub Desktop.
stack-and-sp: test whether stack access without setting the stack pointer register would cause SEGFAULTs on x86-64
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* x86-64 program to test wether stack access without setting the stack poitner register would cause SEGFAULTs | |
* $ ./stack-and-sp # just access stack adjacent memory until SEGFAULT. | |
* $ ./stack-and-sp --set-sp # set stack pointer register before accessing memory. | |
* | |
* Output: | |
* stack-and-sp: do_set_sp: 1 | |
* RLIMIT_STACK: 8192/unlimited (cur/max) | |
* sp0: 0x7ffd5fdd3000 | |
* sp: 0x7ffd5f5d7000, size: 8176K | |
*/ | |
#include <sys/resource.h> | |
#include <assert.h> | |
#include <stdio.h> | |
#include <libgen.h> | |
#include <unistd.h> | |
#include <signal.h> | |
#include <string.h> | |
#include <setjmp.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
enum { STACK_POINTER_DECREASES = 1 }; /* SP decreases on stack growth, always true on x86. | |
Trying to avoid grow "down" or "up" terminology here. */ | |
char *program_name; | |
jmp_buf jbuf; | |
void segv_handler(int sig, siginfo_t *info, void *ctx) | |
{ | |
longjmp(jbuf, 1); | |
} | |
void rlim_print_kbyte(FILE *fout, rlim_t v) | |
{ | |
if (v == RLIM_INFINITY) | |
fprintf(fout, "unlimited"); | |
else | |
fprintf(fout, "%lu", v/1024UL); | |
} | |
int main(int argc, char **argv) | |
{ | |
char s, *sp, *sp0; | |
int r; | |
long page_size, sp_delta; | |
struct rlimit rlim; | |
struct sigaction sa = {0}; | |
int do_set_sp = 0; | |
stack_t sigstk; | |
program_name = basename(argv[0]); | |
if (argc == 2 && strcmp(argv[1], "--set-sp") == 0) | |
do_set_sp = 1; | |
fprintf(stderr, "%s: do_set_sp: %d\n", program_name, do_set_sp); | |
r = getrlimit(RLIMIT_STACK, &rlim); | |
assert(r == 0); | |
/* Output "RLIMIT_STACK: %lu/%lu (cur/max)" but handle RLIM_INFINITY */ | |
fprintf(stderr, "RLIMIT_STACK: "); | |
rlim_print_kbyte(stderr, rlim.rlim_cur); | |
fputc('/', stderr); | |
rlim_print_kbyte(stderr, rlim.rlim_max); | |
fprintf(stderr, " (cur/max)\n"); | |
/* Allocate alternative signal stack: | |
* 1. The SIGSEGV handler needs some stack space to run | |
* 2. The "sp += sp_delta" loop below could consume all available stack space | |
* Thus we must use an alternative signal stack to make 1. and 2. work togather */ | |
if ((sigstk.ss_sp = malloc(SIGSTKSZ)) == NULL) | |
assert(0); | |
sigstk.ss_size = SIGSTKSZ; | |
sigstk.ss_flags = 0; | |
if (sigaltstack(&sigstk, NULL) < 0) | |
assert(0); | |
sa.sa_sigaction = segv_handler; | |
sa.sa_flags = SA_SIGINFO | SA_ONSTACK; | |
r = sigaction(SIGSEGV, &sa, NULL); | |
assert(r == 0); | |
page_size = sysconf(_SC_PAGESIZE); | |
if (STACK_POINTER_DECREASES) { | |
sp_delta = -page_size; | |
/* align sp0 to page boundary */ | |
sp0 = (void*)((uint64_t)(&s) & (~(page_size-1))); | |
} else { | |
sp_delta = page_size; | |
sp0 = (void*)(((uint64_t)(&s) + page_size-1) & (~(page_size-1))); | |
} | |
fprintf(stderr, "sp0: %p\n", sp0); | |
for (sp = sp0; ; sp += sp_delta) { | |
if (setjmp(jbuf) != 0) | |
break; | |
if (do_set_sp) { | |
/* In general, changing SP with inline assembly will cause crashes in the surrounding code. | |
Thus here we set SP, access stack memory, then immediately restore SP. */ | |
__asm__ ( | |
"mov %%rsp, %%rcx;" | |
"mov %[sp], %%rsp;" | |
"movb $0xab, (%%rsp);" | |
"mov %%rcx, %%rsp;" | |
: /* outputs */ | |
: /* inputs */ [sp] "r" (sp) | |
: /* clobbers */ "rcx"); | |
} else { | |
*sp = 0xab; | |
} | |
} | |
fprintf(stderr, "sp: %p, size: %ldK\n", sp, labs(sp0 - sp)/1024); | |
bzero(&sa, sizeof(sa)); | |
sa.sa_handler = SIG_DFL; | |
r = sigaction(SIGSEGV, &sa, NULL); | |
assert(r == 0); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment