NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: kern/60595 (System lockup using gdb(1) on kernel with DEBUG+LOCKDEBUG)



Synopsis: System lockup using gdb(1) on kernel with DEBUG+LOCKDEBUG

Responsible-Changed-From-To: kern-bug-people->riastradh
Responsible-Changed-By: riastradh%NetBSD.org@localhost
Responsible-Changed-When: Thu, 20 Aug 2026 15:43:56 +0000
Responsible-Changed-Why:
Can you please try the attached patch?

(This is a latent bug that was exposed by lifting a rock when I fixed
PR port-amd64/60556: panic in process_read_fpregs_xmm,
<https://gnats.NetBSD.org/60556>.)


# HG changeset patch
# User Taylor R Campbell <riastradh%NetBSD.org@localhost>
# Date 1787240316 0
#      Thu Aug 20 15:38:36 2026 +0000
# Branch trunk
# Node ID c8d5c9a5e81f1bb3969e4ff92f9824899648a0c0
# Parent  d27322fd1985e6602014bf467a3fa01b43c58a94
# EXP-Topic riastradh-pr60595-dbregsalloclock
x86: Pull dbregs buffer allocation out from under lwp_lock.

Sprinkle assertions and notes about locking rules while here.

PR kern/60595: System lockup using gdb(1) on kernel with DEBUG+LOCKDEBUG

diff -r d27322fd1985 -r c8d5c9a5e81f sys/arch/amd64/amd64/process_machdep.c
--- a/sys/arch/amd64/amd64/process_machdep.c	Thu Aug 20 14:35:05 2026 +0000
+++ b/sys/arch/amd64/amd64/process_machdep.c	Thu Aug 20 15:38:36 2026 +0000
@@ -55,6 +55,9 @@
  *	registers or privileged bits in the PSL.
  *	The process is stopped at the time write_fpregs is called.
  *
+ * process_alloc_dbregs(proc)
+ *	Ensure a dbregs buffer is allocated.
+ *
  * process_read_dbregs(proc, regs, sz)
  *	Get the current user-visible register set from the process
  *	and copy it into the regs structure (<machine/reg.h>).
@@ -173,6 +176,14 @@ process_read_dbregs(struct lwp *l, struc
 	return 0;
 }
 
+void
+process_alloc_dbregs(struct lwp *l)
+{
+
+	KASSERT(rw_lock_held(&l->l_proc->p_reflock));
+	x86_dbregs_alloc(l);
+}
+
 int
 process_write_regs(struct lwp *l, const struct reg *regp)
 {
diff -r d27322fd1985 -r c8d5c9a5e81f sys/arch/i386/i386/process_machdep.c
--- a/sys/arch/i386/i386/process_machdep.c	Thu Aug 20 14:35:05 2026 +0000
+++ b/sys/arch/i386/i386/process_machdep.c	Thu Aug 20 15:38:36 2026 +0000
@@ -55,6 +55,9 @@
  *	registers or privileged bits in the PSL.
  *	The process is stopped at the time write_fpregs is called.
  *
+ * process_alloc_dbregs(proc)
+ *	Ensure a dbregs buffer is allocated.
+ *
  * process_read_dbregs(proc, regs)
  *	Get the current user-visible register set from the process
  *	and copy it into the regs structure (<machine/reg.h>).
@@ -149,6 +152,14 @@ process_read_dbregs(struct lwp *l, struc
 	return 0;
 }
 
+void
+process_alloc_dbregs(struct lwp *l)
+{
+
+	KASSERT(rw_lock_held(&l->l_proc->p_reflock));
+	x86_dbregs_alloc(l);
+}
+
 #ifdef PTRACE_HOOKS
 int
 process_write_regs(struct lwp *l, const struct reg *regs)
diff -r d27322fd1985 -r c8d5c9a5e81f sys/arch/x86/include/dbregs.h
--- a/sys/arch/x86/include/dbregs.h	Thu Aug 20 14:35:05 2026 +0000
+++ b/sys/arch/x86/include/dbregs.h	Thu Aug 20 15:38:36 2026 +0000
@@ -108,6 +108,7 @@ enum x86_dr7_length {
 #define X86_DBREGS	4
 
 void x86_dbregs_init(void);
+void x86_dbregs_alloc(struct lwp *);
 void x86_dbregs_clear(struct lwp *);
 void x86_dbregs_abandon(struct lwp *);
 void x86_dbregs_read(struct lwp *, struct dbreg *);
diff -r d27322fd1985 -r c8d5c9a5e81f sys/arch/x86/x86/dbregs.c
--- a/sys/arch/x86/x86/dbregs.c	Thu Aug 20 14:35:05 2026 +0000
+++ b/sys/arch/x86/x86/dbregs.c	Thu Aug 20 15:38:36 2026 +0000
@@ -28,8 +28,12 @@
 
 #include <sys/param.h>
 #include <sys/types.h>
+
 #include <sys/lwp.h>
 #include <sys/pool.h>
+#include <sys/proc.h>
+#include <sys/rwlock.h>
+
 #include <x86/cpufunc.h>
 #include <x86/dbregs.h>
 
@@ -96,6 +100,38 @@ x86_dbregs_reset(void)
 	ldr6(rdr6() & ~X86_BREAKPOINT_CONDITION_DETECTED);
 }
 
+/*
+ * x86_dbregs_alloc(l)
+ *
+ *	Ensure l has a dbregs buffer, allocating one if necessary.
+ *
+ *	Caller must hold l->l_proc->p_reflock, and, on return, must not
+ *	drop it before calling before calling x86_dbregs_read or
+ *	x86_dbregs_write.  Caller must not hold the lwp lock.
+ */
+void
+x86_dbregs_alloc(struct lwp *l)
+{
+	struct pcb *pcb = lwp_getpcb(l);
+	struct dbreg *dbregs;
+
+	KASSERT(rw_lock_held(&l->l_proc->p_reflock));
+
+	dbregs = pool_get(&x86_dbregspl, PR_WAITOK);
+	memcpy(dbregs, &initdbstate, sizeof(initdbstate));
+
+	lwp_lock(l);
+	if (pcb->pcb_dbregs == NULL) {
+		pcb->pcb_dbregs = dbregs;
+		dbregs = NULL;
+		pcb->pcb_flags |= PCB_DBREGS;
+	}
+	lwp_unlock(l);
+
+	if (dbregs)
+		pool_put(&x86_dbregspl, dbregs);
+}
+
 void
 x86_dbregs_clear(struct lwp *l)
 {
@@ -131,16 +167,25 @@ x86_dbregs_abandon(struct lwp *l)
 	kpreempt_enable();
 }
 
+/*
+ * x86_dbregs_read(l, regs)
+ *
+ *	Read l's dbregs into the buffer regs.
+ *
+ *	Caller must hold l->l_proc->p_reflock, and must have previously
+ *	called x86_dbregs_alloc without dropping l->l_proc->p_reflock
+ *	in the interim.  Caller must also hold the lwp lock.
+ */
 void
 x86_dbregs_read(struct lwp *l, struct dbreg *regs)
 {
 	struct pcb *pcb = lwp_getpcb(l);
 
-	if (pcb->pcb_dbregs == NULL) {
-		pcb->pcb_dbregs = pool_get(&x86_dbregspl, PR_WAITOK);
-		memcpy(pcb->pcb_dbregs, &initdbstate, sizeof(initdbstate));
-		pcb->pcb_flags |= PCB_DBREGS;
-	}
+	KASSERT(rw_lock_held(&l->l_proc->p_reflock));
+	KASSERT(lwp_locked(l, NULL));
+	KASSERT(pcb->pcb_dbregs != NULL);
+	KASSERT(pcb->pcb_flags & PCB_DBREGS);
+
 	memcpy(regs, pcb->pcb_dbregs, sizeof(*regs));
 }
 
@@ -280,9 +325,10 @@ x86_dbregs_write(struct lwp *l, const st
 {
 	struct pcb *pcb = lwp_getpcb(l);
 
-	if (pcb->pcb_dbregs == NULL) {
-		pcb->pcb_dbregs = pool_get(&x86_dbregspl, PR_WAITOK);
-	}
+	KASSERT(rw_lock_held(&l->l_proc->p_reflock));
+	KASSERT(lwp_locked(l, NULL));
+	KASSERT(pcb->pcb_dbregs != NULL);
+	KASSERT(pcb->pcb_flags & PCB_DBREGS);
 
 	memcpy(pcb->pcb_dbregs, regs, sizeof(*regs));
 	pcb->pcb_flags |= PCB_DBREGS;
diff -r d27322fd1985 -r c8d5c9a5e81f sys/kern/sys_process_lwpstatus.c
--- a/sys/kern/sys_process_lwpstatus.c	Thu Aug 20 14:35:05 2026 +0000
+++ b/sys/kern/sys_process_lwpstatus.c	Thu Aug 20 15:38:36 2026 +0000
@@ -302,6 +302,9 @@ process_dodbregs(struct lwp *curl /*trac
 	ptrace_regrfunc_t r;
 	ptrace_regwfunc_t w;
 
+	KASSERT(rw_lock_held(&l->l_proc->p_reflock));
+	process_alloc_dbregs(l);
+
 #ifdef COMPAT_NETBSD32
 	const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
 
diff -r d27322fd1985 -r c8d5c9a5e81f sys/sys/ptrace.h
--- a/sys/sys/ptrace.h	Thu Aug 20 14:35:05 2026 +0000
+++ b/sys/sys/ptrace.h	Thu Aug 20 15:38:36 2026 +0000
@@ -344,6 +344,10 @@ int	process_write_regs(struct lwp *, con
 #endif
 #endif
 
+#if defined PT_GETDBREGS || defined PT_SETDBREGS
+void	process_alloc_dbregs(struct lwp *);
+#endif
+
 int	ptrace_machdep_dorequest(struct lwp *, struct lwp **, int,
 	    void *, int);
 






Home | Main Index | Thread Index | Old Index