Re: [syzbot] [kernel?] upstream test error: KMSAN: uninit-value in irqentry_exit_to_kernel_mode_preempt

From: Mark Rutland

Date: Tue May 12 2026 - 07:22:53 EST


On Tue, May 12, 2026 at 11:33:47AM +0200, Alexander Potapenko wrote:
> On Mon, May 11, 2026 at 2:25 PM Thomas Gleixner <tglx@xxxxxxxxxx> wrote:
> > > 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.

For context, can you explain how this is expected to work across
compilation units when the caller and callee *are* instrumented, when an
argument is passed in a register?

Below you suggest that the caller might add "hints", but it's not clear
specifically what this means.

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

Without some understanding of those "hints" you mention, I don't see how
we can do that on the caller 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.

We can do that, but this really suggests that there's a fundemantal
inability to pass arguments between code which is noinstr and code which
isinstrumented with AddressSanitizer, and that's inevitably going to
bite us in future.

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

That doesn't look general at all, so I am not keen on that.

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

How exactly is kmsan_context_state used?

If that's supposed to carry some global or current context, surely
blatting that in entry code will affect the code that was interrupted?

I see kmsan_get_context() has an in_task() check, but that can't help
with nested exceptions, so this doesn't look right at all.

> Do you think it's worth revisiting the instrumentation_begin() approach, or
> shall we go with one of the compiler attributes instead?

I think we need a better understanding of this first.

It looks to me that there are bigger problems here.

Mark.