Commit 49dc6fbc authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'kgdb-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/danielt/linux

Pull kgdb updates from Daniel Thompson:
 "A fairly modest set of changes for this cycle.

  Of particular note are an earlycon fix from Doug Anderson and my own
  changes to get kgdb/kdb to honour the kprobe blocklist. The later
  creates a safety rail that strongly encourages developers not to place
  breakpoints in, for example, arch specific trap handling code.

  Also included are a couple of small fixes and tweaks: an API update,
  eliminate a coverity dead code warning, improved handling of search
  during multi-line printk and a couple of typo corrections"

* tag 'kgdb-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/danielt/linux:
  kdb: Fix pager search for multi-line strings
  kernel: debug: Centralize dbg_[de]activate_sw_breakpoints
  kgdb: Add NOKPROBE labels on the trap handler functions
  kgdb: Honour the kprobe blocklist when setting breakpoints
  kernel/debug: Fix spelling mistake in debug_core.c
  kdb: Use newer api for tasklist scanning
  kgdb: Make "kgdbcon" work properly with "kgdb_earlycon"
  kdb: remove unnecessary null check of dbg_io_ops
parents 09a31a7e d081a6e3
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/linkage.h> #include <linux/linkage.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/kprobes.h>
#ifdef CONFIG_HAVE_ARCH_KGDB #ifdef CONFIG_HAVE_ARCH_KGDB
#include <asm/kgdb.h> #include <asm/kgdb.h>
#endif #endif
...@@ -335,6 +336,23 @@ extern int kgdb_nmicallin(int cpu, int trapnr, void *regs, int err_code, ...@@ -335,6 +336,23 @@ extern int kgdb_nmicallin(int cpu, int trapnr, void *regs, int err_code,
atomic_t *snd_rdy); atomic_t *snd_rdy);
extern void gdbstub_exit(int status); extern void gdbstub_exit(int status);
/*
* kgdb and kprobes both use the same (kprobe) blocklist (which makes sense
* given they are both typically hooked up to the same trap meaning on most
* architectures one cannot be used to debug the other)
*
* However on architectures where kprobes is not (yet) implemented we permit
* breakpoints everywhere rather than blocking everything by default.
*/
static inline bool kgdb_within_blocklist(unsigned long addr)
{
#ifdef CONFIG_KGDB_HONOUR_BLOCKLIST
return within_kprobe_blacklist(addr);
#else
return false;
#endif
}
extern int kgdb_single_step; extern int kgdb_single_step;
extern atomic_t kgdb_active; extern atomic_t kgdb_active;
#define in_dbg_master() \ #define in_dbg_master() \
......
...@@ -80,7 +80,7 @@ static int exception_level; ...@@ -80,7 +80,7 @@ static int exception_level;
struct kgdb_io *dbg_io_ops; struct kgdb_io *dbg_io_ops;
static DEFINE_SPINLOCK(kgdb_registration_lock); static DEFINE_SPINLOCK(kgdb_registration_lock);
/* Action for the reboot notifiter, a global allow kdb to change it */ /* Action for the reboot notifier, a global allow kdb to change it */
static int kgdbreboot; static int kgdbreboot;
/* kgdb console driver is loaded */ /* kgdb console driver is loaded */
static int kgdb_con_registered; static int kgdb_con_registered;
...@@ -94,14 +94,6 @@ int dbg_switch_cpu; ...@@ -94,14 +94,6 @@ int dbg_switch_cpu;
/* Use kdb or gdbserver mode */ /* Use kdb or gdbserver mode */
int dbg_kdb_mode = 1; int dbg_kdb_mode = 1;
static int __init opt_kgdb_con(char *str)
{
kgdb_use_con = 1;
return 0;
}
early_param("kgdbcon", opt_kgdb_con);
module_param(kgdb_use_con, int, 0644); module_param(kgdb_use_con, int, 0644);
module_param(kgdbreboot, int, 0644); module_param(kgdbreboot, int, 0644);
...@@ -163,7 +155,7 @@ early_param("nokgdbroundup", opt_nokgdbroundup); ...@@ -163,7 +155,7 @@ early_param("nokgdbroundup", opt_nokgdbroundup);
/* /*
* Weak aliases for breakpoint management, * Weak aliases for breakpoint management,
* can be overriden by architectures when needed: * can be overridden by architectures when needed:
*/ */
int __weak kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt) int __weak kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
{ {
...@@ -177,17 +169,23 @@ int __weak kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt) ...@@ -177,17 +169,23 @@ int __weak kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE); arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE);
return err; return err;
} }
NOKPROBE_SYMBOL(kgdb_arch_set_breakpoint);
int __weak kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt) int __weak kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
{ {
return copy_to_kernel_nofault((char *)bpt->bpt_addr, return copy_to_kernel_nofault((char *)bpt->bpt_addr,
(char *)bpt->saved_instr, BREAK_INSTR_SIZE); (char *)bpt->saved_instr, BREAK_INSTR_SIZE);
} }
NOKPROBE_SYMBOL(kgdb_arch_remove_breakpoint);
int __weak kgdb_validate_break_address(unsigned long addr) int __weak kgdb_validate_break_address(unsigned long addr)
{ {
struct kgdb_bkpt tmp; struct kgdb_bkpt tmp;
int err; int err;
if (kgdb_within_blocklist(addr))
return -EINVAL;
/* Validate setting the breakpoint and then removing it. If the /* Validate setting the breakpoint and then removing it. If the
* remove fails, the kernel needs to emit a bad message because we * remove fails, the kernel needs to emit a bad message because we
* are deep trouble not being able to put things back the way we * are deep trouble not being able to put things back the way we
...@@ -208,6 +206,7 @@ unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs) ...@@ -208,6 +206,7 @@ unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs)
{ {
return instruction_pointer(regs); return instruction_pointer(regs);
} }
NOKPROBE_SYMBOL(kgdb_arch_pc);
int __weak kgdb_arch_init(void) int __weak kgdb_arch_init(void)
{ {
...@@ -218,6 +217,7 @@ int __weak kgdb_skipexception(int exception, struct pt_regs *regs) ...@@ -218,6 +217,7 @@ int __weak kgdb_skipexception(int exception, struct pt_regs *regs)
{ {
return 0; return 0;
} }
NOKPROBE_SYMBOL(kgdb_skipexception);
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
...@@ -239,6 +239,7 @@ void __weak kgdb_call_nmi_hook(void *ignored) ...@@ -239,6 +239,7 @@ void __weak kgdb_call_nmi_hook(void *ignored)
*/ */
kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs()); kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs());
} }
NOKPROBE_SYMBOL(kgdb_call_nmi_hook);
void __weak kgdb_roundup_cpus(void) void __weak kgdb_roundup_cpus(void)
{ {
...@@ -272,6 +273,7 @@ void __weak kgdb_roundup_cpus(void) ...@@ -272,6 +273,7 @@ void __weak kgdb_roundup_cpus(void)
kgdb_info[cpu].rounding_up = false; kgdb_info[cpu].rounding_up = false;
} }
} }
NOKPROBE_SYMBOL(kgdb_roundup_cpus);
#endif #endif
...@@ -298,6 +300,7 @@ static void kgdb_flush_swbreak_addr(unsigned long addr) ...@@ -298,6 +300,7 @@ static void kgdb_flush_swbreak_addr(unsigned long addr)
/* Force flush instruction cache if it was outside the mm */ /* Force flush instruction cache if it was outside the mm */
flush_icache_range(addr, addr + BREAK_INSTR_SIZE); flush_icache_range(addr, addr + BREAK_INSTR_SIZE);
} }
NOKPROBE_SYMBOL(kgdb_flush_swbreak_addr);
/* /*
* SW breakpoint management: * SW breakpoint management:
...@@ -325,6 +328,7 @@ int dbg_activate_sw_breakpoints(void) ...@@ -325,6 +328,7 @@ int dbg_activate_sw_breakpoints(void)
} }
return ret; return ret;
} }
NOKPROBE_SYMBOL(dbg_activate_sw_breakpoints);
int dbg_set_sw_break(unsigned long addr) int dbg_set_sw_break(unsigned long addr)
{ {
...@@ -388,6 +392,7 @@ int dbg_deactivate_sw_breakpoints(void) ...@@ -388,6 +392,7 @@ int dbg_deactivate_sw_breakpoints(void)
} }
return ret; return ret;
} }
NOKPROBE_SYMBOL(dbg_deactivate_sw_breakpoints);
int dbg_remove_sw_break(unsigned long addr) int dbg_remove_sw_break(unsigned long addr)
{ {
...@@ -509,6 +514,7 @@ static int kgdb_io_ready(int print_wait) ...@@ -509,6 +514,7 @@ static int kgdb_io_ready(int print_wait)
} }
return 1; return 1;
} }
NOKPROBE_SYMBOL(kgdb_io_ready);
static int kgdb_reenter_check(struct kgdb_state *ks) static int kgdb_reenter_check(struct kgdb_state *ks)
{ {
...@@ -556,6 +562,7 @@ static int kgdb_reenter_check(struct kgdb_state *ks) ...@@ -556,6 +562,7 @@ static int kgdb_reenter_check(struct kgdb_state *ks)
return 1; return 1;
} }
NOKPROBE_SYMBOL(kgdb_reenter_check);
static void dbg_touch_watchdogs(void) static void dbg_touch_watchdogs(void)
{ {
...@@ -563,6 +570,7 @@ static void dbg_touch_watchdogs(void) ...@@ -563,6 +570,7 @@ static void dbg_touch_watchdogs(void)
clocksource_touch_watchdog(); clocksource_touch_watchdog();
rcu_cpu_stall_reset(); rcu_cpu_stall_reset();
} }
NOKPROBE_SYMBOL(dbg_touch_watchdogs);
static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs, static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs,
int exception_state) int exception_state)
...@@ -752,6 +760,8 @@ static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs, ...@@ -752,6 +760,8 @@ static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs,
} }
} }
dbg_activate_sw_breakpoints();
/* Call the I/O driver's post_exception routine */ /* Call the I/O driver's post_exception routine */
if (dbg_io_ops->post_exception) if (dbg_io_ops->post_exception)
dbg_io_ops->post_exception(); dbg_io_ops->post_exception();
...@@ -794,6 +804,7 @@ static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs, ...@@ -794,6 +804,7 @@ static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs,
return kgdb_info[cpu].ret_state; return kgdb_info[cpu].ret_state;
} }
NOKPROBE_SYMBOL(kgdb_cpu_enter);
/* /*
* kgdb_handle_exception() - main entry point from a kernel exception * kgdb_handle_exception() - main entry point from a kernel exception
...@@ -838,6 +849,7 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs) ...@@ -838,6 +849,7 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs)
arch_kgdb_ops.enable_nmi(1); arch_kgdb_ops.enable_nmi(1);
return ret; return ret;
} }
NOKPROBE_SYMBOL(kgdb_handle_exception);
/* /*
* GDB places a breakpoint at this function to know dynamically loaded objects. * GDB places a breakpoint at this function to know dynamically loaded objects.
...@@ -872,6 +884,7 @@ int kgdb_nmicallback(int cpu, void *regs) ...@@ -872,6 +884,7 @@ int kgdb_nmicallback(int cpu, void *regs)
#endif #endif
return 1; return 1;
} }
NOKPROBE_SYMBOL(kgdb_nmicallback);
int kgdb_nmicallin(int cpu, int trapnr, void *regs, int err_code, int kgdb_nmicallin(int cpu, int trapnr, void *regs, int err_code,
atomic_t *send_ready) atomic_t *send_ready)
...@@ -897,6 +910,7 @@ int kgdb_nmicallin(int cpu, int trapnr, void *regs, int err_code, ...@@ -897,6 +910,7 @@ int kgdb_nmicallin(int cpu, int trapnr, void *regs, int err_code,
#endif #endif
return 1; return 1;
} }
NOKPROBE_SYMBOL(kgdb_nmicallin);
static void kgdb_console_write(struct console *co, const char *s, static void kgdb_console_write(struct console *co, const char *s,
unsigned count) unsigned count)
...@@ -920,6 +934,20 @@ static struct console kgdbcons = { ...@@ -920,6 +934,20 @@ static struct console kgdbcons = {
.index = -1, .index = -1,
}; };
static int __init opt_kgdb_con(char *str)
{
kgdb_use_con = 1;
if (kgdb_io_module_registered && !kgdb_con_registered) {
register_console(&kgdbcons);
kgdb_con_registered = 1;
}
return 0;
}
early_param("kgdbcon", opt_kgdb_con);
#ifdef CONFIG_MAGIC_SYSRQ #ifdef CONFIG_MAGIC_SYSRQ
static void sysrq_handle_dbg(int key) static void sysrq_handle_dbg(int key)
{ {
......
...@@ -725,7 +725,7 @@ static void gdb_cmd_query(struct kgdb_state *ks) ...@@ -725,7 +725,7 @@ static void gdb_cmd_query(struct kgdb_state *ks)
} }
} }
do_each_thread(g, p) { for_each_process_thread(g, p) {
if (i >= ks->thr_query && !finished) { if (i >= ks->thr_query && !finished) {
int_to_threadref(thref, p->pid); int_to_threadref(thref, p->pid);
ptr = pack_threadid(ptr, thref); ptr = pack_threadid(ptr, thref);
...@@ -735,7 +735,7 @@ static void gdb_cmd_query(struct kgdb_state *ks) ...@@ -735,7 +735,7 @@ static void gdb_cmd_query(struct kgdb_state *ks)
finished = 1; finished = 1;
} }
i++; i++;
} while_each_thread(g, p); }
*(--ptr) = '\0'; *(--ptr) = '\0';
break; break;
...@@ -1061,7 +1061,6 @@ int gdb_serial_stub(struct kgdb_state *ks) ...@@ -1061,7 +1061,6 @@ int gdb_serial_stub(struct kgdb_state *ks)
error_packet(remcom_out_buffer, -EINVAL); error_packet(remcom_out_buffer, -EINVAL);
break; break;
} }
dbg_activate_sw_breakpoints();
fallthrough; /* to default processing */ fallthrough; /* to default processing */
default: default:
default_handle: default_handle:
......
...@@ -306,6 +306,15 @@ static int kdb_bp(int argc, const char **argv) ...@@ -306,6 +306,15 @@ static int kdb_bp(int argc, const char **argv)
if (!template.bp_addr) if (!template.bp_addr)
return KDB_BADINT; return KDB_BADINT;
/*
* This check is redundant (since the breakpoint machinery should
* be doing the same check during kdb_bp_install) but gives the
* user immediate feedback.
*/
diag = kgdb_validate_break_address(template.bp_addr);
if (diag)
return diag;
/* /*
* Find an empty bp structure to allocate * Find an empty bp structure to allocate
*/ */
......
...@@ -149,14 +149,14 @@ kdb_bt(int argc, const char **argv) ...@@ -149,14 +149,14 @@ kdb_bt(int argc, const char **argv)
return 0; return 0;
} }
/* Now the inactive tasks */ /* Now the inactive tasks */
kdb_do_each_thread(g, p) { for_each_process_thread(g, p) {
if (KDB_FLAG(CMD_INTERRUPT)) if (KDB_FLAG(CMD_INTERRUPT))
return 0; return 0;
if (task_curr(p)) if (task_curr(p))
continue; continue;
if (kdb_bt1(p, mask, btaprompt)) if (kdb_bt1(p, mask, btaprompt))
return 0; return 0;
} kdb_while_each_thread(g, p); }
} else if (strcmp(argv[0], "btp") == 0) { } else if (strcmp(argv[0], "btp") == 0) {
struct task_struct *p; struct task_struct *p;
unsigned long pid; unsigned long pid;
......
...@@ -147,7 +147,6 @@ int kdb_stub(struct kgdb_state *ks) ...@@ -147,7 +147,6 @@ int kdb_stub(struct kgdb_state *ks)
return DBG_PASS_EVENT; return DBG_PASS_EVENT;
} }
kdb_bp_install(ks->linux_regs); kdb_bp_install(ks->linux_regs);
dbg_activate_sw_breakpoints();
/* Set the exit state to a single step or a continue */ /* Set the exit state to a single step or a continue */
if (KDB_STATE(DOING_SS)) if (KDB_STATE(DOING_SS))
gdbstub_state(ks, "s"); gdbstub_state(ks, "s");
...@@ -167,7 +166,6 @@ int kdb_stub(struct kgdb_state *ks) ...@@ -167,7 +166,6 @@ int kdb_stub(struct kgdb_state *ks)
* differently vs the gdbstub * differently vs the gdbstub
*/ */
kgdb_single_step = 0; kgdb_single_step = 0;
dbg_deactivate_sw_breakpoints();
return DBG_SWITCH_CPU_EVENT; return DBG_SWITCH_CPU_EVENT;
} }
return kgdb_info[ks->cpu].ret_state; return kgdb_info[ks->cpu].ret_state;
......
...@@ -545,18 +545,18 @@ static int kdb_search_string(char *searched, char *searchfor) ...@@ -545,18 +545,18 @@ static int kdb_search_string(char *searched, char *searchfor)
static void kdb_msg_write(const char *msg, int msg_len) static void kdb_msg_write(const char *msg, int msg_len)
{ {
struct console *c; struct console *c;
const char *cp;
int len;
if (msg_len == 0) if (msg_len == 0)
return; return;
if (dbg_io_ops) { cp = msg;
const char *cp = msg; len = msg_len;
int len = msg_len;
while (len--) { while (len--) {
dbg_io_ops->write_char(*cp); dbg_io_ops->write_char(*cp);
cp++; cp++;
}
} }
for_each_console(c) { for_each_console(c) {
...@@ -706,12 +706,16 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap) ...@@ -706,12 +706,16 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
size_avail = sizeof(kdb_buffer) - len; size_avail = sizeof(kdb_buffer) - len;
goto kdb_print_out; goto kdb_print_out;
} }
if (kdb_grepping_flag >= KDB_GREPPING_FLAG_SEARCH) if (kdb_grepping_flag >= KDB_GREPPING_FLAG_SEARCH) {
/* /*
* This was a interactive search (using '/' at more * This was a interactive search (using '/' at more
* prompt) and it has completed. Clear the flag. * prompt) and it has completed. Replace the \0 with
* its original value to ensure multi-line strings
* are handled properly, and return to normal mode.
*/ */
*cphold = replaced_byte;
kdb_grepping_flag = 0; kdb_grepping_flag = 0;
}
/* /*
* at this point the string is a full line and * at this point the string is a full line and
* should be printed, up to the null. * should be printed, up to the null.
......
...@@ -2299,10 +2299,10 @@ void kdb_ps_suppressed(void) ...@@ -2299,10 +2299,10 @@ void kdb_ps_suppressed(void)
if (kdb_task_state(p, mask_I)) if (kdb_task_state(p, mask_I))
++idle; ++idle;
} }
kdb_do_each_thread(g, p) { for_each_process_thread(g, p) {
if (kdb_task_state(p, mask_M)) if (kdb_task_state(p, mask_M))
++daemon; ++daemon;
} kdb_while_each_thread(g, p); }
if (idle || daemon) { if (idle || daemon) {
if (idle) if (idle)
kdb_printf("%d idle process%s (state I)%s\n", kdb_printf("%d idle process%s (state I)%s\n",
...@@ -2370,12 +2370,12 @@ static int kdb_ps(int argc, const char **argv) ...@@ -2370,12 +2370,12 @@ static int kdb_ps(int argc, const char **argv)
} }
kdb_printf("\n"); kdb_printf("\n");
/* Now the real tasks */ /* Now the real tasks */
kdb_do_each_thread(g, p) { for_each_process_thread(g, p) {
if (KDB_FLAG(CMD_INTERRUPT)) if (KDB_FLAG(CMD_INTERRUPT))
return 0; return 0;
if (kdb_task_state(p, mask)) if (kdb_task_state(p, mask))
kdb_ps1(p); kdb_ps1(p);
} kdb_while_each_thread(g, p); }
return 0; return 0;
} }
......
...@@ -230,10 +230,6 @@ extern struct task_struct *kdb_curr_task(int); ...@@ -230,10 +230,6 @@ extern struct task_struct *kdb_curr_task(int);
#define kdb_task_has_cpu(p) (task_curr(p)) #define kdb_task_has_cpu(p) (task_curr(p))
/* Simplify coexistence with NPTL */
#define kdb_do_each_thread(g, p) do_each_thread(g, p)
#define kdb_while_each_thread(g, p) while_each_thread(g, p)
#define GFP_KDB (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) #define GFP_KDB (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
extern void *debug_kmalloc(size_t size, gfp_t flags); extern void *debug_kmalloc(size_t size, gfp_t flags);
......
...@@ -24,6 +24,21 @@ menuconfig KGDB ...@@ -24,6 +24,21 @@ menuconfig KGDB
if KGDB if KGDB
config KGDB_HONOUR_BLOCKLIST
bool "KGDB: use kprobe blocklist to prohibit unsafe breakpoints"
depends on HAVE_KPROBES
depends on MODULES
select KPROBES
default y
help
If set to Y the debug core will use the kprobe blocklist to
identify symbols where it is unsafe to set breakpoints.
In particular this disallows instrumentation of functions
called during debug trap handling and thus makes it very
difficult to inadvertently provoke recursive trap handling.
If unsure, say Y.
config KGDB_SERIAL_CONSOLE config KGDB_SERIAL_CONSOLE
tristate "KGDB: use kgdb over the serial console" tristate "KGDB: use kgdb over the serial console"
select CONSOLE_POLL select CONSOLE_POLL
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment