В pthread, После достижения желтой зоны в стеке обработчик сигнала останавливает рекурсивную функцию, возвращая ей
однако мы можем продолжать использовать только дополнительную область в желтой зоне,
как убрать мусор перед желтой зоной в стеке ниток?
(скопировано из "ответов") :
#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 в Linux, Ubuntu