Repository navigation
Mock driver - #12
Closed
vlabo wants to merge 16 commits into
Closed
Mock driver#12vlabo wants to merge 16 commits into
vlabo wants to merge 16 commits into
Conversation
Introduce GitHub Actions for nightly fuzzing and PR validation. Update Justfile to include driver tests and fuzzing commands. Add mock_wdk dependency to enable host-based property testing.
Update CI workflows, Justfile, and Cargo.toml paths to reflect the relocation of the fuzz directory from the root to driver/fuzz. Adjust dependency paths and workspace configurations accordingly.
Introduce a new fuzz target that exercises the stateful callout pipeline, decoding operations to pend flows, resolve verdicts, and verify packet-layer actions against permanent verdicts. Add corpus seeds and a unit test to ensure the harness links correctly and its oracles are non-vacuous.
Introduce the `callouts_mt` target to stress the shared device with multiple worker threads driving concurrent callouts. This harness uses a barrier to synchronize threads and includes a quiesce oracle to verify the packet cache drains correctly after shutdown. Add a `Justfile` recipe to run these targets under ThreadSanitizer for race detection.
Wrap FilterEngine in a R/W spinlock so Device::read, write, shutdown, and inject_packet can take &self. Producer (callout) and consumer (read) threads now share one &'static Device without a global lock. Remove tmp_ended_connections_buffer_* from ConnectionCache; clean_ended_connections takes &self and returns freshly-owned vecs, eliminating the &mut self requirement on Device. Lift driver_entry out of its cfg(not(mock)) inner module so the full-driver simulation can invoke the real DriverEntry over the mocked WDK. get_device now returns &'static Device. Add DEVICE_TEST_LOCK to serialize tests that touch the global device slot. Add Device::new_with_blocking_reads to choose blocking/non-blocking event queues at construction. Fix port extraction in packet_util: resolve IPv4 option headers (IHL > 5) and walk IPv6 extension header chains so packet-layer keys match the ALE-layer flow keys. Add simulation fuzz target: drives the full load → callout → blocking read → verdict → unload lifecycle under real OS threads, verifying the packet cache drains on shutdown. Supports both ASAN and ThreadSanitizer.
A Rust daemon (`zf-sim`) loads the real driver over `mock_wdk`, exposes it on a Windows named pipe, and bridges the pipe to the driver's user-space channel. The real Go agent connects exactly as it would to the kernel device, only the transport differs. Key pieces: - `sim/`: new Cargo workspace (daemon + overlapped pipe server + fake network event producer) - `kext_interface/pipe_connection.go`: `OpenPipe` opens the named pipe with synchronous I/O, returning a `*KextFile` the existing `RecvInfo`/verdict path can use unchanged - `kext_interface/kext_file.go`: add `synchronous` mode so `ReadFile`/`WriteFile` pass a nil OVERLAPPED on pipe handles - `kext_tester/main.go`: `ZF_SIM_PIPE` env var routes into `runSimulation`; verdict logic refactored into `handleInfo` shared with the production path - `Justfile`: `sim-build`, `mock-run`, `sim-run` recipes
Replace the three-hardcoded-flow producer with a full concurrent simulation harness. Workers drive semi-realistic ALE + packet-layer flows (DNS, TLS, HTTP, ICMP, reauth, port-reuse) against the real callout entry points; a reinject thread feeds driver-injected packets back through the packet-layer callouts, mirroring kernel behaviour. Everything is reproducible from a printed seed (ZF_SIM_SEED). Supporting changes: - fuzz_api: add TupleSpecV4/V6 and *_tuple callout entry points so the producer is not limited to the fixed pool; add drain_injected, injected_len, verdict_for_key_v4/v6, filter_engine_snapshot - mock_wdk: record injected packets instead of no-op; expose drain / len and filter-engine registration snapshot - sim: add SplitMix64 RNG (rng.rs) and scenario library (scenarios.rs) - fuzz/targets: drain injected queue to prevent unbounded growth
1. **sim**: converted from a finite, config-driven run to an infinite loop with a fixed seed; removed teardown/watchdog code 2. **fuzz_api.rs**: removed legacy packet-level fuzz helpers (get_ports, redirect, key extraction, etc.) 3. **go.work**: new workspace file to build kext_tester against local kext_interface 4. **kext_tester/main.go**: log ConnectionEndV4/V6 events; panic on unexpected types 5. **Justfile**: update mock-run comment to reflect the forever-run semantics ``` refactor(sim): run forever with fixed seed Convert the simulation harness from a finite, config-driven run to an infinite loop. The seed is now hardcoded (SEED constant) so any failure reproduces automatically on the next run; Ctrl+C is the only intended stop condition. Remove the watchdog thread, clean-teardown phase, and all ZF_SIM_* env-var config. Remove the legacy packet-level fuzz helpers from fuzz_api.rs (get_ports, redirect_outbound/inbound, key_from_bytes, owned_nbl, fuzz_device_write) — these are superseded by the callout harness. Add go.work to build kext_tester against the local kext_interface, and log ConnectionEnd events (panic on unexpected types) to help diagnose the running simulation. ```
The agent now loads and starts the mock driver itself via `MockKextService` (mirroring how production loads the kernel driver), so `just sim-run` runs the whole simulation with a single command. A new scenario engine (`scenario.rs`) replaces the previous random-only traffic generator with structured, reproducible test scenarios selectable via `ZF_SIM_SCENARIO`.
Add a `dns` scenario op that performs a real DNS lookup at replay time and expands into the callout sequence (ALE connect carrying the wire query, query datagram out, response in, endpoint close, resource release) plus an optional follow-on TCP session. The lookup uses the OS-configured resolver by default; a `resolver` field overrides it. Add `--loop` / `ZF_SIM_LOOP` / `sim-scenario-loop` (Justfile recipe) to replay a scenario file indefinitely, resetting transient driver state between passes with a new `reset_device_state` fuzz- API function so each iteration sees fresh connection tables and the injected-packet log does not grow without bound. Generalise `dns_query_for` / `dns_response_for` in `scenarios.rs` to accept any domain and IP, add `dns_answer_ip` for parsing live responses, and add a new `sim/src/dns.rs` module that owns the OS-resolver detection and raw-socket query logic.
# Conflicts: # .gitignore # driver/Cargo.toml # driver/src/device.rs # driver/src/entry.rs # driver/src/id_cache.rs # driver/src/lib.rs # driver/src/packet_callouts.rs # driver/src/packet_util.rs # kext_interface/go_command_test.bin # kext_interface/kext_file.go # protocol/rust_info_test.bin
Add docs/LLMs/, a set of auto-generated API references that list every type, signature and doc comment with bodies stripped, so an assistant can orient in the codebase without reading the whole source tree. One file per area: driver, protocol, wdk, mock_wdk, tools (sim + release) and Go user space. Add the two generators that produce them: - tools/rustdoc-gen — parses Rust sources with syn and emits one section per file (module docs, consts, types with their inherent impls, trait impl headers, free functions, extern blocks). - tools/godoc-gen — walks the Go modules and emits one section per package; library packages show exported symbols only, package main shows all declarations. Both skip test code, generated files and build scripts. Wire them up as `just gen-llm-docs`, register godoc-gen in go.work, and point CLAUDE.md at the new references so they are read before exploring the source. docs/LLMs/README.md documents file sizes, token estimates and which file to load for which task.
The fake-driver daemon and the Go agent were Windows-only: the daemon
exposed the driver on a named pipe and the agent had no other transport.
Add a Unix domain socket path on both sides so the whole end-to-end
simulation runs on Unix, and wire it into CI.
- sim: split pipe.rs into pipe/{windows,unix}.rs behind one PipeServer
API; pipe::endpoint() maps the logical ZF_SIM_PIPE name to the OS
address, and main.rs now reads that variable instead of hardcoding
\\.\pipe\ZenithFence.
- Stream sockets have no message boundaries, but the driver needs one
command per write, so commands are length-prefixed (u32 LE) on that
transport. Events stay an unframed byte stream, as from the device.
SOCK_SEQPACKET would truncate against the agent's 1 KiB read buffer.
- kext_interface: add SimConn (io.ReadWriteCloser) with per-OS
openSimConn/SimEndpoint; the Unix framedConn mirrors the framing and
keeps Read buffer-filling semantics. mock_service.go is no longer
Windows-only. Move InterfaceVersion out of the Windows-only kext.go
into version.go so the agent links off Windows.
- kext_tester: split main.go into main_windows.go (kernel service path)
and main_unix.go (simulation only); the shared verdict policy moves to
sim.go and takes io.ReadWriter.
- sim/dns.rs: read the default resolver from /etc/resolv.conf on Unix;
scenario.rs installs a SIGINT handler that flags a watcher thread,
since reporting isn't async-signal-safe.
- Justfile: [windows]/[unix] variants of the sim recipes; CI gains a
sim-linux job running sim-test plus a mixed_v4v6_parallel replay,
which has no `dns` op and so stays offline and deterministic.
The daemon used to load the driver once, serve a single agent and let the producer run forever. Running the real agent against it meant restarting the daemon for every agent run, and the producer failed the run whenever the agent's own policy disagreed with kext_tester's fixed expectations. In random-traffic mode the daemon now loops: wait for an agent, run the producer while it is connected, and on disconnect shut down the driver, join the pumps, unload and load a fresh one for the next agent -- the way the agent's Start/Stop load and unload the kernel driver. The producer takes a stop flag so its workers, monitor and reinjector wind down instead of running forever, and prints the session summary on the way out. Scenario replay still serves one agent and exits. --any-verdict / ZF_SIM_ANY_VERDICT selects VerdictPolicy::Any: the agent may hand out whatever verdict it likes and the producer checks only that the driver enforces the one it got (permit for accept, block for block, block+absorb for drop/undeterminable/failed, anything for temporary verdicts). A verdict that never arrives is counted, not failed, and the flow closes as an application giving up would. Under the default Expected policy nothing changes. On the Go side the simulation pipe is now opened overlapped like the device, so a pending Read no longer blocks writes from other goroutines and Close can cancel it; the synchronous special case is gone. Broken- pipe errors map to ERROR_HANDLE_EOF so a daemon going away looks like the device's queue running down, and ReadVersion answers from InterfaceVersion because the pipe has no IOCTL channel. Both sides accept a full \\.\pipe\ path as the endpoint name, so an agent built with a custom pipe path can point the daemon at it. Adds the `just mock-serve` recipe that starts the daemon in this mode, and regenerates the LLM reference docs.
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.
No description provided.