Remove bpfilter daemon and consolidate every feature in libbpfilter#463
Remove bpfilter daemon and consolidate every feature in libbpfilter#463qdeslandes wants to merge 5 commits intofacebook:mainfrom
Conversation
The cgen/, bpf/, ctx, and xlate modules were compiled directly into the bpfilter binary. Move them into libbpfilter so the filtering logic lives entirely in the shared library and the daemon is a thin entry point.
| pindir_fd = bf_ctx_get_pindir_fd(); | ||
| if (pindir_fd < 0) | ||
| return bf_err_r(pindir_fd, "failed to get pin directory FD"); | ||
|
|
There was a problem hiding this comment.
Claude: Global context leak on bf_ctx_setup early failure paths (must-fix)
After _bf_global_ctx = TAKE_PTR(_ctx) at line 393, the local _ctx is NULL and its _free_bf_ctx_ cleanup attribute becomes a no-op. If bf_ctx_get_pindir_fd() fails (line 395-396) or flock() fails (line 399-400), the function returns without freeing _bf_global_ctx. The caller in main.c does not call bf_ctx_teardown() on failure, so the global context, its BPF token fd, BTF state, and ELF stubs are all leaked.
Only the _bf_ctx_discover() failure path at line 404-405 properly calls _bf_ctx_free(&_bf_global_ctx).
Suggested fix: add _bf_ctx_free(&_bf_global_ctx) to the bf_ctx_get_pindir_fd and flock failure paths (matching the _bf_ctx_discover path), or restructure with a single goto err label.
bpfilter/src/libbpfilter/ctx.c
Lines 393 to 406 in 4a91b8b
| if (r) | ||
| return bf_err_r(r, "failed to load vmlinux BTF"); | ||
|
|
||
| _ctx = calloc(1, sizeof(*_ctx)); |
There was a problem hiding this comment.
Claude: BTF resource leak if calloc fails after bf_btf_setup succeeds (suggestion)
bf_btf_setup() is called at line 106 before calloc() at line 110. If calloc fails, _ctx is NULL, so the _free_bf_ctx_ cleanup attribute is a no-op and bf_btf_teardown() is never called. This leaks whatever global BTF state was initialized.
Suggested fix: move bf_btf_setup() to after the calloc succeeds, or add an explicit bf_btf_teardown() in the calloc failure path.
bpfilter/src/libbpfilter/ctx.c
Lines 106 to 113 in 4a91b8b
| #include "cgen/prog/link.h" | ||
| #include "cgen/prog/map.h" | ||
|
|
||
| static int copy_hookopts(struct bf_hookopts **dest, |
There was a problem hiding this comment.
Claude: Static functions copy_hookopts and copy_set missing _bf_ prefix (must-fix)
Per doc/developers/style.rst and CLAUDE.md, static functions use a leading underscore with the _bf_ prefix (e.g., _bf_ctx_free()). Every other static function across src/libbpfilter/*.c follows this convention. These should be _bf_copy_hookopts and _bf_copy_set respectively.
bpfilter/src/libbpfilter/cli.c
Line 27 in 4a91b8b
bpfilter/src/libbpfilter/cli.c
Line 377 in 4a91b8b
| "BF_REQ_CHAIN_PROG_FD failed"); | ||
|
|
||
| return TAKE_FD(prog_fd); | ||
| return dup(cgen->handle->prog_fd); |
There was a problem hiding this comment.
Claude: bf_chain_prog_fd and bf_chain_logs_fd return bare dup() failure (-1) without proper errno (suggestion)
dup() returns -1 on failure, which happens to equal -EPERM on Linux. The API contract says "negative error value on failure", but returning -1 without converting errno means: (1) the caller cannot distinguish dup() failure from a permission error, and (2) error logging will report "Operation not permitted" rather than the actual error.
The codebase pattern for returning fds (e.g., _bf_ctx_gen_token, bf_ctx_get_pindir_fd) is to use _cleanup_close_ with TAKE_FD():
| return dup(cgen->handle->prog_fd); | |
| _cleanup_close_ int fd = dup(cgen->handle->prog_fd); | |
| if (fd < 0) | |
| return bf_err_r(-errno, "failed to dup program fd for '%s'", name); | |
| return TAKE_FD(fd); |
Same applies to bf_chain_logs_fd at line 286.
bpfilter/src/libbpfilter/cli.c
Line 269 in 4a91b8b
bpfilter/src/libbpfilter/cli.c
Line 286 in 4a91b8b
|
Claude: review of facebook/bpfilter #463 (31553a8) Must fix
Suggestions
Nits
CLAUDE.md improvements
|
Replace the daemon IPC path in libbpfilter/cli.c with direct calls to the cgen and ctx APIs, allowing bfcli to load, attach, and manage chains without a running daemon. Move ctx and elfstub headers to the public libbpfilter API, pull BTF lifetime into bf_ctx_setup/teardown, remove namespace switching and transient mode, and extend bfcli's stage-1 parser with global flags (--verbose, --bpffs-path, --with-bpf-token, deprecated compat options).
Replace daemon e2e tests with namespace and persistence tests that
exercise bpffs-based chain discovery directly, without starting or
stopping the daemon process.
Remove the daemon lifecycle (start_bpfilter/stop_bpfilter) from the
test harness and propagate --bpffs-path through the ${BFCLI} variable
so all test invocations use the sandbox-local bpffs mount.
Skip bf_ctx_setup in bfcli when --dry-run is used so matcher tests
no longer require bpffs access. Fix clang-analyzer BitwiseShift
warning in --verbose option parsing.
Drop namespace unit tests and request namespace accessors that are no
longer part of the public API.
The daemon has been removed in favour of direct library calls. Update comments, documentation, and user-facing strings that still referred to "the daemon" so the terminology is consistent with the new architecture. External kernel headers (src/external/) are left unchanged.
Lock bpfilter directory in bpffs to prevent concurrent access to the chains and BPF objects.
| return bf_err_r(r, "failed to create new context"); | ||
|
|
||
| _bf_global_ctx = TAKE_PTR(_ctx); | ||
| _bf_global_ctx = _ctx; |
There was a problem hiding this comment.
Claude: Double-free on _bf_ctx_discover() failure; dangling _bf_global_ctx on other early-return paths (must-fix)
Commit 2 changed this from _bf_global_ctx = TAKE_PTR(_ctx) to a plain pointer copy. Now both _bf_global_ctx and _ctx point to the same allocation, and _ctx retains its _free_bf_ctx_ cleanup attribute.
_bf_ctx_discover() failure path (double-free): _bf_ctx_free(&_bf_global_ctx) frees the object and nulls the global, but _ctx still holds the stale pointer. On function return, the cleanup fires _bf_ctx_free(&_ctx) which sees non-NULL and frees again.
bf_ctx_get_pindir_fd() / flock() failure paths (dangling global): The cleanup frees through _ctx and nulls it, but _bf_global_ctx still holds the freed pointer. Any subsequent use of the global is use-after-free.
Suggested fix: either restore _bf_global_ctx = TAKE_PTR(_ctx) and add _bf_ctx_free(&_bf_global_ctx) to all early-return paths, or defer the global assignment to after all validation succeeds:
// defer assignment to success path only
r = _bf_ctx_discover();
...
_bf_global_ctx = _ctx;
_bf_global_ctx->lock_fd = TAKE_FD(pindir_fd);
TAKE_PTR(_ctx);bpfilter/src/libbpfilter/ctx.c
Line 393 in 31553a8
| assert(ctx); | ||
| assert(bpffs_path); | ||
|
|
||
| r = bf_btf_setup(); | ||
| if (r) | ||
| return bf_err_r(r, "failed to load vmlinux BTF"); | ||
|
|
||
| _ctx = calloc(1, sizeof(*_ctx)); |
There was a problem hiding this comment.
Claude: BTF resource leak if calloc fails after bf_btf_setup succeeds (suggestion)
bf_btf_setup() succeeds at line 103, then calloc fails at line 107. Since _ctx is NULL, the _free_bf_ctx_ cleanup is a no-op and bf_btf_teardown() is never called, leaking the global BTF state.
Suggested fix: move bf_btf_setup() after calloc succeeds, or add bf_btf_teardown() to the calloc failure path.
bpfilter/src/libbpfilter/ctx.c
Lines 103 to 110 in 31553a8
| #include "cgen/prog/link.h" | ||
| #include "cgen/prog/map.h" | ||
|
|
||
| static int copy_hookopts(struct bf_hookopts **dest, |
There was a problem hiding this comment.
Claude: Static functions copy_hookopts and copy_set missing _bf_ prefix (must-fix)
Per doc/developers/style.rst, static/internal functions use a leading underscore with the _bf_ prefix. Over 115 static functions across src/libbpfilter/ follow this convention (e.g. _bf_ctx_free, _bf_dynbuf_grow, _bf_parse_l4_port_range). These should be _bf_copy_hookopts and _bf_copy_set.
bpfilter/src/libbpfilter/cli.c
Line 27 in 31553a8
| "BF_REQ_CHAIN_PROG_FD failed"); | ||
|
|
||
| return TAKE_FD(prog_fd); | ||
| return dup(cgen->handle->prog_fd); |
There was a problem hiding this comment.
Claude: Bare dup() returns -1 on failure, conflating with -EPERM (suggestion)
dup() returns -1 on failure, which equals -EPERM. The API contract expects negative errno values. The codebase pattern for returning fds uses _cleanup_close_ with TAKE_FD() and proper errno conversion (see _bf_ctx_gen_token, bf_ctx_get_pindir_fd):
| return dup(cgen->handle->prog_fd); | |
| _cleanup_close_ int fd = dup(cgen->handle->prog_fd); | |
| if (fd < 0) | |
| return bf_err_r(-errno, "failed to dup program fd for '%s'", name); | |
| return TAKE_FD(fd); |
Same pattern should be applied to bf_chain_logs_fd at line ~286.
bpfilter/src/libbpfilter/cli.c
Line 269 in 31553a8
As it is now, this PR can be merged into
main, but more polishing will be done before tagging a new release.