kernel_optimize_test/kernel/debug/kdb/kdb_bp.c
Daniel Thompson f2d10ff4a9 kgdb: Honour the kprobe blocklist when setting breakpoints
Currently kgdb has absolutely no safety rails in place to discourage or
prevent a user from placing a breakpoint in dangerous places such as
the debugger's own trap entry/exit and other places where it is not safe
to take synchronous traps.

Introduce a new config symbol KGDB_HONOUR_BLOCKLIST and modify the
default implementation of kgdb_validate_break_address() so that we use
the kprobe blocklist to prohibit instrumentation of critical functions
if the config symbol is set. The config symbol dependencies are set to
ensure that the blocklist will be enabled by default if we enable KGDB
and are compiling for an architecture where we HAVE_KPROBES.

Suggested-by: Peter Zijlstra <peterz@infradead.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Masami Hiramatsu <mhiramat@kernel.org>
Link: https://lore.kernel.org/r/20200927211531.1380577-2-daniel.thompson@linaro.org
Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
2020-09-28 12:14:08 +01:00

567 lines
11 KiB
C

/*
* Kernel Debugger Architecture Independent Breakpoint Handler
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved.
* Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved.
*/
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kdb.h>
#include <linux/kgdb.h>
#include <linux/smp.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include "kdb_private.h"
/*
* Table of kdb_breakpoints
*/
kdb_bp_t kdb_breakpoints[KDB_MAXBPT];
static void kdb_setsinglestep(struct pt_regs *regs)
{
KDB_STATE_SET(DOING_SS);
}
static char *kdb_rwtypes[] = {
"Instruction(i)",
"Instruction(Register)",
"Data Write",
"I/O",
"Data Access"
};
static char *kdb_bptype(kdb_bp_t *bp)
{
if (bp->bp_type < 0 || bp->bp_type > 4)
return "";
return kdb_rwtypes[bp->bp_type];
}
static int kdb_parsebp(int argc, const char **argv, int *nextargp, kdb_bp_t *bp)
{
int nextarg = *nextargp;
int diag;
bp->bph_length = 1;
if ((argc + 1) != nextarg) {
if (strncasecmp(argv[nextarg], "datar", sizeof("datar")) == 0)
bp->bp_type = BP_ACCESS_WATCHPOINT;
else if (strncasecmp(argv[nextarg], "dataw", sizeof("dataw")) == 0)
bp->bp_type = BP_WRITE_WATCHPOINT;
else if (strncasecmp(argv[nextarg], "inst", sizeof("inst")) == 0)
bp->bp_type = BP_HARDWARE_BREAKPOINT;
else
return KDB_ARGCOUNT;
bp->bph_length = 1;
nextarg++;
if ((argc + 1) != nextarg) {
unsigned long len;
diag = kdbgetularg((char *)argv[nextarg],
&len);
if (diag)
return diag;
if (len > 8)
return KDB_BADLENGTH;
bp->bph_length = len;
nextarg++;
}
if ((argc + 1) != nextarg)
return KDB_ARGCOUNT;
}
*nextargp = nextarg;
return 0;
}
static int _kdb_bp_remove(kdb_bp_t *bp)
{
int ret = 1;
if (!bp->bp_installed)
return ret;
if (!bp->bp_type)
ret = dbg_remove_sw_break(bp->bp_addr);
else
ret = arch_kgdb_ops.remove_hw_breakpoint(bp->bp_addr,
bp->bph_length,
bp->bp_type);
if (ret == 0)
bp->bp_installed = 0;
return ret;
}
static void kdb_handle_bp(struct pt_regs *regs, kdb_bp_t *bp)
{
if (KDB_DEBUG(BP))
kdb_printf("regs->ip = 0x%lx\n", instruction_pointer(regs));
/*
* Setup single step
*/
kdb_setsinglestep(regs);
/*
* Reset delay attribute
*/
bp->bp_delay = 0;
bp->bp_delayed = 1;
}
static int _kdb_bp_install(struct pt_regs *regs, kdb_bp_t *bp)
{
int ret;
/*
* Install the breakpoint, if it is not already installed.
*/
if (KDB_DEBUG(BP))
kdb_printf("%s: bp_installed %d\n",
__func__, bp->bp_installed);
if (!KDB_STATE(SSBPT))
bp->bp_delay = 0;
if (bp->bp_installed)
return 1;
if (bp->bp_delay || (bp->bp_delayed && KDB_STATE(DOING_SS))) {
if (KDB_DEBUG(BP))
kdb_printf("%s: delayed bp\n", __func__);
kdb_handle_bp(regs, bp);
return 0;
}
if (!bp->bp_type)
ret = dbg_set_sw_break(bp->bp_addr);
else
ret = arch_kgdb_ops.set_hw_breakpoint(bp->bp_addr,
bp->bph_length,
bp->bp_type);
if (ret == 0) {
bp->bp_installed = 1;
} else {
kdb_printf("%s: failed to set breakpoint at 0x%lx\n",
__func__, bp->bp_addr);
if (!bp->bp_type) {
kdb_printf("Software breakpoints are unavailable.\n"
" Boot the kernel with rodata=off\n"
" OR use hw breaks: help bph\n");
}
return 1;
}
return 0;
}
/*
* kdb_bp_install
*
* Install kdb_breakpoints prior to returning from the
* kernel debugger. This allows the kdb_breakpoints to be set
* upon functions that are used internally by kdb, such as
* printk(). This function is only called once per kdb session.
*/
void kdb_bp_install(struct pt_regs *regs)
{
int i;
for (i = 0; i < KDB_MAXBPT; i++) {
kdb_bp_t *bp = &kdb_breakpoints[i];
if (KDB_DEBUG(BP)) {
kdb_printf("%s: bp %d bp_enabled %d\n",
__func__, i, bp->bp_enabled);
}
if (bp->bp_enabled)
_kdb_bp_install(regs, bp);
}
}
/*
* kdb_bp_remove
*
* Remove kdb_breakpoints upon entry to the kernel debugger.
*
* Parameters:
* None.
* Outputs:
* None.
* Returns:
* None.
* Locking:
* None.
* Remarks:
*/
void kdb_bp_remove(void)
{
int i;
for (i = KDB_MAXBPT - 1; i >= 0; i--) {
kdb_bp_t *bp = &kdb_breakpoints[i];
if (KDB_DEBUG(BP)) {
kdb_printf("%s: bp %d bp_enabled %d\n",
__func__, i, bp->bp_enabled);
}
if (bp->bp_enabled)
_kdb_bp_remove(bp);
}
}
/*
* kdb_printbp
*
* Internal function to format and print a breakpoint entry.
*
* Parameters:
* None.
* Outputs:
* None.
* Returns:
* None.
* Locking:
* None.
* Remarks:
*/
static void kdb_printbp(kdb_bp_t *bp, int i)
{
kdb_printf("%s ", kdb_bptype(bp));
kdb_printf("BP #%d at ", i);
kdb_symbol_print(bp->bp_addr, NULL, KDB_SP_DEFAULT);
if (bp->bp_enabled)
kdb_printf("\n is enabled ");
else
kdb_printf("\n is disabled");
kdb_printf(" addr at %016lx, hardtype=%d installed=%d\n",
bp->bp_addr, bp->bp_type, bp->bp_installed);
kdb_printf("\n");
}
/*
* kdb_bp
*
* Handle the bp commands.
*
* [bp|bph] <addr-expression> [DATAR|DATAW]
*
* Parameters:
* argc Count of arguments in argv
* argv Space delimited command line arguments
* Outputs:
* None.
* Returns:
* Zero for success, a kdb diagnostic if failure.
* Locking:
* None.
* Remarks:
*
* bp Set breakpoint on all cpus. Only use hardware assist if need.
* bph Set breakpoint on all cpus. Force hardware register
*/
static int kdb_bp(int argc, const char **argv)
{
int i, bpno;
kdb_bp_t *bp, *bp_check;
int diag;
char *symname = NULL;
long offset = 0ul;
int nextarg;
kdb_bp_t template = {0};
if (argc == 0) {
/*
* Display breakpoint table
*/
for (bpno = 0, bp = kdb_breakpoints; bpno < KDB_MAXBPT;
bpno++, bp++) {
if (bp->bp_free)
continue;
kdb_printbp(bp, bpno);
}
return 0;
}
nextarg = 1;
diag = kdbgetaddrarg(argc, argv, &nextarg, &template.bp_addr,
&offset, &symname);
if (diag)
return diag;
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
*/
for (bpno = 0, bp = kdb_breakpoints; bpno < KDB_MAXBPT; bpno++, bp++) {
if (bp->bp_free)
break;
}
if (bpno == KDB_MAXBPT)
return KDB_TOOMANYBPT;
if (strcmp(argv[0], "bph") == 0) {
template.bp_type = BP_HARDWARE_BREAKPOINT;
diag = kdb_parsebp(argc, argv, &nextarg, &template);
if (diag)
return diag;
} else {
template.bp_type = BP_BREAKPOINT;
}
/*
* Check for clashing breakpoints.
*
* Note, in this design we can't have hardware breakpoints
* enabled for both read and write on the same address.
*/
for (i = 0, bp_check = kdb_breakpoints; i < KDB_MAXBPT;
i++, bp_check++) {
if (!bp_check->bp_free &&
bp_check->bp_addr == template.bp_addr) {
kdb_printf("You already have a breakpoint at "
kdb_bfd_vma_fmt0 "\n", template.bp_addr);
return KDB_DUPBPT;
}
}
template.bp_enabled = 1;
/*
* Actually allocate the breakpoint found earlier
*/
*bp = template;
bp->bp_free = 0;
kdb_printbp(bp, bpno);
return 0;
}
/*
* kdb_bc
*
* Handles the 'bc', 'be', and 'bd' commands
*
* [bd|bc|be] <breakpoint-number>
* [bd|bc|be] *
*
* Parameters:
* argc Count of arguments in argv
* argv Space delimited command line arguments
* Outputs:
* None.
* Returns:
* Zero for success, a kdb diagnostic for failure
* Locking:
* None.
* Remarks:
*/
static int kdb_bc(int argc, const char **argv)
{
unsigned long addr;
kdb_bp_t *bp = NULL;
int lowbp = KDB_MAXBPT;
int highbp = 0;
int done = 0;
int i;
int diag = 0;
int cmd; /* KDBCMD_B? */
#define KDBCMD_BC 0
#define KDBCMD_BE 1
#define KDBCMD_BD 2
if (strcmp(argv[0], "be") == 0)
cmd = KDBCMD_BE;
else if (strcmp(argv[0], "bd") == 0)
cmd = KDBCMD_BD;
else
cmd = KDBCMD_BC;
if (argc != 1)
return KDB_ARGCOUNT;
if (strcmp(argv[1], "*") == 0) {
lowbp = 0;
highbp = KDB_MAXBPT;
} else {
diag = kdbgetularg(argv[1], &addr);
if (diag)
return diag;
/*
* For addresses less than the maximum breakpoint number,
* assume that the breakpoint number is desired.
*/
if (addr < KDB_MAXBPT) {
lowbp = highbp = addr;
highbp++;
} else {
for (i = 0, bp = kdb_breakpoints; i < KDB_MAXBPT;
i++, bp++) {
if (bp->bp_addr == addr) {
lowbp = highbp = i;
highbp++;
break;
}
}
}
}
/*
* Now operate on the set of breakpoints matching the input
* criteria (either '*' for all, or an individual breakpoint).
*/
for (bp = &kdb_breakpoints[lowbp], i = lowbp;
i < highbp;
i++, bp++) {
if (bp->bp_free)
continue;
done++;
switch (cmd) {
case KDBCMD_BC:
bp->bp_enabled = 0;
kdb_printf("Breakpoint %d at "
kdb_bfd_vma_fmt " cleared\n",
i, bp->bp_addr);
bp->bp_addr = 0;
bp->bp_free = 1;
break;
case KDBCMD_BE:
bp->bp_enabled = 1;
kdb_printf("Breakpoint %d at "
kdb_bfd_vma_fmt " enabled",
i, bp->bp_addr);
kdb_printf("\n");
break;
case KDBCMD_BD:
if (!bp->bp_enabled)
break;
bp->bp_enabled = 0;
kdb_printf("Breakpoint %d at "
kdb_bfd_vma_fmt " disabled\n",
i, bp->bp_addr);
break;
}
if (bp->bp_delay && (cmd == KDBCMD_BC || cmd == KDBCMD_BD)) {
bp->bp_delay = 0;
KDB_STATE_CLEAR(SSBPT);
}
}
return (!done) ? KDB_BPTNOTFOUND : 0;
}
/*
* kdb_ss
*
* Process the 'ss' (Single Step) command.
*
* ss
*
* Parameters:
* argc Argument count
* argv Argument vector
* Outputs:
* None.
* Returns:
* KDB_CMD_SS for success, a kdb error if failure.
* Locking:
* None.
* Remarks:
*
* Set the arch specific option to trigger a debug trap after the next
* instruction.
*/
static int kdb_ss(int argc, const char **argv)
{
if (argc != 0)
return KDB_ARGCOUNT;
/*
* Set trace flag and go.
*/
KDB_STATE_SET(DOING_SS);
return KDB_CMD_SS;
}
/* Initialize the breakpoint table and register breakpoint commands. */
void __init kdb_initbptab(void)
{
int i;
kdb_bp_t *bp;
/*
* First time initialization.
*/
memset(&kdb_breakpoints, '\0', sizeof(kdb_breakpoints));
for (i = 0, bp = kdb_breakpoints; i < KDB_MAXBPT; i++, bp++)
bp->bp_free = 1;
kdb_register_flags("bp", kdb_bp, "[<vaddr>]",
"Set/Display breakpoints", 0,
KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS);
kdb_register_flags("bl", kdb_bp, "[<vaddr>]",
"Display breakpoints", 0,
KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS);
if (arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT)
kdb_register_flags("bph", kdb_bp, "[<vaddr>]",
"[datar [length]|dataw [length]] Set hw brk", 0,
KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS);
kdb_register_flags("bc", kdb_bc, "<bpnum>",
"Clear Breakpoint", 0,
KDB_ENABLE_FLOW_CTRL);
kdb_register_flags("be", kdb_bc, "<bpnum>",
"Enable Breakpoint", 0,
KDB_ENABLE_FLOW_CTRL);
kdb_register_flags("bd", kdb_bc, "<bpnum>",
"Disable Breakpoint", 0,
KDB_ENABLE_FLOW_CTRL);
kdb_register_flags("ss", kdb_ss, "",
"Single Step", 1,
KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS);
/*
* Architecture dependent initialization.
*/
}