[PATCH v3 6/7] LoongArch: Fix thread_struct layout for hbp_break and hbp_watch
From: Tiezhu Yang
Date: Wed Sep 23 2026 - 02:00:29 EST
According to the architectural code comment in processor.h, FPU and vector
registers must be located at the very end of the inherited context within
struct thread_struct because they are conditionally copied during fork().
However, hbp_break and hbp_watch arrays were erroneously placed after the
fpu and lbt fields. Due to the partial memcpy optimization which are used
in arch_dup_task_struct(), the hardware breakpoint members were silently
truncated and ignored during task duplication, accidentally relying on a
subsequent ptrace_hw_copy_thread() to clear the uninitialized pointers
with memset.
Fix this by moving hbp_break and hbp_watch arrays up before the fpu field,
strictly adhering to the established layout convention. This ensures state
cloning boundaries via the native kernel memcpy. Then, remove the helper
ptrace_hw_copy_thread() and its caller to clean up the code.
Fixes: edffa33c7bb5 ("LoongArch: Add hardware breakpoints/watchpoints support")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Tiezhu Yang <yangtiezhu@xxxxxxxxxxx>
---
arch/loongarch/include/asm/hw_breakpoint.h | 4 ----
arch/loongarch/include/asm/processor.h | 13 +++++++------
arch/loongarch/kernel/hw_breakpoint.c | 6 ------
arch/loongarch/kernel/process.c | 1 -
4 files changed, 7 insertions(+), 17 deletions(-)
diff --git a/arch/loongarch/include/asm/hw_breakpoint.h b/arch/loongarch/include/asm/hw_breakpoint.h
index d202052df8a1..e7a6606c86cc 100644
--- a/arch/loongarch/include/asm/hw_breakpoint.h
+++ b/arch/loongarch/include/asm/hw_breakpoint.h
@@ -120,12 +120,8 @@ bool breakpoint_handler(struct pt_regs *regs);
bool watchpoint_handler(struct pt_regs *regs);
#ifdef CONFIG_HAVE_HW_BREAKPOINT
-extern void ptrace_hw_copy_thread(struct task_struct *task);
extern void hw_breakpoint_thread_switch(struct task_struct *next);
#else
-static inline void ptrace_hw_copy_thread(struct task_struct *task)
-{
-}
static inline void hw_breakpoint_thread_switch(struct task_struct *next)
{
}
diff --git a/arch/loongarch/include/asm/processor.h b/arch/loongarch/include/asm/processor.h
index ce8b953f8c79..94c5ce9b1d24 100644
--- a/arch/loongarch/include/asm/processor.h
+++ b/arch/loongarch/include/asm/processor.h
@@ -128,18 +128,19 @@ struct thread_struct {
unsigned long trap_nr;
unsigned long error_code;
unsigned long single_step; /* Used by PTRACE_SINGLESTEP */
+
struct loongarch_vdso_info *vdso;
+ /* Hardware breakpoints pinned to this task. */
+ struct perf_event *hbp_break[LOONGARCH_MAX_BRP];
+ struct perf_event *hbp_watch[LOONGARCH_MAX_WRP];
+
/*
* FPU & vector registers, must be at the last of inherited
* context because they are conditionally copied at fork().
*/
struct loongarch_fpu fpu FPU_ALIGN;
struct loongarch_lbt lbt; /* Also conditionally copied */
-
- /* Hardware breakpoints pinned to this task. */
- struct perf_event *hbp_break[LOONGARCH_MAX_BRP];
- struct perf_event *hbp_watch[LOONGARCH_MAX_WRP];
};
#define thread_saved_ra(tsk) (tsk->thread.sched_ra)
@@ -173,6 +174,8 @@ struct thread_struct {
*/ \
.trap_nr = 0, \
.error_code = 0, \
+ .hbp_break = {0}, \
+ .hbp_watch = {0}, \
/* \
* FPU & vector registers \
*/ \
@@ -182,8 +185,6 @@ struct thread_struct {
.ftop = 0, \
.fpr = {{{0,},},}, \
}, \
- .hbp_break = {0}, \
- .hbp_watch = {0}, \
}
struct task_struct;
diff --git a/arch/loongarch/kernel/hw_breakpoint.c b/arch/loongarch/kernel/hw_breakpoint.c
index 9dcb122218c2..1d881b2c2a50 100644
--- a/arch/loongarch/kernel/hw_breakpoint.c
+++ b/arch/loongarch/kernel/hw_breakpoint.c
@@ -154,12 +154,6 @@ static int hw_breakpoint_slot_setup(struct perf_event **slots, int max_slots,
return -ENOSPC;
}
-void ptrace_hw_copy_thread(struct task_struct *tsk)
-{
- memset(tsk->thread.hbp_break, 0, sizeof(tsk->thread.hbp_break));
- memset(tsk->thread.hbp_watch, 0, sizeof(tsk->thread.hbp_watch));
-}
-
/*
* Unregister breakpoints from this task and reset the pointers in the thread_struct.
*/
diff --git a/arch/loongarch/kernel/process.c b/arch/loongarch/kernel/process.c
index baa683fbfc53..199c951a2e27 100644
--- a/arch/loongarch/kernel/process.c
+++ b/arch/loongarch/kernel/process.c
@@ -226,7 +226,6 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
childregs->regs[2] = tls;
out:
- ptrace_hw_copy_thread(p);
clear_tsk_thread_flag(p, TIF_USEDFPU);
clear_tsk_thread_flag(p, TIF_USEDSIMD);
clear_tsk_thread_flag(p, TIF_USEDLBT);
--
2.42.0