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

[email protected]
Newsgroups gmane.os.netbsd.bugs
Message-ID <[email protected]>
Synopsis: System lockup using gdb(1) on kernel with DEBUG+LOCKDEBUG

Responsible-Changed-From-To: kern-bug-people->riastradh
Responsible-Changed-By: [email protected]
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 <[email protected]>
# 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);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.