Skip to content

Sandbox Security

dxx114514-stack edited this page Oct 1, 2026 · 2 revisions

沙箱与安全

CliJudge 采用纵深防御:选手代码在多层隔离中执行,可信组件单独归类,导入外部内容时显式告警。

信任边界

执行者 模式 隔离
选手提交代码 受限(untrusted) 完整沙箱:全部隔离层生效
编译器(g++/javac/自定义 compile) 可信 保留时间限制(compile_time_limit_ms),跳过隔离层
SPJ / 交互器 / 生成器 / grader 可信(放宽隔离) 保留时间/内存/进程限制,但跳过受限令牌、命名空间与 seccomp 等隔离层,可写工作目录

导入告警:problem import / contest import 检测到题目包携带 SPJ / 交互器 / 生成器 / grader 组件时,输出:

Warning: this package contains special judge/interactor/generator/grader code,
which is executed in a trusted (reduced-isolation) mode during judging.
Only import packages from sources you trust.

→ 只导入可信来源的题目包与 CDF。

contest import(CDF)的告警文案首句为 Warning: this CDF contains …、末句为 Only import contests from sources you trust.;其检测条件只看 spj / interactor / grader / graderFilesPath 字段——CDF 结构本就没有 generator 字段,文案里的 "generator" 属通用模板串。

Windows 隔离层

  1. 进程树隔离 — CREATE_SUSPENDED + Job Object 绑定后才恢复执行,杜绝竞态逃逸
  2. 强制回收 — KILL_ON_JOB_CLOSE:主进程结束则整棵进程树被系统强制秒杀
  3. 禁止脱离 — 禁用 BREAKAWAY,子进程无法脱离沙箱
  4. 权限降级 — 受限令牌剥离 SeDebug / SeImpersonate 等高危特权,Low Integrity Level 运行
  5. 资源限制 — 内存上限、CPU 时间限制双重保障
  6. 数据隔离 — 每次判题独立临时目录,结束后暴力删除

可用编译期宏关闭部分安全特性(有明确风险时才使用):CLIJUDGE_NO_LOWIL(关闭 Low Integrity Level)、CLIJUDGE_NO_RELABEL(关闭完整性重标记)。

Linux 隔离层

  1. 命名空间阶梯 — 依次尝试 user+mnt+pid+net+ipc+uts → user+mnt → 普通 fork(全失败逐级降级);自写 uid/gid 映射
  2. 文件系统 — make-rprivate / → 绑定可写 workDir 与 /tmp → 全新 /proc → 卸载 /sys → 只读重挂载根文件系统(保持 workDir 可写)
  3. 网络 — 新 net namespace(空网卡)+ seccomp 拒绝 socket 双重兜底
  4. seccomp — 架构检查 + syscall 黑名单:mount/ptrace/提权/网络配置等敌意调用 → SIGSYS;clone3/io_uring/bpf 等探测型 → ENOSYS;untrusted && maxProcesses==1 时拒绝进程创建(fork/vfork/非线程 clone → EPERM),线程不受限,其余场景父进程 10ms 轮询进程组超限击杀
  5. 环境变量白名单 — 最小 env 注入(PATH 等),附加变量见 judge.env
  6. RLIMIT — RLIMIT_CPU / RLIMIT_AS(内存)/ RLIMIT_STACK / RLIMIT_FSIZE(写文件上限)

能力预检与自动降级

加固环境(如 Ubuntu 24.04+ 默认 kernel.apparmor_restrict_unprivileged_userns=1)会拒绝 uid_map 自写(EPERM)。沙箱每次进程首跑前执行 probeSandboxCaps() 预检:

  • uid_map 不可写 → 命名空间阶梯直接从 level0(普通 fork)起步,靠 seccomp + RLIMIT 兜底
  • seccomp 不可安装 → 不安装过滤器,靠 RLIMIT 兜底
  • 预检通过的环境(如普通发行版 / WSL)行为不变,仍用完整命名空间隔离

诊断:设置环境变量 CLIJUDGE_SANDBOX_DEBUG=<文件路径> 可记录每次沙箱启动的 level / caps / 失败点 / AppArmor profile(冒烟测试默认开启)。

并发与数据安全

  • 原子写 — 题目/比赛/文章/提交索引与 config.json 均采用「临时文件 + rename 原子替换」,崩溃不会留下半截 JSON;例外:.cjl 语言包与首装内置包用普通 writeFile 写入(非原子,崩溃可能留半截语言包,可重新 displaylang pull 修复)
  • 跨进程锁 — data/.clijudge.lock(Windows LockFileEx / Linux flock)对修改类命令排他,读命令依赖原子快照无需加锁;例外:problem testdata 子命令(含 list)按粗粒度归入修改类同样加锁;进程崩溃后 OS 自动释放锁

使用建议

  • 以普通用户运行即可;Windows 下如遇沙箱执行失败,尝试管理员身份运行
  • Linux 评测 C/C++ 需要 PATH 上有 g++;Python 需要 python3
  • 不要在不可信机器上以高权限运行 CliJudge 本体

返回 Home · 配置见 评测机制 / 数据存储

Clone this wiki locally