在调用之前makecontext
为什么我们需要设置堆栈大小ss_size
?
我刚刚有一个片段的单元测试用例makecontext/swapcontext
,它失败了SIGSEGV
。发生的事情是堆栈大小太小并且不相关的内存(恰好是一些唯一的指针)被损坏并报告了段错误。所以段错误在这些不相关的指针上,我可能有一些字符串,然后内存损坏就不会被注意到。
我本以为当堆栈大小不够时会SIGSEGV
立即提高,但考虑到上述内存损坏,我得出结论不可能从这里恢复。这让我回到了这个问题,为什么我们需要首先设置堆栈大小,而不是用来发出溢出信号?这有什么用途?ss_size
SIGSEGV
编辑:
好吧,这都是关于makecontext(3)的。这些函数仍在用于绿色线程、协程等。考虑到这些任务(在我看来)也不是在 c++ 中,它们没有真正的替代品。
ss_size
在getcontext(3)中定义需要在sigaltstack(2)中定义。uc_stack
ucontext_t
按照上面描述的通过“绘制”内存来显示内存损坏的最小可验证示例。
#include <iostream>
#include <ucontext.h>
#include <memory>
#include <cstring>
#include <stdio.h>
#include <unistd.h>
ucontext_t caller, callee;
void cb(void){
//paint stack with 2
char tmp[7000];
std::memset(tmp,2,7000);
//note stack size specified 6k bytes in size
//this should not be allowed.
//furthermore there is not even some signal raised here
//i expected raised SIGSEGV when this call stack exceeds ss_size
//it makes ss_size useless no?
}
int main(){
//
std::memset(&caller,0,sizeof(caller));
std::memset(&callee,0,sizeof(callee));
//create stack and paint 0
std::unique_ptr<std::byte[]> stack(new std::byte[10000]());
std::memset(stack.get(),0,10000);//paint stack 0
//make context
//note stack specified to [2000,8000)
//that means [0,2000) and [8000,10000) should not be touched
if(getcontext(&callee) == -1) {std::cout << errno << ":" << std::strerror(errno) << std::endl; return 1;}
callee.uc_link = &caller;
callee.uc_stack.ss_sp = stack.get()+2000;
callee.uc_stack.ss_size = 6000; //what is this line good for, what is it guarding?
makecontext(&callee,cb,0);
//swap to callee
if(swapcontext(&caller,&callee) == -1) {std::cout << errno << ":" << std::strerror(errno) << std::endl; return 1;}
//print color - should be 0
//if 2 then memory corrupted by callee
std::cout << int(stack[996]) << std::endl;
std::cout << int(stack[997]) << std::endl;
std::cout << int(stack[998]) << std::endl;
std::cout << int(stack[999]) << std::endl;
return 0;
}
我再次不明白为什么我们需要设置堆栈大小ss_size
,因为看起来它没有被用来防止内存损坏或其他任何事情。看起来它只是在那里,但没有任何用处。但我不敢相信它没有用。那么它“守护”/有什么用呢?
好吧,我不想给这件事带来更多的混乱。目标是通过安装信号处理程序来恢复,从而摆脱固定大小的函数调用堆栈SIGSEGV
,但由于内存损坏,这看起来像是不可能完成的任务;或者有一个可增长的堆栈,例如使用带有标志的mmap(2)MAP_GROWSDOWN
,但这看起来很糟糕,因此不是一个选项。