fix(SQL 沙箱 Minor): 让题目的 memoryLimit 对学生真正生效
## M-1 题目写 64MB 也没意义 —— 唯一的硬顶是子进程那个固定 512MB 的 ulimit -d, 学生实际能吃到 8 倍。旧实现是 setlimit(SQLITE_LIMIT_LENGTH, memoryLimit) 把单值长度贴着题目内存限,但 sql.js 的 wasm 没导出 sqlite3_limit(已核对导出表), 复刻不了。 改成引擎侧按字节记账(ByteBudget):取行时累加,单值超限或结果集累计超限都按 MLE 拒。 max_page_count 管的是库文件页数,管不住「一个 SELECT 拼出一个巨大的值」,所以两者 不重复。查询题和增删改题(dumpTables)两条路都记账,受信脚本不记。 实测 6 条,关键是**同一句 SQL 在 4MB 的题上被拦、在 64MB 的题上放行**, 证明限制跟着题目走而不是一刀切。 第一版测试用的是 100MB 的值,结论是错的:wasm 堆触顶时的兜底和我的记账器报的是 同一句「单个数据值超出内存限制」,大值根本分不清是谁拦的。换成 8MB 才测得准。 这个教训写进注释了。 M-1 的第二半(max_page_count 学生可自行调大)在 I-1 拦 PRAGMA 时已一并修掉。 ## M-3 阶段 5 的分阶段兜底超时顺带解决:出题人预览死循环从 25s 降到 13005ms 实测。 ## M-2 不修 「强制两个测试点期望结果不同」会把合法出题也挡掉(刻意用两组数据验证同一边界、 结果恰好相同),代价是老师被一条看不懂的错误拦住。评审也确认与旧实现一致、非回归。 更合适的是提示而非拒绝,但要动契约和后台前端。理由写进评审文档,免得下次又当新发现。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,27 @@
|
||||
> - **I-2 已修**:子进程用 stderr 报阶段(`prepare` / `student` / `display`),父进程边读边换
|
||||
> 兜底时限 —— 一进学生 SQL 就收到「题目时限 + 2s」。1s 限的题跑飞语句实测 **26s → 3.06s**。
|
||||
> 顺带修正归因:卡在受信脚本(出题人的初始化/标准答案)现在报 SYSTEM_ERROR,不再甩给学生 TLE。
|
||||
> - **未修**:M-1 的「固定 512MB 与题目 `memoryLimit` 脱钩」、M-2、M-3。
|
||||
> **补充(2026-08-08,Minor 收尾)**
|
||||
>
|
||||
> - **M-1 已全部修完。** 第二半(`max_page_count` 学生可调大)在 I-1 拦 PRAGMA 时就一并
|
||||
> 修掉了。第一半改成引擎侧按字节记账(`ByteBudget`):单值超题目内存限、或结果集
|
||||
> 累计超限,都按 MLE 拒。旧实现的 `setlimit(SQLITE_LIMIT_LENGTH)` 复刻不了 ——
|
||||
> sql.js 的 wasm 没导出 `sqlite3_limit`。实测**同一句 SQL 在 4MB 的题上被拦、
|
||||
> 在 64MB 的题上放行**,题目的 `memoryLimit` 现在对学生是真约束。
|
||||
> (测试尺寸特意选在 8MB 而不是 100MB:wasm 堆触顶时的兜底报的是同一句错误信息,
|
||||
> 用大值测根本分不清是谁拦的 —— 第一版测试就是这么误判的。)
|
||||
> - **M-3 已由阶段 5 的分阶段超时顺带解决**:预览从 25s 降到 **13005ms** 实测。
|
||||
> - **M-2 不修**(见下)。
|
||||
|
||||
> **M-2 的处理:不修,理由写在这里**
|
||||
>
|
||||
> 要做「强制两个测试点的期望结果不同」,得在建题时把每个测试点都跑一遍标准答案再比对。
|
||||
> 技术上可行,但它会把一类**合法出题**也挡掉(比如刻意用两组不同数据验证同一个边界、
|
||||
> 结果恰好相同),而代价是老师建题时被一条看不懂的错误拦住。评审也确认这与旧实现行为
|
||||
> 一致、非回归。
|
||||
>
|
||||
> 更合适的做法是「提示」而不是「拒绝」,但那要动契约和后台前端。留给以后,
|
||||
> 现在记在这里,免得下次评审又把它当成新发现。
|
||||
> - `engine.ts` 头部的防护对照表已按实测重写,两处削弱写在正文里。
|
||||
> 另记:stock sql.js 的 wasm **没有导出** `sqlite3_progress_handler` / `sqlite3_interrupt` /
|
||||
> `sqlite3_set_authorizer` / `sqlite3_limit`(已核对导出表),要用得自己编 wasm。
|
||||
|
||||
Reference in New Issue
Block a user