Re: [syzbot] [kernel?] upstream test error: KMSAN: uninit-value in irqentry_exit_to_kernel_mode_preempt
From: Alexander Potapenko
Date: Tue May 12 2026 - 05:38:29 EST
On Mon, May 11, 2026 at 2:25 PM Thomas Gleixner <tglx@xxxxxxxxxx> wrote:
>
Hi Thomas,
thanks for diving into this!
> > irqentry_exit_to_kernel_mode_preempt() is now checking for both `regs`
> > and `state`.
> > Because there is a lot of non-instrumented code around, we fail to
> > initialize these variables.
> > Instead, irqentry_enter_from_kernel_mode() explicitly calls
> > kmsan_unpoison_entry_regs(regs) to take care of the registers, but not
> > the state.
> > We should probably call `kmsan_unpoison_memory(&state, sizeof(state))`
> > at the same place.
>
> Good luck with unpoisoning 'state'. 'state' is not memory to begin with.
My bad. Normally, the compiler would happily move `state` to memory if it is
address-taken, and make sure that its shadow is tracked properly.
But not in this case, because irqentry_enter_from_kernel_mode() is inlined into
a `noinstr` function.
<I omit the elaborate assembly analysis here, tipping my hat!>
> Again. 'state' is a pure register value, which is handed to
> irqentry_exit() and irqentry_exit_to_kernel_mode_preempt().
There is no notion of a 'pure register value' in C, and the compiler may make
arbitrary decisions about whether a particular value is stored on the stack or
in the registers.
Luckily, KMSAN does not have to know about that, because it works on the LLVM IR
level and can track the state of a value regardless of where it is stored.
In particular, it normally works for function return values - unless `noinstr`
kicks in.
>
> But KMSAN magically associates a memory access which it and then claims
> it belongs to a SKB which was allocated in the interrupted code.
>
> What Mark's change actually does is to make the register value 'state'
> observable in an instrumented function, while before that 'state' was
> always confined in the non instrumentable code.
Agreed.
> But as that 'state' argument of irqentry_exit_to_kernel_mode_preempt()
> is a pure register value, which could be even a constant supplied by the
> caller of irqentry_exit(), KMSAN has _ZERO_ business to fiddle with it.
I disagree with a general implication that KMSAN has zero business to fiddle
with values passed in registers. But I agree we are doing a poor job trying
to pull the shadow for `state` out of thin air.
>
> The compiler _cannot_ assume anything about the 'state' argument as
> that's handed in as value in RSI from a completely different compilation
> unit.
>
Again, this only matters because we are calling an instrumented function from
a non-instrumented one, otherwise it's perfectly fine to call between
compilation units.
> Something is wrong in KMSAN/compiler land or do you still believe that
> you just need to unpoison the non existing memory 'state'?
>
When we call an instrumented function from a non-instrumented one, the compiler
is doomed to not understand that and to be unable to track the function
parameters properly. Exactly because `noinstr` implies no instrumentation
whatsoever, the compiler may not add any hints on the caller side that would
help the callee understand what's going on - even if KMSAN is able to see this
`noinstr` function (which is not always the case).
So what we could do is to add annotations manualy on either the caller side or
the callee side.
We can apply `__no_kmsan_checks` to irqentry_exit_to_kernel_mode_preempt(),
making all its inputs initialized. This is the easiest solution, it may
introduce false negatives, but we are on a very thin ice anyway, so perhaps
doing so is better than dealing with more false positives in the interrupt code.
Another option for the callee would be applying `__always_inline`, so that
irqentry_exit_to_kernel_mode_preempt() also becomes non-instrumented.
Given that irqentry_exit_to_kernel_mode_after_preempt() is already
`__always_inline`, it might be the right thing to do.
On the caller side, we could do something creative with instrumentation_begin()
and instrumentation_end(). We've had a discussion about that exactly four years
ago: https://lore.kernel.org/all/20220426164315.625149-29-glider@xxxxxxxxxx/T/#u
, but came to a conclusion that a handful of annotations on the noinstr/instr
boundary may do a better job than a solution that doesn't cover all cases.
In particular, the case of irqentry_exit_to_kernel_mode_preempt() could have
been solved by `__memset(state, 0, sizeof(struct kmsan_context_state))` in
instrumentation_begin(). But it wouldn't solve more complex (yet rare, and
non-existing today) cases where two functions are called from an instrumented
region, and the first function somehow leaves the argument state poisoned.
Do you think it's worth revisiting the instrumentation_begin() approach, or
shall we go with one of the compiler attributes instead?
Thank you,
Alexander
--
Alexander Potapenko
Software Engineer
Google Germany GmbH
Erika-Mann-Straße, 33
80636 München
Geschäftsführer: Paul Manicle, Liana Sebastian
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg