tools: ast-grep: add static analysis rules#502
tools: ast-grep: add static analysis rules#502pzmarzly wants to merge 17 commits intofacebook:mainfrom
Conversation
Claude review of PR #502 (e3f4fe0)Suggestions
Nits
|
755c27b to
d4f68d2
Compare
|
I have not really looked over the code at all, but one thought - would it be easier (for development, review, and not having an overbearing amount of warnings) to start with a single rule that we can begin warning on? After we've seen how that goes, we can begin adding more at a quicker pace. |
d4f68d2 to
02608c0
Compare
| run: mount bpffs /sys/fs/bpf -t bpf | ||
| - name: Install ast-grep | ||
| run: | | ||
| dnf install -y pipx |
There was a problem hiding this comment.
Claude: suggestion: pipx install --global ast-grep-cli is unpinned. A new upstream release could change rule semantics or introduce incompatibilities, breaking CI unexpectedly. Consider pinning to a specific version (e.g., pipx install --global ast-grep-cli==0.37.0) and bumping deliberately when needed.
There was a problem hiding this comment.
I think it's better to fix it when it happens, than to let the pinned version rot.
| raw = raw.strip() | ||
| if not raw: | ||
| continue | ||
| d = json.loads(raw) |
There was a problem hiding this comment.
Claude: suggestion: json.loads(raw) will raise JSONDecodeError on malformed input (e.g., a truncated stream from SIGPIPE or an ast-grep version mismatch mixing diagnostics into the JSON stream). The resulting Python traceback would not indicate which line failed or what the raw content was. Consider wrapping in a try/except json.JSONDecodeError that prints a descriptive message to stderr before re-raising, so CI failures from corrupted streams are easier to triage.
| kind: function_declarator | ||
| has: | ||
| kind: identifier | ||
| regex: "^(_bf_print_insn|bf_logger_).*" |
There was a problem hiding this comment.
Claude: nit: The exclusion regex ^(_bf_print_insn|bf_logger_).* enumerates a single specific function name rather than a pattern. If another function legitimately needs fprintf(stderr, ...) in the future (e.g., a debug dumper), the rule requires a source edit. Consider generalizing to ^(_bf_print_|bf_logger_).* or adding a comment explaining the maintenance expectation.
| 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." |
There was a problem hiding this comment.
Claude: nit: The note field uses a quoted string (note: "...") while most other rules in this PR use >- folded scalar style. The same inconsistency appears in no-fprintf-stderr.yml (line 5) and no-ifndef-guards.yml (line 7). Consider using >- for consistency:
note: >-
bpfilter uses negative errno convention: return -ENOMEM, not ENOMEM.
See error-handling standard.| - 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)$" |
There was a problem hiding this comment.
Claude: nit: The exclusion regex lists 8 specific enum names. Any new kernel-mirror or bitmask enum added to the codebase requires a manual rule edit to this regex. Consider adding a YAML comment above the regex documenting the criteria for inclusion ("bitmask enums and kernel ABI mirrors that intentionally omit sentinels"), so the maintenance contract is clear.
49b4b76 to
e3f4fe0
Compare
|
@claude review |
| raw = raw.strip() | ||
| if not raw: | ||
| continue | ||
| d = json.loads(raw) |
There was a problem hiding this comment.
Claude: suggestion: json.loads(raw) has no error handling. If ast-grep ever writes a non-JSON diagnostic line to stdout (e.g., on a version upgrade or internal warning), the script crashes with an opaque JSONDecodeError traceback. Consider wrapping in try/except:
try:
d = json.loads(raw)
except json.JSONDecodeError:
sys.stderr.write(f"warning: skipping non-JSON line: {raw!r}\n")
continue| - name: Install ast-grep | ||
| run: | | ||
| dnf install -y pipx python3-pyyaml | ||
| pipx install --global ast-grep-cli |
There was a problem hiding this comment.
Claude: suggestion: pipx install --global ast-grep-cli installs an unpinned version. A breaking upstream change to the JSON stream schema would silently break CI. Consider pinning to a known-good version (e.g., pipx install --global ast-grep-cli==X.Y.Z) and documenting the minimum supported version.
| run: mount bpffs /sys/fs/bpf -t bpf | ||
| - name: Install ast-grep | ||
| run: | | ||
| dnf install -y pipx python3-pyyaml |
There was a problem hiding this comment.
Claude: suggestion: Consider baking pipx, python3-pyyaml, and ast-grep-cli into the Fedora Dockerfile rather than installing at CI runtime. Other build dependencies (clang-format, cmocka, etc.) are already pre-installed in the container image. Installing at runtime adds network latency and a failure point on every run.
| 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. |
There was a problem hiding this comment.
Claude: nit: The note says "See error-handling standard" but no document with that exact title exists. The relevant reference is the "Error codes" section in doc/developers/style.rst. Consider: "See doc/developers/style.rst, Error codes section."
| void bf_handle(void) | ||
| { | ||
| fprintf(stderr, "error: %s", msg); | ||
| } |
There was a problem hiding this comment.
Claude: nit: The test only exercises fprintf(stderr, ...) in the invalid cases but the rule also matches vfprintf(stderr, $$$). Similarly, valid cases test the _bf_print_* exclusion but not the bf_logger_* exclusion. Adding test cases for both branches would improve coverage of the rule.
| See `../ignore_known_issues.py` for the filter implementation. | ||
|
|
||
| ## Severity interaction | ||
|
|
There was a problem hiding this comment.
Claude: nit: The README states that ast-grep scan exits non-zero on severity: error matches, causing pipeline failure before the filter runs. However, ast-grep scan --json=stream typically exits 0 regardless of findings (JSON output modes are designed for piping to tools). If this is incorrect, the documented guarantee about error-severity rules being zero-tolerance may not hold. Please verify the actual exit behavior and update accordingly.
If https://ast-grep.github.io/ is installed (e.g. via
pip install ast-grep-cli), we can use it to enforce many of our coding standards. See #510.In this PR, rules with existing violations were introduced with severity
warning, and have aknown_issuesfile that silence known occurrances (otherwise, we'd get a ton of spam on every CI run). As we refactor our codebase, we should change all of them toerrors, and remove theignore_known_issuesmechanism.Test
ast-grep scan --config tools/ast-grep/sgconfig.yml src/bfcli/ src/libbpfilter/Output: https://gist.github.com/pzmarzly/2e7d84b21a3202d1e87710f39c2d3857
List of rules
assert()them at entry.bf_/bfc_prefix (or_bf_/_bfc_when static).bf_*_new()must take aTYPE **output parameter for ownership transfer.`name`over@p name,@ref name,@c namein Doxygen comments./** @brief ... */comment.bf_/bfc_prefix._BF_*_MAX/_BFC_*_MAXsentinel (with listed exceptions for bitmasks and kernel ABI mirrors)._cleanup_close_must be initialized to-1to avoid closing fd 0 on error paths.bf_*_free()/_bf_*_free()must returnvoidand accept a double-pointer parameter.-ENOMEM, notENOMEM).free(); usefreep()or a typedbf_*_free()cleanup function instead (with exempt callers).bf_err,bf_warn, …) instead offprintf(stderr, …)/vfprintf(stderr, …).#pragma once, not#ifndef/#defineinclude guards.//, reserving/* */for multi-line (SPDX excluded).SPDX-License-Identifiercomment.bf_/bfc_prefix; external type references are ignored.