2021SC@SDUSC|seccomp实现安全判题沙箱

seccomp实现安全判题沙箱 需求分析 软件判题器需要一下几点

  • 保证判题的正确性
  • 能够并发进行判题
  • 能够保证判题过程的安全性
  • 能够限制判题过程的资源
  • 能够根据编程语言进行动态定制
  • 能够返回判题过程中使用的资源大小
需要的参数
入参 类型 描述 限制
bin string 可执行文件 无限制
output-file string 输出重定向文件 无限制
input-file string 输入重定向文件 无限制
error-file string 错误重定向文件 无限制
num-thread int 最大可执行线程 不超过cpu核心数
max-memory int 最大内存使用(Byte) 不超过系统内存的1/16
max-stack-size int 最大堆栈大小 无限制
max-output-size int 最大输出文件大小(KB) 一般不超过100MB
max-cpu-time int 最大CPU时间占用(ms) 无限制
max-real-time int 包含系统调度的时间占用(ms) 无限制
arguments vector 可执行文件的运行参数 长度<=255
env vector 执行程序时的环境变量 长度<=255
输出的结果
直接返回值结果
出参 类型 描述
memory int 占用的内存(Byte)
cpu-time int 占用的cpu时间(ms)
real-time int 占用的真实时间(ms)
output-file string 程序的输出结果
error-file string 出错的错误文件结果
exit-code int 程序执行结果的状态码
signal int 沙箱执行结果状态码
判题器实现 前期准备
# 安装seccomp开发环境支持 sudo apt-get install libseccomp-dev

创建限制与返回结果
按照判题器的要求创建资源限制,返回结果以及判题器的配置文件
判题的资源限制
// limit.h struct Limit { int max_real_time; int max_cpu_time; int max_memory; int max_stack_size; int max_output_size; int num_thread; };

判题最后返回的结果文件
// result.h struct Result { int cpu_time; int real_time; long memory; int signal; int exit_code; };

判题器的配置文件
struct Config { Limit limit; std::string input_file; std::string output_file; std::string error_file; };

创建判题器核心
rlimit linux下对资源进行限制的函数是位于**下的getrlimit和setrlimit**
#include int getrlimit(int resource, struct rlimit *rlptr); int setrlimit(int resource, const struct rlimit *rlptr);

