Skip to main content

find-postgres-bug

Find latent bugs in a local PostgreSQL source tree (REL_xx_STABLE branch or HEAD) the way a core hacker does: build a heavily-poisoned debug instance (cassert + cache-discard + -O0/-ggdb3 + core dumps), fuzz it (sqlsmith) until the backend crashes, triage the core into a minimal reproducer (MRE), git-bisect the introducing commit, and render a community-style pgsql-bugs markdown report. The instance never auto-shuts-down. Use when the user says "find a bug in postgres", "fuzz postgres", "hunt for a crash", "test a recent commit", "write a repro", "bisect this crash", "把 PG 跑起来找 bug", "fuzz 一下 postgres", "看看这个 commit 会不会出问题", etc. Triggers whenever the user points at a PG source dir and wants to hunt bugs.

Informations de source

Dépôt
digoal/blog
Dernière activité de la source
22 juin 2026 à 03:15
Langue détectée de SKILL.md
anglais
Étoiles
8 604
Forks
1 924

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Explorateur de fichiers
24 fichiers

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
find-postgres-bug
description
Find latent bugs in a local PostgreSQL source tree (REL_xx_STABLE branch or HEAD) the way a core hacker does: build a heavily-poisoned debug instance (cassert + cache-discard + -O0/-ggdb3 + core dumps), fuzz it (sqlsmith) until the backend crashes, triage the core into a minimal reproducer (MRE), git-bisect the introducing commit, and render a community-style pgsql-bugs markdown report. The instance never auto-shuts-down. Use when the user says "find a bug in postgres", "fuzz postgres", "hunt for a crash", "test a recent commit", "write a repro", "bisect this crash", "把 PG 跑起来找 bug", "fuzz 一下 postgres", "看看这个 commit 会不会出问题", etc. Triggers whenever the user points at a PG source dir and wants to hunt bugs.
# Find Postgres Bug ## Overview Crash-first bug-hunting pipeline for a local PostgreSQL source tree. The order matters and is the whole point: ``` poison the build → fuzz it → triage the core → minimise to an MRE → bisect the commit → report (do NOT self-patch) ``` The leverage is front-loaded. A poisoned build does ~90% of the work by turning latent bugs into immediate crashes; the fuzzer then finds inputs no human would hand-write. Reading commits by eye is a *secondary*, targeted probe — used to aim the fuzzer, not as the primary search. **Source layout assumed:** the standard PG tree (`src/backend/`, `src/include/`, `contrib/`, `src/test/`, …) at `$PG_SRC_DIR`. **Three hard rules:** 1. The skill **never** auto-stops the instance. Run `scripts/stop_pg.sh` explicitly when done. 2. **Never write a patch.** The deliverable is an MRE + backtrace + bisect result, posted upstream. A drive-by fix from someone who just met the code is worth less than a clean reproducer. 3. **A finding only counts if it crashes at HEAD, with the tree exactly as committed.** Never revert a fix (or commit a staged revert) to make something crash and call it a bug — that just re-creates an already-fixed bug and is worthless. Before investing in any candidate, run the pre-flight in Step 0. See [references/already_fixed.md](references/already_fixed.md). --- ## Quick start ```bash export PG_SRC_DIR=/path/to/postgres # 1. POISON: configure+build+initdb+start with cassert, cache-discard, # -O0 -ggdb3, core dumps enabled. The 90% lever. ./scripts/build_and_start_pg.sh # 2. SEED + FUZZ: load some tables, then let sqlsmith run (overnight is best) psql -h /tmp -p 55432 -d postgres -f your_schema.sql # populate the catalog ./scripts/fuzz_sqlsmith.sh 3600 postgres # 1h; use 28800 overnight # 3. TRIAGE: a crash leaves a core under $PGDATA ./scripts/triage_core.sh /path/to/$PGDATA/core.NNNN # 4. MINIMISE: cut the failing query down, confirm it crashes on demand $EDITOR repro/mybug.sql ./scripts/run_repro.sh repro/mybug.sql mybug # "SERVER IS DOWN" = good # 5. BISECT: find the commit that introduced it ./scripts/bisect_run.sh repro/mybug.sql REL_17_STABLE HEAD # 6. REPORT: render markdown for pgsql-bugs (no patch) $EDITOR /tmp/bug.yaml ./scripts/gen_bug_report.sh /tmp/bug.yaml # when finished: ./scripts/stop_pg.sh ``` If a real crash surfaces, the markdown is ready for [pgsql-bugs]. If the fuzzer runs clean, broaden the seed schema / run longer / switch branch. --- ## Workflow Each step has a **success criterion** to verify before moving on. ### Step 0 — Pre-flight: clean tree & live-at-HEAD gate (do this first) Before building or investing in any candidate, rule out the two biggest time-wasters: a dirty working tree, and a "bug" that HEAD already fixed. ```bash cd "$PG_SRC_DIR" git status --short # any M/MM/A? the tree is NOT as-committed git stash list # stashed reverts hide here too ``` - **Dirty tree → stop and ask.** Uncommitted edits (especially a *staged revert* of a fix) mean you are no longer testing HEAD. A `git commit` could re-introduce a fixed bug. Surface this to the user and do not build on top of it. Never `git commit`, `git checkout`, `git restore`, or `git stash` away the user's changes without explicit permission. - **Always build and test the tree exactly as committed.** Do not revert a fix to "reproduce" a bug. If you ever need to confirm a fix works, that is a *fix-verification* task, not a bug find — label it as such and do not report it as a new bug. When the target is a specific commit (the user said "test this commit" or you picked one in Step 6), check it is not already superseded: ```bash ./scripts/check_already_fixed.sh <hash> # is it in HEAD? any later "Fix"/revert touching the same files? ``` **Success:** `git status` is clean (or the user has explicitly accepted a dirty tree), and the candidate commit is confirmed *not* already fixed or reverted upstream. Only then proceed to Step 1. Details and edge cases: [references/already_fixed.md](references/already_fixed.md). ### Step 1 — Poison the build & start (the 90% lever) ```bash export PG_SRC_DIR=/path/to/postgres ./scripts/build_and_start_pg.sh ``` Runs `./configure --enable-debug --enable-cassert --enable-debug-symbols --enable-tap-tests` with `CFLAGS="-O0 -ggdb3"`, auto-detects the cache-poison mechanism by **grepping the tree** (PG14+: `debug_discard_caches=1` GUC; older: `-DCLOBBER_CACHE_ALWAYS`), enables core dumps, and starts on port `${PG_PORT:-55432}`. Idempotent. Read [references/pg_build_options.md](references/pg_build_options.md) for what each flag buys and how to add valgrind / ASan / `RELCACHE_FORCE_RELEASE` when chasing memory bugs. **Success:** `pg_isready -p $PG_PORT` returns 0; the start banner shows `Cache poison: guc` (or `macro`), **not** `none`. If it says `none`, poisoning was disabled — you'll find far fewer bugs. ### Step 2 — Seed & fuzz (primary discovery) ```bash # populate the catalog first — sqlsmith is far better against real tables psql -h /tmp -p $PG_PORT -d postgres -f some_schema.sql ./scripts/fuzz_sqlsmith.sh 3600 postgres ``` The fuzzer generates valid random SQL until the backend crashes. Read [references/fuzzing.md](references/fuzzing.md) for seeding strategy, targeted fuzzing (aim it at a specific commit's new objects/GUC), and alternatives (SQLancer for logic bugs, amcheck for corruption). **Success:** either a new `core*` appears under `$PGDATA` (→ Step 3), or the run is clean — in which case broaden the seed schema, run longer, or switch to a riskier branch/area (see Step 6). ### Step 3 — Triage the core ```bash ./scripts/triage_core.sh <core-file> ``` Produces a full backtrace (lldb on macOS, gdb on Linux). Read the top non-library frames for the failing function + line. If there's no core, the crash was likely an `Assert` trip — find `TRAP:` and the last `STATEMENT` in the server log. See [references/triage_and_bisect.md](references/triage_and_bisect.md). **Success:** you can name the failing function and the query that triggered it. ### Step 4 — Minimise to an MRE Reduce the fuzzer's giant query to the fewest self-contained lines that crash a fresh backend every time. Save as `repro/<name>.sql`. ```bash ./scripts/run_repro.sh repro/<name>.sql <name> ``` `run_repro.sh` now detects a crash (server gone / PANIC / TRAP), not just SQL ERRORs — it prints `SERVER IS DOWN` and exits non-zero when the MRE works. Patterns by subsystem are in [references/repro_patterns.md](references/repro_patterns.md); log reading in [references/log_indicators.md](references/log_indicators.md). **Success:** a 3–10 line `.sql` crashes the backend deterministically from a clean database. **Until this holds, do not theorise about root cause — it's a guess.** ### Step 5 — Bisect the introducing commit ```bash ./scripts/bisect_run.sh repro/<name>.sql <good-ref> <bad-ref> ``` Drives `git bisect run`: each step rebuilds, re-initdbs, runs the MRE, and reports crash-vs-clean. Confirm `<good-ref>` is genuinely clean first, or bisect will blame an innocent commit. Details + decision tree in [references/triage_and_bisect.md](references/triage_and_bisect.md). **Success:** `git bisect` names a first-bad commit; the MRE crashes at that commit and not at its parent. ### Step 6 — Targeted commit probe (when fuzzing comes up dry) If overnight fuzzing finds nothing, aim it. List recent risky commits: ```bash ./scripts/find_suspicious_commits.sh 30 HEAD ``` Read [references/commit_risk_indicators.md](references/commit_risk_indicators.md): bias toward parser/planner/executor/storage/replication paths and toward repeatedly-fixed areas (`Fix`/`Undo thinko`/`back-patch`). Then **pre-create the objects that commit cares about** and re-fuzz, or hand-write a focused repro for its new code path. ```bash ./scripts/analyze_commit.sh <hash> 600 # message + diff slice ``` **Before spending any time on a candidate, screen it (Step 0 rule 3):** ```bash ./scripts/check_already_fixed.sh <hash> ``` - A commit whose message starts with `Fix`/`Revert`/`Undo` is *itself the fix* — the bug it describes is already gone at HEAD. Don't try to reproduce that bug; the repro will pass and you'll have proven nothing. - The useful probe is the **opposite**: treat a recent fix as a hint that the surrounding code is fragile, and hunt for a *new, still-live* crash near it — one that still reproduces on the unmodified HEAD build. - **Never** revert the fix (or any commit) to make it crash. A crash that only appears after you undo a committed fix is not a finding. **Success:** you reproduced a crash on the **unmodified HEAD** build, or confirmed the candidate path is clean for the inputs you exercised. A crash that needs a reverted fix does **not** count. ### Step 7 — Render the bug report (no patch) Fill [references/bug_report_template.md](references/bug_report_template.md) into YAML, then: ```bash ./scripts/gen_bug_report.sh /tmp/<name>.yaml ``` Writes `./markdown/find_postgres_bug-<slug>-<date>.md` with environment, MRE, actual/expected, backtrace/log tail, why-it's-a-bug, and bisect result. Leave "suggested fix" as an explicitly-unverified direction at most. **Success:** the markdown has all required blocks (Environment / Summary / Repro / Actual / Why / Fix) and a log/backtrace snippet containing the `TRAP:`/`PANIC`/`ERROR:` line. **Only render a bug report for a crash that reproduces on the unmodified HEAD build.** If the investigation ended in "already fixed", "already reverted", or "only crashes after I undid a committed fix", do **not** produce a pgsql-bugs report — write a short findings note instead (`markdown/find_postgres_bug-no-new-bug-<slug>-<date>.md`) stating what was ruled out and why. Reporting an already-fixed bug upstream wastes maintainers' time. ### Step 8 — Tear down (only when done) ```bash ./scripts/stop_pg.sh ``` The **only** script that stops the instance. Nothing else will. --- ## Output format One markdown file per bug: `markdown/find_postgres_bug-<slug>-<YYYY-MM-DD>.md`. Template in [assets/bug_report.md.template](assets/bug_report.md.template); field reference in [references/bug_report_template.md](references/bug_report_template.md). Required sections (pgsql-bugs expectations): 1. **Environment** — OS / kernel / compiler / PG version / branch / commit / build flags (incl. cache-poison mode). 2. **Summary** — 1–3 factual sentences, no judgement. 3. **Reproduction** — the MRE: file path + inline `.sql`. 4. **Actual vs expected** — verbatim crash/output vs expected, plus a backtrace or server-log tail. 5. **Why is this a bug** — the spec / docs / invariant it violates. 6. **Suggested fix** — optional, flagged unverified. "I have a reliable reproducer but no fix" is a perfectly good report. --- ## Script reference | Script | Purpose | | --- | --- | | `scripts/pg_env.sh` | Sourceable env (paths, port, `PG_CFLAGS`, `PG_POISON_CACHE`) | | `scripts/build_and_start_pg.sh` | Poisoned configure+make+install+initdb+start; **idempotent, never auto-stops** | | `scripts/fuzz_sqlsmith.sh` | Drive sqlsmith at the instance; detect new cores + crash markers | | `scripts/triage_core.sh` | Backtrace a core (lldb/gdb) | | `scripts/run_repro.sh` | Run a `.sql`; detect crash (server-down/PANIC/TRAP), capture log slice | | `scripts/bisect_run.sh` | `git bisect run` an MRE across good..bad, rebuilding each step | | `scripts/find_suspicious_commits.sh` | List recent risky commits (targeted probe) | | `scripts/check_already_fixed.sh` | Screen a commit: is it in HEAD / itself a Fix / later reverted-or-refixed? | | `scripts/analyze_commit.sh` | Show stat + diff slice for one commit | | `scripts/capture_log.sh` | Grep `$PG_LOG` (tail / errors / full) | | `scripts/gen_bug_report.sh` | Render markdown from a YAML inputs file | | `scripts/stop_pg.sh` | Explicit teardown | Override any default by exporting before running: ```bash PG_PORT=55433 PG_POISON_CACHE=1 ./scripts/build_and_start_pg.sh ``` --- ## Reference index Load only what you need; each file is self-contained. - [references/pg_build_options.md](references/pg_build_options.md) — poisoned-build flags, cache-discard mechanisms, valgrind/ASan, pitfalls. - [references/fuzzing.md](references/fuzzing.md) — sqlsmith usage, seeding, targeted fuzzing, SQLancer/amcheck alternatives. - [references/triage_and_bisect.md](references/triage_and_bisect.md) — backtrace reading, MRE minimisation discipline, `git bisect run`, the "report don't patch" rule, triage decision tree. - [references/commit_risk_indicators.md](references/commit_risk_indicators.md) — commit-log-as-regression-signal + where bugs hide, for the targeted probe. - [references/already_fixed.md](references/already_fixed.md) — the live-at-HEAD rule: clean-tree gate, screening a commit, why revert-to-reproduce is worthless, fix-verification vs bug-find. - [references/repro_patterns.md](references/repro_patterns.md) — minimal repro patterns by subsystem. - [references/log_indicators.md](references/log_indicators.md) — severities, greps, multi-line patterns (asserts, backtraces, context). - [references/bug_report_template.md](references/bug_report_template.md) — YAML fields + how to post to pgsql-bugs. - [references/pg_version_helpers.md](references/pg_version_helpers.md) — version / commit / compiler / OS one-liners for the report. --- ## What this skill does *not* do - It does **not** write or submit a patch. MRE + backtrace + bisect, posted as a markdown draft for the human to send. This is deliberate. - It does **not** build sqlsmith for you — if it's missing, `fuzz_sqlsmith.sh` prints install hints and exits. - It does **not** shut the instance down. Explicit teardown only. - It does **not** guess the root cause before there's a deterministic MRE. Triage produces a backtrace; minimisation produces the proof. --- ## Decision tree (TL;DR) ``` PG source dir?
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub