【发布时间】:2019-12-04 00:26:02
【问题描述】:
在调用makecontext之前为什么要设置堆栈大小ss_size?
我刚刚有一个针对makecontext/swapcontext sn-p 的单元测试用例,它以SIGSEGV 失败。发生的事情是堆栈大小太小并且不相关的内存(恰好是一些唯一的指针)被损坏并报告了段错误。所以段错误在这些不相关的指针上,我本可以有例如一些字符串,然后内存损坏就不会被注意到。
我原以为当堆栈大小 ss_size 不够时,SIGSEGV 会立即提升,但是
考虑到上面描述的内存损坏,我得出结论,这里不可能从SIGSEGV 中恢复。这让我回到了这个问题,为什么我们需要首先设置堆栈大小,而不是用来发出溢出信号?它是干什么用的?
编辑:
这都是关于makecontext(3) 的。这些函数仍在用于绿色线程、协程等。考虑到这些任务(在我看来)也不是在 c++ 中,它们没有真正的替代品。
在getcontext(3) 中定义的ucontext_t 中的uc_stack 需要在sigaltstack(2) 中定义的ss_size。
在显示内存损坏的minimal verifiable example 之后,通过“绘制”内存,如上所述。
#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 标志,但这看起来broken,因此不是一个选项。
【问题讨论】:
-
什么是
ss_size,如何设置?请出示代码。我看到它用于signal stack?为什么在使用 systemV api 时需要使用它?为什么还要用这么老的api呢? -
@KamilCuk 我已经更新了这个问题。希望这可以解决问题。
-
//note stack size specified 6k bytes in size //this should not be allowed.- SIGSEGV 发生和 预期 SIGSEGV 发生是不同的事情。如果您在允许的内存之外写入,您不能期望 SIGSEGV 发生,您的进程空间内可能恰好有一个有效地址。