天天看点

seccomp实现安全判题沙箱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下对资源进行限制的函数是位于**<sys/resource>下的getrlimit和setrlimit**

#include <sys/resource.h>

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"
};
           

对应的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的函数段

如果执行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)};
           

继续阅读