mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-06 05:13:18 +00:00
x86/nmi: Make register_nmi_handler() more robust
register_nmi_handler() has no sanity check whether a handler has been registered already. Such an unintended double-add leads to list corruption and hard to diagnose problems during the next NMI handling. Init the list head in the static NMI action struct and check it for being empty in register_nmi_handler(). [ bp: Fixups. ] Reported-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Borislav Petkov <bp@suse.de> Link: https://lore.kernel.org/lkml/20220511234332.3654455-1-seanjc@google.com
This commit is contained in:
parent
203d8919a9
commit
a7fed5c043
@ -47,6 +47,7 @@ struct nmiaction {
|
||||
#define register_nmi_handler(t, fn, fg, n, init...) \
|
||||
({ \
|
||||
static struct nmiaction init fn##_na = { \
|
||||
.list = LIST_HEAD_INIT(fn##_na.list), \
|
||||
.handler = (fn), \
|
||||
.name = (n), \
|
||||
.flags = (fg), \
|
||||
|
@ -157,7 +157,7 @@ int __register_nmi_handler(unsigned int type, struct nmiaction *action)
|
||||
struct nmi_desc *desc = nmi_to_desc(type);
|
||||
unsigned long flags;
|
||||
|
||||
if (!action->handler)
|
||||
if (WARN_ON_ONCE(!action->handler || !list_empty(&action->list)))
|
||||
return -EINVAL;
|
||||
|
||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||
@ -177,7 +177,7 @@ int __register_nmi_handler(unsigned int type, struct nmiaction *action)
|
||||
list_add_rcu(&action->list, &desc->head);
|
||||
else
|
||||
list_add_tail_rcu(&action->list, &desc->head);
|
||||
|
||||
|
||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
@ -186,7 +186,7 @@ EXPORT_SYMBOL(__register_nmi_handler);
|
||||
void unregister_nmi_handler(unsigned int type, const char *name)
|
||||
{
|
||||
struct nmi_desc *desc = nmi_to_desc(type);
|
||||
struct nmiaction *n;
|
||||
struct nmiaction *n, *found = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||
@ -200,12 +200,16 @@ void unregister_nmi_handler(unsigned int type, const char *name)
|
||||
WARN(in_nmi(),
|
||||
"Trying to free NMI (%s) from NMI context!\n", n->name);
|
||||
list_del_rcu(&n->list);
|
||||
found = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||
synchronize_rcu();
|
||||
if (found) {
|
||||
synchronize_rcu();
|
||||
INIT_LIST_HEAD(&found->list);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_nmi_handler);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user