From 3dd68db1f537f4935e1739ad1c833c10efefcbab Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:39:52 +0000 Subject: [PATCH 01/17] tools: ast-grep: integrate with build and CI --- .github/workflows/ci.yaml | 5 + tests/check/CMakeLists.txt | 50 +++++ tools/ast-grep/ignore_known_issues.py | 189 ++++++++++++++++++ tools/ast-grep/known_issues/README.md | 25 +++ tools/ast-grep/rule-tests/.gitkeep | 0 tools/ast-grep/rules/.gitkeep | 0 tools/ast-grep/sgconfig.yml | 6 + .../ast-grep/utils/is-public-declaration.yml | 10 + tools/ast-grep/utils/is-public-function.yml | 7 + 9 files changed, 292 insertions(+) create mode 100755 tools/ast-grep/ignore_known_issues.py create mode 100644 tools/ast-grep/known_issues/README.md create mode 100644 tools/ast-grep/rule-tests/.gitkeep create mode 100644 tools/ast-grep/rules/.gitkeep create mode 100644 tools/ast-grep/sgconfig.yml create mode 100644 tools/ast-grep/utils/is-public-declaration.yml create mode 100644 tools/ast-grep/utils/is-public-function.yml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9e8bb3e45..bbe1f3461 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -110,6 +110,11 @@ jobs: key: tests-results-${{ github.run_id }} - name: Mount bpffs run: mount bpffs /sys/fs/bpf -t bpf + - name: Install ast-grep + run: | + dnf install -y pipx python3-pyyaml + pipx install --global ast-grep-cli + ast-grep --version - name: Configure the build run: cmake -S $GITHUB_WORKSPACE -B $GITHUB_WORKSPACE/build -DWITH_COVERAGE=1 - name: Build tests diff --git a/tests/check/CMakeLists.txt b/tests/check/CMakeLists.txt index e26f9baf1..2a944b11b 100644 --- a/tests/check/CMakeLists.txt +++ b/tests/check/CMakeLists.txt @@ -51,6 +51,56 @@ set_tests_properties("check.checkstyle" PROPERTIES LABELS "check" ) +find_program(SG_BIN NAMES ast-grep) + +set(SG_PYYAML_RC 1) +if(SG_BIN) + execute_process( + COMMAND python3 -c "import yaml" + RESULT_VARIABLE SG_PYYAML_RC + OUTPUT_QUIET ERROR_QUIET + ) + if(NOT SG_PYYAML_RC EQUAL 0) + message(WARNING + "ast-grep: python3 cannot import yaml; " + "skipping check.astgrep. Install python3-pyyaml (dnf) or " + "python3-yaml (apt) to enable it.") + endif() +endif() + +if(SG_BIN AND SG_PYYAML_RC EQUAL 0) + set(SG_CONFIG ${CMAKE_SOURCE_DIR}/tools/ast-grep/sgconfig.yml) + set(SG_KNOWN_ISSUES ${CMAKE_SOURCE_DIR}/tools/ast-grep/known_issues) + set(SG_FILTER ${CMAKE_SOURCE_DIR}/tools/ast-grep/ignore_known_issues.py) + + add_test(NAME "check.astgrep" + COMMAND bash -c "set -o pipefail; \ +${SG_BIN} test --config ${SG_CONFIG} && \ +${SG_BIN} scan --config ${SG_CONFIG} --json=stream \ + ${CMAKE_SOURCE_DIR}/src/libbpfilter \ + ${CMAKE_SOURCE_DIR}/src/bfcli \ + | python3 ${SG_FILTER} --check \ + --known-issues-dir ${SG_KNOWN_ISSUES} \ + --repo-root ${CMAKE_SOURCE_DIR}" + ) + + set_tests_properties("check.astgrep" PROPERTIES + LABELS "check" + ) + + add_custom_target(astgrep-known-issues + COMMAND bash -c "set -o pipefail; \ +${SG_BIN} scan --config ${SG_CONFIG} --json=stream \ + ${CMAKE_SOURCE_DIR}/src/libbpfilter \ + ${CMAKE_SOURCE_DIR}/src/bfcli \ + | python3 ${SG_FILTER} --regen \ + --known-issues-dir ${SG_KNOWN_ISSUES} \ + --repo-root ${CMAKE_SOURCE_DIR}" + COMMENT "Regenerating ast-grep known_issues/ from current scan" + VERBATIM + ) +endif() + add_custom_target(fixstyle COMMAND ${CLANG_FORMAT_BIN} diff --git a/tools/ast-grep/ignore_known_issues.py b/tools/ast-grep/ignore_known_issues.py new file mode 100755 index 000000000..55ed88f35 --- /dev/null +++ b/tools/ast-grep/ignore_known_issues.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + +"""Filter ast-grep scan output against per-rule known-issues baselines. + +Two modes: + --check (default): read ast-grep JSON stream from stdin, drop matches that + appear in tools/ast-grep/known_issues/.yml, print + the remainder to stderr, exit nonzero iff the remainder + is non-empty. + --regen : same input, but overwrite the known_issues tree with + exactly the set of currently-observed violations. + +The identity key for an entry is (file, textSha, occurrence). Rule is implicit +from the filename stem. See the header comment emitted at the top of each YAML +file for the schema. +""" + +import argparse +import hashlib +import json +import pathlib +import sys +from collections import defaultdict + +try: + import yaml +except ImportError: + sys.stderr.write( + "ERROR: PyYAML is required to run ignore_known_issues.py.\n" + " Fedora/RHEL : dnf install -y python3-pyyaml\n" + " Debian/Ubuntu: apt-get install -y python3-yaml\n" + " pip : pip install pyyaml\n" + ) + sys.exit(2) + + +HEADER_TEMPLATE = """\ +# Known issues for ast-grep rule `{rule}`. +# +# Suppressed during check.astgrep so only NEW violations fail CI. Regenerate +# this file (and all siblings) by running: make -C build astgrep-known-issues +# +# Schema: list of +# file: path relative to repo root +# textSha: first 12 hex chars of sha256(match.text) +# occurrence: 0-based index among identical (file, textSha) matches +# +# Do not hand-edit — the directory is regenerated deterministically. + +""" + + +def parse_args(argv): + p = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + mode = p.add_mutually_exclusive_group() + mode.add_argument("--check", action="store_true", + help="filter stdin against known_issues/ (default)") + mode.add_argument("--regen", action="store_true", + help="rewrite known_issues/ from stdin") + p.add_argument("--known-issues-dir", required=True, type=pathlib.Path, + help="path to the known_issues/ directory") + p.add_argument("--repo-root", required=True, type=pathlib.Path, + help="repo root used to emit relative paths in ast-grep output") + args = p.parse_args(argv) + if not args.regen: + args.check = True + return args + + +def read_observations(stream, repo_root): + prefix = str(repo_root).rstrip("/") + "/" + obs = [] + for raw in stream: + raw = raw.strip() + if not raw: + continue + d = json.loads(raw) + rule = d["ruleId"] + path = d["file"] + if path.startswith(prefix): + path = path[len(prefix):] + text = d["text"] + text_sha = hashlib.sha256(text.encode("utf-8")).hexdigest()[:12] + byte_off = d["range"]["byteOffset"]["start"] + line_num = d["range"]["start"]["line"] + 1 + message = d.get("message", "") + obs.append({ + "rule": rule, + "file": path, + "textSha": text_sha, + "byteOffset": byte_off, + "line": line_num, + "message": message, + }) + return obs + + +def assign_occurrences(observations): + groups = defaultdict(list) + for o in observations: + groups[(o["rule"], o["file"], o["textSha"])].append(o) + out = [] + for items in groups.values(): + items.sort(key=lambda o: o["byteOffset"]) + for i, o in enumerate(items): + o["occurrence"] = i + out.append(o) + return out + + +def load_all_known_issues(directory): + known = {} + if not directory.exists(): + return known + for path in sorted(directory.glob("*.yml")): + with open(path) as f: + data = yaml.safe_load(f) or [] + rule = path.stem + known[rule] = {(e["file"], e["textSha"], e["occurrence"]) for e in data} + return known + + +def write_known_issues_file(path, rule, entries): + header = HEADER_TEMPLATE.format(rule=rule) + body = yaml.safe_dump(entries, default_flow_style=False, sort_keys=False, + width=200, allow_unicode=True) + path.write_text(header + body) + + +def cmd_check(args): + known = load_all_known_issues(args.known_issues_dir) + observed = assign_occurrences(read_observations(sys.stdin, args.repo_root)) + leftovers = [] + for o in observed: + key = (o["file"], o["textSha"], o["occurrence"]) + if key in known.get(o["rule"], set()): + continue + leftovers.append(o) + if not leftovers: + return 0 + leftovers.sort(key=lambda o: (o["file"], o["line"], o["rule"])) + for o in leftovers: + sys.stderr.write( + f"{o['file']}:{o['line']} [{o['rule']}] {o['message']}\n" + ) + sys.stderr.write( + f"\n{len(leftovers)} new ast-grep violation(s) not in known_issues/. " + "Run `make -C build astgrep-known-issues` to refresh the baseline " + "once these are intentional.\n" + ) + return 1 + + +def cmd_regen(args): + observed = assign_occurrences(read_observations(sys.stdin, args.repo_root)) + by_rule = defaultdict(list) + for o in observed: + by_rule[o["rule"]].append({ + "file": o["file"], + "textSha": o["textSha"], + "occurrence": o["occurrence"], + }) + for rule in by_rule: + by_rule[rule].sort( + key=lambda e: (e["file"], e["textSha"], e["occurrence"]) + ) + args.known_issues_dir.mkdir(parents=True, exist_ok=True) + existing = {p.stem: p for p in args.known_issues_dir.glob("*.yml")} + for stem, path in existing.items(): + if stem not in by_rule: + path.unlink() + for rule, entries in by_rule.items(): + write_known_issues_file( + args.known_issues_dir / f"{rule}.yml", rule, entries + ) + return 0 + + +def main(argv): + args = parse_args(argv) + if args.regen: + return cmd_regen(args) + return cmd_check(args) + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/tools/ast-grep/known_issues/README.md b/tools/ast-grep/known_issues/README.md new file mode 100644 index 000000000..227597849 --- /dev/null +++ b/tools/ast-grep/known_issues/README.md @@ -0,0 +1,25 @@ +# ast-grep known issues + +Per-rule baselines of currently-known violations. One YAML file per rule that +has at least one violation in the tree; filename is `.yml`. + +`check.astgrep` subtracts these entries from each scan so only NEW violations +fail CI. To refresh the baseline after fixing (or intentionally introducing) +violations, run: + + make -C build astgrep-known-issues + +The directory is regenerated deterministically — do not hand-edit. Entries in +a file for which no current violation matches are silently ignored, so stale +entries never break CI; they are cleaned up on the next regeneration. + +See `../ignore_known_issues.py` for the filter implementation. + +## Severity interaction + +This suppression mechanism only works for rules declared `severity: warning`. +`ast-grep scan` exits non-zero on any `severity: error` match regardless of +the baseline, and `set -o pipefail` on the CI command then fails the pipeline +before `ignore_known_issues.py` gets a chance to subtract known entries. A +rule promoted to `error` is therefore zero-tolerance: new violations must be +fixed (or the rule's exclusion regex extended), not baselined. diff --git a/tools/ast-grep/rule-tests/.gitkeep b/tools/ast-grep/rule-tests/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/tools/ast-grep/rules/.gitkeep b/tools/ast-grep/rules/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/tools/ast-grep/sgconfig.yml b/tools/ast-grep/sgconfig.yml new file mode 100644 index 000000000..e416dae38 --- /dev/null +++ b/tools/ast-grep/sgconfig.yml @@ -0,0 +1,6 @@ +ruleDirs: + - rules +testConfigs: + - testDir: rule-tests +utilDirs: + - utils diff --git a/tools/ast-grep/utils/is-public-declaration.yml b/tools/ast-grep/utils/is-public-declaration.yml new file mode 100644 index 000000000..24598c0a8 --- /dev/null +++ b/tools/ast-grep/utils/is-public-declaration.yml @@ -0,0 +1,10 @@ +id: is-public-declaration +language: c +rule: + kind: declaration + has: + kind: function_declarator + stopBy: end + not: + has: + kind: storage_class_specifier diff --git a/tools/ast-grep/utils/is-public-function.yml b/tools/ast-grep/utils/is-public-function.yml new file mode 100644 index 000000000..06054724f --- /dev/null +++ b/tools/ast-grep/utils/is-public-function.yml @@ -0,0 +1,7 @@ +id: is-public-function +language: c +rule: + kind: function_definition + not: + has: + kind: storage_class_specifier From 95534683f0fd9c9ccde093835d8fa071eb50774c Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:26 +0000 Subject: [PATCH 02/17] tools: ast-grep: add assert-pointer-params rule --- .../known_issues/assert-pointer-params.yml | 222 ++++++++++++++++++ .../assert-pointer-params-snapshot.yml | 62 +++++ .../rule-tests/assert-pointer-params-test.yml | 61 +++++ .../ast-grep/rules/assert-pointer-params.yml | 40 ++++ 4 files changed, 385 insertions(+) create mode 100644 tools/ast-grep/known_issues/assert-pointer-params.yml create mode 100644 tools/ast-grep/rule-tests/__snapshots__/assert-pointer-params-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/assert-pointer-params-test.yml create mode 100644 tools/ast-grep/rules/assert-pointer-params.yml diff --git a/tools/ast-grep/known_issues/assert-pointer-params.yml b/tools/ast-grep/known_issues/assert-pointer-params.yml new file mode 100644 index 000000000..fde1a7a8e --- /dev/null +++ b/tools/ast-grep/known_issues/assert-pointer-params.yml @@ -0,0 +1,222 @@ +# Known issues for ast-grep rule `assert-pointer-params`. +# +# Suppressed during check.astgrep so only NEW violations fail CI. Regenerate +# this file (and all siblings) by running: make -C build astgrep-known-issues +# +# Schema: list of +# file: path relative to repo root +# textSha: first 12 hex chars of sha256(match.text) +# occurrence: 0-based index among identical (file, textSha) matches +# +# Do not hand-edit — the directory is regenerated deterministically. + +- file: src/bfcli/chain.c + textSha: 015322579f9b + occurrence: 0 +- file: src/bfcli/chain.c + textSha: 161eb117b6f2 + occurrence: 0 +- file: src/bfcli/chain.c + textSha: 165aee3eca9e + occurrence: 0 +- file: src/bfcli/chain.c + textSha: 1fe88ca095cf + occurrence: 0 +- file: src/bfcli/chain.c + textSha: 5fc8d5e15e69 + occurrence: 0 +- file: src/bfcli/chain.c + textSha: 61410cd82b8f + occurrence: 0 +- file: src/bfcli/chain.c + textSha: 7deeb02a1b93 + occurrence: 0 +- file: src/bfcli/chain.c + textSha: 8a289d8dbc84 + occurrence: 0 +- file: src/bfcli/helper.c + textSha: 89dbca7b087f + occurrence: 0 +- file: src/bfcli/helper.c + textSha: 984f1b1efb34 + occurrence: 0 +- file: src/bfcli/opts.c + textSha: 02ddee1f7264 + occurrence: 0 +- file: src/bfcli/ruleset.c + textSha: 38418151179a + occurrence: 0 +- file: src/bfcli/ruleset.c + textSha: 603c80d6b825 + occurrence: 0 +- file: src/bfcli/ruleset.c + textSha: 7e482f2fbaa4 + occurrence: 0 +- file: src/libbpfilter/bpf.c + textSha: 8f665f4bac9c + occurrence: 0 +- file: src/libbpfilter/bpf/flow_hash.bpf.c + textSha: 06d61d7fbe9f + occurrence: 0 +- file: src/libbpfilter/bpf/parse_ipv6_eh.bpf.c + textSha: 0959e5c4de58 + occurrence: 0 +- file: src/libbpfilter/bpf/parse_ipv6_nh.bpf.c + textSha: a1d5b1a1ccc6 + occurrence: 0 +- file: src/libbpfilter/bpf/pkt_log.bpf.c + textSha: 601971e31a07 + occurrence: 0 +- file: src/libbpfilter/bpf/sock_addr_log.bpf.c + textSha: 8e1ac09c0d5f + occurrence: 0 +- file: src/libbpfilter/bpf/update_counters.bpf.c + textSha: fdb5fc199651 + occurrence: 0 +- file: src/libbpfilter/cgen/jmp.c + textSha: 7c034a830be0 + occurrence: 0 +- file: src/libbpfilter/cgen/matcher/meta.c + textSha: d4cadd755138 + occurrence: 0 +- file: src/libbpfilter/cgen/program.c + textSha: 370d08a81bcd + occurrence: 0 +- file: src/libbpfilter/cgen/program.c + textSha: 377375bb9d2e + occurrence: 0 +- file: src/libbpfilter/cgen/program.c + textSha: 4ed58fb6cd02 + occurrence: 0 +- file: src/libbpfilter/cgen/program.c + textSha: 963b7268983b + occurrence: 0 +- file: src/libbpfilter/cgen/program.c + textSha: ef42453862e0 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.c + textSha: 04a539af2bea + occurrence: 0 +- file: src/libbpfilter/chain.c + textSha: ddaae9d2d743 + occurrence: 0 +- file: src/libbpfilter/cli.c + textSha: 2cbbf89bf8a0 + occurrence: 0 +- file: src/libbpfilter/cli.c + textSha: b437cd626883 + occurrence: 0 +- file: src/libbpfilter/cli.c + textSha: d0a04421b2b8 + occurrence: 0 +- file: src/libbpfilter/cli.c + textSha: f410bd8173e4 + occurrence: 0 +- file: src/libbpfilter/ctx.c + textSha: 1074ebe4f8b3 + occurrence: 0 +- file: src/libbpfilter/ctx.c + textSha: 14900f4ba260 + occurrence: 0 +- file: src/libbpfilter/ctx.c + textSha: 72a63c443276 + occurrence: 0 +- file: src/libbpfilter/ctx.c + textSha: 884da285789d + occurrence: 0 +- file: src/libbpfilter/ctx.c + textSha: c53291ea78d6 + occurrence: 0 +- file: src/libbpfilter/ctx.c + textSha: c75097eba5b8 + occurrence: 0 +- file: src/libbpfilter/hook.c + textSha: 46d7ebdfbbff + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 00e6bb2328fa + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 010cf94535f5 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 04f434af96e7 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 0e973964018f + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 233ddd6435cb + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 3698b3adb4e1 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 3fddd93c016c + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 40c0ed87a885 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 42200fc4457a + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 4f0d3824d968 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 55a9e28e8b20 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 57b6fa7a9463 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 5be4034475af + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 71349cc3567c + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: '735981974522' + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 7db9a0816078 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 978a524ccbf3 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: 97f095bf54e8 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: b153a8811a54 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: ba02d24665c1 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: bfca02e10ccb + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: cf14bda17984 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: d66410df254c + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: d9b5e4d188cc + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: dace33f14c8b + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: e20cde019ca9 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: e98cd70f7287 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: e9e461224a16 + occurrence: 0 +- file: src/libbpfilter/pack.c + textSha: feb7a6da0c90 + occurrence: 0 diff --git a/tools/ast-grep/rule-tests/__snapshots__/assert-pointer-params-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/assert-pointer-params-snapshot.yml new file mode 100644 index 000000000..00be886ad --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/assert-pointer-params-snapshot.yml @@ -0,0 +1,62 @@ +id: assert-pointer-params +snapshots: + ? | + int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule) + { + return 0; + } + : labels: + - source: |- + int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule) + { + return 0; + } + style: primary + start: 0 + end: 85 + - source: '*chain' + style: secondary + start: 38 + end: 44 + - source: struct bf_chain *chain + style: secondary + start: 22 + end: 44 + - source: (struct bf_chain *chain, struct bf_rule *rule) + style: secondary + start: 21 + end: 67 + - source: bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule) + style: secondary + start: 4 + end: 67 + ? | + void bf_rule_free(struct bf_rule **rule) + { + return; + } + : labels: + - source: |- + void bf_rule_free(struct bf_rule **rule) + { + return; + } + style: primary + start: 0 + end: 56 + - source: '**rule' + style: secondary + start: 33 + end: 39 + - source: struct bf_rule **rule + style: secondary + start: 18 + end: 39 + - source: (struct bf_rule **rule) + style: secondary + start: 17 + end: 40 + - source: bf_rule_free(struct bf_rule **rule) + style: secondary + start: 5 + end: 40 diff --git a/tools/ast-grep/rule-tests/assert-pointer-params-test.yml b/tools/ast-grep/rule-tests/assert-pointer-params-test.yml new file mode 100644 index 000000000..e60dba742 --- /dev/null +++ b/tools/ast-grep/rule-tests/assert-pointer-params-test.yml @@ -0,0 +1,61 @@ +id: assert-pointer-params +valid: + - | + int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule) + { + assert(chain); + assert(rule); + return 0; + } + - | + int bf_set_size(int count) + { + return count; + } + - | + static int _helper(struct bf_chain *chain) + { + return 0; + } + - | + int main(int argc, const char **argv) + { + return 0; + } + - | + int bf_hook_opts_set(struct bf_hookopts *opts, const char *name) + { + assert(opts); + assert(name); + opts->name = name; + return 0; + } + - | + void closep(int *fd) + { + return; + } + - | + void yyerror(struct bfc_ruleset *ruleset, const char *fmt, ...) + { + return; + } +invalid: + - | + int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule) + { + return 0; + } + - | + void bf_rule_free(struct bf_rule **rule) + { + return; + } +# Known limitation: the rule flags only functions with zero assert() calls. +# A function with multiple pointer parameters but only some asserted will +# pass silently. The following case is a known-pass (documented limitation): +# int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule) +# { +# assert(chain); +# return 0; +# } diff --git a/tools/ast-grep/rules/assert-pointer-params.yml b/tools/ast-grep/rules/assert-pointer-params.yml new file mode 100644 index 000000000..b713f98f9 --- /dev/null +++ b/tools/ast-grep/rules/assert-pointer-params.yml @@ -0,0 +1,40 @@ +id: assert-pointer-params +language: c +severity: warning +message: Functions with pointer parameters must assert() them at entry +note: >- + bpfilter uses assert() for pointer preconditions at function entry. + Limitation: this rule flags functions with zero assert() calls; it cannot + verify that EACH pointer parameter is asserted individually. A function + with three pointer parameters and a single assert() will pass silently. +rule: + all: + - matches: is-public-function + - has: + kind: function_declarator + has: + kind: parameter_list + has: + kind: parameter_declaration + has: + kind: pointer_declarator + stopBy: end + - not: + has: + kind: compound_statement + has: + kind: call_expression + has: + kind: identifier + regex: "^assert$" + stopBy: end + stopBy: end + # Keep this exclusion list in sync with bf-prefix-public-functions.yml, + # which has its own list of non-prefixed public functions. Adding a new + # special-case public function may require updating both rules. + - not: + has: + kind: function_declarator + has: + kind: identifier + regex: "^(main|closep|yyerror)$" From 99d10aa09b7ac748d7f2a5c0480055f51e2465ff Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:33 +0000 Subject: [PATCH 03/17] tools: ast-grep: add bf-prefix-public-functions rule --- .../bf-prefix-public-functions-snapshot.yml | 30 ++++++++++ .../bf-prefix-public-functions-test.yml | 58 +++++++++++++++++++ .../rules/bf-prefix-public-functions.yml | 20 +++++++ 3 files changed, 108 insertions(+) create mode 100644 tools/ast-grep/rule-tests/__snapshots__/bf-prefix-public-functions-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/bf-prefix-public-functions-test.yml create mode 100644 tools/ast-grep/rules/bf-prefix-public-functions.yml diff --git a/tools/ast-grep/rule-tests/__snapshots__/bf-prefix-public-functions-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/bf-prefix-public-functions-snapshot.yml new file mode 100644 index 000000000..4897c7976 --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/bf-prefix-public-functions-snapshot.yml @@ -0,0 +1,30 @@ +id: bf-prefix-public-functions +snapshots: + ? | + int my_func(void) + { + return 0; + } + : labels: + - source: |- + int my_func(void) + { + return 0; + } + style: primary + start: 0 + end: 35 + ? | + void process_packet(struct pkt *p) + { + return; + } + : labels: + - source: |- + void process_packet(struct pkt *p) + { + return; + } + style: primary + start: 0 + end: 50 diff --git a/tools/ast-grep/rule-tests/bf-prefix-public-functions-test.yml b/tools/ast-grep/rule-tests/bf-prefix-public-functions-test.yml new file mode 100644 index 000000000..c3a6543db --- /dev/null +++ b/tools/ast-grep/rule-tests/bf-prefix-public-functions-test.yml @@ -0,0 +1,58 @@ +id: bf-prefix-public-functions +valid: + - | + int bf_chain_new(struct bf_chain **chain) + { + return 0; + } + - | + int bfc_parse(const char *input) + { + return 0; + } + - | + int main(int argc, const char **argv) + { + return 0; + } + - | + void closep(int *fd) + { + return; + } + - | + void freep(void *ptr) + { + free(*(void **)ptr); + } + - | + static int helper(int x) + { + return x + 1; + } + - | + void yyerror(struct bfc_ruleset *ruleset, const char *fmt, ...) + { + return; + } + - | + void _bf_ctx_free(struct bf_ctx **ctx) + { + return; + } + - | + int _bfc_opts_get_cmd(int object, int action) + { + return 0; + } +invalid: + - | + int my_func(void) + { + return 0; + } + - | + void process_packet(struct pkt *p) + { + return; + } diff --git a/tools/ast-grep/rules/bf-prefix-public-functions.yml b/tools/ast-grep/rules/bf-prefix-public-functions.yml new file mode 100644 index 000000000..2bf1a1d8f --- /dev/null +++ b/tools/ast-grep/rules/bf-prefix-public-functions.yml @@ -0,0 +1,20 @@ +id: bf-prefix-public-functions +language: c +severity: error +message: >- + Non-static function does not follow naming convention. + Public library functions must use bf_ prefix, CLI functions bfc_ prefix. + Internal/static functions use leading underscore (_bf_, _bfc_). +note: >- + Rename this function with the appropriate prefix: bf_ for libbpfilter, + bfc_ for CLI utilities, or make it static if it is internal. +rule: + all: + - matches: is-public-function + - not: + has: + kind: function_declarator + has: + kind: identifier + regex: "^(_?bf_|_?bfc_|main|closep|freep|yy\\w+)" + stopBy: end From dc696f233849886c1c37496cc85b7e8947e7ba2b Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:34 +0000 Subject: [PATCH 04/17] tools: ast-grep: add double-pointer-output rule --- .../double-pointer-output-snapshot.yml | 24 ++++++++++++++++ .../rule-tests/double-pointer-output-test.yml | 18 ++++++++++++ .../ast-grep/rules/double-pointer-output.yml | 28 +++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 tools/ast-grep/rule-tests/__snapshots__/double-pointer-output-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/double-pointer-output-test.yml create mode 100644 tools/ast-grep/rules/double-pointer-output.yml diff --git a/tools/ast-grep/rule-tests/__snapshots__/double-pointer-output-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/double-pointer-output-snapshot.yml new file mode 100644 index 000000000..b68842849 --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/double-pointer-output-snapshot.yml @@ -0,0 +1,24 @@ +id: double-pointer-output +snapshots: + ? | + int bf_chain_new(struct bf_chain *chain) + { + return 0; + } + : labels: + - source: |- + int bf_chain_new(struct bf_chain *chain) + { + return 0; + } + style: primary + start: 0 + end: 58 + - source: bf_chain_new + style: secondary + start: 4 + end: 16 + - source: bf_chain_new(struct bf_chain *chain) + style: secondary + start: 4 + end: 40 diff --git a/tools/ast-grep/rule-tests/double-pointer-output-test.yml b/tools/ast-grep/rule-tests/double-pointer-output-test.yml new file mode 100644 index 000000000..4a14004d3 --- /dev/null +++ b/tools/ast-grep/rule-tests/double-pointer-output-test.yml @@ -0,0 +1,18 @@ +id: double-pointer-output +valid: + - | + int bf_chain_new(struct bf_chain **chain) + { + return 0; + } + - | + int bf_set_new(struct bf_set **set, const char *name) + { + return 0; + } +invalid: + - | + int bf_chain_new(struct bf_chain *chain) + { + return 0; + } diff --git a/tools/ast-grep/rules/double-pointer-output.yml b/tools/ast-grep/rules/double-pointer-output.yml new file mode 100644 index 000000000..3d9c62727 --- /dev/null +++ b/tools/ast-grep/rules/double-pointer-output.yml @@ -0,0 +1,28 @@ +id: double-pointer-output +language: c +severity: error +message: >- + Constructor bf_*_new() / bfc_*_new() missing double-pointer output parameter. + Constructors must take a TYPE ** parameter to return the allocated object. +note: >- + Change the output parameter to a double pointer (TYPE **) so the caller + receives ownership via TAKE_PTR() and cleanup attributes work correctly. +rule: + kind: function_definition + has: + kind: function_declarator + has: + kind: identifier + regex: "^(bf|bfc)_\\w+_new(_from_\\w+)?$" + not: + has: + kind: function_declarator + has: + kind: parameter_list + has: + kind: parameter_declaration + has: + kind: pointer_declarator + has: + kind: pointer_declarator + stopBy: end From 6e4c9ff93a55756f25cfcf65841583fbb9fcb4b7 Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:34 +0000 Subject: [PATCH 05/17] tools: ast-grep: add doxygen-prefer-backticks rule --- .../known_issues/doxygen-prefer-backticks.yml | 312 ++++++++++++++++++ .../doxygen-prefer-backticks-snapshot.yml | 36 ++ .../doxygen-prefer-backticks-test.yml | 34 ++ .../rules/doxygen-prefer-backticks.yml | 11 + 4 files changed, 393 insertions(+) create mode 100644 tools/ast-grep/known_issues/doxygen-prefer-backticks.yml create mode 100644 tools/ast-grep/rule-tests/__snapshots__/doxygen-prefer-backticks-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/doxygen-prefer-backticks-test.yml create mode 100644 tools/ast-grep/rules/doxygen-prefer-backticks.yml diff --git a/tools/ast-grep/known_issues/doxygen-prefer-backticks.yml b/tools/ast-grep/known_issues/doxygen-prefer-backticks.yml new file mode 100644 index 000000000..ed0dc99df --- /dev/null +++ b/tools/ast-grep/known_issues/doxygen-prefer-backticks.yml @@ -0,0 +1,312 @@ +# Known issues for ast-grep rule `doxygen-prefer-backticks`. +# +# Suppressed during check.astgrep so only NEW violations fail CI. Regenerate +# this file (and all siblings) by running: make -C build astgrep-known-issues +# +# Schema: list of +# file: path relative to repo root +# textSha: first 12 hex chars of sha256(match.text) +# occurrence: 0-based index among identical (file, textSha) matches +# +# Do not hand-edit — the directory is regenerated deterministically. + +- file: src/libbpfilter/cgen/cgen.h + textSha: 3386bd8074b9 + occurrence: 0 +- file: src/libbpfilter/cgen/cgen.h + textSha: 63544f36033c + occurrence: 0 +- file: src/libbpfilter/cgen/cgen.h + textSha: 7f36b04baf43 + occurrence: 0 +- file: src/libbpfilter/cgen/cgen.h + textSha: ed4e176cc7a7 + occurrence: 0 +- file: src/libbpfilter/cgen/cgroup_skb.c + textSha: 81eb94438abc + occurrence: 0 +- file: src/libbpfilter/cgen/dump.h + textSha: d59ccaab2c04 + occurrence: 0 +- file: src/libbpfilter/cgen/fixup.h + textSha: 3e6bf4bc78b8 + occurrence: 0 +- file: src/libbpfilter/cgen/fixup.h + textSha: 5e79e3459737 + occurrence: 0 +- file: src/libbpfilter/cgen/fixup.h + textSha: be36db6feeef + occurrence: 0 +- file: src/libbpfilter/cgen/fixup.h + textSha: e3afefc6af20 + occurrence: 0 +- file: src/libbpfilter/cgen/fixup.h + textSha: fffd47c0a5ce + occurrence: 0 +- file: src/libbpfilter/cgen/handle.h + textSha: 88f0e87042f5 + occurrence: 0 +- file: src/libbpfilter/cgen/jmp.h + textSha: 37e854480185 + occurrence: 0 +- file: src/libbpfilter/cgen/jmp.h + textSha: 646d76301a01 + occurrence: 0 +- file: src/libbpfilter/cgen/jmp.h + textSha: 703efffd8c21 + occurrence: 0 +- file: src/libbpfilter/cgen/jmp.h + textSha: aa4fc699ea3d + occurrence: 0 +- file: src/libbpfilter/cgen/printer.h + textSha: 13475dd5390c + occurrence: 0 +- file: src/libbpfilter/cgen/printer.h + textSha: 15e606e4f06d + occurrence: 0 +- file: src/libbpfilter/cgen/printer.h + textSha: 4d37e9ba0a88 + occurrence: 0 +- file: src/libbpfilter/cgen/printer.h + textSha: e0a64eee7a4f + occurrence: 0 +- file: src/libbpfilter/cgen/prog/map.c + textSha: 3c8a866a89b2 + occurrence: 0 +- file: src/libbpfilter/cgen/prog/map.h + textSha: ff1cdc81e252 + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: 6bb0ee0c3594 + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: 8e9b53e4bd9c + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: c6e5ecfad415 + occurrence: 0 +- file: src/libbpfilter/cgen/stub.h + textSha: 6f92922e3f16 + occurrence: 0 +- file: src/libbpfilter/cgen/stub.h + textSha: 7601f82a31e0 + occurrence: 0 +- file: src/libbpfilter/cgen/stub.h + textSha: b8ed1e3f3927 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.c + textSha: 09d468a9ccba + occurrence: 0 +- file: src/libbpfilter/cgen/swich.c + textSha: 338bb79aaea7 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.c + textSha: 5190b9eb39a3 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.c + textSha: 8323dbec5a73 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: 046b11033131 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: 2b4e7da54bda + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: 801006f4d6b8 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: 8ab3d65330a5 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: 8d2d7bf6f8a0 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: a05f8446e069 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: b6e26ed038c9 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: b6e4083e2436 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: b6f85878bc30 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: ce59c0acd8e3 + occurrence: 0 +- file: src/libbpfilter/cgen/tc.c + textSha: 81eb94438abc + occurrence: 0 +- file: src/libbpfilter/cgen/xdp.c + textSha: 5334a62e6851 + occurrence: 0 +- file: src/libbpfilter/ctx.c + textSha: 16ef643c97f2 + occurrence: 0 +- file: src/libbpfilter/ctx.c + textSha: 171623e39707 + occurrence: 0 +- file: src/libbpfilter/ctx.c + textSha: 3b10f1490b5c + occurrence: 0 +- file: src/libbpfilter/ctx.c + textSha: 60b95fba186a + occurrence: 0 +- file: src/libbpfilter/ctx.c + textSha: 64c4e2874d14 + occurrence: 0 +- file: src/libbpfilter/ctx.c + textSha: b9158e10ad06 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/bpf.h + textSha: 0d8d0df64ae5 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/bpf.h + textSha: 7ffb41bbd809 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/bpf.h + textSha: 9d7b1fdab6d1 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/btf.h + textSha: 01a5199f91b8 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/btf.h + textSha: 2ca52658812a + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/btf.h + textSha: 8ab566a806d6 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: 00105179342e + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: 037971247775 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: 06dad5773b07 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: 3663965a042f + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: 3cee29de7168 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: 4edb5e264c35 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: 5d691894f102 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: 7903dc1b12ef + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: 98383d9adb63 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: b0b9c4d876e4 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: c07c5bd5750a + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: c7328c516fa5 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: e53ad01bf0be + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: ec2271217398 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: f67286000795 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/counter.h + textSha: 1eb6ec7f3950 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/counter.h + textSha: 26477a5a7291 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/ctx.h + textSha: 21aadf52979d + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/dump.h + textSha: 33c0db33c671 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/dump.h + textSha: d3774a4fa36a + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/dump.h + textSha: d6c9e31532c3 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/dump.h + textSha: e18c11d434e4 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/flavor.h + textSha: 0496debc11cd + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/flavor.h + textSha: e6b35c10ea13 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/helper.h + textSha: 0244c5fa2dc2 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/helper.h + textSha: 4fa0ba9265c9 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/helper.h + textSha: 8c4d6652f4b1 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/helper.h + textSha: 9c4178d2e51a + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/helper.h + textSha: b90d71650ffd + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/helper.h + textSha: d9bb52502529 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/io.h + textSha: c2b751101489 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/logger.h + textSha: 19dff1c3903c + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/logger.h + textSha: 3f025029d644 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/logger.h + textSha: 49027e2eb7c3 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/logger.h + textSha: 9e3ba12774f8 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/logger.h + textSha: b505954be23e + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 1c8478fa797e + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 4716d05a019d + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 924cd926f915 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: b2ea64338f66 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: b336ebc45fdd + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/rule.h + textSha: 585a544cfdaf + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/rule.h + textSha: 8f1d3bd1d0d8 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/rule.h + textSha: fde04936ead1 + occurrence: 0 diff --git a/tools/ast-grep/rule-tests/__snapshots__/doxygen-prefer-backticks-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/doxygen-prefer-backticks-snapshot.yml new file mode 100644 index 000000000..1dfe5f9ef --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/doxygen-prefer-backticks-snapshot.yml @@ -0,0 +1,36 @@ +id: doxygen-prefer-backticks +snapshots: + ? | + /** + * @brief Free a map object. + * + * @param map @ref bf_map object to free. + */ + void bf_map_free(struct bf_map **map); + : labels: + - source: |- + /** + * @brief Free a map object. + * + * @param map @ref bf_map object to free. + */ + style: primary + start: 0 + end: 81 + ? | + /** + * @brief Manage BPF object references. + * + * @ref bf_handle is used to manage references. + */ + int bf_handle_new(void); + : labels: + - source: |- + /** + * @brief Manage BPF object references. + * + * @ref bf_handle is used to manage references. + */ + style: primary + start: 0 + end: 98 diff --git a/tools/ast-grep/rule-tests/doxygen-prefer-backticks-test.yml b/tools/ast-grep/rule-tests/doxygen-prefer-backticks-test.yml new file mode 100644 index 000000000..53f417af8 --- /dev/null +++ b/tools/ast-grep/rule-tests/doxygen-prefer-backticks-test.yml @@ -0,0 +1,34 @@ +id: doxygen-prefer-backticks +valid: + - | + /** + * @brief Create a new chain. + * + * @param chain On success, points to the new `bf_chain`. + * @return 0 on success. + */ + int bf_chain_new(struct bf_chain **chain); + - | + // @param is fine in regular comments + int x = 0; + - | + /** + * @brief Return value description. + * @return A `bf_btf` structure on success. + */ + int bf_foo(void); +invalid: + - | + /** + * @brief Manage BPF object references. + * + * @ref bf_handle is used to manage references. + */ + int bf_handle_new(void); + - | + /** + * @brief Free a map object. + * + * @param map @ref bf_map object to free. + */ + void bf_map_free(struct bf_map **map); diff --git a/tools/ast-grep/rules/doxygen-prefer-backticks.yml b/tools/ast-grep/rules/doxygen-prefer-backticks.yml new file mode 100644 index 000000000..4832594ea --- /dev/null +++ b/tools/ast-grep/rules/doxygen-prefer-backticks.yml @@ -0,0 +1,11 @@ +id: doxygen-prefer-backticks +language: c +severity: warning +message: >- + Prefer `name` over @p name, @ref name, and @c name in Doxygen comments. +note: >- + Replace @p name, @ref name, and @c name with `name` for readability. + This migration is gradual; many existing comments still use the old forms. +rule: + kind: comment + regex: "@(p|ref|c)\\s" From 3b9d81cb41a9ea0c833b23a7d8afa18bed064826 Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:34 +0000 Subject: [PATCH 06/17] tools: ast-grep: add doxygen-public-functions rule --- .../known_issues/doxygen-public-functions.yml | 615 ++++++++++++++++++ .../doxygen-public-functions-snapshot.yml | 17 + .../doxygen-public-functions-test.yml | 27 + .../rules/doxygen-public-functions.yml | 20 + 4 files changed, 679 insertions(+) create mode 100644 tools/ast-grep/known_issues/doxygen-public-functions.yml create mode 100644 tools/ast-grep/rule-tests/__snapshots__/doxygen-public-functions-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/doxygen-public-functions-test.yml create mode 100644 tools/ast-grep/rules/doxygen-public-functions.yml diff --git a/tools/ast-grep/known_issues/doxygen-public-functions.yml b/tools/ast-grep/known_issues/doxygen-public-functions.yml new file mode 100644 index 000000000..8e17f0915 --- /dev/null +++ b/tools/ast-grep/known_issues/doxygen-public-functions.yml @@ -0,0 +1,615 @@ +# Known issues for ast-grep rule `doxygen-public-functions`. +# +# Suppressed during check.astgrep so only NEW violations fail CI. Regenerate +# this file (and all siblings) by running: make -C build astgrep-known-issues +# +# Schema: list of +# file: path relative to repo root +# textSha: first 12 hex chars of sha256(match.text) +# occurrence: 0-based index among identical (file, textSha) matches +# +# Do not hand-edit — the directory is regenerated deterministically. + +- file: src/bfcli/chain.h + textSha: 4798445ff5ae + occurrence: 0 +- file: src/bfcli/chain.h + textSha: 50d56c7a20ef + occurrence: 0 +- file: src/bfcli/chain.h + textSha: 84f18fb6d847 + occurrence: 0 +- file: src/bfcli/chain.h + textSha: a61c0fb8aeaf + occurrence: 0 +- file: src/bfcli/chain.h + textSha: b005af6e43e6 + occurrence: 0 +- file: src/bfcli/chain.h + textSha: ca62e534e389 + occurrence: 0 +- file: src/bfcli/chain.h + textSha: ef719f3786ca + occurrence: 0 +- file: src/bfcli/chain.h + textSha: eff45b664744 + occurrence: 0 +- file: src/bfcli/helper.h + textSha: 58cfdb24e8e4 + occurrence: 0 +- file: src/bfcli/helper.h + textSha: fa7bbcb897ef + occurrence: 0 +- file: src/bfcli/opts.h + textSha: 7bc32e264c20 + occurrence: 0 +- file: src/bfcli/opts.h + textSha: 9d055e5fa656 + occurrence: 0 +- file: src/bfcli/print.h + textSha: 1f8a0cb669f9 + occurrence: 0 +- file: src/bfcli/print.h + textSha: e0337cc09f1c + occurrence: 0 +- file: src/bfcli/ruleset.h + textSha: 05701bdf5ab5 + occurrence: 0 +- file: src/bfcli/ruleset.h + textSha: 063f759af8df + occurrence: 0 +- file: src/bfcli/ruleset.h + textSha: 3faf24b164ae + occurrence: 0 +- file: src/bfcli/ruleset.h + textSha: 4b400c38b389 + occurrence: 0 +- file: src/libbpfilter/cgen/cgen.h + textSha: 19356dba33db + occurrence: 0 +- file: src/libbpfilter/cgen/cgen.h + textSha: 24bd86606e6e + occurrence: 0 +- file: src/libbpfilter/cgen/cgen.h + textSha: 3418d587cd13 + occurrence: 0 +- file: src/libbpfilter/cgen/cgen.h + textSha: 48bd76db46b7 + occurrence: 0 +- file: src/libbpfilter/cgen/cgen.h + textSha: 7f34ef3744d3 + occurrence: 0 +- file: src/libbpfilter/cgen/cgen.h + textSha: 869b751c88fb + occurrence: 0 +- file: src/libbpfilter/cgen/cgen.h + textSha: 92fb3926953c + occurrence: 0 +- file: src/libbpfilter/cgen/cgen.h + textSha: c609ffdfff37 + occurrence: 0 +- file: src/libbpfilter/cgen/cgen.h + textSha: dd60e170d890 + occurrence: 0 +- file: src/libbpfilter/cgen/cgen.h + textSha: f4cfbc050569 + occurrence: 0 +- file: src/libbpfilter/cgen/dump.h + textSha: ef6795f23242 + occurrence: 0 +- file: src/libbpfilter/cgen/fixup.h + textSha: 4fa9b4f4dbf8 + occurrence: 0 +- file: src/libbpfilter/cgen/fixup.h + textSha: 65da81c00ede + occurrence: 0 +- file: src/libbpfilter/cgen/fixup.h + textSha: e7f2f4318a11 + occurrence: 0 +- file: src/libbpfilter/cgen/jmp.h + textSha: bd1298627f39 + occurrence: 0 +- file: src/libbpfilter/cgen/matcher/meta.h + textSha: 47882b120d31 + occurrence: 0 +- file: src/libbpfilter/cgen/printer.h + textSha: 0ef42bf17826 + occurrence: 0 +- file: src/libbpfilter/cgen/printer.h + textSha: 5b0f895acc60 + occurrence: 0 +- file: src/libbpfilter/cgen/printer.h + textSha: 60153e8f62d2 + occurrence: 0 +- file: src/libbpfilter/cgen/printer.h + textSha: 9d5a96159b95 + occurrence: 0 +- file: src/libbpfilter/cgen/printer.h + textSha: 9fb577ce05da + occurrence: 0 +- file: src/libbpfilter/cgen/printer.h + textSha: bf2f9c25c02d + occurrence: 0 +- file: src/libbpfilter/cgen/printer.h + textSha: e1d7d49ff042 + occurrence: 0 +- file: src/libbpfilter/cgen/prog/map.h + textSha: 6868044f1c91 + occurrence: 0 +- file: src/libbpfilter/cgen/prog/map.h + textSha: 8e44cedc155c + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: 0cafb76bad21 + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: 46d81cbcf924 + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: 47329ac7ac60 + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: 5af2f3247881 + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: 69cc917ee64e + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: 7678c7c966f8 + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: a5435322a1b5 + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: bbfa016d1bcb + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: d48d286a8209 + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: e66efdd60341 + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: fda53d7a1507 + occurrence: 0 +- file: src/libbpfilter/cgen/stub.h + textSha: 12dce8e66715 + occurrence: 0 +- file: src/libbpfilter/cgen/stub.h + textSha: 2d0579bff4a1 + occurrence: 0 +- file: src/libbpfilter/cgen/stub.h + textSha: 3dda9d2d34c6 + occurrence: 0 +- file: src/libbpfilter/cgen/stub.h + textSha: 53539d8dfd0e + occurrence: 0 +- file: src/libbpfilter/cgen/stub.h + textSha: 5c706d59211c + occurrence: 0 +- file: src/libbpfilter/cgen/stub.h + textSha: e2ade78a25a7 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: 0515ed3a3215 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: 0d056f8191fc + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: 74644f4fe3c1 + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: 9a85336a111e + occurrence: 0 +- file: src/libbpfilter/cgen/swich.h + textSha: c45e300e1e88 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/bpf.h + textSha: 370a3dfdbeaa + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/bpf.h + textSha: 3d5f843a23ab + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/bpf.h + textSha: '480737400928' + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/bpf.h + textSha: da468ed89d7f + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/bpf.h + textSha: dc15628965db + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/bpf.h + textSha: f978188b3f3c + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/btf.h + textSha: 4b6a2162f51a + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/btf.h + textSha: 4f99e1b0deba + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/btf.h + textSha: 55818ec14b74 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/btf.h + textSha: 87fb41875216 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/btf.h + textSha: 8ac10e8d304f + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/btf.h + textSha: e446ffd39711 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/chain.h + textSha: 14e5844700f0 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/chain.h + textSha: 1c14949b83e0 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/chain.h + textSha: 2248d63eccf1 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/chain.h + textSha: '756495870045' + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/chain.h + textSha: d98bbfe5ae20 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: 38a369ae76ba + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: 5530f685071c + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: 5d8be6d6e698 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: 92f4a8553667 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: 9c26c9fd2e70 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: b3bf660036dc + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: c29d7552d80c + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/core/list.h + textSha: f24fd5e5d704 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/counter.h + textSha: 38c614ee78a6 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/counter.h + textSha: 9d90f4493c3d + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/ctx.h + textSha: 30315d011b1c + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/ctx.h + textSha: 56ab86c122d6 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/ctx.h + textSha: 602d99d23c0d + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/ctx.h + textSha: 708ad4cb1cee + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/ctx.h + textSha: 793e84337f01 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/ctx.h + textSha: 877a66ff8e2e + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/ctx.h + textSha: 9efb7b998a82 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/ctx.h + textSha: ba2816c21fa3 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/ctx.h + textSha: c661917a4e34 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/dump.h + textSha: 470e1de554e8 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/dump.h + textSha: 6fe8b5a44192 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/dump.h + textSha: 96b9118e4a33 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/dump.h + textSha: f88823c98538 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/elfstub.h + textSha: 90588d5f9fde + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/flavor.h + textSha: b0959b1a9f5a + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/helper.h + textSha: 1dc1d017d395 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/helper.h + textSha: 63725a5ceab4 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/helper.h + textSha: cb69ce83c1e3 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/helper.h + textSha: ddc7e51b8dc6 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/hook.h + textSha: 019430b5423a + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/hook.h + textSha: 22b213e8ea75 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/hook.h + textSha: 3e5b924009c4 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/hook.h + textSha: 3f9d5bdcaf85 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/hook.h + textSha: 4c48283b1089 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/hook.h + textSha: 6863e6253de4 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/hook.h + textSha: 794e86209b84 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/hook.h + textSha: 7aa1925528db + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/hook.h + textSha: 9d9225ef3502 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/hook.h + textSha: df03956cc571 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/hook.h + textSha: dfe1c0ad0705 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/hook.h + textSha: e10e9ca760d3 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/hook.h + textSha: e7c1d06e2bc0 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/hook.h + textSha: e80d5a0aa309 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/if.h + textSha: c7d417ba6dc9 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/if.h + textSha: e2e86a59b273 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/io.h + textSha: 5d0b6fb2f2e2 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/logger.h + textSha: 1da097df0d44 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/logger.h + textSha: 46bcbf45bee3 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/logger.h + textSha: 4dc6063f9dfc + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/logger.h + textSha: 868a5696115d + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/logger.h + textSha: 9b8a80f36558 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/logger.h + textSha: 9b8b5738d8a4 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 09b1309262a3 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 1c43a438e509 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 28ae8256c514 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 2f6bb15871b7 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 38c86790eccb + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 4ac4b9e35b23 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 66dfe512f366 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 693a82c2f9f4 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 7b65818c2de7 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 7cf888e7214b + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 8566f4775932 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 9548b4792e9a + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 97fd50710b51 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: 9e2abcada4cc + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: a38eebb5d95e + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: af8d22a62bb4 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: b149e249c911 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: b18bcb8d3855 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: b3b4922204d7 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: b89df71d382c + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: c76615058565 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: d0bf723982f0 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: e3c0991a8035 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: eb5e1efe28bb + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/matcher.h + textSha: ff288730ddf0 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 0596108b77a9 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 063e0d49ee1a + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 1461f7f7af29 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 160215f7d15c + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 191d3d2ddb1e + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 19972d9309b0 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 2364176a6791 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 2932e366b639 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 3209792ab7e0 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 43e50433d724 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 44d4df562068 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 4bad9727413f + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 56690d4595d3 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 5d08529d24f8 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 64c06477ef3b + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 6e1748096532 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 75e20e5a2bbd + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 787d39504021 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 7a52ae8cc174 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 7f668c226482 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 88da80b50ab7 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 8d7188833562 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 8ee450c8c84b + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: 96b617299db4 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: a20bff28f28b + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: a822e0b555d3 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: b661dbf3fdff + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: c0c180d66d4f + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: c64d829a0d6e + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: d58aee0bb25f + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: e7a8e922a90a + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: e82d21b7e5cc + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/pack.h + textSha: f70be3c2ef3a + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/rule.h + textSha: 40acb51276db + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/rule.h + textSha: 818fd2310984 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/rule.h + textSha: d6918414a620 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/rule.h + textSha: e55d42692fe8 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/set.h + textSha: 681544b92a05 + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/set.h + textSha: 75702bfec7cf + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/verdict.h + textSha: 4bd812a2e96d + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/verdict.h + textSha: a75556c1318f + occurrence: 0 +- file: src/libbpfilter/include/bpfilter/verdict.h + textSha: d3d0fcc286e9 + occurrence: 0 diff --git a/tools/ast-grep/rule-tests/__snapshots__/doxygen-public-functions-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/doxygen-public-functions-snapshot.yml new file mode 100644 index 000000000..77f7d3dc4 --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/doxygen-public-functions-snapshot.yml @@ -0,0 +1,17 @@ +id: doxygen-public-functions +snapshots: + ? | + // This is not a Doxygen comment + int bf_set_add(struct bf_set *set, int value); + : labels: + - source: int bf_set_add(struct bf_set *set, int value); + style: primary + start: 33 + end: 79 + ? | + int bf_rule_free(void); + : labels: + - source: int bf_rule_free(void); + style: primary + start: 0 + end: 23 diff --git a/tools/ast-grep/rule-tests/doxygen-public-functions-test.yml b/tools/ast-grep/rule-tests/doxygen-public-functions-test.yml new file mode 100644 index 000000000..74efa5173 --- /dev/null +++ b/tools/ast-grep/rule-tests/doxygen-public-functions-test.yml @@ -0,0 +1,27 @@ +id: doxygen-public-functions +valid: + - | + /** + * @brief Create a new chain from the given parameters. + * + * @param chain On success, points to the new chain. + * @return 0 on success, negative errno on failure. + */ + int bf_chain_new(struct bf_chain **chain, const char *name); + - | + static inline int _bf_helper(int x); + - | + /** + * @brief Free a chain object. + * + * @param chain Pointer to the chain pointer. Set to NULL on return. + */ + void bf_chain_free(struct bf_chain **chain); + - | + int x = 42; +invalid: + - | + int bf_rule_free(void); + - | + // This is not a Doxygen comment + int bf_set_add(struct bf_set *set, int value); diff --git a/tools/ast-grep/rules/doxygen-public-functions.yml b/tools/ast-grep/rules/doxygen-public-functions.yml new file mode 100644 index 000000000..3b016653d --- /dev/null +++ b/tools/ast-grep/rules/doxygen-public-functions.yml @@ -0,0 +1,20 @@ +id: doxygen-public-functions +language: c +severity: warning +files: + - "**/*.h" +message: >- + Public function declaration missing @brief tag. + Public functions should be preceded by a /** @brief ... */ comment. +note: >- + Add a Doxygen comment with an explicit @brief tag before this declaration. + Exception: trivial getters/setters may skip documentation per style.rst. + This rule flags functions whose preceding comment lacks an explicit + @brief tag; implicit-brief Doxygen is also detected as missing. +rule: + all: + - matches: is-public-declaration + - not: + follows: + kind: comment + regex: "@brief" From 2ca0a8083bb0a0a0c27e24beaae714c823f43def Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:41 +0000 Subject: [PATCH 07/17] tools: ast-grep: add enum-bf-prefix rule --- .../__snapshots__/enum-bf-prefix-snapshot.yml | 42 +++++++++++++++++++ .../rule-tests/enum-bf-prefix-test.yml | 32 ++++++++++++++ tools/ast-grep/rules/enum-bf-prefix.yml | 16 +++++++ 3 files changed, 90 insertions(+) create mode 100644 tools/ast-grep/rule-tests/__snapshots__/enum-bf-prefix-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/enum-bf-prefix-test.yml create mode 100644 tools/ast-grep/rules/enum-bf-prefix.yml diff --git a/tools/ast-grep/rule-tests/__snapshots__/enum-bf-prefix-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/enum-bf-prefix-snapshot.yml new file mode 100644 index 000000000..47c08f05d --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/enum-bf-prefix-snapshot.yml @@ -0,0 +1,42 @@ +id: enum-bf-prefix +snapshots: + ? | + enum filter_type + { + FILTER_ACCEPT, + _FILTER_MAX, + }; + : labels: + - source: |- + enum filter_type + { + FILTER_ACCEPT, + _FILTER_MAX, + } + style: primary + start: 0 + end: 56 + - source: filter_type + style: secondary + start: 5 + end: 16 + ? | + enum hook + { + HOOK_XDP, + _HOOK_MAX, + }; + : labels: + - source: |- + enum hook + { + HOOK_XDP, + _HOOK_MAX, + } + style: primary + start: 0 + end: 42 + - source: hook + style: secondary + start: 5 + end: 9 diff --git a/tools/ast-grep/rule-tests/enum-bf-prefix-test.yml b/tools/ast-grep/rule-tests/enum-bf-prefix-test.yml new file mode 100644 index 000000000..488d8bcba --- /dev/null +++ b/tools/ast-grep/rule-tests/enum-bf-prefix-test.yml @@ -0,0 +1,32 @@ +id: enum-bf-prefix +valid: + - | + enum bf_hook + { + BF_HOOK_XDP, + _BF_HOOK_MAX, + }; + - | + enum bfc_action + { + BFC_ACTION_ACCEPT, + _BFC_ACTION_MAX, + }; + - | + enum + { + _BFC_DEPRECATED_FOO = 100, + }; +invalid: + - | + enum hook + { + HOOK_XDP, + _HOOK_MAX, + }; + - | + enum filter_type + { + FILTER_ACCEPT, + _FILTER_MAX, + }; diff --git a/tools/ast-grep/rules/enum-bf-prefix.yml b/tools/ast-grep/rules/enum-bf-prefix.yml new file mode 100644 index 000000000..f8b57e8d8 --- /dev/null +++ b/tools/ast-grep/rules/enum-bf-prefix.yml @@ -0,0 +1,16 @@ +id: enum-bf-prefix +language: c +severity: error +message: >- + Enum tag does not follow naming convention. + Library enums must use bf_ prefix, CLI enums use bfc_ prefix. +note: >- + Rename the enum to bf_ (libbpfilter) or bfc_ (bfcli). + Anonymous enums are allowed (the rule only fires on named enums). +rule: + kind: enum_specifier + has: + field: name + kind: type_identifier + not: + regex: "^(bf_|bfc_)" From 75a4fc22110a17b3d980f86cf7cb35c13cb50a7c Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:41 +0000 Subject: [PATCH 08/17] tools: ast-grep: add enum-sentinel-format rule --- .../enum-sentinel-format-snapshot.yml | 58 +++++++++++++++++++ .../rule-tests/enum-sentinel-format-test.yml | 47 +++++++++++++++ tools/ast-grep/rules/enum-sentinel-format.yml | 37 ++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 tools/ast-grep/rule-tests/__snapshots__/enum-sentinel-format-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/enum-sentinel-format-test.yml create mode 100644 tools/ast-grep/rules/enum-sentinel-format.yml diff --git a/tools/ast-grep/rule-tests/__snapshots__/enum-sentinel-format-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/enum-sentinel-format-snapshot.yml new file mode 100644 index 000000000..0fe209133 --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/enum-sentinel-format-snapshot.yml @@ -0,0 +1,58 @@ +id: enum-sentinel-format +snapshots: + ? | + enum bf_chain_type + { + BF_CHAIN_TYPE_FILTER, + BF_CHAIN_TYPE_NAT, + }; + : labels: + - source: |- + enum bf_chain_type + { + BF_CHAIN_TYPE_FILTER, + BF_CHAIN_TYPE_NAT, + } + style: primary + start: 0 + end: 71 + - source: bf_chain_type + style: secondary + start: 5 + end: 18 + - source: |- + { + BF_CHAIN_TYPE_FILTER, + BF_CHAIN_TYPE_NAT, + } + style: secondary + start: 19 + end: 71 + ? | + enum bf_hook + { + BF_HOOK_XDP, + BF_HOOK_TC_INGRESS, + }; + : labels: + - source: |- + enum bf_hook + { + BF_HOOK_XDP, + BF_HOOK_TC_INGRESS, + } + style: primary + start: 0 + end: 57 + - source: bf_hook + style: secondary + start: 5 + end: 12 + - source: |- + { + BF_HOOK_XDP, + BF_HOOK_TC_INGRESS, + } + style: secondary + start: 13 + end: 57 diff --git a/tools/ast-grep/rule-tests/enum-sentinel-format-test.yml b/tools/ast-grep/rule-tests/enum-sentinel-format-test.yml new file mode 100644 index 000000000..7a54748d3 --- /dev/null +++ b/tools/ast-grep/rule-tests/enum-sentinel-format-test.yml @@ -0,0 +1,47 @@ +id: enum-sentinel-format +valid: + - | + enum bf_hook + { + BF_HOOK_XDP, + BF_HOOK_TC_INGRESS, + _BF_HOOK_MAX, + }; + - | + enum bf_log + { + BF_LOG_DBG, + BF_LOG_INFO, + _BF_LOG_MAX, + }; + - | + enum bf_color + { + BF_COLOR_RESET = 0, + BF_COLOR_RED = 1 << 3, + }; + - | + enum bf_bpf_cmd + { + BF_BPF_PROG_LOAD = 5, + BF_BPF_MAP_CREATE = 0, + }; + - | + enum bf_nf_inet_hooks + { + BF_NF_INET_PRE_ROUTING = 0, + BF_NF_INET_LOCAL_IN = 1, + }; +invalid: + - | + enum bf_hook + { + BF_HOOK_XDP, + BF_HOOK_TC_INGRESS, + }; + - | + enum bf_chain_type + { + BF_CHAIN_TYPE_FILTER, + BF_CHAIN_TYPE_NAT, + }; diff --git a/tools/ast-grep/rules/enum-sentinel-format.yml b/tools/ast-grep/rules/enum-sentinel-format.yml new file mode 100644 index 000000000..4e9e91b93 --- /dev/null +++ b/tools/ast-grep/rules/enum-sentinel-format.yml @@ -0,0 +1,37 @@ +id: enum-sentinel-format +language: c +severity: error +message: >- + Enum missing _MAX sentinel value. + All enums should have a _BF_*_MAX or _BFC_*_MAX sentinel for bounds checking. +note: >- + Add a sentinel like _BF__MAX as the last enumerator. + Anonymous enums are allowed (the rule only fires on named enums). + Exceptions: bitmask enums (bf_color, bf_style), kernel ABI mirror enums + (bf_nf_inet_hooks, bf_bpf_cmd, bf_bpf_prog_type, bf_bpf_attach_type, + bf_bpf_map_type), and negative-indexed sentinel enums (bf_counter_type) + do not require sentinels. +rule: + kind: enum_specifier + all: + - has: + field: name + kind: type_identifier + - has: + kind: enumerator_list + - not: + has: + kind: enumerator_list + has: + kind: enumerator + has: + kind: identifier + regex: "^_B(F|FC)_.*_MAX$" + # Excluded enums: bitmask enums (bf_color, bf_style), kernel ABI mirrors + # that must match an external numbering (bf_nf_inet_hooks, bf_bpf_*), and + # negative-indexed sentinel enums (bf_counter_type). Add new entries only + # when an enum legitimately cannot have a _BF_*_MAX trailing sentinel. + - not: + has: + kind: type_identifier + regex: "^(bf_color|bf_style|bf_nf_inet_hooks|bf_bpf_cmd|bf_bpf_prog_type|bf_bpf_attach_type|bf_bpf_map_type|bf_counter_type)$" From d852cb1762399b8c9bf8e5f2768cce4b70bccdaa Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:41 +0000 Subject: [PATCH 09/17] tools: ast-grep: add fd-init-minus-one rule --- .../fd-init-minus-one-snapshot.yml | 22 +++++++++++++++++ .../rule-tests/fd-init-minus-one-test.yml | 24 +++++++++++++++++++ tools/ast-grep/rules/fd-init-minus-one.yml | 14 +++++++++++ 3 files changed, 60 insertions(+) create mode 100644 tools/ast-grep/rule-tests/__snapshots__/fd-init-minus-one-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/fd-init-minus-one-test.yml create mode 100644 tools/ast-grep/rules/fd-init-minus-one.yml diff --git a/tools/ast-grep/rule-tests/__snapshots__/fd-init-minus-one-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/fd-init-minus-one-snapshot.yml new file mode 100644 index 000000000..5bb6ec8a5 --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/fd-init-minus-one-snapshot.yml @@ -0,0 +1,22 @@ +id: fd-init-minus-one +snapshots: + ? | + void foo(void) + { + _cleanup_close_ int fd = 0; + } + : labels: + - source: _cleanup_close_ int fd = 0; + style: primary + start: 21 + end: 48 + ? | + void foo(void) + { + _cleanup_close_ int fd; + } + : labels: + - source: _cleanup_close_ int fd; + style: primary + start: 21 + end: 44 diff --git a/tools/ast-grep/rule-tests/fd-init-minus-one-test.yml b/tools/ast-grep/rule-tests/fd-init-minus-one-test.yml new file mode 100644 index 000000000..f203a18b8 --- /dev/null +++ b/tools/ast-grep/rule-tests/fd-init-minus-one-test.yml @@ -0,0 +1,24 @@ +id: fd-init-minus-one +valid: + - | + void foo(void) + { + _cleanup_close_ int fd = -1; + } + - | + void foo(void) + { + _cleanup_close_ int mnt_fd = -1; + _cleanup_close_ int bpffs_fd = -1; + } +invalid: + - | + void foo(void) + { + _cleanup_close_ int fd = 0; + } + - | + void foo(void) + { + _cleanup_close_ int fd; + } diff --git a/tools/ast-grep/rules/fd-init-minus-one.yml b/tools/ast-grep/rules/fd-init-minus-one.yml new file mode 100644 index 000000000..8bccb80b6 --- /dev/null +++ b/tools/ast-grep/rules/fd-init-minus-one.yml @@ -0,0 +1,14 @@ +id: fd-init-minus-one +language: c +severity: error +message: >- + Variable with _cleanup_close_ not initialized to -1. + File descriptors using cleanup attributes must be initialized to -1 + (the neutral/invalid value) to prevent closing fd 0 (stdin) on error paths. +note: >- + Change the declaration to: _cleanup_close_ int fd = -1; +rule: + kind: declaration + regex: "_cleanup_close_" + not: + regex: "=\\s*-\\s*1\\s*;" From a8761a4ddb49053426e2c4eab4bb330d12998070 Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:41 +0000 Subject: [PATCH 10/17] tools: ast-grep: add free-function-pattern rule --- .../free-function-pattern-snapshot.yml | 46 +++++++++++++++++++ .../rule-tests/free-function-pattern-test.yml | 18 ++++++++ .../ast-grep/rules/free-function-pattern.yml | 37 +++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 tools/ast-grep/rule-tests/__snapshots__/free-function-pattern-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/free-function-pattern-test.yml create mode 100644 tools/ast-grep/rules/free-function-pattern.yml diff --git a/tools/ast-grep/rule-tests/__snapshots__/free-function-pattern-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/free-function-pattern-snapshot.yml new file mode 100644 index 000000000..1f5d40d89 --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/free-function-pattern-snapshot.yml @@ -0,0 +1,46 @@ +id: free-function-pattern +snapshots: + ? | + int bf_chain_free(struct bf_chain **chain) + { + return 0; + } + : labels: + - source: |- + int bf_chain_free(struct bf_chain **chain) + { + return 0; + } + style: primary + start: 0 + end: 60 + - source: bf_chain_free + style: secondary + start: 4 + end: 17 + - source: bf_chain_free(struct bf_chain **chain) + style: secondary + start: 4 + end: 42 + ? | + void bf_chain_free(struct bf_chain *chain) + { + return; + } + : labels: + - source: |- + void bf_chain_free(struct bf_chain *chain) + { + return; + } + style: primary + start: 0 + end: 58 + - source: bf_chain_free + style: secondary + start: 5 + end: 18 + - source: bf_chain_free(struct bf_chain *chain) + style: secondary + start: 5 + end: 42 diff --git a/tools/ast-grep/rule-tests/free-function-pattern-test.yml b/tools/ast-grep/rule-tests/free-function-pattern-test.yml new file mode 100644 index 000000000..916d16f2c --- /dev/null +++ b/tools/ast-grep/rule-tests/free-function-pattern-test.yml @@ -0,0 +1,18 @@ +id: free-function-pattern +valid: + - | + void bf_chain_free(struct bf_chain **chain) + { + return; + } +invalid: + - | + int bf_chain_free(struct bf_chain **chain) + { + return 0; + } + - | + void bf_chain_free(struct bf_chain *chain) + { + return; + } diff --git a/tools/ast-grep/rules/free-function-pattern.yml b/tools/ast-grep/rules/free-function-pattern.yml new file mode 100644 index 000000000..7b45a5fbd --- /dev/null +++ b/tools/ast-grep/rules/free-function-pattern.yml @@ -0,0 +1,37 @@ +id: free-function-pattern +language: c +severity: error +message: >- + Free function _?bf_*_free() / _?bfc_*_free() violates conventions. + Free functions must return void and take a double-pointer parameter (TYPE **). +note: >- + Ensure the function returns void, takes TYPE ** as parameter, + is a no-op if *ptr is NULL, and sets *ptr to NULL after freeing. + Limitation: this rule only checks the function signature. + The "no-op if *ptr is NULL" and "set *ptr to NULL" requirements must be + verified by review or by runtime tests. +rule: + all: + - kind: function_definition + - has: + kind: function_declarator + has: + kind: identifier + regex: "^_?(bf|bfc)_\\w+_free$" + - any: + - not: + has: + kind: primitive_type + regex: "^void$" + - not: + has: + kind: function_declarator + has: + kind: parameter_list + has: + kind: parameter_declaration + has: + kind: pointer_declarator + has: + kind: pointer_declarator + stopBy: end From 1f4eb422678704358a7ce9b862901c109fa7d5ec Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:48 +0000 Subject: [PATCH 11/17] tools: ast-grep: add negative-errno-returns rule --- .../negative-errno-returns-snapshot.yml | 58 +++++++++++++++++++ .../negative-errno-returns-test.yml | 50 ++++++++++++++++ .../ast-grep/rules/negative-errno-returns.yml | 12 ++++ 3 files changed, 120 insertions(+) create mode 100644 tools/ast-grep/rule-tests/__snapshots__/negative-errno-returns-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/negative-errno-returns-test.yml create mode 100644 tools/ast-grep/rules/negative-errno-returns.yml diff --git a/tools/ast-grep/rule-tests/__snapshots__/negative-errno-returns-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/negative-errno-returns-snapshot.yml new file mode 100644 index 000000000..2c3236126 --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/negative-errno-returns-snapshot.yml @@ -0,0 +1,58 @@ +id: negative-errno-returns +snapshots: + ? | + int bf_arg_parse(const char *s) + { + return E2BIG; + } + : labels: + - source: return E2BIG; + style: primary + start: 38 + end: 51 + - source: E2BIG + style: secondary + start: 45 + end: 50 + ? | + int bf_chain_new(struct bf_chain **chain) + { + return ENOMEM; + } + : labels: + - source: return ENOMEM; + style: primary + start: 48 + end: 62 + - source: ENOMEM + style: secondary + start: 55 + end: 61 + ? | + int bf_io_read(int fd) + { + return EIO; + } + : labels: + - source: return EIO; + style: primary + start: 29 + end: 40 + - source: EIO + style: secondary + start: 36 + end: 39 + ? | + int bf_rule_load(struct bf_rule *rule) + { + return EINVAL; + } + : labels: + - source: return EINVAL; + style: primary + start: 45 + end: 59 + - source: EINVAL + style: secondary + start: 52 + end: 58 diff --git a/tools/ast-grep/rule-tests/negative-errno-returns-test.yml b/tools/ast-grep/rule-tests/negative-errno-returns-test.yml new file mode 100644 index 000000000..46c3a6441 --- /dev/null +++ b/tools/ast-grep/rule-tests/negative-errno-returns-test.yml @@ -0,0 +1,50 @@ +id: negative-errno-returns +valid: + - | + int bf_chain_new(struct bf_chain **chain) + { + return -ENOMEM; + } + - | + int bf_rule_load(struct bf_rule *rule) + { + return bf_err_r(-EINVAL, "invalid rule"); + } + - | + int bf_ctx_setup(struct bf_ctx *ctx) + { + return 0; + } + - | + int bf_set_add(struct bf_set *set, const void *elem) + { + return r; + } + - | + int bf_hook_parse(struct bf_hook *hook) + { + if (!hook) + return -EINVAL; + return -ENOMEM; + } +invalid: + - | + int bf_chain_new(struct bf_chain **chain) + { + return ENOMEM; + } + - | + int bf_rule_load(struct bf_rule *rule) + { + return EINVAL; + } + - | + int bf_io_read(int fd) + { + return EIO; + } + - | + int bf_arg_parse(const char *s) + { + return E2BIG; + } diff --git a/tools/ast-grep/rules/negative-errno-returns.yml b/tools/ast-grep/rules/negative-errno-returns.yml new file mode 100644 index 000000000..1d7295e95 --- /dev/null +++ b/tools/ast-grep/rules/negative-errno-returns.yml @@ -0,0 +1,12 @@ +id: negative-errno-returns +language: c +severity: error +message: Error returns must be negative (e.g. -ENOMEM, not ENOMEM) +note: >- + bpfilter uses negative errno convention: return -ENOMEM, not ENOMEM. + See error-handling standard. +rule: + kind: return_statement + has: + kind: identifier + regex: "^E[A-Z0-9]{2,}$" From 32687cd60608fad61abdd744d21dad1c8ec83b84 Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:48 +0000 Subject: [PATCH 12/17] tools: ast-grep: add no-direct-free rule --- .../ast-grep/known_issues/no-direct-free.yml | 15 ++++++++ .../__snapshots__/no-direct-free-snapshot.yml | 16 +++++++++ .../rule-tests/no-direct-free-test.yml | 36 +++++++++++++++++++ tools/ast-grep/rules/no-direct-free.yml | 26 ++++++++++++++ 4 files changed, 93 insertions(+) create mode 100644 tools/ast-grep/known_issues/no-direct-free.yml create mode 100644 tools/ast-grep/rule-tests/__snapshots__/no-direct-free-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/no-direct-free-test.yml create mode 100644 tools/ast-grep/rules/no-direct-free.yml diff --git a/tools/ast-grep/known_issues/no-direct-free.yml b/tools/ast-grep/known_issues/no-direct-free.yml new file mode 100644 index 000000000..6a5045e89 --- /dev/null +++ b/tools/ast-grep/known_issues/no-direct-free.yml @@ -0,0 +1,15 @@ +# Known issues for ast-grep rule `no-direct-free`. +# +# Suppressed during check.astgrep so only NEW violations fail CI. Regenerate +# this file (and all siblings) by running: make -C build astgrep-known-issues +# +# Schema: list of +# file: path relative to repo root +# textSha: first 12 hex chars of sha256(match.text) +# occurrence: 0-based index among identical (file, textSha) matches +# +# Do not hand-edit — the directory is regenerated deterministically. + +- file: src/libbpfilter/cli.c + textSha: eceeb1550f93 + occurrence: 0 diff --git a/tools/ast-grep/rule-tests/__snapshots__/no-direct-free-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/no-direct-free-snapshot.yml new file mode 100644 index 000000000..b1644a8a3 --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/no-direct-free-snapshot.yml @@ -0,0 +1,16 @@ +id: no-direct-free +snapshots: + ? | + void process(char *buf) + { + free(buf); + } + : labels: + - source: free(buf) + style: primary + start: 30 + end: 39 + - source: free + style: secondary + start: 30 + end: 34 diff --git a/tools/ast-grep/rule-tests/no-direct-free-test.yml b/tools/ast-grep/rule-tests/no-direct-free-test.yml new file mode 100644 index 000000000..87d40e9e5 --- /dev/null +++ b/tools/ast-grep/rule-tests/no-direct-free-test.yml @@ -0,0 +1,36 @@ +id: no-direct-free +valid: + - | + void bf_chain_free(struct bf_chain **chain) + { + freep((void *)chain); + } + - | + static inline void freep(void *ptr) + { + free(*(void **)ptr); + *(void **)ptr = NULL; + } + - | + void bf_list_free(bf_list **list) + { + free(*list); + *list = NULL; + } + - | + static void _bf_swich_option_free(struct bf_swich_option **option) + { + free(*option); + *option = NULL; + } + - | + void bf_swich_cleanup(struct bf_swich *swich) + { + free(swich->default_opt); + } +invalid: + - | + void process(char *buf) + { + free(buf); + } diff --git a/tools/ast-grep/rules/no-direct-free.yml b/tools/ast-grep/rules/no-direct-free.yml new file mode 100644 index 000000000..0dc3a4e7b --- /dev/null +++ b/tools/ast-grep/rules/no-direct-free.yml @@ -0,0 +1,26 @@ +id: no-direct-free +language: c +severity: warning +message: >- + Direct call to free() detected. Use freep() or a typed bf_*_free() cleanup + function instead to prevent double-free via cleanup attributes. +note: >- + Replace free(ptr) with the appropriate cleanup function. + Use freep((void *)&ptr) for generic allocations, + or bf_X_free(&obj) for typed objects. + Exempt callers: freep, _?bf_*_free, _?bfc_*_free, _?bf_*_cleanup, + _?bfc_*_cleanup. +rule: + kind: call_expression + has: + kind: identifier + regex: "^free$" + not: + inside: + kind: function_definition + stopBy: end + has: + kind: function_declarator + has: + kind: identifier + regex: "^(freep|_?bf_\\w+_(free|cleanup)|_?bfc_\\w+_(free|cleanup))$" From a81bca0e9dd49c3e212ece9322293d4835a99ee7 Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:49 +0000 Subject: [PATCH 13/17] tools: ast-grep: add no-fprintf-stderr rule --- .../no-fprintf-stderr-snapshot.yml | 12 ++++++++++ .../rule-tests/no-fprintf-stderr-test.yml | 23 +++++++++++++++++++ tools/ast-grep/rules/no-fprintf-stderr.yml | 20 ++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 tools/ast-grep/rule-tests/__snapshots__/no-fprintf-stderr-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/no-fprintf-stderr-test.yml create mode 100644 tools/ast-grep/rules/no-fprintf-stderr.yml diff --git a/tools/ast-grep/rule-tests/__snapshots__/no-fprintf-stderr-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/no-fprintf-stderr-snapshot.yml new file mode 100644 index 000000000..708a992de --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/no-fprintf-stderr-snapshot.yml @@ -0,0 +1,12 @@ +id: no-fprintf-stderr +snapshots: + ? | + void bf_handle(void) + { + fprintf(stderr, "error: %s", msg); + } + : labels: + - source: 'fprintf(stderr, "error: %s", msg)' + style: primary + start: 27 + end: 60 diff --git a/tools/ast-grep/rule-tests/no-fprintf-stderr-test.yml b/tools/ast-grep/rule-tests/no-fprintf-stderr-test.yml new file mode 100644 index 000000000..936e75372 --- /dev/null +++ b/tools/ast-grep/rule-tests/no-fprintf-stderr-test.yml @@ -0,0 +1,23 @@ +id: no-fprintf-stderr +valid: + - | + void bf_handle(void) + { + bf_err("something went wrong"); + } + - | + void bf_process(const char *s) + { + bf_warn("unexpected value: %s", s); + } + - | + static void _bf_print_insn(void *private_data, const char *fmt, ...) + { + fprintf(stderr, fmt); + } +invalid: + - | + void bf_handle(void) + { + fprintf(stderr, "error: %s", msg); + } diff --git a/tools/ast-grep/rules/no-fprintf-stderr.yml b/tools/ast-grep/rules/no-fprintf-stderr.yml new file mode 100644 index 000000000..159bc226d --- /dev/null +++ b/tools/ast-grep/rules/no-fprintf-stderr.yml @@ -0,0 +1,20 @@ +id: no-fprintf-stderr +language: c +severity: error +message: Use logging macros (bf_err, bf_warn, etc.) instead of fprintf(stderr, ...) or vfprintf(stderr, ...) +note: >- + bpfilter has structured logging via bf_err(), bf_warn(), bf_info(), bf_dbg(). + Direct fprintf(stderr) / vfprintf(stderr) bypasses log levels and formatting. +rule: + any: + - pattern: fprintf(stderr, $$$) + - pattern: vfprintf(stderr, $$$) + not: + inside: + kind: function_definition + has: + kind: function_declarator + has: + kind: identifier + regex: "^(_bf_print_|bf_logger_).*" + stopBy: end From 680a7821f2b7d980cf3dd317950234bcac6a41ca Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:49 +0000 Subject: [PATCH 14/17] tools: ast-grep: add no-ifndef-guards rule --- .../no-ifndef-guards-snapshot.yml | 24 +++++++++++++++++++ .../rule-tests/no-ifndef-guards-test.yml | 21 ++++++++++++++++ tools/ast-grep/rules/no-ifndef-guards.yml | 17 +++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 tools/ast-grep/rule-tests/__snapshots__/no-ifndef-guards-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/no-ifndef-guards-test.yml create mode 100644 tools/ast-grep/rules/no-ifndef-guards.yml diff --git a/tools/ast-grep/rule-tests/__snapshots__/no-ifndef-guards-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/no-ifndef-guards-snapshot.yml new file mode 100644 index 000000000..afd590428 --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/no-ifndef-guards-snapshot.yml @@ -0,0 +1,24 @@ +id: no-ifndef-guards +snapshots: + ? | + #ifndef BF_CHAIN_H + #define BF_CHAIN_H + struct bf_chain; + #endif + : labels: + - source: |- + #ifndef BF_CHAIN_H + #define BF_CHAIN_H + struct bf_chain; + #endif + style: primary + start: 0 + end: 61 + - source: | + #ifndef BF_CHAIN_H + #define BF_CHAIN_H + struct bf_chain; + #endif + style: secondary + start: 0 + end: 62 diff --git a/tools/ast-grep/rule-tests/no-ifndef-guards-test.yml b/tools/ast-grep/rule-tests/no-ifndef-guards-test.yml new file mode 100644 index 000000000..aaf43401b --- /dev/null +++ b/tools/ast-grep/rule-tests/no-ifndef-guards-test.yml @@ -0,0 +1,21 @@ +id: no-ifndef-guards +valid: + - | + #pragma once + struct bf_chain; + int bf_chain_new(struct bf_chain **chain); + - | + #pragma once + #ifdef __cplusplus + extern "C" { + #endif + int bf_init(void); + #ifdef __cplusplus + } + #endif +invalid: + - | + #ifndef BF_CHAIN_H + #define BF_CHAIN_H + struct bf_chain; + #endif diff --git a/tools/ast-grep/rules/no-ifndef-guards.yml b/tools/ast-grep/rules/no-ifndef-guards.yml new file mode 100644 index 000000000..b0208ad9c --- /dev/null +++ b/tools/ast-grep/rules/no-ifndef-guards.yml @@ -0,0 +1,17 @@ +id: no-ifndef-guards +language: c +severity: error +files: + - "**/*.h" +message: Use #pragma once instead of #ifndef/#define include guards +note: >- + bpfilter uses #pragma once exclusively for header guards. +rule: + kind: preproc_ifdef + regex: "^#ifndef\\b" + not: + has: + kind: identifier + regex: "^__" + inside: + kind: translation_unit From 781ccf98f28881cf5b97a16523b52d5d05855a0a Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:54 +0000 Subject: [PATCH 15/17] tools: ast-grep: add single-line-comment-style rule --- .../single-line-comment-style.yml | 30 +++++++++++++++++++ .../single-line-comment-style-snapshot.yml | 10 +++++++ .../single-line-comment-style-test.yml | 23 ++++++++++++++ .../rules/single-line-comment-style.yml | 17 +++++++++++ 4 files changed, 80 insertions(+) create mode 100644 tools/ast-grep/known_issues/single-line-comment-style.yml create mode 100644 tools/ast-grep/rule-tests/__snapshots__/single-line-comment-style-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/single-line-comment-style-test.yml create mode 100644 tools/ast-grep/rules/single-line-comment-style.yml diff --git a/tools/ast-grep/known_issues/single-line-comment-style.yml b/tools/ast-grep/known_issues/single-line-comment-style.yml new file mode 100644 index 000000000..97e071f61 --- /dev/null +++ b/tools/ast-grep/known_issues/single-line-comment-style.yml @@ -0,0 +1,30 @@ +# Known issues for ast-grep rule `single-line-comment-style`. +# +# Suppressed during check.astgrep so only NEW violations fail CI. Regenerate +# this file (and all siblings) by running: make -C build astgrep-known-issues +# +# Schema: list of +# file: path relative to repo root +# textSha: first 12 hex chars of sha256(match.text) +# occurrence: 0-based index among identical (file, textSha) matches +# +# Do not hand-edit — the directory is regenerated deterministically. + +- file: src/libbpfilter/bpf/parse_ipv6_nh.bpf.c + textSha: b33f93d6db3d + occurrence: 0 +- file: src/libbpfilter/cgen/packet.c + textSha: ff9eca23825a + occurrence: 0 +- file: src/libbpfilter/cgen/program.c + textSha: d19ae8633583 + occurrence: 0 +- file: src/libbpfilter/cgen/program.h + textSha: 2a86cf74b14a + occurrence: 0 +- file: src/libbpfilter/helper.c + textSha: 0096a7b428af + occurrence: 0 +- file: src/libbpfilter/matcher.c + textSha: bd6edaf8304b + occurrence: 0 diff --git a/tools/ast-grep/rule-tests/__snapshots__/single-line-comment-style-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/single-line-comment-style-snapshot.yml new file mode 100644 index 000000000..3126567bd --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/single-line-comment-style-snapshot.yml @@ -0,0 +1,10 @@ +id: single-line-comment-style +snapshots: + ? | + /* This should be a // comment */ + int x = 0; + : labels: + - source: /* This should be a // comment */ + style: primary + start: 0 + end: 33 diff --git a/tools/ast-grep/rule-tests/single-line-comment-style-test.yml b/tools/ast-grep/rule-tests/single-line-comment-style-test.yml new file mode 100644 index 000000000..854cba990 --- /dev/null +++ b/tools/ast-grep/rule-tests/single-line-comment-style-test.yml @@ -0,0 +1,23 @@ +id: single-line-comment-style +valid: + - | + // This is a correct single-line comment + int x = 0; + - | + /* SPDX-License-Identifier: GPL-2.0-only */ + int x = 0; + - | + /* + * This is a correct multi-line comment + * spanning multiple lines. + */ + int x = 0; + - | + /** + * @brief Doxygen comment is fine. + */ + int bf_foo(void); +invalid: + - | + /* This should be a // comment */ + int x = 0; diff --git a/tools/ast-grep/rules/single-line-comment-style.yml b/tools/ast-grep/rules/single-line-comment-style.yml new file mode 100644 index 000000000..f90366b50 --- /dev/null +++ b/tools/ast-grep/rules/single-line-comment-style.yml @@ -0,0 +1,17 @@ +id: single-line-comment-style +language: c +severity: warning +message: >- + Single-line comments should use // style, not /* */ style. +note: >- + Replace /* comment */ with // comment. Reserve /* */ for multi-line comments. +rule: + all: + - kind: comment + - regex: "^/\\*[^*!]" + - not: + regex: "\\n" + - not: + regex: "SPDX" + - not: + regex: "NOLINT" From 4f83382382a14f8adbbf4f4057b826dfddd499c4 Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:55 +0000 Subject: [PATCH 16/17] tools: ast-grep: add spdx-license-header rule --- .../spdx-license-header-snapshot.yml | 12 ++++++++++++ .../rule-tests/spdx-license-header-test.yml | 14 ++++++++++++++ tools/ast-grep/rules/spdx-license-header.yml | 18 ++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 tools/ast-grep/rule-tests/__snapshots__/spdx-license-header-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/spdx-license-header-test.yml create mode 100644 tools/ast-grep/rules/spdx-license-header.yml diff --git a/tools/ast-grep/rule-tests/__snapshots__/spdx-license-header-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/spdx-license-header-snapshot.yml new file mode 100644 index 000000000..a86e6d7ef --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/spdx-license-header-snapshot.yml @@ -0,0 +1,12 @@ +id: spdx-license-header +snapshots: + ? | + #include + int main(void) { return 0; } + : labels: + - source: | + #include + int main(void) { return 0; } + style: primary + start: 0 + end: 48 diff --git a/tools/ast-grep/rule-tests/spdx-license-header-test.yml b/tools/ast-grep/rule-tests/spdx-license-header-test.yml new file mode 100644 index 000000000..d9f0a1c51 --- /dev/null +++ b/tools/ast-grep/rule-tests/spdx-license-header-test.yml @@ -0,0 +1,14 @@ +id: spdx-license-header +valid: + - | + /* SPDX-License-Identifier: GPL-2.0-only */ + #include + int main(void) { return 0; } + - | + // SPDX-License-Identifier: GPL-2.0-only + #include + void foo(void) {} +invalid: + - | + #include + int main(void) { return 0; } diff --git a/tools/ast-grep/rules/spdx-license-header.yml b/tools/ast-grep/rules/spdx-license-header.yml new file mode 100644 index 000000000..203df7fef --- /dev/null +++ b/tools/ast-grep/rules/spdx-license-header.yml @@ -0,0 +1,18 @@ +id: spdx-license-header +language: c +severity: error +message: File must contain an SPDX license header +note: >- + Every source file must contain an SPDX license identifier comment, + conventionally at the top of the file. Both styles are accepted: + /* SPDX-License-Identifier: GPL-2.0-only */ and + // SPDX-License-Identifier: GPL-2.0-only. + Limitation: this rule matches any SPDX comment anywhere in the file, + it does not verify that the comment is the first line. +rule: + kind: translation_unit + not: + has: + kind: comment + regex: "SPDX-License-Identifier" + stopBy: end From e3f4fe029bee846b90fd05a14ea25b20d936e2e2 Mon Sep 17 00:00:00 2001 From: Pawel Zmarzly Date: Tue, 21 Apr 2026 13:40:55 +0000 Subject: [PATCH 17/17] tools: ast-grep: add struct-bf-prefix rule --- .../struct-bf-prefix-snapshot.yml | 52 +++++++++++++++++++ .../rule-tests/struct-bf-prefix-test.yml | 23 ++++++++ tools/ast-grep/rules/struct-bf-prefix.yml | 21 ++++++++ 3 files changed, 96 insertions(+) create mode 100644 tools/ast-grep/rule-tests/__snapshots__/struct-bf-prefix-snapshot.yml create mode 100644 tools/ast-grep/rule-tests/struct-bf-prefix-test.yml create mode 100644 tools/ast-grep/rules/struct-bf-prefix.yml diff --git a/tools/ast-grep/rule-tests/__snapshots__/struct-bf-prefix-snapshot.yml b/tools/ast-grep/rule-tests/__snapshots__/struct-bf-prefix-snapshot.yml new file mode 100644 index 000000000..8e3ce76af --- /dev/null +++ b/tools/ast-grep/rule-tests/__snapshots__/struct-bf-prefix-snapshot.yml @@ -0,0 +1,52 @@ +id: struct-bf-prefix +snapshots: + ? | + struct chain + { + int count; + }; + : labels: + - source: |- + struct chain + { + int count; + } + style: primary + start: 0 + end: 31 + - source: |- + { + int count; + } + style: secondary + start: 13 + end: 31 + - source: chain + style: secondary + start: 7 + end: 12 + ? | + struct filter_rule + { + int id; + }; + : labels: + - source: |- + struct filter_rule + { + int id; + } + style: primary + start: 0 + end: 34 + - source: |- + { + int id; + } + style: secondary + start: 19 + end: 34 + - source: filter_rule + style: secondary + start: 7 + end: 18 diff --git a/tools/ast-grep/rule-tests/struct-bf-prefix-test.yml b/tools/ast-grep/rule-tests/struct-bf-prefix-test.yml new file mode 100644 index 000000000..413c057ac --- /dev/null +++ b/tools/ast-grep/rule-tests/struct-bf-prefix-test.yml @@ -0,0 +1,23 @@ +id: struct-bf-prefix +valid: + - | + struct bf_chain + { + int count; + }; + - | + struct bfc_opts + { + int argc; + }; +invalid: + - | + struct chain + { + int count; + }; + - | + struct filter_rule + { + int id; + }; diff --git a/tools/ast-grep/rules/struct-bf-prefix.yml b/tools/ast-grep/rules/struct-bf-prefix.yml new file mode 100644 index 000000000..8afd81930 --- /dev/null +++ b/tools/ast-grep/rules/struct-bf-prefix.yml @@ -0,0 +1,21 @@ +id: struct-bf-prefix +language: c +severity: error +message: >- + Struct tag does not follow naming convention. + Library structs must use bf_ prefix, CLI structs use bfc_ prefix. +note: >- + Rename the struct to bf_ (libbpfilter) or bfc_ (bfcli). + Only struct definitions (with a body) are checked -- references to + external types like `struct stat` or `struct nlmsghdr` are ignored. +rule: + all: + - kind: struct_specifier + - has: + field: body + kind: field_declaration_list + - has: + field: name + kind: type_identifier + not: + regex: "^(bf_|bfc_)"