Skip to content
Snippets Groups Projects
  • Kees Cook's avatar
    lib/bug.c: exclude non-BUG/WARN exceptions from report_bug() · 1b4cfe3c
    Kees Cook authored
    Commit b8347c21 ("x86/debug: Handle warnings before the notifier
    chain, to fix KGDB crash") changed the ordering of fixups, and did not
    take into account the case of x86 processing non-WARN() and non-BUG()
    exceptions.  This would lead to output of a false BUG line with no other
    information.
    
    In the case of a refcount exception, it would be immediately followed by
    the refcount WARN(), producing very strange double-"cut here":
    
      lkdtm: attempting bad refcount_inc() overflow
      ------------[ cut here ]------------
      Kernel BUG at 0000000065f29de5 [verbose debug info unavailable]
      ------------[ cut here ]------------
      refcount_t overflow at lkdtm_REFCOUNT_INC_OVERFLOW+0x6b/0x90 in cat[3065], uid/euid: 0/0
      WARNING: CPU: 0 PID: 3065 at kernel/panic.c:657 refcount_error_report+0x9a/0xa4
      ...
    
    In the prior ordering, exceptions were searched first:
    
       do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str,
       ...
                    if (fixup_exception(regs, trapnr))
                            return 0;
    
      -               if (fixup_bug(regs, trapnr))
      -                       return 0;
      -
    
    As a result, fixup_bugs()'s is_valid_bugaddr() didn't take into account
    needing to search the exception list first, since that had already
    happened.
    
    So, instead of searching the exception list twice (once in
    is_valid_bugaddr() and then again in fixup_exception()), just add a
    simple sanity check to report_bug() that will immediately bail out if a
    BUG() (or WARN()) entry is not found.
    
    Link: http://lkml.kernel.org/r/20180301225934.GA34350@beast
    
    
    Fixes: b8347c21 ("x86/debug: Handle warnings before the notifier chain, to fix KGDB crash")
    Signed-off-by: default avatarKees Cook <keescook@chromium.org>
    Cc: Ingo Molnar <mingo@kernel.org>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Cc: Peter Zijlstra <peterz@infradead.org>
    Cc: Borislav Petkov <bp@alien8.de>
    Cc: Richard Weinberger <richard.weinberger@gmail.com>
    Cc: <stable@vger.kernel.org>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    1b4cfe3c
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
bug.c 5.48 KiB
// SPDX-License-Identifier: GPL-2.0
/*
  Generic support for BUG()

  This respects the following config options:

  CONFIG_BUG - emit BUG traps.  Nothing happens without this.
  CONFIG_GENERIC_BUG - enable this code.
  CONFIG_GENERIC_BUG_RELATIVE_POINTERS - use 32-bit pointers relative to
	the containing struct bug_entry for bug_addr and file.
  CONFIG_DEBUG_BUGVERBOSE - emit full file+line information for each BUG

  CONFIG_BUG and CONFIG_DEBUG_BUGVERBOSE are potentially user-settable
  (though they're generally always on).

  CONFIG_GENERIC_BUG is set by each architecture using this code.

  To use this, your architecture must:

  1. Set up the config options:
     - Enable CONFIG_GENERIC_BUG if CONFIG_BUG

  2. Implement BUG (and optionally BUG_ON, WARN, WARN_ON)
     - Define HAVE_ARCH_BUG
     - Implement BUG() to generate a faulting instruction
     - NOTE: struct bug_entry does not have "file" or "line" entries
       when CONFIG_DEBUG_BUGVERBOSE is not enabled, so you must generate
       the values accordingly.

  3. Implement the trap
     - In the illegal instruction trap handler (typically), verify
       that the fault was in kernel mode, and call report_bug()
     - report_bug() will return whether it was a false alarm, a warning,
       or an actual bug.
     - You must implement the is_valid_bugaddr(bugaddr) callback which
       returns true if the eip is a real kernel address, and it points
       to the expected BUG trap instruction.

    Jeremy Fitzhardinge <jeremy@goop.org> 2006
 */

#define pr_fmt(fmt) fmt

#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/bug.h>
#include <linux/sched.h>
#include <linux/rculist.h>

extern struct bug_entry __start___bug_table[], __stop___bug_table[];

static inline unsigned long bug_addr(const struct bug_entry *bug)
{
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
	return bug->bug_addr;
#else
	return (unsigned long)bug + bug->bug_addr_disp;
#endif
}

#ifdef CONFIG_MODULES
/* Updates are protected by module mutex */
static LIST_HEAD(module_bug_list);

static struct bug_entry *module_find_bug(unsigned long bugaddr)
{
	struct module *mod;
	struct bug_entry *bug = NULL;