kgdb patches for 5.10-rc1

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.
 
 Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEELzVBU1D3lWq6cKzwfOMlXTn3iKEFAl+IdyAACgkQfOMlXTn3
 iKHeTw//RDAWm0IU00z9+ZlTyksTk0vePuYKwgEm8zp+XYvY0NvpgWyZ5MWd8b3K
 WJmsTfXMHNoPCg3464XCrQDyIhrfhxk0nrdOpgbsQMb7HdjYrnltPdG3l8W9kvVv
 MjMH98QOBaYAY75nd8pGoPVTOmODrhowWo6+y4me2CnJGKOOV/yHmctBhlOJhbeo
 TCUIDP/NmC63N8Oziteym1TZ5dhschBb/85qEb72wXaiGEZTaVC9GEFEgCqfADHX
 51KxbtZoJWirbXu2aYaK5MHEb/0NWPMItiER7y8ZrTiPHMRre4N5DpCMpKpp3/qd
 YRtEnNnT+Ay0ijCt2FjznSsEh2ecLI0qSO4QDQz320QJCj7qgcjJ0++yEayrzz8W
 IxCbwkUP8X5m1srXSxvOTKfuu29wiMCqNkJA0rgjpA2u4Yn5KO0ZRmBoHtW1Sq8E
 MhbRTixU/vFYosjd/mKubj/f4DFrMILo+FJTqdewBUhT/Q6Vr9l660JzvwWnKKJF
 e1EHNYtWo4J+EkL9z++5d9PzDl0d56DcE8rfH53Dkg075Wnma3tdq2Z7WxT3M7EP
 K3U32BI9obu+lPHxl4FtAobCIDjP6NtmmMo3zzzA1fPtXNzAjy7qZ+Ss6POQppkn
 7v+PFYdFJ8VKo3PNxMWnFhgwSDOYYxCPjCxs+bjaMBvHNVgg2Ig=
 =x91W
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2020-10-16 12:47:18 -07:00
commit 49dc6fbce3
10 changed files with 101 additions and 34 deletions

View File

@ -16,6 +16,7 @@
#include <linux/linkage.h>
#include <linux/init.h>
#include <linux/atomic.h>
#include <linux/kprobes.h>
#ifdef CONFIG_HAVE_ARCH_KGDB
#include <asm/kgdb.h>
#endif
@ -335,6 +336,23 @@ extern int kgdb_nmicallin(int cpu, int trapnr, void *regs, int err_code,
atomic_t *snd_rdy);
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 atomic_t kgdb_active;
#define in_dbg_master() \

View File

