KCSAN for Embedded Drivers: Turn a Sampled Race Report into an Ownership Hypothesis
Treat a KCSAN report as evidence about conflicting accesses, then write down the ownership or ordering rule those accesses were supposed to obey. The first useful question is not which line should receive an annotation. It is whether the reader may overlap the writer, what state each expects, and which synchronization makes those expectations valid.
The reference is upstream Linux v6.12. KCSAN uses compiler instrumentation and watchpoint-based sampling. Its version-pinned configuration requires architecture and compiler support, a debug kernel, and excludes KASAN in this release. The v6.12 documentation specifies GCC or Clang version 11 or newer, but the final configuration’s compiler capability checks still matter. No diagnostic kernel, concurrency workload or sampling-rate benchmark was executed here.
Make a two-access evidence sheet
Keep the complete first report and identify both accesses separately. Record read, write or read-modify-write; address and byte range; function and source line; task or interrupt context; CPU; object generation; and any locks visible in the relevant call path. A pair of familiar function names is not enough to establish that both operate on the same logical field.
Map the address to a member of the actual object layout, using matching binaries and source. Overlapping ranges can matter even when the starting addresses differ. Check unions, packed fields and bulk updates rather than assuming an access size equals the size of the named source variable. If allocation reuse is possible, establish that both observations concern the same lifetime.
A normal report can include stacks for both conflicting threads and an observed value change. A report identifying an unknown origin supplies less information: the watched value changed, but KCSAN could not identify the other access. Missing instrumentation and device-originated writes are possibilities described by the source. Do not invent a second software stack or conclude that an unknown origin proves a DMA fault.

Write the contract that would make the accesses legal
For each access ask who owns the field, when ownership transfers, and whether the operation requires one value or a consistent group of values. A status counter used only for approximate diagnostics has a different contract from a length paired with a buffer pointer. A reference count has a different contract again. Name the intended invariant before selecting a primitive.
Consider a hypothetical driver whose receive path reads active_length while a configuration path changes both that length and the buffer it describes. If the reader must see a matching pair, making only the length an atomic access does not establish a coherent snapshot. The hypothesis should predict a specific violation: a reader can combine the previous buffer with the next length.
The Linux memory-model guidance on marking shared accesses distinguishes annotations and access semantics. READ_ONCE and WRITE_ONCE are useful tools with defined purposes, but they are not universal ownership transfer, lifetime protection or ordering for surrounding data. Select locking, publication rules or another reviewed protocol because it establishes the needed invariant.
Separate a real fix from a suppressed report
A data_race annotation tells the detector that the annotated race is intentional; it is not a repair for an unsafe shared-state update. Similarly, excluding a function or compilation unit changes observation coverage. If the value is intentionally approximate, document which values and inconsistencies consumers tolerate, why object lifetime remains safe, and why no control decision relies on a coherent snapshot.
Check both sides of the proposed change. Protecting the writer with a lock does not order a reader that never takes that lock. Changing a flag does not protect a pointer after the pointed-to allocation is freed. A report may disappear after annotation while the product still has a higher-level concurrency bug that contains no reportable plain-access race.
Keep configuration-driven suppression separate from source changes. v6.12 offers value-change-only, permissive and strict modes, as well as report filters. Record the effective settings in each run. A reduction in report count after enabling a filter is a measurement of filtering, not evidence that shared state became correct.
Build a targeted concurrency experiment
- Choose the smallest externally valid operations that reach the reported read and write. Specify object creation and teardown so the same generation can be followed.
- Run each path alone first and confirm its checkpoints. This establishes that a later quiet concurrency test did not simply miss one participant.
- Run them concurrently with bounded repetition and a recoverable target. Capture operation counts and input sequence, not only elapsed uptime.
- Vary one relevant scheduling condition, such as workqueue overlap or completion timing, while preserving the ownership question.
- Apply the proposed synchronization change and repeat the same overlap cases, then test cancellation, failure and cleanup boundaries.
When adding gates for reproducibility, do not block inside an atomic context or while holding a lock required by the other participant. An instrumentation gate can remove the overlap or create a deadlock unrelated to the original defect. Document its placement and repeat with the gates removed after the controlled case is understood.
Verify detector activity and understand negative results
Read the available KCSAN debugfs statistics and save the final image configuration, sampling controls and report filters. Confirm that the object and both relevant translation units are instrumented. A test that never sets useful watchpoints or excludes the suspected caller is a coverage failure even if the board runs normally.
More frequent watchpoints and longer observation delays can increase opportunities to observe a race while also perturbing timing. Do not prescribe a universal delay or equate an instrumented response time with production performance. Use a separate normal-build run to qualify deadlines and throughput after the source-level contract is repaired.
A positive report is a reason to investigate a conflicting-access path. A negative sampled run is bounded by the paths, accesses and observation windows exercised. Also remember that race freedom is narrower than full algorithmic correctness: an all-atomic protocol can still publish an invalid state or lose an update through a flawed multi-step design.
Deliver an ownership hypothesis that can be challenged
The final review packet should pair the raw report with the two-access sheet, intended invariant, source-level change and reproduction matrix. Include unknown-origin cases as unresolved until their coverage or writer is explained. State which checks were actually executed and which remain proposals.
For the hypothetical length/buffer example, acceptance requires a consistent snapshot and valid lifetime across concurrent reconfiguration, including failure and teardown. The absence of a repeated KCSAN report helps support that result only when both operations still execute under comparable coverage. The durable artifact is the synchronization argument, with evidence that exercises it.