Skip to content

Commit

Permalink
powerpc/xmon: Restrict when kernel is locked down
Browse files Browse the repository at this point in the history
Xmon should be either fully or partially disabled depending on the
kernel lockdown state.

Put xmon into read-only mode for lockdown=integrity and prevent user
entry into xmon when lockdown=confidentiality. Xmon checks the lockdown
state on every attempted entry:

 (1) during early xmon'ing

 (2) when triggered via sysrq

 (3) when toggled via debugfs

 (4) when triggered via a previously enabled breakpoint

The following lockdown state transitions are handled:

 (1) lockdown=none -> lockdown=integrity
     set xmon read-only mode

 (2) lockdown=none -> lockdown=confidentiality
     clear all breakpoints, set xmon read-only mode,
     prevent user re-entry into xmon

 (3) lockdown=integrity -> lockdown=confidentiality
     clear all breakpoints, set xmon read-only mode,
     prevent user re-entry into xmon

Suggested-by: Andrew Donnellan <ajd@linux.ibm.com>
Signed-off-by: Christopher M. Riedl <cmr@informatik.wtf>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20190907061124.1947-3-cmr@informatik.wtf
  • Loading branch information
Christopher M. Riedl authored and mpe committed Oct 28, 2019
1 parent 96664de commit 69393cb
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 21 deletions.
103 changes: 82 additions & 21 deletions arch/powerpc/xmon/xmon.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <linux/nmi.h>
#include <linux/ctype.h>
#include <linux/highmem.h>
#include <linux/security.h>

#include <asm/debugfs.h>
#include <asm/ptrace.h>
Expand Down Expand Up @@ -187,6 +188,8 @@ static void dump_tlb_44x(void);
static void dump_tlb_book3e(void);
#endif

static void clear_all_bpt(void);

#ifdef CONFIG_PPC64
#define REG "%.16lx"
#else
Expand Down Expand Up @@ -283,10 +286,38 @@ Commands:\n\
" U show uptime information\n"
" ? help\n"
" # n limit output to n lines per page (for dp, dpa, dl)\n"
" zr reboot\n\
zh halt\n"
" zr reboot\n"
" zh halt\n"
;

#ifdef CONFIG_SECURITY
static bool xmon_is_locked_down(void)
{
static bool lockdown;

if (!lockdown) {
lockdown = !!security_locked_down(LOCKDOWN_XMON_RW);
if (lockdown) {
printf("xmon: Disabled due to kernel lockdown\n");
xmon_is_ro = true;
}
}

if (!xmon_is_ro) {
xmon_is_ro = !!security_locked_down(LOCKDOWN_XMON_WR);
if (xmon_is_ro)
printf("xmon: Read-only due to kernel lockdown\n");
}

return lockdown;
}
#else /* CONFIG_SECURITY */
static inline bool xmon_is_locked_down(void)
{
return false;
}
#endif

static struct pt_regs *xmon_regs;

static inline void sync(void)
Expand Down Expand Up @@ -438,7 +469,10 @@ static bool wait_for_other_cpus(int ncpus)

return false;
}
#endif /* CONFIG_SMP */
#else /* CONFIG_SMP */
static inline void get_output_lock(void) {}
static inline void release_output_lock(void) {}
#endif

