linux平台设置C/C++沙箱环境问题求助

__GUNS_N_ROSES__ 2012-02-24 10:51:25
想在linux上实现一个C/C++的沙箱环境,在做内存限制的时候遇到了点麻烦,赶时间,想要早点做完,但卡这里了。困扰了许多天,资料也仔细找过,不是随随便便就跑来问的,诚意请务必相信 = =。

我的测试代码:

setmemory.cpp

#include <stdio.h>
#include <sys/resource.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
int setLimit(int type, long val){
rlimit res;
res.rlim_cur = val;
res.rlim_max = val+1;
return setrlimit(type, &res);
}

int main()
{
pid_t pid;
pid = fork();
if(pid < 0){
printf("%d:%s\n", errno, strerror(errno));
return -1;
} else if (pid == 0){
if(setLimit(RLIMIT_DATA, 1024*1024*1) < 0){ // byte
printf("%d:%s\n", errno, strerror(errno));
return -1;
}
if(setLimit(RLIMIT_CPU, 3) < 0){ // second
printf("%d:%s\n", errno, strerror(errno));
return -1;
}
if(ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0){
printf("%d:%s\n", errno, strerror(errno));
return -1;
}
execlp("./test", "test", NULL);
} else {
// father
int status;
int ret;
while((ret = waitpid(pid, &status, 0)) > 0){
if(WIFSIGNALED(status)){
//child over
printf("child process over by kill sig.\n");
break;
}
if(!WIFSTOPPED(status)){
printf("child process over succeed.\n");
break;
}

int sig = WSTOPSIG(status);
if(sig != SIGTRAP){
//! debug sig
switch(sig){
case SIGFPE:
printf("float point err.\n");
break;
case SIGXCPU:
printf("TLE.\n");
break;
case SIGBUS:
case SIGSEGV:
printf("segment err.\n");
break;
case SIGILL:
case SIGKILL:
printf("runtime err.\n");
break;
case SIGALRM:
;
}

//retrans to child
ptrace(PTRACE_SYSCALL, pid, NULL, sig);
continue;
}

ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
}
printf("father process over.\n");
}
return 0;
}



其中test.cpp的代码:

int main()
{
int a[1000000] = {0};
for(;;);
return 0;
}


超时退出是没问题的,内存怎么限制都没有。有人说得用root权限,我用这个权限去运行也不行。
另外这个版本代码简化过了,本来设置了更多setrlimit选项,outfile、core,也设了chdir之类的,我想应该关系不大先去掉了。
也想过可能是虚拟内存的原因,RLIMIT_AS应该是设置虚拟内存的,但设了之后会有其他问题,比如加载libc.so.6的时候会出现failed to map segment from shared oject等等,不知道是不是跟我虚拟机里内存比较小的缘故(闲置内存基本只有5、6十M),而且如果不把虚拟内存设置为0又怎么尽量准确的测量内存呢?

查过man setrlimit,关于RLIMIT_DATA的部分说是会触发brk和sbrk,并根据软限制报出ENOMEM错误。但实际测试时,子进程并没有退出,而是超时之后按照TLE退出。

看过一个版本的源代码是这么做的:


//获得系统调用相关的寄存器值
ptrace(PTRACE_GETREGS, this->pid, 0, ®s);

switch(regs.ORIG_EAX) {
case SYS_brk:
if(!insyscall) {
brk = (unsigned long)regs.EBX;
insyscall = true;
}else{
if(((unsigned long)regs.EAX) < brk) {
DLOG(INFO) << "TraceProcess()::traceMe(): brk request " << brk << " return " << regs.EAX << "\n";

//超内存了
this->_result = MEMORY_LIMIT_EXCEEDED;
ptrace(PTRACE_KILL, this->pid, 0, 0);
continue;
}
insyscall = false;
}
break;
}


他用EBX去比较地址,但我觉得如果申请的内存区不连续是不是用某个具体的地址去比较会有问题?

另外我在SYS_brk中输出EBX的值,发现比较的时候EBX的值一般都是0,就是说这时候内存上限地址并没有放在EBX中,而且我底层不是很熟,关于那段代码的开发者为什么认为上限值会放在EBX中也不太清楚。

以上问题希望各位大大们能指点一下,今天在线等,还需要其他信息我补上。
...全文
217 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
__GUNS_N_ROSES__ 2012-02-24
  • 打赏
  • 举报
回复
这么快就沉了...泪...

刚试了下编译的时候加上--static,注释了RLIMIT_DATA,改用RLIMIT_AS,这样libc.so.6的报错是没了。这样把RLIMIT_AS设为32M以后跑了下程序,test里申请1000W长度int数组的时候的确因为内存超出退出了,而且400W长度int数组也的确因为未超出内存而超时结束。

但是如果我不设置RLIMIT_DATA我怎么知道子进程是否用了物理内存呢?这样大概会产生比较大的误差空间。就算把RLIMIT_DATA设为0,完全使用虚拟内存会不会导致IO效率大大降低从造成时间测试误差大?
__GUNS_N_ROSES__ 2012-02-24
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 justkk 的回复:]

呵呵,简单点的也不懂..

是不是可以捕获什么信号?
[/Quote]

问题就是木有什么信号啊,就发一个SIGKILL,这个信号有很多种其他情况也会触发,很难区分。man里说好的ENOMEM也没给出~~

justkk 2012-02-24
  • 打赏
  • 举报
回复
呵呵,简单点的也不懂..

是不是可以捕获什么信号?
__GUNS_N_ROSES__ 2012-02-24
  • 打赏
  • 举报
回复
沉了...

换个简单点的问题吧:如何捕捉申请内存超出RLIMIT_AS限制时的异常情况?
wangjieest 2012-02-24
  • 打赏
  • 举报
回复
据说,linux的堆栈比window下还小....

64,683

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