第 7 / 21 篇

第 6 章:把候选执行与验收证据隔开

如果候选能够修改评分脚本或读取标准答案,分数再精确也缺少可信基础。但另一个极端也不正确:只要把程序放进子进程,就宣称实现了安全沙箱。隔离必须按实际机制与威胁模型描述。

6.1 实验真正传递了什么

父进程加载参考答案,创建新的临时目录,复制 candidate.py 与 task.json,再放入该任务的初始文件。子进程通过绝对路径的 Python 解释器,以 -I 隔离模式运行;它的标准输入关闭,stdout 与 stderr 不进入评测日志。

候选写出 response.json 与任务产物,父进程在子进程结束后读取并评分。评分器和答案文件没有被复制到工作区,候选也不会得到父进程中的 truth 对象。测试检查 candidate_visible_files,防止将来改动时无意把评分材料混进输入。

Python 的 -I 影响解释器路径和环境处理,不限制系统调用、网络、绝对路径或进程权限。这里的保证是“没有通过协议给候选答案”,而不是“候选无法从其他途径获取答案”。

6.2 合作式隔离与对抗式隔离

随书候选都是作者可审查的合作程序,因此适合演示输入分离与生命周期。它们使用与父进程相同的操作系统身份,公开仓库中的答案也不是秘密。恶意候选可以尝试访问宿主文件;当前实现不能作为执行陌生代码的公共服务。

生产评测需要按风险增加文件系统、身份、网络、资源与进程边界。评分器应保存在候选不能写的位置,答案与凭据需要独立访问控制,产物导出也要验证。即便使用容器,还需要核对挂载、用户、网络和宿主权限,而非仅检查“是否有 Docker”。

6.3 输出也需要合同

父进程限制候选响应文件最多 4096 字节,要求 JSON 对象。候选超时、退出异常、缺失文件或输出格式不合法,都会记录为不可正常评分,而不是凭空得到一个成功或免费结果。

响应文件上限不是候选总磁盘、内存或 CPU 配额;本实验没有针对恶意写盘实现硬隔离。stdout 不保留可以减少偶然泄漏和无界日志,但它也不替代运行资源控制。所有限制都应说清约束对象。

6.4 评分时读取当前产物

不要只相信候选附带的“文件哈希”。本实验由父进程读取工作区中的真实文件,计算哈希并保存,再检查完整内容与保护条件。候选声明只进入 response,不能取代独立验收。

真实代码任务还要考虑后台进程是否继续修改工作区、符号链接是否指向外部,以及验收时看到的是哪一版内容。可靠隔离与稳定快照应在评分前建立;无法保证时,要将结论标为有条件,而不是默认为已经解决。

练习。 画出候选、答案、评分器、工作区和报告五个对象的读写关系。标出本实验真正阻止了哪条路径,又有哪些路径只是合作约定。将“不能读答案”改写成可以验证的权限要求。