diff options
Diffstat (limited to 'kernel/debug/kdb/kdb_main.c')
| -rw-r--r-- | kernel/debug/kdb/kdb_main.c | 267 | 
1 files changed, 171 insertions, 96 deletions
diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index 379650b984f8..f191bddf64b8 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -12,6 +12,7 @@   */  #include <linux/ctype.h> +#include <linux/types.h>  #include <linux/string.h>  #include <linux/kernel.h>  #include <linux/kmsg_dump.h> @@ -23,6 +24,7 @@  #include <linux/vmalloc.h>  #include <linux/atomic.h>  #include <linux/module.h> +#include <linux/moduleparam.h>  #include <linux/mm.h>  #include <linux/init.h>  #include <linux/kallsyms.h> @@ -42,6 +44,12 @@  #include <linux/slab.h>  #include "kdb_private.h" +#undef	MODULE_PARAM_PREFIX +#define	MODULE_PARAM_PREFIX "kdb." + +static int kdb_cmd_enabled = CONFIG_KDB_DEFAULT_ENABLE; +module_param_named(cmd_enable, kdb_cmd_enabled, int, 0600); +  #define GREP_LEN 256  char kdb_grep_string[GREP_LEN];  int kdb_grepping_flag; @@ -121,6 +129,7 @@ static kdbmsg_t kdbmsgs[] = {  	KDBMSG(BADLENGTH, "Invalid length field"),  	KDBMSG(NOBP, "No Breakpoint exists"),  	KDBMSG(BADADDR, "Invalid address"), +	KDBMSG(NOPERM, "Permission denied"),  };  #undef KDBMSG @@ -188,6 +197,26 @@ struct task_struct *kdb_curr_task(int cpu)  }  /* + * Check whether the flags of the current command and the permissions + * of the kdb console has allow a command to be run. + */ +static inline bool kdb_check_flags(kdb_cmdflags_t flags, int permissions, +				   bool no_args) +{ +	/* permissions comes from userspace so needs massaging slightly */ +	permissions &= KDB_ENABLE_MASK; +	permissions |= KDB_ENABLE_ALWAYS_SAFE; + +	/* some commands change group when launched with no arguments */ +	if (no_args) +		permissions |= permissions << KDB_ENABLE_NO_ARGS_SHIFT; + +	flags |= KDB_ENABLE_ALL; + +	return permissions & flags; +} + +/*   * kdbgetenv - This function will return the character string value of   *	an environment variable.   * Parameters: @@ -476,6 +505,15 @@ int kdbgetaddrarg(int argc, const char **argv, int *nextarg,  	kdb_symtab_t symtab;  	/* +	 * If the enable flags prohibit both arbitrary memory access +	 * and flow control then there are no reasonable grounds to +	 * provide symbol lookup. +	 */ +	if (!kdb_check_flags(KDB_ENABLE_MEM_READ | KDB_ENABLE_FLOW_CTRL, +			     kdb_cmd_enabled, false)) +		return KDB_NOPERM; + +	/*  	 * Process arguments which follow the following syntax:  	 *  	 *  symbol | numeric-address [+/- numeric-offset] @@ -641,8 +679,13 @@ static int kdb_defcmd2(const char *cmdstr, const char *argv0)  		if (!s->count)  			s->usable = 0;  		if (s->usable) -			kdb_register(s->name, kdb_exec_defcmd, -				     s->usage, s->help, 0); +			/* macros are always safe because when executed each +			 * internal command re-enters kdb_parse() and is +			 * safety checked individually. +			 */ +			kdb_register_flags(s->name, kdb_exec_defcmd, s->usage, +					   s->help, 0, +					   KDB_ENABLE_ALWAYS_SAFE);  		return 0;  	}  	if (!s->usable) @@ -1003,25 +1046,22 @@ int kdb_parse(const char *cmdstr)  	if (i < kdb_max_commands) {  		int result; + +		if (!kdb_check_flags(tp->cmd_flags, kdb_cmd_enabled, argc <= 1)) +			return KDB_NOPERM; +  		KDB_STATE_SET(CMD);  		result = (*tp->cmd_func)(argc-1, (const char **)argv);  		if (result && ignore_errors && result > KDB_CMD_GO)  			result = 0;  		KDB_STATE_CLEAR(CMD); -		switch (tp->cmd_repeat) { -		case KDB_REPEAT_NONE: -			argc = 0; -			if (argv[0]) -				*(argv[0]) = '\0'; -			break; -		case KDB_REPEAT_NO_ARGS: -			argc = 1; -			if (argv[1]) -				*(argv[1]) = '\0'; -			break; -		case KDB_REPEAT_WITH_ARGS: -			break; -		} + +		if (tp->cmd_flags & KDB_REPEAT_WITH_ARGS) +			return result; + +		argc = tp->cmd_flags & KDB_REPEAT_NO_ARGS ? 1 : 0; +		if (argv[argc]) +			*(argv[argc]) = '\0';  		return result;  	} @@ -1921,10 +1961,14 @@ static int kdb_rm(int argc, const char **argv)   */  static int kdb_sr(int argc, const char **argv)  { +	bool check_mask = +	    !kdb_check_flags(KDB_ENABLE_ALL, kdb_cmd_enabled, false); +  	if (argc != 1)  		return KDB_ARGCOUNT; +  	kdb_trap_printk++; -	__handle_sysrq(*argv[1], false); +	__handle_sysrq(*argv[1], check_mask);  	kdb_trap_printk--;  	return 0; @@ -2157,6 +2201,8 @@ static void kdb_cpu_status(void)  	for (start_cpu = -1, i = 0; i < NR_CPUS; i++) {  		if (!cpu_online(i)) {  			state = 'F';	/* cpu is offline */ +		} else if (!kgdb_info[i].enter_kgdb) { +			state = 'D';	/* cpu is online but unresponsive */  		} else {  			state = ' ';	/* cpu is responding to kdb */  			if (kdb_task_state_char(KDB_TSK(i)) == 'I') @@ -2210,7 +2256,7 @@ static int kdb_cpu(int argc, const char **argv)  	/*  	 * Validate cpunum  	 */ -	if ((cpunum > NR_CPUS) || !cpu_online(cpunum)) +	if ((cpunum > NR_CPUS) || !kgdb_info[cpunum].enter_kgdb)  		return KDB_BADCPUNUM;  	dbg_switch_cpu = cpunum; @@ -2375,6 +2421,8 @@ static int kdb_help(int argc, const char **argv)  			return 0;  		if (!kt->cmd_name)  			continue; +		if (!kdb_check_flags(kt->cmd_flags, kdb_cmd_enabled, true)) +			continue;  		if (strlen(kt->cmd_usage) > 20)  			space = "\n                                    ";  		kdb_printf("%-15.15s %-20s%s%s\n", kt->cmd_name, @@ -2629,7 +2677,7 @@ static int kdb_grep_help(int argc, const char **argv)  }  /* - * kdb_register_repeat - This function is used to register a kernel + * kdb_register_flags - This function is used to register a kernel   * 	debugger command.   * Inputs:   *	cmd	Command name @@ -2641,12 +2689,12 @@ static int kdb_grep_help(int argc, const char **argv)   *	zero for success, one if a duplicate command.   */  #define kdb_command_extend 50	/* arbitrary */ -int kdb_register_repeat(char *cmd, -			kdb_func_t func, -			char *usage, -			char *help, -			short minlen, -			kdb_repeat_t repeat) +int kdb_register_flags(char *cmd, +		       kdb_func_t func, +		       char *usage, +		       char *help, +		       short minlen, +		       kdb_cmdflags_t flags)  {  	int i;  	kdbtab_t *kp; @@ -2694,19 +2742,18 @@ int kdb_register_repeat(char *cmd,  	kp->cmd_func   = func;  	kp->cmd_usage  = usage;  	kp->cmd_help   = help; -	kp->cmd_flags  = 0;  	kp->cmd_minlen = minlen; -	kp->cmd_repeat = repeat; +	kp->cmd_flags  = flags;  	return 0;  } -EXPORT_SYMBOL_GPL(kdb_register_repeat); +EXPORT_SYMBOL_GPL(kdb_register_flags);  /*   * kdb_register - Compatibility register function for commands that do   *	not need to specify a repeat state.  Equivalent to - *	kdb_register_repeat with KDB_REPEAT_NONE. + *	kdb_register_flags with flags set to 0.   * Inputs:   *	cmd	Command name   *	func	Function to execute the command @@ -2721,8 +2768,7 @@ int kdb_register(char *cmd,  	     char *help,  	     short minlen)  { -	return kdb_register_repeat(cmd, func, usage, help, minlen, -				   KDB_REPEAT_NONE); +	return kdb_register_flags(cmd, func, usage, help, minlen, 0);  }  EXPORT_SYMBOL_GPL(kdb_register); @@ -2764,80 +2810,109 @@ static void __init kdb_inittab(void)  	for_each_kdbcmd(kp, i)  		kp->cmd_name = NULL; -	kdb_register_repeat("md", kdb_md, "<vaddr>", +	kdb_register_flags("md", kdb_md, "<vaddr>",  	  "Display Memory Contents, also mdWcN, e.g. md8c1", 1, -			    KDB_REPEAT_NO_ARGS); -	kdb_register_repeat("mdr", kdb_md, "<vaddr> <bytes>", -	  "Display Raw Memory", 0, KDB_REPEAT_NO_ARGS); -	kdb_register_repeat("mdp", kdb_md, "<paddr> <bytes>", -	  "Display Physical Memory", 0, KDB_REPEAT_NO_ARGS); -	kdb_register_repeat("mds", kdb_md, "<vaddr>", -	  "Display Memory Symbolically", 0, KDB_REPEAT_NO_ARGS); -	kdb_register_repeat("mm", kdb_mm, "<vaddr> <contents>", -	  "Modify Memory Contents", 0, KDB_REPEAT_NO_ARGS); -	kdb_register_repeat("go", kdb_go, "[<vaddr>]", -	  "Continue Execution", 1, KDB_REPEAT_NONE); -	kdb_register_repeat("rd", kdb_rd, "", -	  "Display Registers", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("rm", kdb_rm, "<reg> <contents>", -	  "Modify Registers", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("ef", kdb_ef, "<vaddr>", -	  "Display exception frame", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("bt", kdb_bt, "[<vaddr>]", -	  "Stack traceback", 1, KDB_REPEAT_NONE); -	kdb_register_repeat("btp", kdb_bt, "<pid>", -	  "Display stack for process <pid>", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("bta", kdb_bt, "[D|R|S|T|C|Z|E|U|I|M|A]", -	  "Backtrace all processes matching state flag", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("btc", kdb_bt, "", -	  "Backtrace current process on each cpu", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("btt", kdb_bt, "<vaddr>", +	  KDB_ENABLE_MEM_READ | KDB_REPEAT_NO_ARGS); +	kdb_register_flags("mdr", kdb_md, "<vaddr> <bytes>", +	  "Display Raw Memory", 0, +	  KDB_ENABLE_MEM_READ | KDB_REPEAT_NO_ARGS); +	kdb_register_flags("mdp", kdb_md, "<paddr> <bytes>", +	  "Display Physical Memory", 0, +	  KDB_ENABLE_MEM_READ | KDB_REPEAT_NO_ARGS); +	kdb_register_flags("mds", kdb_md, "<vaddr>", +	  "Display Memory Symbolically", 0, +	  KDB_ENABLE_MEM_READ | KDB_REPEAT_NO_ARGS); +	kdb_register_flags("mm", kdb_mm, "<vaddr> <contents>", +	  "Modify Memory Contents", 0, +	  KDB_ENABLE_MEM_WRITE | KDB_REPEAT_NO_ARGS); +	kdb_register_flags("go", kdb_go, "[<vaddr>]", +	  "Continue Execution", 1, +	  KDB_ENABLE_REG_WRITE | KDB_ENABLE_ALWAYS_SAFE_NO_ARGS); +	kdb_register_flags("rd", kdb_rd, "", +	  "Display Registers", 0, +	  KDB_ENABLE_REG_READ); +	kdb_register_flags("rm", kdb_rm, "<reg> <contents>", +	  "Modify Registers", 0, +	  KDB_ENABLE_REG_WRITE); +	kdb_register_flags("ef", kdb_ef, "<vaddr>", +	  "Display exception frame", 0, +	  KDB_ENABLE_MEM_READ); +	kdb_register_flags("bt", kdb_bt, "[<vaddr>]", +	  "Stack traceback", 1, +	  KDB_ENABLE_MEM_READ | KDB_ENABLE_INSPECT_NO_ARGS); +	kdb_register_flags("btp", kdb_bt, "<pid>", +	  "Display stack for process <pid>", 0, +	  KDB_ENABLE_INSPECT); +	kdb_register_flags("bta", kdb_bt, "[D|R|S|T|C|Z|E|U|I|M|A]", +	  "Backtrace all processes matching state flag", 0, +	  KDB_ENABLE_INSPECT); +	kdb_register_flags("btc", kdb_bt, "", +	  "Backtrace current process on each cpu", 0, +	  KDB_ENABLE_INSPECT); +	kdb_register_flags("btt", kdb_bt, "<vaddr>",  	  "Backtrace process given its struct task address", 0, -			    KDB_REPEAT_NONE); -	kdb_register_repeat("env", kdb_env, "", -	  "Show environment variables", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("set", kdb_set, "", -	  "Set environment variables", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("help", kdb_help, "", -	  "Display Help Message", 1, KDB_REPEAT_NONE); -	kdb_register_repeat("?", kdb_help, "", -	  "Display Help Message", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("cpu", kdb_cpu, "<cpunum>", -	  "Switch to new cpu", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("kgdb", kdb_kgdb, "", -	  "Enter kgdb mode", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("ps", kdb_ps, "[<flags>|A]", -	  "Display active task list", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("pid", kdb_pid, "<pidnum>", -	  "Switch to another task", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("reboot", kdb_reboot, "", -	  "Reboot the machine immediately", 0, KDB_REPEAT_NONE); +	  KDB_ENABLE_MEM_READ | KDB_ENABLE_INSPECT_NO_ARGS); +	kdb_register_flags("env", kdb_env, "", +	  "Show environment variables", 0, +	  KDB_ENABLE_ALWAYS_SAFE); +	kdb_register_flags("set", kdb_set, "", +	  "Set environment variables", 0, +	  KDB_ENABLE_ALWAYS_SAFE); +	kdb_register_flags("help", kdb_help, "", +	  "Display Help Message", 1, +	  KDB_ENABLE_ALWAYS_SAFE); +	kdb_register_flags("?", kdb_help, "", +	  "Display Help Message", 0, +	  KDB_ENABLE_ALWAYS_SAFE); +	kdb_register_flags("cpu", kdb_cpu, "<cpunum>", +	  "Switch to new cpu", 0, +	  KDB_ENABLE_ALWAYS_SAFE_NO_ARGS); +	kdb_register_flags("kgdb", kdb_kgdb, "", +	  "Enter kgdb mode", 0, 0); +	kdb_register_flags("ps", kdb_ps, "[<flags>|A]", +	  "Display active task list", 0, +	  KDB_ENABLE_INSPECT); +	kdb_register_flags("pid", kdb_pid, "<pidnum>", +	  "Switch to another task", 0, +	  KDB_ENABLE_INSPECT); +	kdb_register_flags("reboot", kdb_reboot, "", +	  "Reboot the machine immediately", 0, +	  KDB_ENABLE_REBOOT);  #if defined(CONFIG_MODULES) -	kdb_register_repeat("lsmod", kdb_lsmod, "", -	  "List loaded kernel modules", 0, KDB_REPEAT_NONE); +	kdb_register_flags("lsmod", kdb_lsmod, "", +	  "List loaded kernel modules", 0, +	  KDB_ENABLE_INSPECT);  #endif  #if defined(CONFIG_MAGIC_SYSRQ) -	kdb_register_repeat("sr", kdb_sr, "<key>", -	  "Magic SysRq key", 0, KDB_REPEAT_NONE); +	kdb_register_flags("sr", kdb_sr, "<key>", +	  "Magic SysRq key", 0, +	  KDB_ENABLE_ALWAYS_SAFE);  #endif  #if defined(CONFIG_PRINTK) -	kdb_register_repeat("dmesg", kdb_dmesg, "[lines]", -	  "Display syslog buffer", 0, KDB_REPEAT_NONE); +	kdb_register_flags("dmesg", kdb_dmesg, "[lines]", +	  "Display syslog buffer", 0, +	  KDB_ENABLE_ALWAYS_SAFE);  #endif  	if (arch_kgdb_ops.enable_nmi) { -		kdb_register_repeat("disable_nmi", kdb_disable_nmi, "", -		  "Disable NMI entry to KDB", 0, KDB_REPEAT_NONE); -	} -	kdb_register_repeat("defcmd", kdb_defcmd, "name \"usage\" \"help\"", -	  "Define a set of commands, down to endefcmd", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("kill", kdb_kill, "<-signal> <pid>", -	  "Send a signal to a process", 0, KDB_REPEAT_NONE); -	kdb_register_repeat("summary", kdb_summary, "", -	  "Summarize the system", 4, KDB_REPEAT_NONE); -	kdb_register_repeat("per_cpu", kdb_per_cpu, "<sym> [<bytes>] [<cpu>]", -	  "Display per_cpu variables", 3, KDB_REPEAT_NONE); -	kdb_register_repeat("grephelp", kdb_grep_help, "", -	  "Display help on | grep", 0, KDB_REPEAT_NONE); +		kdb_register_flags("disable_nmi", kdb_disable_nmi, "", +		  "Disable NMI entry to KDB", 0, +		  KDB_ENABLE_ALWAYS_SAFE); +	} +	kdb_register_flags("defcmd", kdb_defcmd, "name \"usage\" \"help\"", +	  "Define a set of commands, down to endefcmd", 0, +	  KDB_ENABLE_ALWAYS_SAFE); +	kdb_register_flags("kill", kdb_kill, "<-signal> <pid>", +	  "Send a signal to a process", 0, +	  KDB_ENABLE_SIGNAL); +	kdb_register_flags("summary", kdb_summary, "", +	  "Summarize the system", 4, +	  KDB_ENABLE_ALWAYS_SAFE); +	kdb_register_flags("per_cpu", kdb_per_cpu, "<sym> [<bytes>] [<cpu>]", +	  "Display per_cpu variables", 3, +	  KDB_ENABLE_MEM_READ); +	kdb_register_flags("grephelp", kdb_grep_help, "", +	  "Display help on | grep", 0, +	  KDB_ENABLE_ALWAYS_SAFE);  }  /* Execute any commands defined in kdb_cmds.  */  |