我一直在记录 sandbox_exec 演化成通用工具的过程。这篇文章记录 Sandlock v1.4.0——它从"聪明的包装器"变成"多层安全系统"的那个版本。
仓库: github.com/bkmashiro/Sandlock
重构:822 行 → 8 个模块
v1.3.0 单文件达到 822 行,维护变得困难。我们把它拆分:
我一直在记录 sandbox_exec 演化成通用工具的过程。这篇文章记录 Sandlock v1.4.0——它从"聪明的包装器"变成"多层安全系统"的那个版本。
仓库: github.com/bkmashiro/Sandlock
v1.3.0 单文件达到 822 行,维护变得困难。我们把它拆分:
更新 2026-03-09: sandbox_exec 已演化为 Sandlock——模块化全栈沙箱,增加了 strict mode、语言级沙箱(Python/JS)、源码扫描器和 LD_PRELOAD hook。参见 Sandlock v1.4:从单文件到全栈沙箱 和 GitHub 仓库。
前两篇文章覆盖了威胁模型和 seccomp 沙箱。这篇讲更进一步:一个基于 WebAssembly 的执行环境,安全属性来自编译目标本身,而不是 OS 级别的过滤器。
用 seccomp,我们写了一个 62 条目的阻断列表。当新的危险 syscall 出现(比如 io_uring),就往列表里加。这个安全模型是"阻断坏的东西"。
上周我们发布了 sandbox_exec——一个用 seccomp-bpf 隔离学生代码的 224 行 C 程序。当时的诚实答案是:"WASM 的安全模型更干净,但 Python 生态还没准备好。"
这周我们量化了"还没准备好"到底意味着多少毫秒。答案比预想的更有意思。
上周我们上线了 sandbox_exec——一个用 224 行 C 代码编写的程序,利用 seccomp-bpf 在 AWS Lambda 里隔离学生代码。当时的诚实回答是:「WASM 更干净,但 Python 生态系统还没准备好。」
这周我们精确地测量了「Python 生态系统还没准备好」在毫秒层面的代价。答案比预期的更加微妙。
在我维护的 OJ 平台 Leverage 上的一次比赛期间,排行榜停止更新了。停了大约半天。学生在继续提交代码、获得评测结果,但他们的排名没有变化。我们最终追踪到一个 15 分钟的定时任务,它严重阻塞了 Node.js 事件循环,导致进程无响应。
这篇文章讲的是哪里出了问题、为什么最直观的修复实际上没有解决任何问题,以及用 Redis 有序集合替换整个定时任务、实现 O(log N) 实时更新的设计。
排名系统的工作方式如下:
// rank.service.ts — 简化版
async rebuildSaAndRank(divisionId: number, ids: number[]) {
// 第一步:加载所有提交
const submissions = await Submission.createQueryBuilder('s')
.where('s.divisionId = :divisionId', { divisionId })
.orderBy('s.createdAt', 'ASC')
.getRawMany()
// 第二步:在内存中为每个用户计算分数
const userDatas: Map<UserId, ScoreAggregate>[] = []
for (const submission of submissions) {
// ... 处理每个提交,更新用户分数 map
// 通过 cloneDeep 创建完整的每日历史记录
}
// 第三步:给所有人排序
const ranked = [...userDatas[0].entries()]
.sort(([, a], [, b]) => compareScores(a, b))
// 第四步:写回结果 — 每个用户一次 UPDATE
for (const [userId, scoreAggregate] of ranked) {
await ContestUser.update({ userId, contestId }, {
rank: /* 计算出的排名 */,
score: scoreAggregate.score,
})
}
}
今天我的 MSc 毕业项目正式启动。需求听起来简单:在 AWS Lambda 里安全地运行学生提交的代码。约束让这件事变得有趣。
Lambda Feedback 是 Imperial College 用于学生在线提交和评测代码的平台,底层使用 serverless functions 执行代码。
为了性能,Lambda 会复用容器。五分钟前处理过 A 同学提交的实例,下一个请求可能是 B 同学的。同一个文件系统,同一个进程内存,同一个 /tmp。
今天我的 MSc 项目正式启动。前提听起来很简单:在 AWS Lambda 里安全地运行学生代码。约束条件让它变得有趣。
Lambda Feedback 是一个平台,学生在这里提交代码并实时得到评测。后端使用 Serverless 函数——AWS Lambda 启动一个容器,运行代码,返回结果。
为了性能,Lambda 会复用容器。五分钟前处理了学生 A 提交的函数,可能会处理学生 B 的下一个请求。同一个文件系统,同一个进程内存,同一个 /tmp。