netebpfext: Enable multi-attach for the sock_addr listen hook (#5339)#5384
Draft
mikeagun wants to merge 19 commits into
Draft
netebpfext: Enable multi-attach for the sock_addr listen hook (#5339)#5384mikeagun wants to merge 19 commits into
mikeagun wants to merge 19 commits into
Conversation
native_module_helper_t owns a generated .sys file that the destructor
deletes when _delete_file_on_destruction is true. The class previously
relied on the implicit copy/move members:
- The implicit copy ctor copied _delete_file_on_destruction and
_file_name verbatim, so two instances could end up racing to delete
the same file.
- The implicit move ctor moved _file_name (which leaves the moved-from
string in an implementation-defined valid state) but only copied the
bool flag, leaving the moved-from instance still claiming ownership.
Whether the source destructor then attempted to delete the wrong file
depended on std::string's moved-from representation.
Make the type non-copyable and add explicit noexcept move operations that
transfer ownership of _delete_file_on_destruction and clear it on the
source, so only the destination's destructor deletes the file.
This is required for multi-attach tests that push several
native_module_helper_t instances into std::vector containers, which
may reallocate and move-construct elements.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The sock_addr bind hook's process_verdict callback silently ignored address/port writes by attached programs (the WFP ALE_RESOURCE_ASSIGNMENT layer does not support context rewrite) but did not restore the bpf_sock_addr_t context between program invocations. Under multi-attach, an earlier program could mutate context fields such as user_ip / user_port / msg_src_* and those mutated values would then be visible to any subsequent attached program, allowing one program to influence the policy decisions of another. Rename _net_ebpf_extension_sock_addr_bind_process_verdict to _net_ebpf_extension_sock_addr_authorize_process_verdict and have it restore *sock_addr_ctx = *original_context after each program, reusing the existing original_context snapshot pattern used by the CONNECT_AUTHORIZATION accumulator. Update bind_classify to set original_context from a stack-local snapshot of the WFP-populated context before invoking programs. The accumulator is also intended to be used by the sock_addr listen hook once it becomes multi-attach. Naming it for the abstract role (authorization gates that do not support context rewrite) rather than for the bind hook avoids a second rename when listen is wired in. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ions
Microsoft's FWPS_CLASSIFY_OUT0 contract gates writes to actionType on
the FWPS_RIGHT_ACTION_WRITE bit; a callout with higher weight can
terminate WFP arbitration and revoke that right before reaching our
callout. The sock_addr bind, listen, recv_accept, and
authorize_connection classify routines wrote actionType unconditionally
on entry and from the verdict-driven switch in their Exit blocks, so
they could attempt to overwrite a prior terminating decision.
Mirror the early-exit rights check already used by
net_ebpf_extension_sock_addr_redirect_connection_classify:
- For bind, listen, and recv_accept: bail out before the default
actionType = FWP_ACTION_PERMIT write.
- For authorize_connection: set a rights_revoked flag and gate the
Exit-block switch on it, since that classify defers actionType
writes to Exit.
This is independent of multi-attach but visible alongside it, since
multi-attach configurations are more likely to share filter layers
with other callouts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Multi-attach tests for the sock_addr bind and listen hooks need to bind
and listen inside specific network compartments to exercise wildcard +
compartment-specific filter selectivity. The existing socket_helper
classes create and bind sockets on the current thread compartment with
no way to override.
Add an optional compartment_id parameter (default
UNSPECIFIED_COMPARTMENT_ID = leave compartment untouched, current
behavior) to every public socket_helper constructor and forward it
through the inheritance chain.
The new compartment_scope RAII helper in socket_helper.cpp brackets
the relevant socket calls with SetCurrentThreadCompartmentId save /
restore, so:
- _base_socket creates and binds the socket inside the requested
compartment.
- _stream_server_socket additionally re-enters the compartment for
listen() and the accept-socket setup, since the base ctor's scope
has already destructed by the time the derived ctor body runs.
UNSPECIFIED_COMPARTMENT_ID is a no-op fast path, so existing call sites
that omit the parameter are bit-for-bit identical to before.
iphlpapi.lib is pulled in via #pragma comment(lib, ...) in
socket_helper.cpp to avoid touching the test project files.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add per-module map discovery, program_policies for targeted map updates, attachment_step for dynamic detach/reattach, and compartment_id on program_spec for compartment-aware attachment. Existing single-program call sites remain source-identical and behavior-identical. New framework types: - program_ref: bounds-checked reference to a specific module/program - program_policy_spec: per-module verdict/policy configuration - attachment_step/attachment_action: dynamic attach/detach between steps - counter_expectation: reserved for future invocation counting The legacy global-first-match map discovery is preserved as a fallback for callers that do not use program_policies. When program_policies is non-empty, per-module maps are used instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 9 multi-program bind test scenarios exercising the verdict accumulation, short-circuit, hard-permit-over-WFP, and dynamic detach/reattach behaviors of the sock_addr-aligned bind hook (cgroup/bind4 / cgroup/bind6). Each scenario runs across TCP/UDP and IPv4/IPv6 via TEMPLATE_TEST_CASE. Test scenarios: - All programs PROCEED_SOFT → bind allowed - Second program REJECT after soft → bind denied - First program REJECT (short-circuit) → bind denied - PROCEED_SOFT + PROCEED_HARD mix → hard permit (higher priority) - Multi-program soft blocked by WFP filter → bind denied - Multi-program hard overrides WFP filter → bind allowed - Detach middle program (REJECT) → bind recovers to allow - Detach and reattach with changed verdict → bind denied - Three programs all soft → bind allowed Helper functions added: - sock_addr_bind_module(): creates a module_spec for the bind hook - sock_addr_bind_verdict(): creates a per-module policy spec - sock_addr_bind_wfp_layer(): returns the WFP layer GUID for bind All tests tagged [bind_tests][multi_attach] for selective execution. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Expand the brief mention of multi-attach behavior in BindHook.md with detailed documentation of: - Verdict priority ordering (REJECT > PROCEED_HARD > PROCEED_SOFT) - Short-circuit on REJECT behavior - WFP interaction with accumulated verdicts (soft vs hard permit) - Default verdict when no programs are attached - Unknown/invalid verdict treatment Add a Multi-Attach Test Coverage table summarizing the 9 test scenarios now exercised in socket_tests.cpp [bind_tests][multi_attach]. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix FR-1/BT-4: Check bpf_prog_detach2 return code in detach_program helper — SAFE_REQUIRE(rc == 0) before clearing the attached flag. Add coverage for adversarial review gaps: - HARD-first ordering with WFP (BT-2): [HARD, SOFT] + WFP → allow - REJECT + HARD combinations (BT-9): both [REJECT, HARD] and [HARD, REJECT] → bind denied (REJECT priority > HARD) - Third-program-decisive (BT-7): [SOFT, SOFT, REJECT] → denied and [SOFT, SOFT, HARD] + WFP → allowed - Detach first and detach last (BT-8/F2): asymmetric detach positions Remove unused TCP_ONLY_PARAMS macro (BT-13). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… cleanup Move the FWPS_RIGHT_ACTION_WRITE check in authorize_connection_classify to after the connection context cache lookup. Previously, the rights check at the top of the function caused an early exit that skipped _net_ebpf_ext_find_and_remove_connection_context, leaving stale cache entries from the redirect layer when a higher-weight WFP callout had revoked write permission. The cache cleanup must run unconditionally so redirect-layer entries are always consumed, regardless of whether this callout can write the classify output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mikeagun
force-pushed
the
pr2-listen-multi-attach
branch
from
June 19, 2026 20:57
57dd9e3 to
98cf56a
Compare
…rograms The socket test framework's detach_program helper uses bpf_prog_detach2 uniformly for all attach types. However, BPF_ATTACH_TYPE_BIND was not included in _does_attach_type_support_attachable_fd, causing detach to return -ENOTSUP (-129) for legacy bind programs (bind_policy tests). Add BPF_ATTACH_TYPE_BIND to the whitelist. The bind provider accepts attach data but ignores it (always global scope), so this is safe for both the attach and detach paths. Also remove the unused counter_expectations scaffolding from the test framework -- it was never populated by any test and added reader overhead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mikeagun
force-pushed
the
pr2-listen-multi-attach
branch
from
June 23, 2026 20:39
21bfa5e to
f6009f1
Compare
Remove the compartment_id parameter from all socket helper constructors and the compartment_scope RAII class. This infrastructure is unused by any current bind multi-attach test (no test sets program_spec.compartment_id) and will be reintroduced in a focused follow-up branch. Changes: - Remove compartment_id param from _base_socket, _client_socket, _datagram_client_socket, _stream_client_socket, _server_socket, _datagram_server_socket, _stream_server_socket constructors - Remove compartment_scope class and iphlpapi dependency - Remove compartment_id field from program_spec struct - Simplify attach_program/detach_program lambdas to use hardcoded 0 (wildcard compartment), which is the value all tests were using Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address PR feedback on the libbpf-compat attach path. The legacy bind hook (BPF_ATTACH_TYPE_BIND) has no per-target attach parameter and its provider ignores any supplied client data, so bpf_prog_attach/bpf_prog_detach2 would accept a non-zero attachable_fd and silently treat it as the wildcard attachment -- giving callers the false impression that a program was scoped to a specific target. Reject a non-zero attachable_fd for such attach types with EINVAL. The wildcard value is still encoded as a 4-byte zero payload, which bpf_prog_detach2 relies on to locate the matching link to detach. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b71da926-7c8c-4526-87ea-084c89545896
Address PR feedback on the bind multi-attach test suite: replace non-ASCII arrows and dashes in comments and test descriptions with ASCII equivalents, and spell out the PROCEED_SOFT / PROCEED_HARD verdict names in the scenario comments so they match the enum used in the code. Comment/description text only; no test behavior changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b71da926-7c8c-4526-87ea-084c89545896
Address PR feedback on docs/BindHook.md: simplify the "Hard overrides WFP" scenario row to the minimal programs that demonstrate it, and add a note that the "no subsequent WFP filter can override" guarantee for PROCEED_HARD applies to WFP filters only -- among eBPF programs the most-restrictive verdict wins, so a later program's REJECT still denies a bind another program permitted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b71da926-7c8c-4526-87ea-084c89545896
mikeagun
force-pushed
the
pr2-listen-multi-attach
branch
from
July 17, 2026 02:43
f6009f1 to
1f3bf35
Compare
Change the listen hook from ATTACH_CAPABILITY_SINGLE_ATTACH_PER_HOOK to ATTACH_CAPABILITY_MULTI_ATTACH_WITH_WILDCARD and wire in the shared authorize_process_verdict callback for verdict accumulation. Refactor authorize_listen_classify to use the accumulated verdict from net_ebpf_sock_addr_ctx.verdict (mirroring bind_classify) instead of the raw per-call program result. This enables the most-restrictive-wins accumulation and REJECT short-circuit behavior across multiple attached listen programs. Changes: - listen_dispatch_table: add .process_verdict callback - Provider registration: SINGLE_ATTACH → MULTI_ATTACH_WITH_WILDCARD - authorize_listen_classify: initialize verdict to PROCEED_SOFT, snapshot context for restoration between programs, use accumulated verdict for action decision, clear FWPS_RIGHT_ACTION_WRITE on invoke failure (fix accidental divergence from bind) All existing single-attach listen behaviors preserved: - REAUTHORIZE early-return (listen-specific, not present in bind) - context_deleting early-return - Compartment filter - No-program default permit - Hard-permit/block rights clearing - Classify logging Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 7 multi-program listen test scenarios exercising the same verdict accumulation behaviors as the bind tests, adapted for TCP-only IPv4/IPv6. Test scenarios: - All programs PROCEED_SOFT → listen allowed - Second program REJECT → listen denied - First program REJECT (short-circuit) → listen denied - HARD permit overrides WFP block → listen allowed - REJECT beats HARD → listen denied - Detach middle program → listen recovers Helper functions added: - sock_addr_listen_module(): creates a module_spec for the listen hook - sock_addr_listen_verdict(): creates a per-module listen policy spec - sock_addr_listen_wfp_layer(): returns the ALE_AUTH_LISTEN WFP layer GUID All tests tagged [sock_addr_tests][multi_attach] for selective execution. Listen is TCP-only so tests use tcp_v4_params/tcp_v6_params. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add detailed multi-attach documentation to ListenHook.md mirroring the bind hook documentation: - Verdict priority ordering (REJECT > PROCEED_HARD > PROCEED_SOFT) - Short-circuit on REJECT behavior - WFP interaction with accumulated verdicts - Default verdict when no programs are attached - Unknown/invalid verdict treatment - Multi-attach test coverage table Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extend the is_listen_test detection in execute_connection_test to also check program_policies[].listen_verdict, not just the legacy top-level listen_verdict field. Without this, multi-attach listen tests using program_policies would create a dual-stack (IPv6) server socket, missing the IPv4 listen WFP layer entirely. This caused IPv4 listen-deny tests to spuriously pass because the listen hook was never triggered for the dual-stack socket on the IPv4 ALE_AUTH_LISTEN layer. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mikeagun
force-pushed
the
pr2-listen-multi-attach
branch
from
July 17, 2026 19:37
1f3bf35 to
fb8710a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Enable multi-attach support for the sock_addr-aligned listen hook (\cgroup/listen4\ / \cgroup/listen6) and add a multi-attach test suite. Resolves #5339.
Depends on #5381 (bind multi-attach framework + tests).
Changes
Production code (\
etebpfext/net_ebpf_ext_sock_addr.c)
Listen multi-attach test suite (7 scenarios x TCP x IPv4/IPv6)
Documentation
Preserved behaviors
Validation
etebpfext_unit\ build clean (Debug/x64)
Resolves #5339