Skip to content

Instantly share code, notes, and snippets.

@scottt
Created June 11, 2018 23:12
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scottt/9f770c3ce2a349f1b6576662a8d70c75 to your computer and use it in GitHub Desktop.
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
/* 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