KCSAN: data-race in ktime_get_coarse_ts64 / timekeeping_update_from_shadow (4)

Status: moderation: reported on 2026/11/15 04:51
Subsystems: kernel
Labels: race:benign prio:low
[Documentation on labels]
Reported-by: syzbot+708f1141bf273f4c09e6@syzkaller.appspotmail.com
First crash: 1h29m, last: 1h29m
✨ AI-generated root cause summary:

In ktime_get_coarse_ts64(), plain reads of timekeeper fields (tk_xtime_coarse() reading tk->xtime_sec and tk->coarse_nsec, and tk->wall_to_monotonic) occur inside a sequence counter read section (read_seqcount_begin(&tk_core.seq) ... read_seqcount_retry(&tk_core.seq, seq)). Concurrently, timer interrupt processing on another CPU in timekeeping_update_from_shadow() copies updated timekeeper data into tk_core.timekeeper via memcpy(&tkd->timekeeper, tk, sizeof(*tk)) inside a write_seqcount_begin() ... write_seqcount_end() critical section. KCSAN attempts to suppress reports for seqlock readers by marking the subsequent memory accesses as atomic via kcsan_atomic_next(KCSAN_SEQLOCK_REGION_MAX). When the calling thread is preempted or executes nested code that clears or exhausts current->kcsan_ctx.atomic_next before tk->coarse_nsec is read, KCSAN observes an unannotated plain read racing with the concurrent memcpy() and flags a data race.

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit aacb77975041:

Technical Root Cause Analysis

Overview and Files Involved

  • Source File: kernel/time/timekeeping.c
  • Functions: ktime_get_coarse_ts64(), tk_xtime_coarse(), and timekeeping_update_from_shadow()
  • Structs and Fields:
    • struct tk_data tk_core (timekeeper_data[TIMEKEEPER_CORE]), containing seqcount_raw_spinlock_t seq and struct timekeeper timekeeper.
    • struct timekeeper: specifically tk->coarse_nsec, tk->xtime_sec, and tk->wall_to_monotonic.

Detailed Mechanism and Invariant Violation

  1. Reader Side (ktime_get_coarse_ts64): In kernel/time/timekeeping.c:

    void ktime_get_coarse_ts64(struct timespec64 *ts)
    {
        struct timekeeper *tk = &tk_core.timekeeper;
        struct timespec64 now, mono;
        unsigned int seq;
    
        do {
            seq = read_seqcount_begin(&tk_core.seq);
    
            now = tk_xtime_coarse(tk);
            mono = tk->wall_to_monotonic;
        } while (read_seqcount_retry(&tk_core.seq, seq));
    
        set_normalized_timespec64(ts, now.tv_sec + mono.tv_sec,
                                  now.tv_nsec + mono.tv_nsec);
    }
    

    tk_xtime_coarse(tk) performs plain (unannotated) reads of tk->xtime_sec and tk->coarse_nsec:

    static inline struct timespec64 tk_xtime_coarse(const struct timekeeper *tk)
    {
        struct timespec64 ts;
    
        ts.tv_sec = tk->xtime_sec;
        ts.tv_nsec = tk->coarse_nsec;
        return ts;
    }
    

    These reads are protected against writer inconsistency via the sequence counter tk_core.seq.

  2. Writer Side (timekeeping_update_from_shadow): During timer interrupt processing (update_wall_time() -> __timekeeping_advance() -> timekeeping_update_from_shadow()), CPU 0 acquires write_seqcount_begin(&tkd->seq) and writes updated timekeeper state to tk_core.timekeeper using memcpy():

    WRITE_ONCE(tkd->timekeeper.xtime_sec, tk->xtime_sec);
    WRITE_ONCE(tkd->timekeeper.ktime_sec, tk->ktime_sec);
    memcpy(&tkd->timekeeper, tk, sizeof(*tk));
    write_seqcount_end(&tkd->seq);
    
  3. C-Level Concurrency and KCSAN Interaction:

    • Under the C memory model, concurrent plain reads (tk->coarse_nsec) and plain writes (memcpy) constitute a data race. In the kernel, sequence locks and sequence counters allow lockless reads racing with writes, discarding torn or partially updated reads via read_seqcount_retry().
    • To accommodate raw seqcount_t readers without false-positive data race reports on every reader access, KCSAN's read_seqcount_begin() invokes kcsan_atomic_next(KCSAN_SEQLOCK_REGION_MAX) (in include/linux/seqlock.h), which sets get_ctx()->atomic_next = 1000. This temporarily marks the subsequent 1000 memory accesses by that context as atomic so should_watch() will not install watchpoints.
    • However, read_seqcount_begin() does not disable preemption, and the kernel in the report is running with lazy preemption (PREEMPT(lazy)). The reader is running a BPF test program pinned via bpf_prog_run_pin_on_cpu(), which uses migrate_disable() but keeps preemption enabled.
    • When task 7067 is preempted or executes nested kernel code between read_seqcount_begin(&tk_core.seq) and tk_xtime_coarse(), current->kcsan_ctx.atomic_next can either be decremented to 0 by >1000 intervening instrumented operations or cleared entirely to 0 by any nested read_seqcount_retry() (which calls kcsan_atomic_next(0)).
    • Once atomic_next == 0, should_watch() in kernel/kcsan/core.c treats ts.tv_nsec = tk->coarse_nsec as a normal, non-atomic plain read and sets a KCSAN watchpoint.
    • When the timer interrupt on CPU 0 invokes timekeeping_update_from_shadow(), check_access() calls find_watchpoint() on &tkd->timekeeper and hits task 7067's watchpoint. Because a non-atomic read racing with a write is flagged by KCSAN regardless of whether the writer called kcsan_nestable_atomic_begin() via write_seqcount_begin(), KCSAN reports a data-race between ktime_get_coarse_ts64() and timekeeping_update_from_shadow().
