Skip to content

lib: cgen: fix the state map BTF for chains with several rules - #626

Open
prashantpcm94 wants to merge 1 commit into
meta:mainfrom
prashantpcm94:log-rate-state-map-btf
Open

prashantpcm94 wants to merge 1 commit into
meta:mainfrom
prashantpcm94:log-rate-state-map-btf

Conversation

@prashantpcm94

Copy link
Copy Markdown
Contributor

Summary

A chain with 2 rules or more fails to load as soon as one of its rules uses log ... every <rate>:

$ bfcli chain set --from-str "chain c BF_HOOK_XDP{ifindex=1} ACCEPT rule ip4.proto tcp counter ACCEPT rule ip4.proto icmp log every 1s counter DROP"
error  : bf_map bf_smap: failed to create map: Invalid argument
error  : failed to create the state bf_map object: Invalid argument
error  : failed to load the state map: Invalid argument

The state map has a single value holding one struct bf_rule_state per rule (n_rules * sizeof(struct bf_rule_state)), but its BTF describes the value as a single bf_rule_state. The kernel rejects a BTF value type whose size doesn't match the map's value size, so only single-rule chains could use log ... every. This describes the value as an array of bf_rule_state, one per rule.

Related issue

None.

Testing

CMake release build (-DNO_DOCS=1) on CentOS Stream 9, kernel 6.16.1, gcc 11.5, clang 22.1.3, libbpf 1.6.3, then make -C $BUILD test_bin and ctest (without fuzzing.parser.infinite, which runs until stopped):

99% tests passed, 1 tests failed out of 128
The following tests FAILED:
	127 - check.iwyu (Not Run)

check.iwyu didn't run because iwyu_tool.py isn't installed here (it only checks cgen/program.c). check.astgrep and check.astgrep.test pass (in a second build with ast-grep on PATH).

  • e2e.rules.log: a new chain set of a 2-rule XDP chain whose second rule uses log every 1s. It fails on main with the error above and passes with this PR.
  • By hand:
    • chains with 2 and 3 rules using log ... every fail on main and load with this PR; bpftool map dump name bf_smap shows one last_log_ts per rule;
    • on a 2-rule chain whose rules both use log internet every 1s, 12 ICMP packets (5 pings, then 1 more after 1.2 s) give 2 log entries for the second rule, and the first TCP packet (first rule) and the first ICMP packet (second rule), sent a few milliseconds apart, are both logged: each rule has its own state.

Notes for the reviewer

  • The key's u32 type ID is kept in a local int because btf__add_array() takes int type IDs and key_type_id is a uint32_t (clang-tidy flags the narrowing).

AI disclosure

Claude Code found the issue, drafted the change, the test, the commit message and this description under my direction, and ran the tests listed under Testing. I reviewed the change and the results.

Checklist

  • I understand every line in this PR and can explain why it is correct
  • I built the project and ran the tests covering this change
  • make -C $BUILD test_bin test passes (check.iwyu not run, see Testing) and the code follows the style guide
  • Commits are formatted as component: subcomponent: short description
  • I have disclosed any AI usage above

The state map's single value holds one struct bf_rule_state per rule of
the chain, but its BTF describes the value as a single bf_rule_state.
The kernel rejects a BTF value type whose size doesn't match the map's
value size, so any chain with 2 rules or more that uses "log ... every"
fails to load with -EINVAL.

Describe the value as an array of bf_rule_state, one per rule.
@prashantpcm94

Copy link
Copy Markdown
Contributor Author

@claude review

@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Claude review failed — see workflow run for details.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant