On Mon, May 9, 2022 at 6:50 PM Alexander Potapenko <glider@xxxxxxxxxx> wrote: > > > The callchain is: > > > > asm_sysvec_apic_timer_interrupt <- ASM entry in gate > > sysvec_apic_timer_interrupt(regs) <- noinstr C entry point > > irqentry_enter(regs) <- unpoisons @reg > > __sysvec_apic_timer_interrupt(regs) <- the actual handler > > set_irq_regs(regs) <- stores regs > > local_apic_timer_interrupt() > > ... > > tick_handler() <- One of the 4 variants > > regs = get_irq_regs(); <- retrieves regs > > update_process_times(user_tick = user_mode(regs)) > > account_process_tick(user_tick) > > irqtime_account_process_tick(user_tick) > > line 382: } else if { user_tick } <- KMSAN complains > > > > I'm even more confused now. > > Ok, I think I know what's going on. > > Indeed, calling kmsan_unpoison_memory() in irqentry_enter() was > supposed to be enough, but we have code in kmsan_unpoison_memory() (as > well as other runtime functions) that checks for kmsan_in_runtime() > and bails out to prevent potential recursion if KMSAN code starts > calling itself. > > kmsan_in_runtime() is implemented as follows: > > ============================================== > static __always_inline bool kmsan_in_runtime(void) > { > if ((hardirq_count() >> HARDIRQ_SHIFT) > 1) > return true; > return kmsan_get_context()->kmsan_in_runtime; > } > ============================================== > (see the code here: > https://lore.kernel.org/lkml/20220426164315.625149-13-glider@xxxxxxxxxx/#Z31mm:kmsan:kmsan.h) > > If we are running in the task context (in_task()==true), > kmsan_get_context() returns a per-task `struct *kmsan_ctx`. > If `in_task()==false` and `hardirq_count()>>HARDIRQ_SHIFT==1`, it > returns a per-CPU one. > Otherwise kmsan_in_runtime() is considered true to avoid dealing with > nested interrupts. > > So in the case when `hardirq_count()>>HARDIRQ_SHIFT` is greater than > 1, kmsan_in_runtime() becomes a no-op, which leads to false positives. Should be "kmsan_unpoison_memory() becomes a no-op..."