Merge branches 'acpi-battery', 'acpi-video' and 'acpi-misc'
[sfrench/cifs-2.6.git] / kernel / debug / kdb / kdb_main.c
index fa6deda894a174d28dee00846a8f8309fae86ab3..ead4da947127075e825c9085ff20d69a23046dd4 100644 (file)
@@ -45,6 +45,7 @@
 #include <linux/proc_fs.h>
 #include <linux/uaccess.h>
 #include <linux/slab.h>
+#include <linux/security.h>
 #include "kdb_private.h"
 
 #undef MODULE_PARAM_PREFIX
@@ -166,10 +167,62 @@ 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.
+ * Update the permissions flags (kdb_cmd_enabled) to match the
+ * current lockdown state.
+ *
+ * Within this function the calls to security_locked_down() are "lazy". We
+ * avoid calling them if the current value of kdb_cmd_enabled already excludes
+ * flags that might be subject to lockdown. Additionally we deliberately check
+ * the lockdown flags independently (even though read lockdown implies write
+ * lockdown) since that results in both simpler code and clearer messages to
+ * the user on first-time debugger entry.
+ *
+ * The permission masks during a read+write lockdown permits the following
+ * flags: INSPECT, SIGNAL, REBOOT (and ALWAYS_SAFE).
+ *
+ * The INSPECT commands are not blocked during lockdown because they are
+ * not arbitrary memory reads. INSPECT covers the backtrace family (sometimes
+ * forcing them to have no arguments) and lsmod. These commands do expose
+ * some kernel state but do not allow the developer seated at the console to
+ * choose what state is reported. SIGNAL and REBOOT should not be controversial,
+ * given these are allowed for root during lockdown already.
+ */
+static void kdb_check_for_lockdown(void)
+{
+       const int write_flags = KDB_ENABLE_MEM_WRITE |
+                               KDB_ENABLE_REG_WRITE |
+                               KDB_ENABLE_FLOW_CTRL;
+       const int read_flags = KDB_ENABLE_MEM_READ |
+                              KDB_ENABLE_REG_READ;
+
+       bool need_to_lockdown_write = false;
+       bool need_to_lockdown_read = false;
+
+       if (kdb_cmd_enabled & (KDB_ENABLE_ALL | write_flags))
+               need_to_lockdown_write =
+                       security_locked_down(LOCKDOWN_DBG_WRITE_KERNEL);
+
+       if (kdb_cmd_enabled & (KDB_ENABLE_ALL | read_flags))
+               need_to_lockdown_read =
+                       security_locked_down(LOCKDOWN_DBG_READ_KERNEL);
+
+       /* De-compose KDB_ENABLE_ALL if required */
+       if (need_to_lockdown_write || need_to_lockdown_read)
+               if (kdb_cmd_enabled & KDB_ENABLE_ALL)
+                       kdb_cmd_enabled = KDB_ENABLE_MASK & ~KDB_ENABLE_ALL;
+
+       if (need_to_lockdown_write)
+               kdb_cmd_enabled &= ~write_flags;
+
+       if (need_to_lockdown_read)
+               kdb_cmd_enabled &= ~read_flags;
+}
+
+/*
+ * Check whether the flags of the current command, the permissions of the kdb
+ * console and the lockdown state allow a command to be run.
  */
