Skip to content

fuzz: 增加 cursor/token replay semantic fuzz #138

@membphis

Description

@membphis

背景

父任务:#132

Go encoding/json/jsontext.FuzzCoder 会随机读取 token/value,再写回,并验证重放后的 JSON 与原输入语义一致。qjson 没有流式 token API,但 Phase 2 有 root getter、cursor、object entry、array index、path getter 和 skip-cache,这些读取路径同样需要语义级 fuzz 覆盖。

当前仓库已有 fuzz_parse_lazy:它以 serde_json::Value 为语义 oracle,通过 qjson public cursor FFI 重建完整 JSON model,并覆盖部分 varied-order sibling lookup。本 issue 收敛为增强现有 fuzz_parse_lazy,不新增独立 fuzz target。

目标

fuzz_parse_lazy 明确定义为 qjson Phase 2 semantic replay fuzz target,覆盖遍历、访问顺序、root/cursor getter 一致性和 skip-cache cold/warm 路径,验证不同 Phase 2 读取方式得到一致语义。

重点覆盖:

  • root cursor 全量遍历并重建 JSON semantic model。
  • qjson_cursor_object_entry_at 顺序遍历对象,覆盖所有 entry,包括 duplicate key。
  • qjson_cursor_field 按 varied order 访问对象字段,覆盖同一 container 的 cold path / warm skip-cache path。
  • qjson_cursor_index 按 varied order 访问数组元素,覆盖同一 container 的 cold path / warm skip-cache path。
  • root getter 与 cursor getter 对同一路径结果一致。
  • duplicate key、escaped key、path-like key、wide object、wide array、nested object/array、root scalar。

语义边界

  • 语义 oracle 仍使用 serde_json::Value
  • 数字比较按 qjson getter 语义归一化为 f64,不把 serde 的 arbitrary precision number model 当作 qjson 承诺。
  • object 的整体 semantic model 按 serde/qjson getter 可观察语义使用 last-wins。
  • qjson_cursor_object_entry_at replay 必须保留并检查所有 object entries,包括 duplicate key。
  • qjson_cursor_field / path getter 不对 duplicate key 断言等于 last-wins;duplicate key 通过顺序 entry replay 覆盖。
  • root getter vs cursor getter 一致性只覆盖 qjson path 能无歧义表达的路径:路径上对象 key 在对应 parent 内唯一,且 key 不包含 ., [, ]
  • Lua mutation/materialize/encode 的 duplicate-key 行为不属于本 issue:未修改对象保留原有 key/value;一旦修改则 last-wins,这由 Lua lazy table / materialize 相关测试覆盖。

建议实现

  • 继续增强 fuzz/fuzz_targets/fuzz_parse_lazy.rs,不新增独立 target。
  • 保留当前 serde oracle + qjson cursor replay 主流程。
  • replay object 时通过 qjson_cursor_object_entry_at 顺序读取全部 entries,并用 last-wins model 与 serde 比较。
  • replay array 时通过 qjson_cursor_index 顺序重建,并额外用 varied order 重读元素。
  • 对 unique key object,用 varied order 重复调用 qjson_cursor_field,并与 entry replay 得到的 value 比较,以明确覆盖 skip-cache cold/warm path。
  • 在 replay 过程中收集 path-safe leaves/container paths;对这些路径分别调用 root getter 和从 root cursor 出发的 cursor getter,断言 type/value/错误行为一致。
  • string 使用 *_get_str,number 使用 *_get_f64,bool 使用 *_get_bool,container/null 使用 *_typeof / *_len 等不解码 value 的 API。
  • 对触发过 bug 的随机访问顺序,最小化后提交为 fuzz/corpus/fuzz_parse_lazy/ regression corpus。

CI 与文档

  • PR CI 继续通过 cargo +nightly fuzz run fuzz_parse_lazy -- -runs=0 replay corpus。
  • scheduled timed fuzz 继续运行 fuzz_parse_lazy
  • corpus seed 至少覆盖 escaped key、duplicate key、path-like key、wide object、wide array、nested object/array 和 root scalar。
  • CONTRIBUTING.md 的 fuzzing 段落说明:fuzz_parse_lazy 是 Phase 2 semantic replay target,验证读取语义一致性;fuzz_parse_eager 验证 eager parse accept/reject;fuzz_ffi_ops 验证任意 FFI 操作序列的 panic-barrier 和 pointer-safety。

验收标准

  • fuzz_parse_lazy 覆盖 cursor replay、object_entry_at 顺序遍历、varied-order field/index lookup、root/cursor getter 一致性。
  • path getter 一致性仅覆盖 path-safe unique-key 路径;duplicate key 和 path-like key 通过 entry replay 覆盖。
  • skip-cache warm path 有明确覆盖机制,并在文档中说明。
  • PR CI 能 replay fuzz_parse_lazy corpus。
  • scheduled timed fuzz 能运行 fuzz_parse_lazy
  • 文档说明 fuzz_parse_lazyfuzz_parse_eager / fuzz_ffi_ops 的边界。
  • 不把 Lua mutation/materialize/encode 的 duplicate-key 语义纳入本 issue。

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions