lkml.org 
[lkml]   [2022]   [Jun]   [5]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH MANUALSEL 4.14 1/4] x86/nmi: Make register_nmi_handler() more robust
    Date
    From: Thomas Gleixner <tglx@linutronix.de>

    [ Upstream commit a7fed5c0431dbfa707037848830f980e0f93cfb3 ]

    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
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    arch/x86/include/asm/nmi.h | 1 +
    arch/x86/kernel/nmi.c | 12 ++++++++----
    2 files changed, 9 insertions(+), 4 deletions(-)

    diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h
    index 75ded1d13d98..121bf8878506 100644
    --- a/arch/x86/include/asm/nmi.h
    +++ b/arch/x86/include/asm/nmi.h
    @@ -49,6 +49,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), \
    diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c
    index d796a7f6a74a..e8a99f11ab3f 100644
    --- a/arch/x86/kernel/nmi.c
    +++ b/arch/x86/kernel/nmi.c
    @@ -159,7 +159,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;

    init_irq_work(&action->irq_work, nmi_max_handler);
    @@ -181,7 +181,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;
    }
    @@ -190,7 +190,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);
    @@ -204,12 +204,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);

    --
    2.35.1
    \
     
     \ /
      Last update: 2022-06-05 15:59    [W:3.770 / U:1.980 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site