Re: [PATCH v3 28/46] kmsan: entry: handle register passing from uninstrumented code

From: Alexander Potapenko
Date: Mon May 09 2022 - 12:52:41 EST


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..."