[GIT PULL v4.5] Fix INT1 recursion with unregistered breakpoints

From: Jeff V. Merkey
Date: Mon Jan 11 2016 - 19:35:54 EST


The following changes since commit ee78027142ab39d4f3c0e1af71ed343e0ff2dafd:

Merge pull request #12 from torvalds/master (2015-12-19 11:12:20 -0700)

are available in the git repository at:


https://github.com/jeffmerkey/linux.git fixes

for you to fetch changes up to b5f894bf53e7c401cc5a88b8a8b13059a176a538:

Fix INT1 Recursion with unregistered breakpoints (2015-12-19 20:33:59 -0700)

----------------------------------------------------------------
Jeff Merkey (1):
Fix INT1 Recursion with unregistered breakpoints

arch/x86/include/uapi/asm/debugreg.h | 1 +
arch/x86/kernel/hw_breakpoint.c | 25 +++++++++++++++++++++++--
2 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/arch/x86/include/uapi/asm/debugreg.h b/arch/x86/include/uapi/asm/debugreg.h
index 3c0874d..78fc83c 100644
--- a/arch/x86/include/uapi/asm/debugreg.h
+++ b/arch/x86/include/uapi/asm/debugreg.h
@@ -38,6 +38,7 @@
#define DR_RW_EXECUTE (0x0) /* Settings for the access types to trap on */
#define DR_RW_WRITE (0x1)
#define DR_RW_READ (0x3)
+#define DR_RW_MASK (0x3) /* mask for breakpoint type field */

#define DR_LEN_1 (0x0) /* Settings for data length to trap on */
#define DR_LEN_2 (0x4)
diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c
index 50a3fad..d199834 100644
--- a/arch/x86/kernel/hw_breakpoint.c
+++ b/arch/x86/kernel/hw_breakpoint.c
@@ -444,7 +444,7 @@ EXPORT_SYMBOL_GPL(hw_breakpoint_restore);
static int hw_breakpoint_handler(struct die_args *args)
{
int i, cpu, rc = NOTIFY_STOP;
- struct perf_event *bp;
+ struct perf_event *bp = NULL;
unsigned long dr7, dr6;
unsigned long *dr6_p;

@@ -477,6 +477,14 @@ static int hw_breakpoint_handler(struct die_args *args)
continue;

/*
+ * Check if we got an execute breakpoint, if so
+ * set the resume flag to avoid int1 recursion.
+ */
+ if (((dr7 >> ((i * DR_CONTROL_SIZE) + DR_CONTROL_SHIFT))
+ & DR_RW_MASK) == DR_RW_EXECUTE)
+ args->regs->flags |= X86_EFLAGS_RF;
+
+ /*
* The counter may be concurrently released but that can only
* occur from a call_rcu() path. We can then safely fetch
* the breakpoint, use its callback, touch its counter
@@ -503,7 +511,8 @@ static int hw_breakpoint_handler(struct die_args *args)

/*
* Set up resume flag to avoid breakpoint recursion when
- * returning back to origin.
+ * returning back to origin. perf_bp_event may
+ * change the flags so check twice.
*/
if (bp->hw.info.type == X86_BREAKPOINT_EXECUTE)
args->regs->flags |= X86_EFLAGS_RF;
@@ -519,6 +528,18 @@ static int hw_breakpoint_handler(struct die_args *args)
(dr6 & (~DR_TRAP_BITS)))
rc = NOTIFY_DONE;

+ /*
+ * if we are about to signal to
+ * do_debug() to stop further processing
+ * and we have not ascertained the source
+ * of the breakpoint, log it as spurious.
+ */
+ if (rc == NOTIFY_STOP && !bp) {
+ printk_ratelimited(KERN_INFO
+ "INFO: spurious INT1 exception dr6: 0x%lX dr7: 0x%lX\n",
+ dr6, dr7);
+ }
+
set_debugreg(dr7, 7);
put_cpu();