In pthread, After reaching yellow zone in stack, signal handler stop the recursive function by making it return
however, we can only continue to use extra area in yellow zone,
how to clear the rubbish before the yellow zone in the thread stack ?
(Copied from "answers"):
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#include <sys/mman.h>
#include <unistd.h>
#include <assert.h>
#include <sys/resource.h>
#define ALT_STACK_SIZE (64*1024)
#define YELLOW_ZONE_PAGES (1)
typedef struct {
size_t stack_size;
char* stack_pointer;
char* red_zone_boundary;
char* yellow_zone_boundary;
sigjmp_buf return_point;
size_t red_zone_size;
} ThreadInfo;
static pthread_key_t thread_info_key;
static struct sigaction newAct, oldAct;
bool gofromyellow = false;
int call_times = 0;
static void main_routine(){
// make it overflow
if(gofromyellow == true)
{
printf("return from yellow zone, called %d times\n", call_times);
return;
}
else
{
call_times = call_times + 1;
main_routine();
gofromyellow = true;
}
}
// red zone management
static void stackoverflow_routine(){
fprintf(stderr, "stack overflow error.\n");
fflush(stderr);
}
// yellow zone management
static void yellow_zone_hook(){
fprintf(stderr, "exceed yellow zone.\n");
fflush(stderr);
}
static int get_stack_info(void** stackaddr, size_t* stacksize){
int ret = -1;
pthread_attr_t attr;
pthread_attr_init(&attr);
if(pthread_getattr_np(pthread_self(), &attr) == 0){
ret = pthread_attr_getstack(&attr, stackaddr, stacksize);
}
pthread_attr_destroy(&attr);
return ret;
}
static int is_in_stack(const ThreadInfo* tinfo, char* pointer){
return (tinfo->stack_pointer <= pointer) && (pointer < tinfo->stack_pointer + tinfo->stack_size);
}
static int is_in_red_zone(const ThreadInfo* tinfo, char* pointer){
if(tinfo->red_zone_boundary){
return (tinfo->stack_pointer <= pointer) && (pointer < tinfo->red_zone_boundary);
}
}
static int is_in_yellow_zone(const ThreadInfo* tinfo, char* pointer){
if(tinfo->yellow_zone_boundary){
return (tinfo->red_zone_boundary <= pointer) && (pointer < tinfo->yellow_zone_boundary);
}
}
static void set_yellow_zone(ThreadInfo* tinfo){
int pagesize = sysconf(_SC_PAGE_SIZE);
assert(pagesize > 0);
tinfo->yellow_zone_boundary = tinfo->red_zone_boundary + pagesize * YELLOW_ZONE_PAGES;
mprotect(tinfo->red_zone_boundary, pagesize * YELLOW_ZONE_PAGES, PROT_NONE);
}
static void reset_yellow_zone(ThreadInfo* tinfo){
size_t pagesize = tinfo->yellow_zone_boundary - tinfo->red_zone_boundary;
if(mmap(tinfo->red_zone_boundary, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0) == 0){
perror("mmap failed"), exit(1);
}
mprotect(tinfo->red_zone_boundary, pagesize, PROT_READ | PROT_WRITE);
tinfo->yellow_zone_boundary = 0;
}
static void signal_handler(int sig, siginfo_t* sig_info, void* sig_data){
if(sig == SIGSEGV){
ThreadInfo* tinfo = (ThreadInfo*) pthread_getspecific(thread_info_key);
char* fault_address = (char*) sig_info->si_addr;
if(is_in_stack(tinfo, fault_address)){
if(is_in_red_zone(tinfo, fault_address)){
siglongjmp(tinfo->return_point, 1);
}else if(is_in_yellow_zone(tinfo, fault_address)){
reset_yellow_zone(tinfo);
yellow_zone_hook();
gofromyellow = true;
return;
} else {
//inside stack not related overflow SEGV happen
}
}
}
}
static void register_application_info(){
pthread_key_create(&thread_info_key, NULL);
sigemptyset(&newAct.sa_mask);
sigaddset(&newAct.sa_mask, SIGSEGV);
newAct.sa_sigaction = signal_handler;
newAct.sa_flags = SA_SIGINFO | SA_RESTART | SA_ONSTACK;
sigaction(SIGSEGV, &newAct, &oldAct);
}
static void register_thread_info(ThreadInfo* tinfo){
stack_t ss;
pthread_setspecific(thread_info_key, tinfo);
get_stack_info((void**)&tinfo->stack_pointer, &tinfo->stack_size);
printf("stack size %d mb\n", tinfo->stack_size/1024/1024 );
tinfo->red_zone_boundary = tinfo->stack_pointer + tinfo->red_zone_size;
set_yellow_zone(tinfo);
ss.ss_sp = (char*)malloc(ALT_STACK_SIZE);
ss.ss_size = ALT_STACK_SIZE;
ss.ss_flags = 0;
sigaltstack(&ss, NULL);
}
static void* thread_routine(void* p){
ThreadInfo* tinfo = (ThreadInfo*)p;
register_thread_info(tinfo);
if(sigsetjmp(tinfo->return_point, 1) == 0){
main_routine();
} else {
stackoverflow_routine();
}
free(tinfo);
printf("after tinfo, end thread\n");
return 0;
}
int main(int argc, char** argv){
register_application_info();
if( argc == 2 ){
int stacksize = atoi(argv[1]);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 1024 * 1024 * stacksize);
{
pthread_t pid0;
ThreadInfo* tinfo = (ThreadInfo*)calloc(1, sizeof(ThreadInfo));
pthread_attr_getguardsize(&attr, &tinfo->red_zone_size);
pthread_create(&pid0, &attr, thread_routine, tinfo);
pthread_join(pid0, NULL);
}
} else {
printf("Usage: %s stacksize(mb)\n", argv[0]);
}
return 0;
}
C language in linux, ubuntu