系统的资源由最初的0号进程获取,后续所有的子进程继承父进程的资源限制
每个子进程也可以调用setrlimit改变资源限制
rlimit的结构
struct rlimit { rlim_t rlim_cur; //current limit rlim_t rlim_max; //max limit value for "rlim_cur" };

【2021SC@SDUSC|seccomp实现安全判题沙箱】对应的resource参数可选如下:
RLIMIT_AS/RLIMIT_VMEM: 这两个资源表示的是同一个含义,都是只address space限制,可用内存用户地址空间最大长度,会影响到sbrk和mmap函数。 RLIMIT_STACK:栈的长度,默认一般是8K RLIMIT_CORE:程序crash后生成的core dump文件的大小,如果为0将不生成对应的core文件。 RLIMIT_NOFILE:进程能够打开的最多文件数目,此限制会影响到sysconf的_SC_OPEN_MAX的返回值。 RLIMIT_NPROC:每个用户ID能够拥有的最大子进程数目,此限制会影响到sysconf的_SC_CHILD_MAX的返回值。 RLIMIT_NICE:对应进程的优先级nice值。 RLIMIT_SWAP:进程能够消耗的最大swap空间。 RLIMIT_CPU:CPU时间的最大值(秒单位),超过此限制后会发送SIGXCPU信号给进程。 RLIMIT_DATA:数据段的最大长度。默认为unlimited RLIMIT_FSIZE:创建文件的最大字节长度。默认为ulimited RLIMIT_MSGQUEUE:为posix消息队列可分配的最大存储字节数 RLIMIT_SIGPENDING:可排队的信号最大数量 RLIMIT_NPTS:可同时打开的伪终端数目 RLIMIT_RSS:最大可驻内存字节长度 RLIMIT_SBSIZE:单个用户所有套接字缓冲区的最大长度 RLIMIT_MEMLOCK:一个进程使用mlock能够锁定存储空间中的最大字节长度

如果设置resource的值为 RLIM_INFINITY 表示无限制
exit code 返回的状态码是0表示执行成功
对于完整的fork流程之后,会有一个退出的状态码,默认成功的状态码是0.其他的都是一些错误的状态码
在所有的状态码中1, 2, 126 – 165 和 255 是系统定义的有意义的状态码,用户定义的时候应当避免这些状态码
exit的函数段
void exit(int return_code)

如果执行exit()中的return_code大于255时,会对255取模
如果return_code是负数,返回值会做运算65536 + return_code * 256
一些特殊的常见的系统定义的状态码
  • exit(1): It indicates abnormal termination of a program perhaps as a result a minor problem in the code.
  • exit(2): It is similar to exit(1) but is displayed when the error occurred is a major one. This statement is rarely seen.
  • exit(127): It indicates command not found.
  • exit(132): It indicates that a program was aborted (received SIGILL), perhaps as a result of illegal instruction or that the binary is probably corrupt.
  • exit(133): It indicates that a program was aborted (received SIGTRAP), perhaps as a result of dividing an integer by zero.
  • exit(134): It indicates that a program was aborted (received SIGABRT), perhaps as a result of a failed assertion.
  • exit(136): It indicates that a program was aborted (received SIGFPE), perhaps as a result of floating point exception or integer overflow.
  • exit(137): It indicates that a program took up too much memory.
  • exit(138): It indicates that a program was aborted (received SIGBUS), perhaps as a result of unaligned memory access.
  • exit(139): It indicates Segmentation Fault which means that the program was trying to access a memory location not allocated to it. This mostly occurs while using pointers or trying to access an out-of-bounds array index.
  • exit(158/152): It indicates that a program was aborted (received SIGXCPU), perhaps as a result of CPU time limit exceeded.
  • exit(159/153): It indicates that a program was aborted (received SIGXFSZ), perhaps as a result of File size limit exceeded.
rule 对于seccomp的规则主要有两类
  • 以c语言为代表的,无虚拟机运行的语言
  • 以Java为代表的有虚拟机运行的语言
有虚拟机参与的语言,可以交由虚拟机来保证一部分的安全性
对于判题器核心有一点,对于不同的语言应当有不同的限制,下面列举了需要运行最少需要的权限
语言 权限 备注
C/C++ 见下 没有虚拟机,权限不需太多
JAVA 见下 虚拟机运行必须需要的权限
GO 同上JAVA 虚拟机运行必须需要的权限
//大部分编程题运行需要的最小权限 int sys_call_whitelist[] = {SCMP_SYS(read), SCMP_SYS(fstat), SCMP_SYS(mmap), SCMP_SYS(mprotect), SCMP_SYS(munmap), SCMP_SYS(uname), SCMP_SYS(arch_prctl), SCMP_SYS(brk), SCMP_SYS(access), SCMP_SYS(exit_group), SCMP_SYS(close), SCMP_SYS(readlink), SCMP_SYS(sysinfo), SCMP_SYS(write), SCMP_SYS(writev), SCMP_SYS(lseek), SCMP_SYS(clock_gettime), SCMP_SYS(pread64)};

//JVM运行所需要的最小权限 int sys_call_whitelist[] = {SCMP_SYS(access), SCMP_SYS(arch_prctl), SCMP_SYS(brk), SCMP_SYS(clock_getres), SCMP_SYS(clone), SCMP_SYS(close), SCMP_SYS(connect), SCMP_SYS(execve), SCMP_SYS(exit_group), SCMP_SYS(fchdir), SCMP_SYS(fcntl), SCMP_SYS(fstat), SCMP_SYS(ftruncate), SCMP_SYS(futex), SCMP_SYS(getcwd), SCMP_SYS(getdents), SCMP_SYS(geteuid), SCMP_SYS(getpid), SCMP_SYS(gettid), SCMP_SYS(getuid), SCMP_SYS(kexec_load), SCMP_SYS(kill), SCMP_SYS(lseek), SCMP_SYS(lstat), SCMP_SYS(mkdir), SCMP_SYS(mmap), SCMP_SYS(mprotect), SCMP_SYS(munmap), SCMP_SYS(openat), SCMP_SYS(prctl), SCMP_SYS(pread64), SCMP_SYS(prlimit64), SCMP_SYS(pselect6), SCMP_SYS(read), SCMP_SYS(readlink), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigprocmask), SCMP_SYS(rt_sigreturn), SCMP_SYS(sched_getaffinity), SCMP_SYS(sched_yield), SCMP_SYS(set_robust_list), SCMP_SYS(set_tid_address), SCMP_SYS(socket), SCMP_SYS(stat), SCMP_SYS(sysinfo), SCMP_SYS(uname), SCMP_SYS(unlink), SCMP_SYS(write)};

    推荐阅读