From: Ada Couprie Diaz <ada.coupriediaz@arm.com>
To: linux-arm-kernel@lists.infradead.org
Cc: Mark Rutland <mark.rutland@arm.com>,
Rob Herring <robh@kernel.org>,
Anshuman Khandual <anshuman.khandual@arm.com>,
Catalin Marinas <catalin.marinas@arm.com>,
Will Deacon <will@kernel.org>
Subject: [PATCH 2/2] arm64/debug: mask exceptions when switching cpu-bound watchpoints
Date: Wed, 27 May 2026 17:15:53 +0100 [thread overview]
Message-ID: <20260527161553.97676-3-ada.coupriediaz@arm.com> (raw)
In-Reply-To: <20260527161553.97676-1-ada.coupriediaz@arm.com>
When hit, EL1 watchpoints always disable EL0 watchpoints to avoid
the kernel triggering them during uaccess, and re-enable them depending
on the current task's `wps_disabled`.
This can lead to an inconsistent state when task switching between two
tasks with different `wps_disabled` and CPU-bound watchpoints :
`hw_breakpoint_thread_switch()` is called without debug exceptions masked
and could raise a watchpoint exception at EL1 while toggling the CPU-bound
watchpoints.
As the watchpoint and single step handlers are not aware of the switch
nor of any "next task", this can lead to an inconsistent enabled/disabled
state for CPU-bound hardware watchpoints.
In the worst case, some CPU-bound watchpoint will not trigger
when expected until the next time they are enabled (similar task switch,
single step after another watchpoint).
It can also lead to a watchpoint triggering while we were stepping another
watchpoint, but as far as I can tell this is covered by the existing
behaviour of the watchpoint and single step handlers.
Mask all DAIF exceptions if we are toggling CPU-bound watchpoint
during thread-switch to avoid this edge case.
Add some context on what breakpoints are switched in this function
and some reasoning as to why we are masking here.
Signed-off-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>
---
arch/arm64/kernel/hw_breakpoint.c | 24 ++++++++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index ce99a00c8596..ce4f58f19bf4 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -20,6 +20,7 @@
#include <linux/uaccess.h>
#include <asm/current.h>
+#include <asm/daifflags.h>
#include <asm/debug-monitors.h>
#include <asm/esr.h>
#include <asm/exception.h>
@@ -899,7 +900,12 @@ bool try_step_suspended_breakpoints(struct pt_regs *regs)
NOKPROBE_SYMBOL(try_step_suspended_breakpoints);
/*
- * Context-switcher for restoring suspended breakpoints.
+ * Context-switcher for restoring suspended breakpoints
+ * which are not task-bound.
+ *
+ * Breakpoints of the previous task are uninstalled before
+ * this function is called, in perf_event_task_sched_out(), and those
+ * of the next task are installed after, in perf_event_task_sched_in().
*/
void hw_breakpoint_thread_switch(struct task_struct *next)
{
@@ -924,10 +930,24 @@ void hw_breakpoint_thread_switch(struct task_struct *next)
!next_debug_info->bps_disabled);
/* Update watchpoints. */
- if (current_debug_info->wps_disabled != next_debug_info->wps_disabled)
+ if (current_debug_info->wps_disabled != next_debug_info->wps_disabled) {
+ unsigned long mask;
+ /*
+ * This is called with local_irqs masked, but not DAIF.D.
+ * EL1 watchpoints always toggle EL0 watchpoints when hit,
+ * re-enabling them depending on the _current_ task.
+ *
+ * Mask all exceptions to avoid creating an inconsistent state
+ * by triggering a watchpoint mid-way through the switch.
+ */
+ mask = local_daif_save();
+
toggle_bp_registers(AARCH64_DBG_REG_WCR,
DBG_ACTIVE_EL0,
!next_debug_info->wps_disabled);
+
+ local_daif_restore(mask);
+ }
}
/*
--
2.43.0
prev parent reply other threads:[~2026-05-27 16:16 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-27 16:15 [PATCH 0/2] arm64/debug: clean up some HW BP edge cases Ada Couprie Diaz
2026-05-27 16:15 ` [PATCH 1/2] arm64/debug: update perf slots in sync with BP registers Ada Couprie Diaz
2026-05-27 16:15 ` Ada Couprie Diaz [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260527161553.97676-3-ada.coupriediaz@arm.com \
--to=ada.coupriediaz@arm.com \
--cc=anshuman.khandual@arm.com \
--cc=catalin.marinas@arm.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=mark.rutland@arm.com \
--cc=robh@kernel.org \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox