LETTers Online dev - crazybox

LETTers Online中,crazybox的设计和开发记录。

—–Building—–

经过调研,有以下几种方式完成sandbox部分的开发:

  • 使用QDUOJ后端的沙箱
  • 使用DMOJ后端的沙箱
  • 使用docker作为沙箱
  • 使用ioi所用沙箱
  • 完全自己实现沙箱和判题
    • 沙箱常见设计
      • ptrace (vsftpd)
      • seuid (chromium)
      • seccomp (chromium)

自己实现

参考资料:

linux3.5以后linux支持了Will Drewry的Seccomp-BPF的特性

主要有两个任务,一个是统计时间和内存,一个是限制非法的系统调用和限制时间以及内存

统计时间和内存

先明确一些概念:

时间

  • CPU time: 为CPU执行用户进程操作和内核系统调用所耗时间总和 (user+sys)
  • real time / wall time: 实际流逝时间

内存

  • VSS- Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
  • RSS- Resident Set Size 实际使用物理内存(包含共享库占用的内存)

方案

  • 方案1: 在python当中可以用psutil获取时间和max虚拟内存和max(不靠谱)
  • 方案2: 利用/usr/bin/time获取时间和内存

一些记录

思路:首先完成纯c使用seccomp开发沙箱
c libraries vs executable

然后将其包装成python函数进行使用

用seccomp-filter的方式开发沙箱,要确定哪些系统调用是合法的,可以通过strace -c command 查看一个命令的系统调用次数和时间占用。

使用setrlimit限制内存和时间 介绍

https://docs.python.org/3/extending/extending.html
https://stackoverflow.com/questions/11041299/python-h-no-such-file-or-directory
https://cmake.org/cmake/help/latest/module/FindPythonLibs.html
sudo apt-get install python3-dev  # for python3.x installs

在这两个链接中,分别有整个64位系统下的system call以及对应的分类:

syscall_64.tbl

Classification and Grouping of Linux System Calls

代码处理思路:

  • 编译行为只需要利用setrlimit对资源做出限制即可(主要是编译时间和编译输出)
  • 运行行为需要放入seccomp沙箱当中

seccomp权限控制

  • 允许任意的read调用
  • 只允许向stdout和stderr的write调用
  • 只允许列表中文件的的open调用
  • 允许任意的close调用

打印可执行程序系统调用列表:
strace -f -c ./act_samples 2>&1 | sed -n '8,$p' | awk '{print $NF}'

交互题思路:

在python中调用os.pipe()和os.fork(),对两个程序分别调用cbox,利用两个管道分别重定向双方的stdin和stdout link

总体流程

首先主进程fork出一个从进程,从进程依次设置以下限制:

  • prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
  • ptrace(PTRACE_TRACEME, 0, NULL, NULL);
  • 从配置文件当中读取时间内存(注意这里的时间是cpu时间)等限制,使用setrlimit做出限制
  • chdirchroot: chroot需要额外权限,可能不会使用
  • 重定向stdin, stdout, stderr
  • 增加seccomp规则
  • 给自己发送SIGSTOP信号,这一步是因为,父进程需要通过ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESECCOMP);来开始追踪子进程的非法系统调用,必须要在设置seccomp规则之后才能开始合法追踪,于是父进程可以先wait一下,如果获取到了子进程正常暂停,那么说明之前的限制都正确应用且此时可以开始追踪子进程。
  • 调用execve(config->file, config->argv, config->envp);执行程序,注意在seccomp规则中需要额外增加一条允许execve(config->file...)这样的系统调用执行。另外注意调用execve之后,如果本身是被ptrace的进程,在执行成功execve之后,会发送一个SIGTRAP的信号。

主进程本身在fork之后,需要配合从进程进行一些处理:

  • 首先调用一次wait,如果不是异常退出状态,说明限制正常应用,继续处理,否则退出程序。正常的信号应该为SIGSTOP(18)
  • 设置ptrace追踪属性:ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESECCOMP);
  • 在子进程execve之前,用setpgid(child,child);将子进程的组id改为它自己,这样方便使用kill(-child, XXX)来向子进程及其派生进程发送信号。
  • 调用ptrace(PTRACE_CONT, child, 0, 0);,然后子程序应该执行到execve完成之后
  • 调用wait获取状态,信号应该为SIGTRAP(5)
  • 调用ptrace(PTRACE_CONT, child, 0, 0);,从这里开始子程序应该开始真正的执行过程
  • 调用wait获取状态,此时获取到状态时应该有三种情况:
    • 正常退出 / 资源异常
    • 使用了非法的系统调用
    • 超过wall time限制

在最后正常退出 / 资源异常放在一起是因为这种情况下都可以从wait返回的status中拿到大部分信息;使用了非法的系统调用需要特殊处理一下并且手动终止程序;超过wall time限制则是使用setitimer设置定时器向自身发送SIGALRM信号,并在对应的信号处理函数中将子进程组全部关闭。


文章作者: crazyX
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 crazyX !
评论
 上一篇
Extending Python with C or C++ Extending Python with C or C++
探索在linux环境下,如何用c/c++语言扩展部分python逻辑。 开发环境: ubuntu 16.04 python3 clion 简单的例子 FindPythonLibs在这里可以看到cmake如何添加python.h支持,
2018-10-20
下一篇 
icpc-tools resolver 滚榜工具使用指北 icpc-tools resolver 滚榜工具使用指北
网上ICPC 官方滚榜工具的资料并不多 = = 半年多之前给学校办校赛折腾了一会,虽然最终效果很成功,实际上还是有一些玄学问题没解决……另外整个流程其实是比较复杂的……特此记录一下 第一个是中文支持,当时觉得可能是java内字符编码的
2018-05-22
  目录