Don't forget to reset the hardware debug registers when a process that
was using them exits. Don't allow a user process to cause the kernel to take a TRCTRAP on a user space address. Reviewed by: jlemon, sef Approved by: jkh
This commit is contained in:
parent
6ecfb1da44
commit
de8050f9b8
@ -2239,6 +2239,76 @@ set_dbregs(p, dbregs)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return > 0 if a hardware breakpoint has been hit, and the
|
||||
* breakpoint was in user space. Return 0, otherwise.
|
||||
*/
|
||||
int
|
||||
user_dbreg_trap(void)
|
||||
{
|
||||
u_int32_t dr7, dr6; /* debug registers dr6 and dr7 */
|
||||
u_int32_t bp; /* breakpoint bits extracted from dr6 */
|
||||
int nbp; /* number of breakpoints that triggered */
|
||||
caddr_t addr[4]; /* breakpoint addresses */
|
||||
int i;
|
||||
|
||||
dr7 = rdr7();
|
||||
if ((dr7 & 0x000000ff) == 0) {
|
||||
/*
|
||||
* all GE and LE bits in the dr7 register are zero,
|
||||
* thus the trap couldn't have been caused by the
|
||||
* hardware debug registers
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
nbp = 0;
|
||||
dr6 = rdr6();
|
||||
bp = dr6 & 0x0000000f;
|
||||
|
||||
if (!bp) {
|
||||
/*
|
||||
* None of the breakpoint bits are set meaning this
|
||||
* trap was not caused by any of the debug registers
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* at least one of the breakpoints were hit, check to see
|
||||
* which ones and if any of them are user space addresses
|
||||
*/
|
||||
|
||||
if (bp & 0x01) {
|
||||
addr[nbp++] = (caddr_t)rdr0();
|
||||
}
|
||||
if (bp & 0x02) {
|
||||
addr[nbp++] = (caddr_t)rdr1();
|
||||
}
|
||||
if (bp & 0x04) {
|
||||
addr[nbp++] = (caddr_t)rdr2();
|
||||
}
|
||||
if (bp & 0x08) {
|
||||
addr[nbp++] = (caddr_t)rdr3();
|
||||
}
|
||||
|
||||
for (i=0; i<nbp; i++) {
|
||||
if (addr[i] <
|
||||
(caddr_t)VM_MAXUSER_ADDRESS) {
|
||||
/*
|
||||
* addr[i] is in user space
|
||||
*/
|
||||
return nbp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* None of the breakpoints are in user space.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifndef DDB
|
||||
void
|
||||
Debugger(const char *msg)
|
||||
|
@ -1586,6 +1586,23 @@ ENTRY(load_cr4)
|
||||
movl %eax,%cr4
|
||||
ret
|
||||
|
||||
/* void load_dr6(u_int dr6) */
|
||||
ENTRY(load_dr6)
|
||||
movl 4(%esp),%eax
|
||||
movl %eax,%dr6
|
||||
ret
|
||||
|
||||
/* void reset_dbregs() */
|
||||
ENTRY(reset_dbregs)
|
||||
movl $0,%eax
|
||||
movl %eax,%dr7 /* disable all breapoints first */
|
||||
movl %eax,%dr0
|
||||
movl %eax,%dr1
|
||||
movl %eax,%dr2
|
||||
movl %eax,%dr3
|
||||
movl %eax,%dr6
|
||||
ret
|
||||
|
||||
/*****************************************************************************/
|
||||
/* setjump, longjump */
|
||||
/*****************************************************************************/
|
||||
|
@ -1586,6 +1586,23 @@ ENTRY(load_cr4)
|
||||
movl %eax,%cr4
|
||||
ret
|
||||
|
||||
/* void load_dr6(u_int dr6) */
|
||||
ENTRY(load_dr6)
|
||||
movl 4(%esp),%eax
|
||||
movl %eax,%dr6
|
||||
ret
|
||||
|
||||
/* void reset_dbregs() */
|
||||
ENTRY(reset_dbregs)
|
||||
movl $0,%eax
|
||||
movl %eax,%dr7 /* disable all breapoints first */
|
||||
movl %eax,%dr0
|
||||
movl %eax,%dr1
|
||||
movl %eax,%dr2
|
||||
movl %eax,%dr3
|
||||
movl %eax,%dr6
|
||||
ret
|
||||
|
||||
/*****************************************************************************/
|
||||
/* setjump, longjump */
|
||||
/*****************************************************************************/
|
||||
|
@ -519,8 +519,26 @@ kernel_trap:
|
||||
frame.tf_eflags &= ~PSL_T;
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Ignore 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 (user_dbreg_trap()) {
|
||||
/*
|
||||
* Reset breakpoint bits because the
|
||||
* processor doesn't
|
||||
*/
|
||||
load_dr6(rdr6() & 0xfffffff0);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Fall through.
|
||||
* Fall through (TRCTRAP kernel mode, kernel address)
|
||||
*/
|
||||
case T_BPTFLT:
|
||||
/*
|
||||
|
@ -246,6 +246,13 @@ cpu_exit(p)
|
||||
#ifdef USER_LDT
|
||||
user_ldt_free(pcb);
|
||||
#endif
|
||||
if (pcb->pcb_flags & PCB_DBREGS) {
|
||||
/*
|
||||
* disable all hardware breakpoints
|
||||
*/
|
||||
reset_dbregs();
|
||||
pcb->pcb_flags &= ~PCB_DBREGS;
|
||||
}
|
||||
cnt.v_swtch++;
|
||||
cpu_switch(p);
|
||||
panic("cpu_exit");
|
||||
|
@ -452,6 +452,54 @@ load_gs(u_int sel)
|
||||
__asm __volatile("movl %0,%%gs" : : "rm" (sel));
|
||||
}
|
||||
|
||||
static __inline u_int
|
||||
rdr0(void)
|
||||
{
|
||||
u_int data;
|
||||
__asm __volatile("movl %%dr0,%0" : "=rm" (data));
|
||||
return (data);
|
||||
}
|
||||
|
||||
static __inline u_int
|
||||
rdr1(void)
|
||||
{
|
||||
u_int data;
|
||||
__asm __volatile("movl %%dr1,%0" : "=rm" (data));
|
||||
return (data);
|
||||
}
|
||||
|
||||
static __inline u_int
|
||||
rdr2(void)
|
||||
{
|
||||
u_int data;
|
||||
__asm __volatile("movl %%dr2,%0" : "=rm" (data));
|
||||
return (data);
|
||||
}
|
||||
|
||||
static __inline u_int
|
||||
rdr3(void)
|
||||
{
|
||||
u_int data;
|
||||
__asm __volatile("movl %%dr3,%0" : "=rm" (data));
|
||||
return (data);
|
||||
}
|
||||
|
||||
static __inline u_int
|
||||
rdr6(void)
|
||||
{
|
||||
u_int data;
|
||||
__asm __volatile("movl %%dr6,%0" : "=rm" (data));
|
||||
return (data);
|
||||
}
|
||||
|
||||
static __inline u_int
|
||||
rdr7(void)
|
||||
{
|
||||
u_int data;
|
||||
__asm __volatile("movl %%dr7,%0" : "=rm" (data));
|
||||
return (data);
|
||||
}
|
||||
|
||||
#else /* !__GNUC__ */
|
||||
|
||||
int breakpoint __P((void));
|
||||
@ -497,5 +545,7 @@ void ltr __P((u_short sel));
|
||||
u_int rcr0 __P((void));
|
||||
u_int rcr3 __P((void));
|
||||
u_int rcr4 __P((void));
|
||||
void load_dr6 __P((u_int dr6));
|
||||
void reset_dbregs __P((void));
|
||||
|
||||
#endif /* !_MACHINE_CPUFUNC_H_ */
|
||||
|
@ -95,6 +95,7 @@ void setidt __P((int idx, alias_for_inthand_t *func, int typ, int dpl,
|
||||
int selec));
|
||||
void swi_vm __P((void));
|
||||
void userconfig __P((void));
|
||||
int user_dbreg_trap __P((void));
|
||||
int vm_page_zero_idle __P((void));
|
||||
|
||||
#endif /* !_MACHINE_MD_VAR_H_ */
|
||||
|
@ -2239,6 +2239,76 @@ set_dbregs(p, dbregs)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return > 0 if a hardware breakpoint has been hit, and the
|
||||
* breakpoint was in user space. Return 0, otherwise.
|
||||
*/
|
||||
int
|
||||
user_dbreg_trap(void)
|
||||
{
|
||||
u_int32_t dr7, dr6; /* debug registers dr6 and dr7 */
|
||||
u_int32_t bp; /* breakpoint bits extracted from dr6 */
|
||||
int nbp; /* number of breakpoints that triggered */
|
||||
caddr_t addr[4]; /* breakpoint addresses */
|
||||
int i;
|
||||
|
||||
dr7 = rdr7();
|
||||
if ((dr7 & 0x000000ff) == 0) {
|
||||
/*
|
||||
* all GE and LE bits in the dr7 register are zero,
|
||||
* thus the trap couldn't have been caused by the
|
||||
* hardware debug registers
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
nbp = 0;
|
||||
dr6 = rdr6();
|
||||
bp = dr6 & 0x0000000f;
|
||||
|
||||
if (!bp) {
|
||||
/*
|
||||
* None of the breakpoint bits are set meaning this
|
||||
* trap was not caused by any of the debug registers
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* at least one of the breakpoints were hit, check to see
|
||||
* which ones and if any of them are user space addresses
|
||||
*/
|
||||
|
||||
if (bp & 0x01) {
|
||||
addr[nbp++] = (caddr_t)rdr0();
|
||||
}
|
||||
if (bp & 0x02) {
|
||||
addr[nbp++] = (caddr_t)rdr1();
|
||||
}
|
||||
if (bp & 0x04) {
|
||||
addr[nbp++] = (caddr_t)rdr2();
|
||||
}
|
||||
if (bp & 0x08) {
|
||||
addr[nbp++] = (caddr_t)rdr3();
|
||||
}
|
||||
|
||||
for (i=0; i<nbp; i++) {
|
||||
if (addr[i] <
|
||||
(caddr_t)VM_MAXUSER_ADDRESS) {
|
||||
/*
|
||||
* addr[i] is in user space
|
||||
*/
|
||||
return nbp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* None of the breakpoints are in user space.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifndef DDB
|
||||
void
|
||||
Debugger(const char *msg)
|
||||
|
@ -1586,6 +1586,23 @@ ENTRY(load_cr4)
|
||||
movl %eax,%cr4
|
||||
ret
|
||||
|
||||
/* void load_dr6(u_int dr6) */
|
||||
ENTRY(load_dr6)
|
||||
movl 4(%esp),%eax
|
||||
movl %eax,%dr6
|
||||
ret
|
||||
|
||||
/* void reset_dbregs() */
|
||||
ENTRY(reset_dbregs)
|
||||
movl $0,%eax
|
||||
movl %eax,%dr7 /* disable all breapoints first */
|
||||
movl %eax,%dr0
|
||||
movl %eax,%dr1
|
||||
movl %eax,%dr2
|
||||
movl %eax,%dr3
|
||||
movl %eax,%dr6
|
||||
ret
|
||||
|
||||
/*****************************************************************************/
|
||||
/* setjump, longjump */
|
||||
/*****************************************************************************/
|
||||
|
@ -519,8 +519,26 @@ kernel_trap:
|
||||
frame.tf_eflags &= ~PSL_T;
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Ignore 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 (user_dbreg_trap()) {
|
||||
/*
|
||||
* Reset breakpoint bits because the
|
||||
* processor doesn't
|
||||
*/
|
||||
load_dr6(rdr6() & 0xfffffff0);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Fall through.
|
||||
* Fall through (TRCTRAP kernel mode, kernel address)
|
||||
*/
|
||||
case T_BPTFLT:
|
||||
/*
|
||||
|
@ -246,6 +246,13 @@ cpu_exit(p)
|
||||
#ifdef USER_LDT
|
||||
user_ldt_free(pcb);
|
||||
#endif
|
||||
if (pcb->pcb_flags & PCB_DBREGS) {
|
||||
/*
|
||||
* disable all hardware breakpoints
|
||||
*/
|
||||
reset_dbregs();
|
||||
pcb->pcb_flags &= ~PCB_DBREGS;
|
||||
}
|
||||
cnt.v_swtch++;
|
||||
cpu_switch(p);
|
||||
panic("cpu_exit");
|
||||
|
@ -452,6 +452,54 @@ load_gs(u_int sel)
|
||||
__asm __volatile("movl %0,%%gs" : : "rm" (sel));
|
||||
}
|
||||
|
||||
static __inline u_int
|
||||
rdr0(void)
|
||||
{
|
||||
u_int data;
|
||||
__asm __volatile("movl %%dr0,%0" : "=rm" (data));
|
||||
return (data);
|
||||
}
|
||||
|
||||
static __inline u_int
|
||||
rdr1(void)
|
||||
{
|
||||
u_int data;
|
||||
__asm __volatile("movl %%dr1,%0" : "=rm" (data));
|
||||
return (data);
|
||||
}
|
||||
|
||||
static __inline u_int
|
||||
rdr2(void)
|
||||
{
|
||||
u_int data;
|
||||
__asm __volatile("movl %%dr2,%0" : "=rm" (data));
|
||||
return (data);
|
||||
}
|
||||
|
||||
static __inline u_int
|
||||
rdr3(void)
|
||||
{
|
||||
u_int data;
|
||||
__asm __volatile("movl %%dr3,%0" : "=rm" (data));
|
||||
return (data);
|
||||
}
|
||||
|
||||
static __inline u_int
|
||||
rdr6(void)
|
||||
{
|
||||
u_int data;
|
||||
__asm __volatile("movl %%dr6,%0" : "=rm" (data));
|
||||
return (data);
|
||||
}
|
||||
|
||||
static __inline u_int
|
||||
rdr7(void)
|
||||
{
|
||||
u_int data;
|
||||
__asm __volatile("movl %%dr7,%0" : "=rm" (data));
|
||||
return (data);
|
||||
}
|
||||
|
||||
#else /* !__GNUC__ */
|
||||
|
||||
int breakpoint __P((void));
|
||||
@ -497,5 +545,7 @@ void ltr __P((u_short sel));
|
||||
u_int rcr0 __P((void));
|
||||
u_int rcr3 __P((void));
|
||||
u_int rcr4 __P((void));
|
||||
void load_dr6 __P((u_int dr6));
|
||||
void reset_dbregs __P((void));
|
||||
|
||||
#endif /* !_MACHINE_CPUFUNC_H_ */
|
||||
|
@ -95,6 +95,7 @@ void setidt __P((int idx, alias_for_inthand_t *func, int typ, int dpl,
|
||||
int selec));
|
||||
void swi_vm __P((void));
|
||||
void userconfig __P((void));
|
||||
int user_dbreg_trap __P((void));
|
||||
int vm_page_zero_idle __P((void));
|
||||
|
||||
#endif /* !_MACHINE_MD_VAR_H_ */
|
||||
|
@ -519,8 +519,26 @@ kernel_trap:
|
||||
frame.tf_eflags &= ~PSL_T;
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Ignore 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 (user_dbreg_trap()) {
|
||||
/*
|
||||
* Reset breakpoint bits because the
|
||||
* processor doesn't
|
||||
*/
|
||||
load_dr6(rdr6() & 0xfffffff0);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Fall through.
|
||||
* Fall through (TRCTRAP kernel mode, kernel address)
|
||||
*/
|
||||
case T_BPTFLT:
|
||||
/*
|
||||
|
Loading…
x
Reference in New Issue
Block a user