@ -80,7 +80,7 @@ static int exception_level;
struct kgdb_io *dbg_io_ops;
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;
/* kgdb console driver is loaded */
static int kgdb_con_registered;
@ -94,14 +94,6 @@ int dbg_switch_cpu;
/* Use kdb or gdbserver mode */
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(kgdbreboot, int, 0644);
@ -163,7 +155,7 @@ early_param("nokgdbroundup", opt_nokgdbroundup);
/*
* 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)
{
@ -177,17 +169,23 @@ int __weak kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE);
return err;
}
NOKPROBE_SYMBOL(kgdb_arch_set_breakpoint);
int __weak kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
{
return copy_to_kernel_nofault((char *)bpt->bpt_addr,
(char *)bpt->saved_instr, BREAK_INSTR_SIZE);
}
NOKPROBE_SYMBOL(kgdb_arch_remove_breakpoint);
int __weak kgdb_validate_break_address(unsigned long addr)
{
struct kgdb_bkpt tmp;
int err;
if (kgdb_within_blocklist(addr))
return -EINVAL;
/* Validate setting the breakpoint and then removing it. If the
* 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
@ -208,6 +206,7 @@ unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs)
{
return instruction_pointer(regs);
}
NOKPROBE_SYMBOL(kgdb_arch_pc);
int __weak kgdb_arch_init(void)
{
@ -218,6 +217,7 @@ int __weak kgdb_skipexception(int exception, struct pt_regs *regs)
{
return 0;
}
NOKPROBE_SYMBOL(kgdb_skipexception);
#ifdef CONFIG_SMP
@ -239,6 +239,7 @@ void __weak kgdb_call_nmi_hook(void *ignored)
*/
kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs());
}
NOKPROBE_SYMBOL(kgdb_call_nmi_hook);
void __weak kgdb_roundup_cpus(void)
{
@ -272,6 +273,7 @@ void __weak kgdb_roundup_cpus(void)
kgdb_info[cpu].rounding_up = false;
}
}
NOKPROBE_SYMBOL(kgdb_roundup_cpus);
#endif
@ -298,6 +300,7 @@ static void kgdb_flush_swbreak_addr(unsigned long addr)
/* Force flush instruction cache if it was outside the mm */
flush_icache_range(addr, addr + BREAK_INSTR_SIZE);
}
NOKPROBE_SYMBOL(kgdb_flush_swbreak_addr);
/*
* SW breakpoint management:
@ -325,6 +328,7 @@ int dbg_activate_sw_breakpoints(void)
}
return ret;
}
NOKPROBE_SYMBOL(dbg_activate_sw_breakpoints);
int dbg_set_sw_break(unsigned long addr)
{
@ -388,6 +392,7 @@ int dbg_deactivate_sw_breakpoints(void)
}
return ret;
}
NOKPROBE_SYMBOL(dbg_deactivate_sw_breakpoints);
int dbg_remove_sw_break(unsigned long addr)
{
@ -509,6 +514,7 @@ static int kgdb_io_ready(int print_wait)
}
return 1;
}
NOKPROBE_SYMBOL(kgdb_io_ready);
static int kgdb_reenter_check(struct kgdb_state *ks)
{
@ -556,6 +562,7 @@ static int kgdb_reenter_check(struct kgdb_state *ks)
return 1;
}
NOKPROBE_SYMBOL(kgdb_reenter_check);
static void dbg_touch_watchdogs(void)
{
@ -563,6 +570,7 @@ static void dbg_touch_watchdogs(void)
clocksource_touch_watchdog();
rcu_cpu_stall_reset();
}
NOKPROBE_SYMBOL(dbg_touch_watchdogs);
static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs,
int exception_state)
@ -752,6 +760,8 @@ cpu_master_loop:
}
}
dbg_activate_sw_breakpoints();
/* Call the I/O driver's post_exception routine */
if (dbg_io_ops->post_exception)
dbg_io_ops->post_exception();
@ -794,6 +804,7 @@ kgdb_restore:
return kgdb_info[cpu].ret_state;
}
NOKPROBE_SYMBOL(kgdb_cpu_enter);
/*
* kgdb_handle_exception() - main entry point from a kernel exception
@ -838,6 +849,7 @@ out:
arch_kgdb_ops.enable_nmi(1);
return ret;
}
NOKPROBE_SYMBOL(kgdb_handle_exception);
/*
* GDB places a breakpoint at this function to know dynamically loaded objects.
@ -872,6 +884,7 @@ int kgdb_nmicallback(int cpu, void *regs)
#endif
return 1;
}
NOKPROBE_SYMBOL(kgdb_nmicallback);
int kgdb_nmicallin(int cpu, int trapnr, void *regs, int err_code,
atomic_t *send_ready)
@ -897,6 +910,7 @@ int kgdb_nmicallin(int cpu, int trapnr, void *regs, int err_code,
#endif
return 1;
}
NOKPROBE_SYMBOL(kgdb_nmicallin);
static void kgdb_console_write(struct console *co, const char *s,
unsigned count)
@ -920,6 +934,20 @@ static struct console kgdbcons = {
.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
static void sysrq_handle_dbg(int key)
{

View File

@ -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) {
int_to_threadref(thref, p->pid);
ptr = pack_threadid(ptr, thref);
@ -735,7 +735,7 @@ static void gdb_cmd_query(struct kgdb_state *ks)
finished = 1;
}
i++;
} while_each_thread(g, p);
}
*(--ptr) = '\0';
break;
@ -1061,7 +1061,6 @@ int gdb_serial_stub(struct kgdb_state *ks)
error_packet(remcom_out_buffer, -EINVAL);
break;
}
dbg_activate_sw_breakpoints();
fallthrough; /* to default processing */
default:
default_handle:

