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:
Brian S. Dean 2000-02-20 20:51:23 +00:00
parent 6ecfb1da44
commit de8050f9b8
14 changed files with 364 additions and 3 deletions

View File

@ -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)

View File

@ -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 */
/*****************************************************************************/

View File

@ -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 */
/*****************************************************************************/

View File

@ -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:
/*

View File

@ -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");

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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)

View File

@ -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 */
/*****************************************************************************/

View File

@ -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:
/*

View File

@ -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");

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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:
/*