NetBSD-Bugs archive

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

kern/57550: amd64: Setting a watch point on a buffer for read(2) causes the kernel to panic



>Number:         57550
>Category:       kern
>Synopsis:       amd64: Setting a watch point on a buffer for read(2) causes the kernel to panic
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Jul 30 03:45:00 +0000 2023
>Originator:     PHO
>Release:        -CURRENT
>Organization:
TNF
>Environment:
System: NetBSD netbsd-current.home.cielonegro.org 10.99.6 NetBSD 10.99.6 (GENERIC) #0: Sun Jul 23 21:06:54 JST 2023  pho%netbsd-current.home.cielonegro.org@localhost:/home/pho/sysbuild/amd64/obj/usr/src/sys/arch/amd64/compile/GENERIC amd64
Architecture: x86_64
Machine: amd64
>Description:
When a hardware watch point is set in a memory region of a user process, and it's passed to a syscall, the kernel panics instead of generating a SIGTRAP like this:
---
[ 213838.7597637] fatal trace trap in supervisor mode
[ 213838.7597637] trap type 5 code 0 rip 0xffffffff80234353 cs 0x8 rflags 0x50202 cr2 0x2bd4df ilevel 0 rsp 0xffffde0139397ca8
[ 213838.7597637] curlwp 0xfffffee02d9c3940 pid 29253.29253 lowest kstack 0xffffde01393932c0
Stopped in pid 29253.29253 (hack) at	netbsd:copyout+0x33:	repe movsq	(%rsi),%es:(%rdi)
copyout() at netbsd:copyout+0x33
ubc_uiomove() at netbsd:ubc_uiomove+0x165
ffs_read() at netbsd:ffs_read+0xf0
VOP_READ() at netbsd:VOP_READ+0x42
vn_read() at netbsd:vn_read+0x16f
dofileread() at netbsd:dofileread+0x79
sys_read() at netbsd:sys_read+0x49
syscall() at netbsd:syscall+0x196
[ 213838.7597637] --- syscall (number 3) ---
---

The issue is confirmed to affect netbsd-10 and netbsd-9. It possibly affects netbsd-8 too.
>How-To-Repeat:
Compile the following program with -g:
---
#include <fcntl.h>
#include <unistd.h>

char buf[16];

int main(void) {
    int fd = open("/dev/urandom", O_RDONLY);
    read(fd, buf, sizeof(buf));
    return 0;
}
---

Open it in gdb, do "watch buf" and "run".
>Fix:
The following patch at least fixes the panic, but still doesn't generate SIGTRAP. Help, I couldn't figure out why:

diff --git a/sys/arch/amd64/amd64/trap.c b/sys/arch/amd64/amd64/trap.c
index 6e555545ecd..2645c3f0acf 100644
--- a/sys/arch/amd64/amd64/trap.c
+++ b/sys/arch/amd64/amd64/trap.c
@@ -617,19 +617,15 @@ pagefltcommon:
 
 	case T_TRCTRAP:
 		/*
-		 * Ignore debug register trace traps due to
+		 * Handle debug register trace traps due to
 		 * accesses in the user's address space, which
 		 * can happen under several conditions such as
 		 * if a user sets a watchpoint on a buffer and
 		 * then passes that buffer to a system call.
-		 * We still want to get TRCTRAPS for addresses
-		 * in kernel space because that is useful when
-		 * debugging the kernel.
 		 */
-		if (x86_dbregs_user_trap())
-			break;
-
-		goto we_re_toast;
+		if (!x86_dbregs_user_trap())
+			goto we_re_toast;
+		/* FALLTHROUGH */
 
 	case T_BPTFLT|T_USER:		/* bpt instruction fault */
 	case T_TRCTRAP|T_USER:		/* trace trap */
diff --git a/sys/arch/i386/i386/trap.c b/sys/arch/i386/i386/trap.c
index 98769168f5c..eff4b78f73a 100644
--- a/sys/arch/i386/i386/trap.c
+++ b/sys/arch/i386/i386/trap.c
@@ -732,19 +732,15 @@ faultcommon:
 
 	case T_TRCTRAP:
 		/*
-		 * Ignore debug register trace traps due to
+		 * Handle debug register trace traps due to
 		 * accesses in the user's address space, which
 		 * can happen under several conditions such as
 		 * if a user sets a watchpoint on a buffer and
 		 * then passes that buffer to a system call.
-		 * We still want to get TRCTRAPS for addresses
-		 * in kernel space because that is useful when
-		 * debugging the kernel.
 		 */
-		if (x86_dbregs_user_trap())
-			break;
-
-		goto we_re_toast;
+		if (!x86_dbregs_user_trap())
+			goto we_re_toast;
+		/* FALLTHROUGH */
 
 	case T_BPTFLT|T_USER:		/* bpt instruction fault */
 	case T_TRCTRAP|T_USER:		/* trace trap */
diff --git a/sys/arch/x86/x86/dbregs.c b/sys/arch/x86/x86/dbregs.c
index b473c696cc4..2bb3dc6c710 100644
--- a/sys/arch/x86/x86/dbregs.c
+++ b/sys/arch/x86/x86/dbregs.c
@@ -47,7 +47,11 @@ static struct dbreg initdbstate;
 	X86_DR6_DR2_BREAKPOINT_CONDITION_DETECTED | \
 	X86_DR6_DR3_BREAKPOINT_CONDITION_DETECTED )
 
-#define X86_GLOBAL_BREAKPOINT	( \
+#define X86_ANY_BREAKPOINTS	( \
+	X86_DR7_LOCAL_DR0_BREAKPOINT | \
+	X86_DR7_LOCAL_DR1_BREAKPOINT | \
+	X86_DR7_LOCAL_DR2_BREAKPOINT | \
+	X86_DR7_LOCAL_DR3_BREAKPOINT | \
 	X86_DR7_GLOBAL_DR0_BREAKPOINT | \
 	X86_DR7_GLOBAL_DR1_BREAKPOINT | \
 	X86_DR7_GLOBAL_DR2_BREAKPOINT | \
@@ -202,9 +206,9 @@ x86_dbregs_user_trap(void)
 	register_t bp;
 
 	dr7 = rdr7();
-	if ((dr7 & X86_GLOBAL_BREAKPOINT) == 0) {
+	if ((dr7 & X86_ANY_BREAKPOINTS) == 0) {
 		/*
-		 * All Global Breakpoint bits are zero, thus the trap couldn't
+		 * All Breakpoint bits are zero, thus the trap couldn't
 		 * have been caused by the hardware debug registers.
 		 */
 		return 0;



Home | Main Index | Thread Index | Old Index