【问题标题】:how to clear stack after stack overflow signal occur堆栈溢出信号发生后如何清除堆栈
【发布时间】:2010-06-06 08:37:19
【问题描述】:

在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;
}

linux、ubuntu中的C语言

【问题讨论】:

  • 请指定您谈论的平台/语言

标签: c multithreading posix pthreads


【解决方案1】:

信号处理程序是处理程序错误的不好方法。它们本质上是异步的,您可以在其中做的很少。

递归函数也是一个坏主意——你不能保证你不会溢出堆栈。是的,您分配了堆栈大小,是的,您正在处理溢出警告,但是必须有更好的方法来做您想做的事情……只需要一位同事在您的递归函数中添加一些堆栈局部变量,然后你改变可能的递归次数......

无论如何回答你原来的问题...

  • 在信号处理程序中,设置一个全局 (或线程局部)变量指示 堆栈溢出条件

  • 在 递归函数,检查 变量,如果已设置,请使用 'C's 魔术堆栈自动清理 关键字...

哦,忘了说那个神奇的堆栈清理关键字是什么:

return;

您还可以使用它在递归函数中返回错误条件,因此每个调用者将退出处理并将错误条件返回给其调用者...

【讨论】:

  • 可以'return'清除之前的所有调用吗?或者只是清除之前的一个电话?
  • '在递归函数中,检查变量'...并返回。这将返回到父递归函数,它将检查变量并返回它的父级,它将检查变量......等等......
  • 它确实有效,我可以看到它恢复到原来的 if(gofromyellow == true) { printf("从黄色区域返回,称为 %d 次\n",次);返回; } else { call_times = call_times + 1; main_routine(call_times); call_times = call_times - 1; printf("从黄区返回,%d 次\n", call_times); //gofromyellow = true; }
  • 除了递归调用使堆栈溢出信号发生,任何其他方式使堆栈溢出,都可以“返回”处理
  • 递归本身并没有错。您测试您的基本条件,您可以测试超过您选择设置的任何限制的深度。
【解决方案2】:

虽然我觉得这个问题有些令人困惑,但我怀疑您想要做的是使用siglongjmp 返回,就像您目前对红色区域所做的那样。这会将堆栈回退到sigsetjmp 点。

【讨论】:

    猜你喜欢
    • 2019-05-18
    • 2011-11-20
    • 2012-01-12
    • 1970-01-01
    • 2014-07-20
    • 2011-08-13
    • 1970-01-01
    • 1970-01-01
    • 2012-04-15
    相关资源
    最近更新 更多