Skip to content

Commit 1cecb5d

Browse files
melvergregkh
authored andcommitted
kcsan, seqlock: Support seqcount_latch_t
[ Upstream commit 5c1806c ] While fuzzing an arm64 kernel, Alexander Potapenko reported: | BUG: KCSAN: data-race in ktime_get_mono_fast_ns / timekeeping_update | | write to 0xffffffc082e74248 of 56 bytes by interrupt on cpu 0: | update_fast_timekeeper kernel/time/timekeeping.c:430 [inline] | timekeeping_update+0x1d8/0x2d8 kernel/time/timekeeping.c:768 | timekeeping_advance+0x9e8/0xb78 kernel/time/timekeeping.c:2344 | update_wall_time+0x18/0x38 kernel/time/timekeeping.c:2360 | [...] | | read to 0xffffffc082e74258 of 8 bytes by task 5260 on cpu 1: | __ktime_get_fast_ns kernel/time/timekeeping.c:372 [inline] | ktime_get_mono_fast_ns+0x88/0x174 kernel/time/timekeeping.c:489 | init_srcu_struct_fields+0x40c/0x530 kernel/rcu/srcutree.c:263 | init_srcu_struct+0x14/0x20 kernel/rcu/srcutree.c:311 | [...] | | value changed: 0x000002f875d33266 -> 0x000002f877416866 | | Reported by Kernel Concurrency Sanitizer on: | CPU: 1 UID: 0 PID: 5260 Comm: syz.2.7483 Not tainted 6.12.0-rc3-dirty torvalds#78 This is a false positive data race between a seqcount latch writer and a reader accessing stale data. Since its introduction, KCSAN has never understood the seqcount_latch interface (due to being unannotated). Unlike the regular seqlock interface, the seqcount_latch interface for latch writers never has had a well-defined critical section, making it difficult to teach tooling where the critical section starts and ends. Introduce an instrumentable (non-raw) seqcount_latch interface, with which we can clearly denote writer critical sections. This both helps readability and tooling like KCSAN to understand when the writer is done updating all latch copies. Fixes: 88ecd15 ("seqlock, kcsan: Add annotations for KCSAN") Reported-by: Alexander Potapenko <[email protected]> Co-developed-by: "Peter Zijlstra (Intel)" <[email protected]> Signed-off-by: "Peter Zijlstra (Intel)" <[email protected]> Signed-off-by: Marco Elver <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sasha Levin <[email protected]>
1 parent e93d035 commit 1cecb5d

File tree

2 files changed

+72
-16
lines changed

2 files changed

+72
-16
lines changed

Documentation/locking/seqlock.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ Use seqcount_latch_t when the write side sections cannot be protected
153153
from interruption by readers. This is typically the case when the read
154154
side can be invoked from NMI handlers.
155155

156-
Check `raw_write_seqcount_latch()` for more information.
156+
Check `write_seqcount_latch()` for more information.
157157

158158

159159
.. _seqlock_t:

include/linux/seqlock.h

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,23 @@ static __always_inline unsigned raw_read_seqcount_latch(const seqcount_latch_t *
682682
return READ_ONCE(s->seqcount.sequence);
683683
}
684684

