NetBSD Problem Report #60595

From www@netbsd.org  Sat Aug 15 08:35:33 2026
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
	 client-signature RSA-PSS (2048 bits) client-digest SHA256)
	(Client CN "mail.netbsd.org", Issuer "YR2" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id 6BE101A923C
	for <gnats-bugs@gnats.NetBSD.org>; Sat, 15 Aug 2026 08:35:33 +0000 (UTC)
Message-Id: <20260815083532.158541A923E@mollari.NetBSD.org>
Date: Sat, 15 Aug 2026 08:35:32 +0000 (UTC)
From: rvp@SDF.ORG
Reply-To: rvp@SDF.ORG
To: gnats-bugs@NetBSD.org
Subject: System lockup using gdb(1) on kernel with DEBUG+LOCKDEBUG
X-Send-Pr-Version: www-1.0
X-From4GNATS: "rvp@SDF.ORG via gnats" <gnats-admin@NetBSD.org>

>Number:         60595
>Category:       kern
>Synopsis:       System lockup using gdb(1) on kernel with DEBUG+LOCKDEBUG
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    riastradh
>State:          needs-pullups
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat Aug 15 08:40:00 +0000 2026
>Closed-Date:    
>Last-Modified:  Sat Aug 29 12:50:03 +0000 2026
>Originator:     RVP
>Release:        11.99.7
>Organization:
>Environment:
NetBSD CoreBook.local 11.99.7 NetBSD 11.99.7 (COREBOOK_DIAG) #0: Sat Aug 15 04:21:15 UTC 2026  bld@CoreBook.local:/tmp/obj/usr/src/sys/arch/amd64/compile/COREBOOK_DIAG amd64
>Description:
On a kernel compiled with DEBUG and LOCKDEBUG, running gdb(1) on any
trivial program locks up the whole system.
>How-To-Repeat:
As any user on a kernel with DEBUG & LOCKDEBUG:

```
$ echo 'int main(void) { return 0; }' > t.c
$ cc -o t t.c
$ gdb t
(gdb) run
```

System locks up completely. Must power off to recover.
>Fix:
No idea.

>Release-Note:

>Audit-Trail:

State-Changed-From-To: open->feedback
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Thu, 20 Aug 2026 14:41:47 +0000
State-Changed-Why:
Can you bisect on the kernel to find when this started happening?


Responsible-Changed-From-To: kern-bug-people->riastradh
Responsible-Changed-By: riastradh@NetBSD.org
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>
# 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);



From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60595 CVS commit: src/sys
Date: Thu, 20 Aug 2026 16:20:55 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Thu Aug 20 16:20:55 UTC 2026

 Modified Files:
 	src/sys/arch/amd64/amd64: process_machdep.c
 	src/sys/arch/i386/i386: process_machdep.c
 	src/sys/arch/x86/include: dbregs.h
 	src/sys/arch/x86/x86: dbregs.c
 	src/sys/kern: sys_process_lwpstatus.c
 	src/sys/sys: ptrace.h

 Log Message:
 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

 This fixes a bug that was revealed by lifting up rock while fixing:

 PR kern/60556: panic in process_read_fpregs_xmm


 To generate a diff of this commit:
 cvs rdiff -u -r1.50 -r1.51 src/sys/arch/amd64/amd64/process_machdep.c
 cvs rdiff -u -r1.97 -r1.98 src/sys/arch/i386/i386/process_machdep.c
 cvs rdiff -u -r1.8 -r1.9 src/sys/arch/x86/include/dbregs.h
 cvs rdiff -u -r1.15 -r1.16 src/sys/arch/x86/x86/dbregs.c
 cvs rdiff -u -r1.7 -r1.8 src/sys/kern/sys_process_lwpstatus.c
 cvs rdiff -u -r1.77 -r1.78 src/sys/sys/ptrace.h

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

State-Changed-From-To: feedback->needs-pullups
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Thu, 20 Aug 2026 20:28:05 +0000
State-Changed-Why:
fixed in HEAD, needs pullup to whatever branches PR 60556 fixes go to


From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60595 CVS commit: src/sys/kern
Date: Fri, 28 Aug 2026 16:15:15 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Fri Aug 28 16:15:15 UTC 2026

 Modified Files:
 	src/sys/kern: sys_process_lwpstatus.c

 Log Message:
 ptrace(2): Tidy register access: no sketchy function pointer casts.

 Followups for:

 PR port-amd64/60556: panic in process_read_fpregs_xmm

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

 PR kern/60658: netbsd32_process_read_dbregs() needs the same fix as
 native amd64 process_read_dbregs()


 To generate a diff of this commit:
 cvs rdiff -u -r1.8 -r1.9 src/sys/kern/sys_process_lwpstatus.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60595 CVS commit: src/sys/kern
Date: Sat, 29 Aug 2026 12:46:56 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Sat Aug 29 12:46:56 UTC 2026

 Modified Files:
 	src/sys/kern: sys_process_lwpstatus.c

 Log Message:
 ptrace(2): Fix compat32 register set types in previous.

 Not always `struct reg32' or `struct fpreg32', notably on arm32 --
 make sure to say `process_reg32'/`process_fpreg32'/`process_dbreg32'.
 (Here `compat32' means oabi?)

 Followups for:

 PR port-amd64/60556: panic in process_read_fpregs_xmm

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

 PR kern/60658: netbsd32_process_read_dbregs() needs the same fix as
 native amd64 process_read_dbregs()


 To generate a diff of this commit:
 cvs rdiff -u -r1.9 -r1.10 src/sys/kern/sys_process_lwpstatus.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

>Unformatted:

 #14 lockdebug_abort1 (func=0xffffffff80aaa5a0 <__func__.5> "assert_sleepable",

     line=93, ld=0xffffd1807d8498c8, s=7, 
     msg=0xffffffff80b10dc3 "spin lock held", dopanic=<optimized out>)
     at ../../../../kern/subr_lockdebug.c:796
 #15 0xffffffff8073dab9 in assert_sleepable ()
     at ../../../../kern/kern_lock.c:93
 #16 0xffffffff8079c2b8 in pool_get (
     pp=pp@entry=0xffffffff80e7e3e0 <x86_dbregspl>, flags=flags@entry=1)
     at ../../../../kern/subr_pool.c:1113
 #17 0xffffffff8023d643 in x86_dbregs_read (l=<optimized out>, 
     regs=0xffffd1925ee8e980) at ../../../../arch/x86/x86/dbregs.c:140
 #18 0xffffffff80238fbd in process_read_dbregs (l=<optimized out>, 
     regs=<optimized out>, sz=<optimized out>)
     at ../../../../arch/amd64/amd64/process_machdep.c:171
 #19 0xffffffff807bf53e in proc_regio (l=0xffffb69b65ed1000, 
     uio=0xffffd1925ee8ee30, ks=<optimized out>, 
     r=0xffffffff80238fb4 <process_read_dbregs>, 
     w=0xffffffff802391b7 <process_write_dbregs>)
     at ../../../../kern/sys_process_lwpstatus.c:181
 #20 0xffffffff807c0482 in ptrace_regs (l=0xffffb69b658be800, 
     lt=0xffffd1925ee8ee10, rq=<optimized out>, 


 on amd64 getting registers does a pool_get() probably this locking
 change causes it:


  #include <sys/cdefs.h>
 -__KERNEL_RCSID(0, "$NetBSD: sys_process_lwpstatus.c,v 1.5 2025/01/11 19:42:04 christos Exp $");
 +__KERNEL_RCSID(0, "$NetBSD: sys_process_lwpstatus.c,v 1.6 2026/08/08 00:45:52 riastradh Exp $");

  #ifdef _KERNEL_OPT
  #include "opt_ptrace.h"
 @@ -163,14 +163,21 @@
         if (kl > uio->uio_resid)
                 kl = uio->uio_resid;

 -       error = (*r)(l, buf, &ks);
 +       lwp_lock(l);
 +       if (l->l_stat != LSSTOP)
 +               error = EBUSY;
 +       else
 +               error = (*r)(l, buf, &ks);
 +       lwp_unlock(l);
         if (error == 0)
                 error = uiomove(kv, kl, uio);
         if (error == 0 && uio->uio_rw == UIO_WRITE) {
 +               lwp_lock(l);
                 if (l->l_stat != LSSTOP)
                         error = EBUSY;
                 else
                         error = (*w)(l, buf, ks);
 +               lwp_unlock(l);
         }

         uio->uio_offset = 0;

 But I have not tried to revert it.

 christos

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.