-static inline bool kdb_check_flags(kdb_cmdflags_t flags, int permissions,
+static bool kdb_check_flags(kdb_cmdflags_t flags, int permissions,
                                   bool no_args)
 {
        /* permissions comes from userspace so needs massaging slightly */
@@ -1180,6 +1233,9 @@ static int kdb_local(kdb_reason_t reason, int error, struct pt_regs *regs,
                kdb_curr_task(raw_smp_processor_id());
 
        KDB_DEBUG_STATE("kdb_local 1", reason);
+
+       kdb_check_for_lockdown();
+
        kdb_go_count = 0;
        if (reason == KDB_REASON_DEBUG) {
                /* special case below */
@@ -2203,8 +2259,8 @@ static void kdb_cpu_status(void)
                        state = 'D';    /* cpu is online but unresponsive */
                } else {
                        state = ' ';    /* cpu is responding to kdb */
-                       if (kdb_task_state_char(KDB_TSK(i)) == 'I')
-                               state = 'I';    /* idle task */
+                       if (kdb_task_state_char(KDB_TSK(i)) == '-')
+                               state = '-';    /* idle task */
                }
                if (state != prev_state) {
                        if (prev_state != '?') {
@@ -2271,37 +2327,30 @@ static int kdb_cpu(int argc, const char **argv)
 void kdb_ps_suppressed(void)
 {
        int idle = 0, daemon = 0;
-       unsigned long mask_I = kdb_task_state_string("I"),
-                     mask_M = kdb_task_state_string("M");
        unsigned long cpu;
        const struct task_struct *p, *g;
        for_each_online_cpu(cpu) {
                p = kdb_curr_task(cpu);
-               if (kdb_task_state(p, mask_I))
+               if (kdb_task_state(p, "-"))
                        ++idle;
        }
        for_each_process_thread(g, p) {
-               if (kdb_task_state(p, mask_M))
+               if (kdb_task_state(p, "ims"))
                        ++daemon;
        }
        if (idle || daemon) {
                if (idle)
-                       kdb_printf("%d idle process%s (state I)%s\n",
+                       kdb_printf("%d idle process%s (state -)%s\n",
                                   idle, idle == 1 ? "" : "es",
                                   daemon ? " and " : "");
                if (daemon)
-                       kdb_printf("%d sleeping system daemon (state M) "
+                       kdb_printf("%d sleeping system daemon (state [ims]) "
                                   "process%s", daemon,
                                   daemon == 1 ? "" : "es");
                kdb_printf(" suppressed,\nuse 'ps A' to see all.\n");
        }
 }
 
-/*
- * kdb_ps - This function implements the 'ps' command which shows a
- *     list of the active processes.
- *             ps [DRSTCZEUIMA]   All processes, optionally filtered by state
- */
 void kdb_ps1(const struct task_struct *p)
 {
        int cpu;
@@ -2330,17 +2379,25 @@ void kdb_ps1(const struct task_struct *p)
        }
 }
 
+/*
+ * kdb_ps - This function implements the 'ps' command which shows a
+ *         list of the active processes.
+ *
+ * ps [<state_chars>]   Show processes, optionally selecting only those whose
+ *                      state character is found in <state_chars>.
+ */
 static int kdb_ps(int argc, const char **argv)
 {
        struct task_struct *g, *p;
-       unsigned long mask, cpu;
+       const char *mask;
+       unsigned long cpu;
 
        if (argc == 0)
                kdb_ps_suppressed();
        kdb_printf("%-*s      Pid   Parent [*] cpu State %-*s Command\n",
                (int)(2*sizeof(void *))+2, "Task Addr",
                (int)(2*sizeof(void *))+2, "Thread");
-       mask = kdb_task_state_string(argc ? argv[1] : NULL);
+       mask = argc ? argv[1] : kdbgetenv("PS");
        /* Run the active tasks first */
        for_each_online_cpu(cpu) {
                if (KDB_FLAG(CMD_INTERRUPT))
@@ -2742,8 +2799,8 @@ static kdbtab_t maintab[] = {
        },
        {       .name = "bta",
                .func = kdb_bt,
-               .usage = "[D|R|S|T|C|Z|E|U|I|M|A]",
-               .help = "Backtrace all processes matching state flag",
+               .usage = "[<state_chars>|A]",
+               .help = "Backtrace all processes whose state matches",
                .flags = KDB_ENABLE_INSPECT,
        },
        {       .name = "btc",
@@ -2797,7 +2854,7 @@ static kdbtab_t maintab[] = {
        },
        {       .name = "ps",
                .func = kdb_ps,
-               .usage = "[<flags>|A]",
+               .usage = "[<state_chars>|A]",
                .help = "Display active task list",
                .flags = KDB_ENABLE_INSPECT,
        },