115 lines
7.8 KiB
Markdown
115 lines
7.8 KiB
Markdown
# Rust 判题工程
|
||
|
||
Cargo workspace 分为五个 crate:
|
||
|
||
| Crate | 职责 | 当前能力 |
|
||
| --- | --- | --- |
|
||
| `kf-contracts` | 跨服务通知和执行契约 | 最小 v1 通知、共享 JSON 样例检查 |
|
||
| `kf-sandbox` | Linux 隔离、限制、计量与清理 | 规格/配置检查与 `probe` 只读观察;`run` 拒绝执行 |
|
||
| `kf-engine` | 语言适配、测试点编排、比较与确定性汇总 | 预留库边界,无模拟判定 |
|
||
| `kf-worker` | 拉取、租约、工件准备与本地编排 | `--check` 入口,无消费循环 |
|
||
| `kf-coordinator` | 节点登记、租约、状态与结果写入权 | HTTP 存活检查,无任务端点 |
|
||
|
||
```bash
|
||
# 从仓库根目录调用。
|
||
make install
|
||
make check-judge
|
||
make sandbox-probe
|
||
make worker-check
|
||
make coordinator
|
||
```
|
||
|
||
工具链由 `rust-toolchain.toml` 固定,依赖由 `Cargo.lock` 固定。
|
||
协调器默认只监听 `127.0.0.1:8100`,唯一接口是 `/health/live`。
|
||
`KF_COORDINATOR_LISTEN` 可覆盖地址;在业务协议落地前不对外暴露内部服务。
|
||
|
||
沙箱的 `probe` 输出只表明当前进程看到的 namespace/cgroup/seccomp 信息,
|
||
不代表已经验证创建权限、委派或安全隔离。`run` 返回结构化拒绝和退出码 2。
|
||
真正的权限操作和 `unsafe` 代码只能落在沙箱模块,实施时逐项记录安全理由与 Linux 验证。
|
||
当前统一禁止 `unsafe`,需要时在对应模块明确缩小例外范围。
|
||
|
||
## 本地执行规格校验
|
||
|
||
```bash
|
||
cd judge
|
||
cargo run --locked -p kf-sandbox -- validate --spec crates/sandbox/tests/fixtures/spec.valid.json
|
||
```
|
||
|
||
`validate` 最多读取 64 KiB JSON,返回校验结果和 `execution_allowed: false`。
|
||
`run` 继续拒绝执行。当前规格仅用于本地 Worker/启动器边界,未改变平台 API 或跨服务任务通知。
|
||
|
||
规格使用 `native`、`compile_c`、`compile_cpp` profile;原生可执行文件必须引用输入工件在
|
||
`/work` 下的路径,编译器入口固定为运行镜像内的 `/usr/bin/gcc` 或 `/usr/bin/g++`。
|
||
输入工件只声明逻辑 ID 和只读文件相对路径,cwd 与导出路径也位于 `/work`;
|
||
请求不能提供宿主挂载、运行根、UID/GID 或策略上限。路径采用规范 ASCII 文件名,
|
||
不接受 `..`、绝对路径、空分段、反斜杠或隐式规范化。
|
||
|
||
限额须逐项显式提供:时间单位为微秒,内存、栈、输出、文件与工作目录容量单位为字节,
|
||
进程和 FD 为个数。`ResourceLimits::for_profile` 提供调用方模板,可信 `SpecPolicy` 可进一步收紧上限;
|
||
缺失或为零的限额不会变成无限制。CPU 总时间和墙钟时间分别校验,不假设多线程 CPU 时间小于墙钟。
|
||
环境变量仅接受 `LANG`/`LC_ALL` 的 `C`、`C.UTF-8` 和 `TZ=UTC`。
|
||
|
||
这些检查尚未验证工件归属、实际文件类型、符号链接、身份与隔离能力;初始化器必须继续完成这些检查,
|
||
保留独立 VM 中的执行、安全和资源验收。
|
||
|
||
## 本地执行结果与归因
|
||
|
||
`outcome.rs` 定义可信监督器的本地结果记录,含退出码/信号、计量、输出工件、启动阶段错误、
|
||
取消、限额观察及独立清理状态。`ExecutionReport::classify` 校验记录与已校验规格的一致性,
|
||
返回执行分类;退出码 0 仅代表程序正常退出,Accepted 仍由引擎比较输出后确定。
|
||
这些类型尚未接入实际监督器,也没有改变平台跨服务协议。
|
||
|
||
计量必须显式区分 `measured` 与 `unavailable`,读取失败、计数器重置及非法数据不能当作零。
|
||
CPU 和内存统计覆盖从纳入任务 cgroup 开始的初始化、线程和后代;CPU 不扣除估算的初始化费用。
|
||
墙钟使用 exec 放行至确认任务树为空并完成最终快照之间的单调时间。
|
||
|
||
主归因先报告基础设施/计量/清理故障,同时保留全部限额证据;正常监督下按首次观察时间选择
|
||
取消或限额,相同时取消优先,限额依次为内存、CPU、墙钟、进程、stdout、stderr、文件和工作目录。
|
||
这是监督器观察顺序,不能宣称是内核事件的真实先后。重复限额、越界时间和不支持的证据拒绝分类。
|
||
|
||
SIGKILL、内存峰值或单独的 `oom_kill` 不会推断任务内存超限。初版内存归因要求可信采集器核对
|
||
任务 cgroup 的配置及基线,再提供 `max`、`oom`、`oom_kill`、`oom_group_kill` 的正增量;
|
||
计数器在相同任务 cgroup 上采集,覆盖其后代。
|
||
参见 [cgroup v2 内存事件](https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files)。
|
||
FD、栈和其他信号仍按原退出状态记录,缺少专门证据时不猜测原因。
|
||
|
||
输出保留字节数受限,`observed_bytes` 在截断时只是已读下界,`reached_eof` 单独记录。
|
||
超限输出须有对应证据;缺失计量、无说明的截断或不完整读取报告基础设施失败。
|
||
工件只能导出规格已申请的路径,单文件与已导出总量均受限;这些字段检查不能替代实际文件安全检查。
|
||
准备失败和清理失败分别保留,真实进程树终止、资源回收与 VM 验收仍待后续阶段实现。
|
||
|
||
## 可信运行配置与启动前检查
|
||
|
||
```bash
|
||
cd judge
|
||
cargo run --locked -p kf-sandbox -- check-runtime --config ../deploy/sandbox.runtime.example.json
|
||
```
|
||
|
||
首轮支持策略为 Linux x86_64、内核至少 6.6;这是项目保守支持范围,内核版本不能代替逐项能力验证。
|
||
所需接口包括 user/mount/PID/IPC/UTS/network/cgroup namespace、pidfd 等待、身份降权、seccomp filter,
|
||
以及实际委派子树中的 CPU/memory/pids 控制器、`cgroup.kill`、`memory.peak`、内存/进程事件和交换空间限制。
|
||
初始镜像类型固定为 `linux_gnu_cxx_x86_64_v1`:受控只读目录镜像、GNU C/C++ 编译器、动态加载器和
|
||
可信 `manifest.json`;镜像内容、权限和只读挂载验证仍未实现。
|
||
|
||
配置由可信部署方提供,最多 64 KiB;不能由执行请求覆盖。`runtime.rs` 校验受控 state/tasks/image
|
||
目录布局、独立 cgroup 子树、普通 Worker 身份和独立任务 UID/GID 槽位;任务身份从 100000 起,
|
||
不与配置中的 Worker 重叠。槽位仅定义映射,真正分配前须核查系统账号占用,直到旧任务树为空且
|
||
清理完成后才可复用,服务重启也须恢复租用状态。
|
||
|
||
样例最多并发 2 个任务,每任务上限 512 MiB 内存、32 个进程和 256 MiB 工作空间;
|
||
整体内存预算 1.5 GiB、进程预算 128、工作空间预算 1 GiB,分别给监督器保留 256 MiB、32 个进程和 64 MiB。
|
||
CPU 带宽在同一 100 ms 周期中,每任务配额 100 ms、监督器预留 50 ms、父级配额 250 ms。
|
||
校验采用检查过的整数运算,整体预算至少覆盖「任务上限 × 并发数 + 监督器预留」,拒绝溢出或不足。
|
||
CPU 带宽和 CPU 总时间分别配置;预算目前只完成政策校验,真正应用和核验由后续 cgroup/监督器实现。
|
||
工作空间的 tmpfs 占用仍计入任务内存;镜像存储与工件长期保留由部署/工件提供方单独管理。
|
||
|
||
`check-runtime` 只读取当前环境和配置路径,输出结构化拒绝,退出码 2;配置有效时明确
|
||
`configuration_valid: true`、`execution_allowed: false`。目录/接口可见与不可见分别记录,
|
||
配置归属、路径真实包含关系、镜像完整性、身份池、helper、委派、预算实际生效、pidfd、namespace、
|
||
降权、seccomp 和清理能力均保留未验证状态。它不创建目录、改账号、启动服务或运行用户程序。
|
||
即使观察值里的 `isolation_verified` 被误设为 true,也不能解除未实现的执行拒绝。
|
||
|
||
接口语义参见 [cgroup v2](https://docs.kernel.org/admin-guide/cgroup-v2.html)、
|
||
[pidfd_open](https://man7.org/linux/man-pages/man2/pidfd_open.2.html) 和
|
||
[seccomp filter](https://docs.kernel.org/userspace-api/seccomp_filter.html)。
|