685+
/**
686+
* read_seqcount_latch() - pick even/odd latch data copy
687+
* @s: Pointer to seqcount_latch_t
688+
*
689+
* See write_seqcount_latch() for details and a full reader/writer usage
690+
* example.
691+
*
692+
* Return: sequence counter raw value. Use the lowest bit as an index for
693+
* picking which data copy to read. The full counter must then be checked
694+
* with read_seqcount_latch_retry().
695+
*/
696+
static __always_inline unsigned read_seqcount_latch(const seqcount_latch_t *s)
697+
{
698+
kcsan_atomic_next(KCSAN_SEQLOCK_REGION_MAX);
699+
return raw_read_seqcount_latch(s);
700+
}
701+
685702
/**
686703
* raw_read_seqcount_latch_retry() - end a seqcount_latch_t read section
687704
* @s: Pointer to seqcount_latch_t
@@ -696,9 +713,34 @@ raw_read_seqcount_latch_retry(const seqcount_latch_t *s, unsigned start)
696713
return unlikely(READ_ONCE(s->seqcount.sequence) != start);
697714
}
698715

716+
/**
717+
* read_seqcount_latch_retry() - end a seqcount_latch_t read section
718+
* @s: Pointer to seqcount_latch_t
719+
* @start: count, from read_seqcount_latch()
720+
*
721+
* Return: true if a read section retry is required, else false
722+
*/
723+
static __always_inline int
724+
read_seqcount_latch_retry(const seqcount_latch_t *s, unsigned start)
725+
{
726+
kcsan_atomic_next(0);
727+
return raw_read_seqcount_latch_retry(s, start);
728+
}
729+
699730
/**
700731
* raw_write_seqcount_latch() - redirect latch readers to even/odd copy
701732
* @s: Pointer to seqcount_latch_t
733+
*/
734+
static __always_inline void raw_write_seqcount_latch(seqcount_latch_t *s)
735+
{
736+
smp_wmb(); /* prior stores before incrementing "sequence" */
737+
s->seqcount.sequence++;
738+
smp_wmb(); /* increment "sequence" before following stores */
739+
}
740+
741+
/**
742+
* write_seqcount_latch_begin() - redirect latch readers to odd copy
743+
* @s: Pointer to seqcount_latch_t
702744
*
703745
* The latch technique is a multiversion concurrency control method that allows
704746
* queries during non-atomic modifications. If you can guarantee queries never
@@ -726,17 +768,11 @@ raw_read_seqcount_latch_retry(const seqcount_latch_t *s, unsigned start)
726768
*
727769
* void latch_modify(struct latch_struct *latch, ...)
728770
* {
729-
* smp_wmb(); // Ensure that the last data[1] update is visible
730-
* latch->seq.sequence++;
731-
* smp_wmb(); // Ensure that the seqcount update is visible
732-
*
771+
* write_seqcount_latch_begin(&latch->seq);
733772
* modify(latch->data[0], ...);
734-
*
735-
* smp_wmb(); // Ensure that the data[0] update is visible
736-
* latch->seq.sequence++;
737-
* smp_wmb(); // Ensure that the seqcount update is visible
738-
*
773+
* write_seqcount_latch(&latch->seq);
739774
* modify(latch->data[1], ...);
775+
* write_seqcount_latch_end(&latch->seq);
740776
* }
741777
*
742778
* The query will have a form like::
@@ -747,13 +783,13 @@ raw_read_seqcount_latch_retry(const seqcount_latch_t *s, unsigned start)
747783
* unsigned seq, idx;
748784
*
749785
* do {
750-
* seq = raw_read_seqcount_latch(&latch->seq);
786+
* seq = read_seqcount_latch(&latch->seq);
751787
*
752788
* idx = seq & 0x01;
753789
* entry = data_query(latch->data[idx], ...);
754790
*
755791
* // This includes needed smp_rmb()
756-
* } while (raw_read_seqcount_latch_retry(&latch->seq, seq));
792+
* } while (read_seqcount_latch_retry(&latch->seq, seq));
757793
*
758794
* return entry;
759795
* }
@@ -777,11 +813,31 @@ raw_read_seqcount_latch_retry(const seqcount_latch_t *s, unsigned start)
777813
* When data is a dynamic data structure; one should use regular RCU
778814
* patterns to manage the lifetimes of the objects within.
779815
*/
780-
static inline void raw_write_seqcount_latch(seqcount_latch_t *s)
816+
static __always_inline void write_seqcount_latch_begin(seqcount_latch_t *s)
781817
{
782-
smp_wmb(); /* prior stores before incrementing "sequence" */
783-
s->seqcount.sequence++;
784-
smp_wmb(); /* increment "sequence" before following stores */
818+
kcsan_nestable_atomic_begin();
819+
raw_write_seqcount_latch(s);
820+
}
821+
822+
/**
823+
* write_seqcount_latch() - redirect latch readers to even copy
824+
* @s: Pointer to seqcount_latch_t
825+
*/
826+
static __always_inline void write_seqcount_latch(seqcount_latch_t *s)
827+
{
828+
raw_write_seqcount_latch(s);
829+
}
830+
831+
/**
832+
* write_seqcount_latch_end() - end a seqcount_latch_t write section
833+
* @s: Pointer to seqcount_latch_t
834+
*
835+
* Marks the end of a seqcount_latch_t writer section, after all copies of the
836+
* latch-protected data have been updated.
837+
*/
838+
static __always_inline void write_seqcount_latch_end(seqcount_latch_t *s)
839+
{
840+
kcsan_nestable_atomic_end();
785841
}
786842

787843
/*

0 commit comments

Comments
 (0)