▶ ▼ ✨ AI Jobs (3)
ID Workflow Result Correct Bug Created Started Finished Revision
1a7821c8 assessment-security DenialOfService: ❌ Exploitable: ❌ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ❌ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ✅ UserNamespace: ✅ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ KCSAN: data-race in ktime_get_coarse_ts64 / timekeeping_update_from_shadow (4) 2026/11/15 03:46 2026/11/15 03:46 2026/11/15 04:43 819ebe34
79808aa2 assessment-kcsan 🏃 KCSAN: data-race in ktime_get_coarse_ts64 / timekeeping_update_from_shadow (4) 2026/11/15 03:46 2026/11/15 03:46 819ebe34
407aefbc assessment-kcsan Benign: ✅ ❓ KCSAN: data-race in ktime_get_coarse_ts64 / timekeeping_update_from_shadow (4) 2026/11/15 03:46 2026/11/15 03:46 2026/11/15 04:23 819ebe34
▶ ▼ Similar bugs (3)
Kernel Title Rank 🛈 Repro Cause bisect Fix bisect Count Last Reported Patched Status
upstream KCSAN: data-race in ktime_get_coarse_ts64 / timekeeping_update_from_shadow prio:low kernel 6 1 207d 207d 0/29 auto-obsoleted due to no activity on 2026/06/16 22:24
upstream KCSAN: data-race in ktime_get_coarse_ts64 / timekeeping_update_from_shadow (2) race:benign prio:high kernel 6 1 134d 134d 0/29 auto-obsoleted due to no activity on 2026/08/28 06:25
upstream KCSAN: data-race in ktime_get_coarse_ts64 / timekeeping_update_from_shadow (3) race:benign prio:high kernel 6 1 62d 62d 0/29 auto-obsoleted due to no activity on 2026/11/09 00:48

Sample crash report:
==================================================================
BUG: KCSAN: data-race in ktime_get_coarse_ts64 / timekeeping_update_from_shadow

write to 0xffffffff8963c948 of 320 bytes by interrupt on cpu 0:
 timekeeping_update_from_shadow+0x46e/0x4b0 kernel/time/timekeeping.c:869
 __timekeeping_advance+0xb36/0xcf0 kernel/time/timekeeping.c:2666
 timekeeping_advance kernel/time/timekeeping.c:2674 [inline]
 update_wall_time+0x21/0x50 kernel/time/timekeeping.c:2684
 tick_do_update_jiffies64+0x169/0x1c0 kernel/time/tick-sched.c:149
 tick_sched_do_timer kernel/time/tick-sched.c:253 [inline]
 tick_nohz_handler+0x88/0x380 kernel/time/tick-sched.c:310
 __run_hrtimer kernel/time/hrtimer.c:2079 [inline]
 __hrtimer_run_queues+0x1f8/0x510 kernel/time/hrtimer.c:2136
 hrtimer_interrupt+0x27b/0x860 kernel/time/hrtimer.c:2255
 local_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1051 [inline]
 __sysvec_apic_timer_interrupt+0x5f/0x1c0 arch/x86/kernel/apic/apic.c:1068
 instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
 sysvec_apic_timer_interrupt+0x6f/0x80 arch/x86/kernel/apic/apic.c:1062
 asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
 native_safe_halt arch/x86/include/asm/irqflags.h:48 [inline]
 pv_native_safe_halt+0xf/0x20 arch/x86/kernel/paravirt.c:63
 arch_safe_halt arch/x86/kernel/process.c:767 [inline]
 default_idle+0x9/0x20 arch/x86/kernel/process.c:768
 default_idle_call+0x3b/0x60 kernel/sched/idle.c:122
 cpuidle_idle_call kernel/sched/idle.c:199 [inline]
 do_idle+0x183/0x2d0 kernel/sched/idle.c:355
 cpu_startup_entry+0x24/0x30 kernel/sched/idle.c:458
 rest_init+0x101/0x110 init/main.c:789
 start_kernel+0x49f/0x4d0 init/main.c:1247
 x86_64_start_reservations+0x24/0x30 arch/x86/kernel/head64.c:310
 x86_64_start_kernel+0xfc/0x100 arch/x86/kernel/head64.c:291
 common_startup_64+0x13e/0x157

read to 0xffffffff8963c9b8 of 4 bytes by task 7067 on cpu 1:
 tk_xtime_coarse kernel/time/timekeeping.c:218 [inline]
 ktime_get_coarse_ts64+0x6d/0x120 kernel/time/timekeeping.c:2819
 ktime_get_coarse include/linux/timekeeping.h:132 [inline]
 ktime_get_coarse_ns include/linux/timekeeping.h:138 [inline]
 ____bpf_ktime_get_coarse_ns kernel/bpf/helpers.c:204 [inline]
 bpf_ktime_get_coarse_ns+0x39/0xb0 kernel/bpf/helpers.c:202
 bpf_prog_ce3dfd7f9330a559+0x1e/0x29
 bpf_dispatcher_nop_func include/linux/bpf.h:1547 [inline]
 __bpf_prog_run include/linux/filter.h:813 [inline]
 bpf_prog_run include/linux/filter.h:820 [inline]
 bpf_prog_run_pin_on_cpu include/linux/filter.h:837 [inline]
 bpf_flow_dissect+0x157/0x360 net/core/flow_dissector.c:1024
 bpf_prog_test_run_flow_dissector+0x25d/0x360 net/bpf/test_run.c:1565
 bpf_prog_test_run+0x208/0x340 kernel/bpf/syscall.c:4860
 __sys_bpf+0x914/0xc50 kernel/bpf/syscall.c:6478
 __do_sys_bpf kernel/bpf/syscall.c:6582 [inline]
 __se_sys_bpf kernel/bpf/syscall.c:6579 [inline]
 __x64_sys_bpf+0x69/0x80 kernel/bpf/syscall.c:6579
 x64_sys_call+0xf26/0x2550 arch/x86/include/generated/asm/syscalls_64.h:322
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x116/0x380 arch/x86/entry/syscall_64.c:89
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

value changed: 0x2687016f -> 0x271f97ef

Reported by Kernel Concurrency Sanitizer on:
CPU: 1 UID: 0 PID: 7067 Comm: syz.2.857 Not tainted syzkaller #0 PREEMPT(lazy) 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 11/06/2026
==================================================================

Crashes (1):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/11/15 03:46 upstream aacb77975041 819ebe34 .config console log report info [disk image] [vmlinux] [kernel image] ci2-upstream-kcsan-gce KCSAN: data-race in ktime_get_coarse_ts64 / timekeeping_update_from_shadow
* Struck through repros no longer work on HEAD.