static inline int unrecoverable_excp(struct pt_regs *regs)
{
Expand All @@ -455,6 +489,7 @@ static int xmon_core(struct pt_regs *regs, int fromipi)
int cmd = 0;
struct bpt *bp;
long recurse_jmp[JMP_BUF_LEN];
bool locked_down;
unsigned long offset;
unsigned long flags;
#ifdef CONFIG_SMP
Expand All @@ -465,6 +500,8 @@ static int xmon_core(struct pt_regs *regs, int fromipi)
local_irq_save(flags);
hard_irq_disable();

locked_down = xmon_is_locked_down();

if (!fromipi) {
tracing_enabled = tracing_is_on();
tracing_off();
Expand Down Expand Up @@ -518,7 +555,8 @@ static int xmon_core(struct pt_regs *regs, int fromipi)

if (!fromipi) {
get_output_lock();
excprint(regs);
if (!locked_down)
excprint(regs);
if (bp) {
printf("cpu 0x%x stopped at breakpoint 0x%tx (",
cpu, BP_NUM(bp));
Expand Down Expand Up @@ -570,10 +608,14 @@ static int xmon_core(struct pt_regs *regs, int fromipi)
}
remove_bpts();
disable_surveillance();
/* for breakpoint or single step, print the current instr. */
if (bp || TRAP(regs) == 0xd00)
ppc_inst_dump(regs->nip, 1, 0);
printf("enter ? for help\n");

if (!locked_down) {
/* for breakpoint or single step, print curr insn */
if (bp || TRAP(regs) == 0xd00)
ppc_inst_dump(regs->nip, 1, 0);
printf("enter ? for help\n");
}

mb();
xmon_gate = 1;
barrier();
Expand All @@ -597,8 +639,9 @@ static int xmon_core(struct pt_regs *regs, int fromipi)
spin_cpu_relax();
touch_nmi_watchdog();
} else {
cmd = cmds(regs);
if (cmd != 0) {
if (!locked_down)
cmd = cmds(regs);
if (locked_down || cmd != 0) {
/* exiting xmon */
insert_bpts();
xmon_gate = 0;
Expand Down Expand Up @@ -635,13 +678,16 @@ static int xmon_core(struct pt_regs *regs, int fromipi)
"can't continue\n");
remove_bpts();
disable_surveillance();
/* for breakpoint or single step, print the current instr. */
if (bp || TRAP(regs) == 0xd00)
ppc_inst_dump(regs->nip, 1, 0);
printf("enter ? for help\n");
if (!locked_down) {
/* for breakpoint or single step, print current insn */
if (bp || TRAP(regs) == 0xd00)
ppc_inst_dump(regs->nip, 1, 0);
printf("enter ? for help\n");
}
}

cmd = cmds(regs);
if (!locked_down)
cmd = cmds(regs);

insert_bpts();
in_xmon = 0;
Expand Down Expand Up @@ -670,7 +716,10 @@ static int xmon_core(struct pt_regs *regs, int fromipi)
}
}
#endif
insert_cpu_bpts();
if (locked_down)
clear_all_bpt();
else
insert_cpu_bpts();

touch_nmi_watchdog();
local_irq_restore(flags);
Expand Down Expand Up @@ -3768,6 +3817,11 @@ static void xmon_init(int enable)
#ifdef CONFIG_MAGIC_SYSRQ
static void sysrq_handle_xmon(int key)
{
if (xmon_is_locked_down()) {
clear_all_bpt();
xmon_init(0);
return;
}
/* ensure xmon is enabled */
xmon_init(1);
debugger(get_irq_regs());
Expand All @@ -3789,7 +3843,6 @@ static int __init setup_xmon_sysrq(void)
device_initcall(setup_xmon_sysrq);
#endif /* CONFIG_MAGIC_SYSRQ */

#ifdef CONFIG_DEBUG_FS
static void clear_all_bpt(void)
{
int i;
Expand All @@ -3807,18 +3860,22 @@ static void clear_all_bpt(void)
iabr = NULL;
dabr.enabled = 0;
}

printf("xmon: All breakpoints cleared\n");
}

#ifdef CONFIG_DEBUG_FS
static int xmon_dbgfs_set(void *data, u64 val)
{
xmon_on = !!val;
xmon_init(xmon_on);

/* make sure all breakpoints removed when disabling */
if (!xmon_on)
if (!xmon_on) {
clear_all_bpt();
get_output_lock();
printf("xmon: All breakpoints cleared\n");
release_output_lock();
}

return 0;
}

Expand All @@ -3844,7 +3901,11 @@ static int xmon_early __initdata;

static int __init early_parse_xmon(char *p)
{
if (!p || strncmp(p, "early", 5) == 0) {
if (xmon_is_locked_down()) {
xmon_init(0);
xmon_early = 0;
xmon_on = 0;
} else if (!p || strncmp(p, "early", 5) == 0) {
/* just "xmon" is equivalent to "xmon=early" */
xmon_init(1);
xmon_early = 1;
Expand Down
2 changes: 2 additions & 0 deletions include/linux/security.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,14 @@ enum lockdown_reason {
LOCKDOWN_MODULE_PARAMETERS,
LOCKDOWN_MMIOTRACE,
LOCKDOWN_DEBUGFS,
LOCKDOWN_XMON_WR,
LOCKDOWN_INTEGRITY_MAX,
LOCKDOWN_KCORE,
LOCKDOWN_KPROBES,
LOCKDOWN_BPF_READ,
LOCKDOWN_PERF,
LOCKDOWN_TRACEFS,
LOCKDOWN_XMON_RW,
LOCKDOWN_CONFIDENTIALITY_MAX,
};

Expand Down
2 changes: 2 additions & 0 deletions security/lockdown/lockdown.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ static const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
[LOCKDOWN_MODULE_PARAMETERS] = "unsafe module parameters",
[LOCKDOWN_MMIOTRACE] = "unsafe mmio",
[LOCKDOWN_DEBUGFS] = "debugfs access",
[LOCKDOWN_XMON_WR] = "xmon write access",
[LOCKDOWN_INTEGRITY_MAX] = "integrity",
[LOCKDOWN_KCORE] = "/proc/kcore access",
[LOCKDOWN_KPROBES] = "use of kprobes",
[LOCKDOWN_BPF_READ] = "use of bpf to read kernel RAM",
[LOCKDOWN_PERF] = "unsafe use of perf",
[LOCKDOWN_TRACEFS] = "use of tracefs",
[LOCKDOWN_XMON_RW] = "xmon read and write access",
[LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
};

Expand Down

0 comments on commit 69393cb

Please sign in to comment.