View File

@ -306,6 +306,15 @@ static int kdb_bp(int argc, const char **argv)
if (!template.bp_addr)
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
*/

View File

@ -149,14 +149,14 @@ kdb_bt(int argc, const char **argv)
return 0;
}
/* Now the inactive tasks */
kdb_do_each_thread(g, p) {
for_each_process_thread(g, p) {
if (KDB_FLAG(CMD_INTERRUPT))
return 0;
if (task_curr(p))
continue;
if (kdb_bt1(p, mask, btaprompt))
return 0;
} kdb_while_each_thread(g, p);
}
} else if (strcmp(argv[0], "btp") == 0) {
struct task_struct *p;
unsigned long pid;

View File

@ -147,7 +147,6 @@ int kdb_stub(struct kgdb_state *ks)
return DBG_PASS_EVENT;
}
kdb_bp_install(ks->linux_regs);
dbg_activate_sw_breakpoints();
/* Set the exit state to a single step or a continue */
if (KDB_STATE(DOING_SS))
gdbstub_state(ks, "s");
@ -167,7 +166,6 @@ int kdb_stub(struct kgdb_state *ks)
* differently vs the gdbstub
*/
kgdb_single_step = 0;
dbg_deactivate_sw_breakpoints();
return DBG_SWITCH_CPU_EVENT;
}
return kgdb_info[ks->cpu].ret_state;

View File

@ -545,18 +545,18 @@ static int kdb_search_string(char *searched, char *searchfor)
static void kdb_msg_write(const char *msg, int msg_len)
{
struct console *c;
const char *cp;
int len;
if (msg_len == 0)
return;
if (dbg_io_ops) {
const char *cp = msg;
int len = msg_len;
cp = msg;
len = msg_len;
while (len--) {
dbg_io_ops->write_char(*cp);
cp++;
}
while (len--) {
dbg_io_ops->write_char(*cp);
cp++;
}
for_each_console(c) {
@ -706,12 +706,16 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
size_avail = sizeof(kdb_buffer) - len;
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
* 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;
}
/*
* at this point the string is a full line and
* should be printed, up to the null.

View File

@ -2299,10 +2299,10 @@ void kdb_ps_suppressed(void)
if (kdb_task_state(p, mask_I))
++idle;
}
kdb_do_each_thread(g, p) {
for_each_process_thread(g, p) {
if (kdb_task_state(p, mask_M))
++daemon;
} kdb_while_each_thread(g, p);
}
if (idle || daemon) {
if (idle)
kdb_printf("%d idle process%s (state I)%s\n",
@ -2370,12 +2370,12 @@ static int kdb_ps(int argc, const char **argv)
}
kdb_printf("\n");
/* Now the real tasks */
kdb_do_each_thread(g, p) {
for_each_process_thread(g, p) {
if (KDB_FLAG(CMD_INTERRUPT))
return 0;
if (kdb_task_state(p, mask))
kdb_ps1(p);
} kdb_while_each_thread(g, p);
}
return 0;
}

View File

@ -230,10 +230,6 @@ extern struct task_struct *kdb_curr_task(int);
#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)
extern void *debug_kmalloc(size_t size, gfp_t flags);

View File

@ -24,6 +24,21 @@ menuconfig 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
tristate "KGDB: use kgdb over the serial console"
select CONSOLE_POLL