feat(handlers): kernel_module_disable direct kernel IO (Phase 6, PR 3/3)#111
Merged
Conversation
Third and final direct-kernel-IO handler port, completing Phase 6. kernel_module_disable now writes the /etc/modprobe.d blacklist drop-in atomically (fsatomic) and unloads the running module via delete_module(2), instead of the shell printf + modprobe -r pipeline, when running in agent mode. - kernelio: DeleteModule (delete_module(2), O_NONBLOCK, best-effort; ENOENT → ErrModuleNotLoaded). ModuleTransport capability (FileTransport + DeleteModule). FakeSysctlTransport gains DeleteModule + a recorder so it satisfies ModuleTransport. - local.Transport implements ModuleTransport (DeleteModule delegate). - kernel_module_disable Apply/Capture/Rollback gain a kernel-IO branch selected by transport.(kernelio.ModuleTransport), falling back to the modprobe + shell file-write path. The runtime unload is best-effort on BOTH paths (the persistent blacklist+install-/bin/true entry is the load-bearing change). Both paths write a byte-identical drop-in and record an identical PreState shape. - spec kernelio-module (Tier 1, 4 ACs); DeleteModule guard test + handler round-trip tests (apply writes+unloads, unload-failure-is-best-effort, remove-when-absent, restore-when-existed, shell fallback). Existing shell-path tests unchanged. Failure-mode analysis: 1. What could this do wrong in production? Removing an operator's pre-existing blacklist file, or failing to keep a module out. Mitigated: the blacklist write is atomic (fsatomic); rollback restores prior content when the file existed at capture and removes it only when it did not (file_existed flag), so a host with an operator blacklist is left exactly as found; the load-bearing change is the persistent blacklist, not the best-effort unload. 2. Captured-state sufficiency: rollback consumes file_existed + prior_content (same tuple Capture records on both paths). A capture read error surfaces ErrCaptureIncomplete rather than an empty prior. 3. Edge case not safe for / gated: delete_module(2) removes only the named module (not deps) — adequate since the blacklist is load-bearing; a module in use (EBUSY) or not loaded is a best-effort no-op, never a failed Apply. Module RELOAD on rollback is not attempted on either path (may need reboot) — a disclosed, shared limitation. The live delete_module(2) success path needs root + a loaded module and is left to live validation; kensa-fuzz atomicity + the two-human rollback-handler review (CONTRIBUTING) remain the founder's gate. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
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.
Phase 6, PR 3 of 3 — completes Phase 6.
kernel_module_disablenow writes the/etc/modprobe.dblacklist drop-in atomically (fsatomic) and unloads the running module viadelete_module(2), instead of the shellprintf+modprobe -rpipeline, in agent mode.What
kernelio:DeleteModule(delete_module(2),O_NONBLOCK, best-effort;ENOENT→ErrModuleNotLoaded).ModuleTransportcapability (FileTransport+DeleteModule).FakeSysctlTransportgainsDeleteModule+ a recorder.local.TransportimplementsModuleTransport.kernel_module_disableApply/Capture/Rollback gain a kernel-IO branch viatransport.(kernelio.ModuleTransport), falling back to the modprobe + shell path. The runtime unload is best-effort on both paths (the persistent blacklist+install /bin/trueis the load-bearing change). Both paths write a byte-identical drop-in and an identicalPreStateshape.kernelio-module(Tier 1, 4 ACs) + tests (apply writes+unloads, unload-failure-is-best-effort, remove-when-absent, restore-when-existed, shell fallback).Verification
go test ./...green;go build ./...clean;golangci-lint0;comment-lintclean;specter syncall pass.Failure-mode analysis
file_existedflag) — a host with an operator blacklist is left exactly as found; the load-bearing change is the persistent blacklist, not the best-effort unload.file_existed+prior_content(same tuple Capture records on both paths); a read error →ErrCaptureIncomplete.delete_module(2)removes only the named module (not deps) — adequate since the blacklist is load-bearing; in-use/not-loaded is a best-effort no-op. Module reload on rollback is not attempted on either path (may need reboot) — disclosed, shared. The livedelete_module(2)success path needs root + a loaded module →kensa-fuzz+ two-human rollback-handler review (CONTRIBUTING) are the founder's gate.🤖 Generated with Claude Code