Skip to content

feat(ci): promote sanitized-fuzz from weekly to daily, scale rounds 5-7x (#109) - #129

Merged
Liam0205 merged 2 commits into
masterfrom
feat/daily-sanitized-fuzz
Jun 19, 2026
Merged

feat(ci): promote sanitized-fuzz from weekly to daily, scale rounds 5-7x (#109)#129
Liam0205 merged 2 commits into
masterfrom
feat/daily-sanitized-fuzz

Conversation

@Liam0205

Copy link
Copy Markdown
Owner

Follow-up to #128.

Why scale up

The smoke run on #128 (100 ASan + 50 TSan via workflow_dispatch) measured the workflow at ~9 minutes end-to-end, with ~1.4 s/round across both sanitizers. Per-round time is dominated by the orchestration loop in differential-fuzz.py (spawn go/java/cpp, serialize JSON outputs), not by sanitizer instrumentation overhead. That makes the original weekly 1500/500-round cadence ~50 minutes of fuzz work — far below what the runner can absorb.

Public-repo Actions minutes are unmetered. The real cap is wall time per run. Promote to daily, fill the envelope.

Changes

Before (weekly) After (daily)
Cadence Sundays 22:00 UTC+8 Daily 12:00 UTC+8 (04:00 UTC)
ASan rounds 1500 (~36m projected) 5000 (~120m)
TSan rounds 500 (~12m projected) 3500 (~80m)
ASan step timeout 240m 130m (inner timeout 125m)
TSan step timeout 90m 100m (inner timeout 95m)
Job timeout 355m 240m
Filename weekly-sanitized-fuzz.yml daily-sanitized-fuzz.yml (git mv, 85% similarity)
Workflow name / group / artifacts / issue titles "Weekly …" "Daily …"

Total projected wall time: ~205m, leaving ~35m headroom for runner-side noise within the 240m job ceiling.

Per-round symmetry between ASan and TSan

Smoke-run measured ASan and TSan at ~equal per-round time (~1.4 s) despite TSan's notional 5-10x raw cpp slowdown. Reason: each round in differential-fuzz.py spawns go/java/cpp child processes and serializes their outputs, so the cpp-side sanitizer cost is amortized into a smaller fraction of round time than headline numbers suggest.

Treat this as the operating model. If a future change lifts cpp's per-round share (e.g. in-process fuzz harness), budgets need re-deriving.

#128 small (1) — cancellation bypass

Bot flagged that a job timeout could cancel mid-fuzz, making !cancelled() false and bypassing the incomplete-issue step. With the larger envelope (240m vs 200m), this becomes more likely than before, but the ~35m headroom is still ample on a typical run. Observe-and-iterate remains the right call — first few daily runs will validate the actual wall-time distribution.

Test plan

  • YAML valid (python3 -c 'import yaml; yaml.safe_load(...)')
  • All "weekly" / "Weekly" string references renamed (grep -i weekly → 0 hits)
  • git mv detected as 85% similarity rename (history preserved)
  • Step / inner timeouts arithmetically fit inside timeout-minutes: 240
  • Post-merge: trigger via workflow_dispatch with default rounds to validate the 5000+3500 envelope on a real runner before the first cron fires

…-7x (#109)

Smoke-run on #128 (100 ASan + 50 TSan rounds via workflow_dispatch)
showed the workflow takes ~9 minutes end-to-end and measured ~1.4 s/round
across both sanitizers — orchestration cost, not sanitizer overhead,
dominates per-round time. That leaves significant headroom: the original
1500/500-round weekly cadence projects to ~50 minutes (1.4 s × 2000) of
actual fuzz work, well under the 6h hosted-runner ceiling.

Public-repo Actions minutes are unmetered, so the bottleneck is wall
time per run, not budget. Promote to daily (12:00 UTC+8 = 04:00 UTC)
and raise rounds to fill a ~3.5h fuzz envelope:

| Pass | rounds (was → now) | wall ~ | notes                           |
|------|--------------------|--------|---------------------------------|
| ASan | 1500 → **5000**    | ~120m  | step 130m, inner timeout 125m   |
| TSan |  500 → **3500**    | ~80m   | step 100m, inner timeout 95m    |

Job timeout-minutes raised 200 → 240 to accept the larger fuzz envelope
plus build (~3.5m for both sanitizer Debug builds) + apt + codegen
(~3m) + setup (~1m) ≈ 205m, leaving ~35m runner-noise headroom.

Per-round cost is empirically symmetric across ASan and TSan because
the per-round work in differential-fuzz.py is dominated by spawning
go / java / cpp child processes and serializing their JSON outputs;
the cpp-side sanitizer slowdown is amortized to background. So the
TSan-vs-ASan ratio is closer to 1:1 in this orchestration than the
3-10x raw cpp ratio would suggest. Treat this as the operating model;
future cpp work that lifts cpp's per-round share (say, in-process fuzz)
will need budgets re-derived.

Rename `weekly-sanitized-fuzz.yml` → `daily-sanitized-fuzz.yml` (git
mv) so cron / file name / workflow name stay coherent. Updated the
workflow `name:`, concurrency group, all comments, all step summary
labels, all issue titles, and the divergence/incomplete artifact
filename. No semantic logic change beyond cadence + rounds + budgets.

The bot-flagged cancellation-bypass small in #128 (job timeout
canceling mid-fuzz so `!cancelled()` evaluates false on the
incomplete-issue step) becomes more likely with the larger envelope
but still has ~35m headroom on a typical run; observe-and-iterate
remains the right call.

#109 / #124 background unchanged: this workflow is the deep-diagnostic
complement to nightly diff-fuzz, designed to surface sanitizer stacks
at the moment a divergence occurs rather than after a Release-mode
divergence triggers a reactive 40x TSan replay (which on #109 / #124
produced "No ThreadSanitizer report" because the rare window had
already closed).
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 审查

项目 结果
结论 💬 COMMENT
审查截止 bf5762a48a7bcdbaf5d555c3bfe3c31b6616e2dc

cadence/rounds/timeout 调整合理,git mv 保留了历史,所有 "weekly" 引用已同步重命名。无阻塞问题;但 incomplete-pass issue 模板内残留了旧的 timeout/rounds 数值,且预算注释对构建耗时估计偏乐观。

🟠 重要建议 (1)

  • 文件: .github/workflows/daily-sanitized-fuzz.yml L413-L416
  • 问题: "Open issue on incomplete pass" 步骤的 triage 文案是旧值,未随本 PR 同步更新:写的是 `per-pass timeout (ASan 230m, TSan 85m)` 和 `TSan 500 rounds`,而本 PR 实际已改为 inner timeout `ASan 125m / TSan 95m`、`TSan 3500 rounds`。这段文案会在 fuzz pass 超时(exit=124)时写进自动创建的 issue,给后续 triage 提供错误的 timeout 与 round 数,误导判断「该 bump limit 还是 cut rounds」。
  • 建议: 将这段文案中的 `230m`→`125m`、`85m`→`95m`、`500 rounds`→`3500 rounds` 同步修正。

🟢 小问题 (1)

  • 文件: .github/workflows/daily-sanitized-fuzz.yml L18-L19 / L52-L57
  • 问题: 头部注释与 job 预算注释把构建估为 `build (~5m)`,但实际有三次 C++ 构建——ASan、TSan,以及 codegen 步骤 `cross-validate.sh` 触发的 Release 旁路构建(见 L117-L123 自身注释),加上 go/java 构建与 apt。fuzz inner timeout 合计 220m(125+95),叠加更真实的构建耗时后,`240m` job ceiling 的实际余量小于注释宣称的 `~35m`。PR 描述已采取 observe-and-iterate 策略,此处仅作为佐证:首批 daily run 后建议据实测复核预算。

…unds (#129)

Address bot-reviewer concern: the "Open issue on incomplete pass" step's
triage suggestion text held over from #128 (ASan 230m / TSan 85m / TSan
500 rounds), but this PR cut ASan inner to 125m, raised TSan inner to
95m, and raised TSan rounds to 3500. A future incomplete-pass issue
filed by this workflow would have shown stale numbers and misled the
"bump the limit or cut rounds" decision.

Refresh the per-exit-code triage paragraph to reflect:

- ASan inner timeout 125m, 5000 rounds (~120m projected, ~5m head)
- TSan inner timeout 95m, 3500 rounds (~80m projected, ~15m head)
- Add a one-liner: routine exit=124 → runner noise, bump timeout first;
  only cut rounds if recurrent (not the previous "may need budget
  verification" hedge that no longer applies after the smoke run).

No behavior change — pure documentation accuracy fix in a step that
fires only on degraded runs.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 bf5762a..a67ce77
审查截止 a67ce778fc125aeae51d50e4a2f4e0217d16ecfe

本次增量(a67ce77)精准修复了上次审查的🟠重要建议:incomplete-pass issue 的 triage 文案已从旧值同步为当前实际配置,且新增了 exit=124 的处置一行(routine 噪声→先 bump timeout,复发再 cut rounds)。纯文档准确性修正,无行为变更。

已交叉核对全文件,triage 文案与各处配置完全一致:

上次的 🟢 小问题(头部把构建估为 ~5m、240m job ceiling 实际余量可能小于注释宣称的 ~35m)未在本增量中触及。PR 描述已明确采取 observe-and-iterate 策略,首批 daily run 后据实测复核预算即可,不阻塞合并。

增量改动无阻塞问题,质量良好。

@Liam0205
Liam0205 merged commit 619db9c into master Jun 19, 2026
21 checks passed
@Liam0205
Liam0205 deleted the feat/daily-sanitized-fuzz branch June 19, 2026 14:48
Liam0205 added a commit that referenced this pull request Jun 19, 2026
Re-release attempt. v0.10.7 was tagged on 2026-06-17 (commit 5618f49)
but its release.yml workflow_run trigger failed to fire (root cause
fixed in #126), so PyPI / Maven Central never received v0.10.7
artifacts and there is no GitHub Release entry.

Re-publishing v0.10.7 by force-moving the tag to a new commit is not
viable: Go module proxy already cached pine-go/v0.10.7 → 5618f49 (see
proxy.golang.org/.../@v/v0.10.7.info, served at 2026-06-17 15:47Z).
Moving the tag would create an unfixable mismatch between the git tag
SHA and the proxy-cached SHA, so any `go get @v0.10.7` keeps fetching
5618f49 regardless of what git says.

Bump to 0.10.8 instead, which captures the post-#126 commits inline:

  bc8d6ac chore: bump 0.10.6 → 0.10.7 + #119 / #125 fixes
  6471007 fix(ci): release.yml workflow_call gate (#123)
  ce4dbff fix(ci): incomplete-pass signal (#109)
  b934876 feat(ci): weekly sanitized fuzz (#109)
  ef24382 feat(ci): weekly → daily, scale rounds (#109)
  619db9c fix(ci): refresh incomplete-pass triage doc (#129)

bump-version.sh ran the full four-runtime test + cross-validate sweep
before this commit; all 12 cross-validate sections PASS.

Next: make tag-release will create v0.10.8 + pine-go/v0.10.8 and push,
which now triggers release.yml directly via push:tags (no more
workflow_run quirk) → invokes ci.yml as workflow_call gate → on green
publishes to PyPI / Maven Central.

v0.10.7 stays as a stale git tag pointing at 5618f49. Document it as
"never published" if anyone notices the gap; not worth deleting since
deletion does not unmake the proxy cache.
Liam0205 added a commit that referenced this pull request Jun 21, 2026
…put (#132 #133)

The 2026-06-20 and 2026-06-21 daily-sanitized-fuzz runs both tripped
exit=124 on BOTH the ASan and the TSan pass — the workflow's
diagnostic capability went to zero for two consecutive days. Both
issues (#132, #133) auto-filed by the incomplete-pass detector are
unanimous: ASan 5000-round and TSan 3500-round budgets are too
aggressive for production-grade runner pressure.

Per-round time was assumed at ~1.4 s/round based on the #128 smoke run
(100 / 50 rounds in 9 minutes). Production runs measure ~1.5 s/round
ASan and ~1.6 s/round TSan — close to the smoke-run estimate but not
within the 5-10 % buffer the original budget allowed. Possible reasons:

- Smoke runs do not accumulate sanitizer shadow-memory pressure that
  builds over thousands of rounds.
- Runner noise compounds: 100 rounds across a quiet runner ≠ 5000
  rounds across a runner that may swap, GC-pause, page-fault, etc.
- ASan / UBSan with -O1 -g and TSan with shadow-of-shadow tracking
  both have non-linear cost growth as the heap and seen-event sets
  fill up.

Cut rounds 40 % and re-derive the budgets from the measured rates,
with a 20 % headroom inside each inner timeout (vs. the previous
~5-15 %, which proved insufficient):

| | rounds (was → now) | rate | inner timeout (was → now) | headroom |
|---|---|---|---|---|
| ASan | 5000 → **3000** | 1.5 s/r | 125m → **90m** | ~15m of 90m (20 %) |
| TSan | 3500 → **2000** | 1.6 s/r | 95m → **65m** | ~10m of 65m (~18 %) |

Step `timeout-minutes` and the job-level `timeout-minutes` follow the
same pattern: 130/100 → 95/70, 240 → 180. Total wall-time projection
~140m, leaving ~40m headroom against the 180m job ceiling for build
+ apt + codegen-parity overhead.

Also refreshed:

- Header rationale comment (now references the #132/#133 production
  measurements, not the smoke-run estimate)
- Step-summary table headers (defaults `'5000'` / `'3500'` → `'3000'`
  / `'2000'`)
- Incomplete-pass issue triage paragraph (`125m` → `90m`,
  `95m` → `65m`, round counts and rate sources reflect production)

Coverage trade-off: 2000 + 3000 rounds is still ~5x the original
weekly cadence (1500/500), and unlike weekly we run daily — so
seven-day cumulative coverage is 21000 ASan + 14000 TSan rounds vs.
the previous weekly 1500 + 500. Net coverage strictly improved over
the original design, just trimmed below the over-aggressive #129
levels.

Closes #132. Closes #133.

Out of scope: #109 root-cause fix (TSan finally captured the race in
#131; analysis posted on #109; fix design pending). Daily fuzz must
run cleanly first or the next #109 race window will silently slip past
again — which is exactly what this commit restores.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant