Skip to content

Releases: linux-analysis/cvehound

CVEhound 1.8.0

Choose a tag to compare

@evdenis evdenis released this 18 Oct 11:05

A whole-tree scan that drops a file is an error, no longer a clean verdict. Handed a directory, spatch skips any file it raised on, prints EXN: <exception> in <file> and still exits 0, which read as "not vulnerable" for a file never scanned; only timeouts were caught. SpatchTruncated ("error": "truncated" in reports) names the files and the exceptions. A torn AST-cache entry, the cause seen in practice, is evicted and the scan rerun once without the cache.

A scan of named files that dies on a torn AST-cache entry is rerun once without the cache, after evicting the entries of every file it was given; stock spatch exits non-zero there without naming the file, which used to fail the CVE on every run until --cache-clear.

sympy is no longer a dependency, which saves 47 MB installed and an import in every pool worker. --kernel-config used it only to evaluate and print Kbuild conditions; a small parser for that grammar (CONFIG symbols, &, |, ~, parentheses) now does both. No verdict changes; a few logic strings in reports come out shorter.

The project moved to github.com/linux-analysis/cvehound, and cvehound-spatch with it.

Full Changelog: 1.7.0...1.8.0

CVEhound 1.7.0

Choose a tag to compare

@evdenis evdenis released this 08 Sep 21:10

Native git support, as subcommands: cvehound scan --rev REV... scans tags, commits or branches without a checkout (the files each rule reads come straight from the object database into a temp directory; several revisions report side by side), cvehound diff A..B and cvehound diff --patch FILE classify what a change does to each touched rule's verdict (fixed / introduced / still-vulnerable, --per-commit names the commit, --fail-on introduced exits 3 for CI), and cvehound bisect --cve X A..B finds the commit at which a verdict flipped, walking only the commits that touch the rule's files. Bare cvehound --kernel DIR is unchanged and still means scan.

A scan of a git tree now ends with what history says about each finding's fix: present (as the upstream commit or a stable backport citing it — the rule still firing then points at a bad backport or a regression), reverted, absent, or unknown. Evidence annotates and never filters; the opt-in --prune-unintroduced skips rules whose introducing commit is provably not in the scanned history. Reports of git checkouts also record kernel.commit, kernel.describe and kernel.dirty.

cvehound diff also reads the range's commit messages against the CVE metadata, so a release's known fixes, introducing commits, backports and Fixes:-trailer candidates are listed even for CVEs no rule exists for.

The git modes use plain git over persistent cat-file --batch pipes; no new dependency. For cvehound.oracle consumers: BlobMaterializer and object_header now take an ObjectReader — GitPython's Repo.git rather than the Repo (a one-token change at the call site), or cvehound.gitrepo.GitRepo.

The sandbox grants the repository's git dir (and any alternates) read-only for the post-scan evidence pass; nothing else changed in what a scan may touch.

A finding now names its files the way the scanned tree does: results[CVE].files[].file and the --- header of spatch_output are relative to the kernel directory in every mode, matching the config block of the same record. They used to be absolute, which under --rev and diff meant a path into a temporary directory that is gone by the time the report is read.

Nineteen rules collapsed into the invariant they were repeating once per call site, protocol variant or struct field; twenty-one more stopped requiring a parameter type or struct field name that appears nowhere in their fix commit (rules derivable from their fix diff alone: 50.6% → 53.7%); three dropped a disjunction branch that cannot match any file the rule reads; and the exists keyword left 360 rule headers, where a starred line had already put every one of them in exists mode. Each change was verified report-identical at the fix, at the commit before it and at the introducing commit.

CVE-2022-0812 now detects the reply-header site too. Its second rule starred nothing and nothing depended on it, so it could neither report nor add its function name to the grep query, and that half of the CVE went unchecked.

Dropped the CVE-2022-33981 rule. Half of it depended on a virtual cvehound never defines, which spatch drops before translating, and the half that did run covers only the pre-fix spelling, while a tree carrying the fix with CONFIG_BLK_DEV_FD_RAWCMD enabled is still affected. A clean scan implied a check that was not happening; the remaining case needs the kernel-config machinery rather than a semantic patch.

Rules no longer declare virtual detect and cvehound no longer passes -D detect: the flag and the declaration existed only to license each other. The content format is bumped to 2 for it, so 1.4.0-1.6.1, which still pass the flag, decline the rolling content set with a message to upgrade instead of installing rules that would fail on all of them.

Fixed --kernel-config aborting with a recursion error on riscv for kernels from v7.2: arch/riscv/kernel/vdso_cfi/Makefile includes a Makefile from a sibling directory named through src :=, which the include resolver substituted with the including directory, so the include resolved back to that Makefile itself until the recursion limit. Includes now carry a cycle guard and a Makefile-local src wins over the default; the config map is unchanged on every other arch.

The rule-authoring tools cover the whole corpus now: validate-rule.sh handles a fix that deletes the file, Files: paths that exist at only one end of the commit range, and the .grep rules, which had no detection check at all; the new compare-rule.sh gates a rule rewrite on the grep query, the files that survive it and the starred-line report all being no worse than before.

Full Changelog: 1.6.1...1.7.0

CVEhound 1.6.1

Choose a tag to compare

@evdenis evdenis released this 01 Sep 10:12

Fixed `.grep` rules silently reporting a clean kernel on macOS: the rules are PCRE, but check_cve() called plain `grep`, and BSD grep's missing `-P` support (exit 2) was read the same as "no match" (exit 1) — five CVEs cleared without a byte examined. cvehound now probes for `-P` support before running .grep rules, trying `grep` then `ggrep`.

`cvehound[spatch]` now resolves on Apple Silicon: cvehound-spatch publishes a macosx_11_0_arm64 wheel as of 1.3.2.post3, so the extra's marker no longer excludes darwin/arm64 (Intel Macs still have no wheel and stay excluded).

Full Changelog: 1.6.0...1.6.1

CVEhound 1.6.0

Choose a tag to compare

@evdenis evdenis released this 29 Aug 21:35

spatch now runs as a warm server by default when the bundled cvehound-spatch supports it (cvehound[spatch] >= 1.3.2.post2): one server per worker, forking a fresh process per rule, instead of starting spatch from scratch 500+ times. Roughly 5% off a scan today and more as the rule set grows. Every rule still runs in its own process, so nothing about what a scan finds changes — reports are byte-identical either way. Any other spatch keeps the old behaviour, decided from what the installed wheel records rather than by probing, and a build that turns out not to support it after all falls back with a warning instead of failing. --zygote=off opts out, --zygote=on demands it and fails if it is unavailable.

New --cache[=DIR], off by default, letting rules that target the same file share the parsed C between them. It is worth it when rescanning a tree (~25% faster) or running thousands of rules, and not worth it for a single scan of today's rule set, which it makes marginally slower while spending ~310 MB per tree. cvehound evicts least-recently-used entries past a few GB, since coccinelle never prunes anything; --cache-clear empties it.

Scans are now sandboxed by default. Landlock confines the run to the kernel tree (read-only), the rules and a temp directory with no network, and a seccomp filter closes what Landlock structurally cannot express — UDP and netlink sockets, io_uring, ptrace, namespace syscalls. Both are installed once just before the worker pool, so they cover the workers, every spatch, and the /bin/sh, git, find, rm and diff that spatch shells out to. --sandbox=off skips it, --sandbox=strict refuses to scan when the kernel cannot provide it, and the default falls back to an unconfined scan on a kernel without Landlock. A self-test runs after the lock: an over-tight policy would otherwise show up as "0 files match" rather than as an error.

spatch now runs with GIT_CONFIG_GLOBAL=/dev/null and GIT_CONFIG_NOSYSTEM=1. Handed a directory it prefilters by shelling out to git grep, and that git was reading the user's ~/.gitconfig, so a setting there could change what a scan found on one machine but not another. cvehound supplies safe.directory for the scanned tree over GIT_CONFIG_* (the "command" scope, which git treats as protected), so a checkout owned by another user still scans — previously that needed the user's own git config, and without it git refused the repository and spatch reported "0 files match" with exit 0.

Full Changelog: 1.5.0...1.6.0

CVEhound 1.5.0

Choose a tag to compare

@evdenis evdenis released this 27 Aug 07:55

spatch runs are now bounded by two time budgets instead of none: SPATCH_TIMEOUT (CPU-seconds, via spatch's own --timeout) and SPATCH_WALL_TIMEOUT (a wall-clock watchdog covering the rule parsing and I/O that --timeout can't see). A rule that fires either budget now fails the CVE and is recorded in report['errors'], instead of silently returning partial or no results.

validate-rule.sh (the write-cve-rule skill) now fails a rule that would blow the same matching budget cvehound enforces at runtime, and checks grep-prefilter selectivity: a rule with no or overly common literal tokens makes spatch parse the whole tree, which used to go undetected until a whole-tree scan actually paid for it.

CVE-2020-12352 collapsed from ten near-identical starred rules into the one invariant they all repeat. Past Coccinelle's max_cnf = 5, CNF conversion gives up and drops the grep query entirely, so the old rule parsed the entire kernel tree (650s CPU) for a function that no longer exists upstream; the collapsed rule scans in 0.2s with unchanged verdicts.

Five more rules (CVE-2019-15925, CVE-2022-0492, CVE-2017-7558, CVE-2020-11884, CVE-2023-50431) stopped leaking common tokens into their grep query, cutting whole-tree parse costs by one to two orders of magnitude with no change in verdicts.

Fixed content-latest publishing: the push trigger watched master, which this repo stopped using, so every rule push since had been a no-op — the rolling content set still served pre-conversion (script:python) rules that the bundled python-less spatch refuses to run. Now watches main.

docs/WRITING_RULES.md gained Rule 8 (keep the grep query selective), documenting how to measure and avoid this class of cost, and documents exactly what the two spatch time budgets cover and don't.

--runslow is substantially faster and more reliable: whole-tree verdicts are now memoized, worktree scans spread across every worker instead of one branch per group, metadata lookups batch through one long-lived git process instead of forking one per entry, and spatch's GC is tuned for its actual allocation pattern. CI can dispatch a slow run against a named subset of rules, and the missing_backports allowlist now fails once a listed backport actually lands instead of silently XPASSing forever.

Full Changelog: 1.4.0...1.5.0

CVEhound 1.4.0

Choose a tag to compare

@evdenis evdenis released this 25 Aug 13:26
  • Added an optional bundled coccinelle: pip install 'cvehound[spatch]' brings a prebuilt, tailored spatch (cvehound-spatch, Linux x86_64 and aarch64), so scanning no longer requires coccinelle on the system
  • Made spatch resolution explicit and documented: --spatch (or a spatch config key), then $CVEHOUND_SPATCH, then the bundled package, then PATH; a binary named explicitly is an error when it does not resolve, and reports record which one produced the findings
  • Converted all 534 coccinelle rules from @script:python reporting to * context mode, and cvehound now parses spatch's diff — rules no longer need a python-enabled spatch, and a detection and the locations it reports can no longer disagree
  • Fixed a CVE-2023-42753 false positive on linux-next, Files: coverage for CVE-2021-28971 and CVE-2021-3656, and CVE-2022-1016 guarding on a file other than the one it detects in; sped up CVE-2016-5195, CVE-2017-1000112, CVE-2019-14821 and CVE-2021-28971 detection by orders of magnitude
  • Reworked out-of-band content updates: cvehound update installs a checksummed, versioned rules+metadata set atomically under ~/.local/share/cvehound/ and never touches the installed package; --version and JSON reports now record which content produced the findings
  • Made a missing diff(1) an error instead of a silent no-detection run, and report what spatch said when it fails
  • Fixed python -m cvehound crashing on Python 3.14 and macOS under the forkserver/spawn start methods
  • Dropped support for coccinelle < 1.1.0

Full Changelog: 1.3.0...1.4.0

Detection content (rolling)

Pre-release

Choose a tag to compare

@evdenis evdenis released this 23 Aug 14:59

Rolling detection content consumed by cvehound update. Assets are replaced in place by .github/workflows/content.yml. Do not delete.

CVEhound 1.3.0

Choose a tag to compare

@evdenis evdenis released this 18 Aug 16:34
  • Added detection rules for 16 new CVE IDs and updated 25 existing rules
  • Moved upstream-rejected CVEs to the disputed rules group
  • Fixed Kbuild parsing on kernels >= 5.14 and added --arch with ARCH to SRCARCH normalization
  • Made kernel-config affectedness evaluation sounder and tightened --check-strict
  • Replaced the deprecated CVE metadata source with kernel.org vulns.git and CIP kernel-sec, and switched exploit status to CISA KEV
  • Linked CVE records to cve.org and removed CWE and CVSS fields from reports
  • Hardened CVE rule execution and improved metadata, condition-evaluation and worker performance
  • Raised the minimum Python version to 3.11
  • Migrated packaging and development tooling to uv, Ruff and ty; replaced the lxml runtime dependency with PyYAML

Full Changelog: 1.2.1...1.3.0

CVEHound 1.2.1

Choose a tag to compare

@evdenis evdenis released this 04 May 11:01

Full Changelog: 1.2.0...1.2.1

CVEhound 1.2.0

Choose a tag to compare

@evdenis evdenis released this 15 Feb 13:17

What's Changed

  • Added +98 new CVE descriptions
  • Added --exclude <file> option to exclude CVE-ids from the file from checks
  • Added multiprocessing support
  • Fixed ordering in reports (reproducibility)
  • Support macOS (it's possible to run the tool on the system)
  • Support Python 3.10

New Contributors

Full Changelog: 1.1.0...1.2.0