Kernel Usercopy Failure Injection: Test Partial Setup Before a Bad Userspace Access
Inject a failure at each relevant user-memory access and check the interface’s state-transition contract, not only whether it returns an error. A failed input copy must not leave unvalidated data published. A failed output copy needs a deliberate policy for work already committed, because returning an error cannot automatically undo a device operation.
This article uses upstream Linux v6.12, its fail_usercopy capability and the generic user-access wrappers. Architecture-specific helpers and vendor changes require separate inspection. All interface names and scenarios below are hypothetical. No kernel fault injection, invalid-pointer workload or hardware transaction was performed.
Inventory the copy boundaries of one interface
Choose a single ioctl or read/write operation and mark every transfer between userspace and kernel memory. For each, record direction, helper, expected size, validation immediately afterward, resources already acquired and whether any irreversible effect has occurred.
A hypothetical CONFIGURE ioctl might copy a header, allocate temporary storage, copy a payload, validate it, commit a configuration and copy a result back. Those stages create several independent failure boundaries. Testing only an invalid header pointer leaves payload cleanup and post-commit result delivery unexamined.
Keep ABI layout review separate from this experiment. The upstream ioctl guidance is relevant to stable interface design, but correct structure widths and compatibility handling do not establish rollback behavior when an actual user access fails.
Enable the right capability and inspect the real helper
The v6.12 option is CONFIG_FAULT_INJECTION_USERCOPY, under the generic fault-injection facility, as shown in the pinned configuration. With the debugfs control facility enabled, the implementation creates the fail_usercopy controls and passes eligible checks to the generic injection framework.
The capability’s existence does not mean every possible user-memory access is instrumented identically. Inspect the compiled architecture path and selected wrapper. In the v6.12 generic header, several copy wrappers call should_fail_usercopy, while the shown __copy_from_user_inatomic path does not contain that same hook. A direct raw helper or architecture-specific access needs its own coverage check.
Keep the device idle and recoverable, restrict the test task and fault window, and record all changed controls. The fault-injection documentation describes task and stack filtering and Nth-fault control. A task’s fail-nth count spans enabled capabilities; zero probability in another capability does not by itself exclude that capability from the Nth mechanism. Verify which hook actually failed.
Respect the usercopy return contract
For copy_to_user and copy_from_user style operations, the returned value is the number of bytes not copied, with zero indicating full completion. Do not test only for a negative return as though these helpers returned a conventional negative errno. The enclosing driver interface must translate failure according to its own documented contract.
Other access helpers have different conventions, so the ledger must name the exact helper. Never mechanically reuse the copy-length test for every function containing “user” in its name. Also distinguish the kernel helper result from a syscall’s return value and userspace errno.
The generic header documents destination zero-padding for ordinary copy_from_user on a short copy, unlike its double-underscore variants. Zero-padding does not make a failed input valid. A partially filled structure must not pass into the normal operation merely because the remainder contains zeros that look plausible.

Use a stage-specific failure matrix
| Injected boundary | State already acquired | Acceptance question |
|---|---|---|
| Header input copy | No operation-owned resources | Does the request stop before validation or state change uses incomplete input? |
| Payload input copy | Private temporary buffer | Is temporary state released once, with no partial publication? |
| Result output before commit, if the API has that design | Prepared but uncommitted state | Does the defined abort path retain a valid original configuration? |
| Result output after commit | Externally visible operation already completed | Does the API define observable committed state and a safe retry/query policy? |
The alternatives for result output are interface design choices, not a recommendation to move every copy ahead of a commit. Doing so can expose a successful-looking result before a later commit fails. Review the operation’s complete externally visible semantics instead of rearranging calls solely to simplify one error branch.
Run a narrow, attributable campaign
- Verify a successful operation with injection disabled and record its externally visible state.
- Prepare the test process, buffers and descriptors before arming a failure. Map the intended copy site to a source checkpoint.
- Inject one eligible failure during the bounded operation and record the actual failed hook, helper result and syscall result.
- Disable the injection before collecting unrelated results or performing the normal recovery operation.
- Inspect private allocations, references, registrations, pending work and device state. Repeat a valid request to establish recovery.
Preallocate the diagnostic record path where practical. A harness that performs unrelated usercopy operations while armed can fail its own setup or reporting rather than the driver. If a different hook consumes the fault, classify that run as off-target and refine the window; do not count the desired stage as covered.
Distinguish injected rejection from a genuine partial copy
Several v6.12 generic injection checks reject before the raw copy and return the requested length as uncopied. That is valuable for testing the driver’s failure branch, but it does not establish behavior after some bytes have already crossed the boundary.
A separate controlled invalid-range or protection-boundary test can exercise genuine user-access faults on an isolated test process, with the exact architecture’s behavior checked. Do not promise a precise copied prefix without observing it. Test that any partial result is handled safely under the helper and interface contracts, while keeping production processes and valuable data out of the experiment.
For read/write interfaces, a positive short count can be a valid API outcome rather than a required all-or-nothing error. Define which bytes or records are consumed and whether retry resumes or repeats work. For an ioctl that has already committed a hardware action, a failed result copy may require a separate state query or request-identity policy to prevent accidental duplicate execution.
Close with state evidence, not errno alone
Suppose the hypothetical CONFIGURE ioctl rejects a payload copy after allocating a temporary table. A valid repair releases that table and preserves the old active configuration. If result delivery fails after a successful commit, the review instead needs an explicit way for the caller to discover the new state and retry safely. Those are different acceptance outcomes.
Deliver the copy-site inventory, injected and genuine-fault cases, resource/state observations and exact untested helper paths. A passing campaign demonstrates the specified interface behavior at covered user-access boundaries. It does not prove that all pointer handling, compatibility layout or concurrent application mutation is correct.