Messages in this thread Patch in this message | | | Date | Mon, 6 Sep 1999 22:59:05 +0200 | From | Petr Vandrovec <> | Subject | Re: debug registers in 2.3.x + PATCH |
| |
Hi Linus, in short, search for lines '@ Cut here...' and '@ And here...'. You'll find patch destined for 2.3.17 between them. Today I found that lazy DR7 setting can (and will) affect VM86 processes if there is address match between vm86 task (first megabyte) and debug registers. Because of ptrace() interface is not restricted, everyone who has rights to trace at least one task can affect all vm86 tasks in your box :-( Patch moves check for nulity of DR7 before check for VM86, so spurious exceptions caused by DR0-DR3 match are ignored. Other debug exceptions are still processed by VM86 specific code as were before.
Hi Alan, because of do_debug() in 2.2.12 kernel is same as 2.3.16, I think that 2.2.x series is affected too and maybe that someone can count this problem as security bug...
Hi others, to follow up myself, I wrote test sample which confirmed my thinkings that lazy DR7 is buggy if you have more than zero VM86 tasks and found one another thing: On my dual SMP board, (PIII/450), if I remove sleep(1) from catch449.c, (see source below) I get: attach: Success pokeusr dr0: No such process pokeusr dr7: No such process cont: No such process With sleep(1) it works for first catch449. Running 2nd, 3rd.. instance of catch449 is still a bit tricky. Am I stupid or is there race somewhere? catch449's child is listed with status "T" in tasklist, which corresponds to seen behavior - attach was successful, but continue failed :-( Now back to topic, if I run two catch449.c (for each CPU one), every start of emu.c ends immediately with:
vm86 returned: 262 (type=6, arg=1) VM Info: Loops: 1, Except: 0, Var: 0
And 6 = TRAP, 1 = INT1 (trace) :-( If I run first emu and start catch449 after that, emu immediately stops :-( So I wrote patch attached here too. This patch moves check for breakpoints caused by DR_TRAP[0-3] before VM86 check, so it correctly clears DR7 in this case. I left check for other possible sources (mainly Trace Flag) below check, so DOS debuggers which sets TF still works (tested with Borland TD). Diff is against 2.3.16 and machine with this patch is happilly running on my desk just now. 2.2.12 contains same code, so maybe we should apply it to 2.2.x kernels too, what do you think Alan?
@ Cut here... --- linux/arch/i386/kernel/traps.c Wed Aug 18 20:27:34 1999 +++ linux/arch/i386/kernel/traps.c Mon Sep 6 22:45:22 1999 @@ -355,11 +355,17 @@ unsigned int condition; struct task_struct *tsk = current; + __asm__ __volatile__("movl %%db6,%0" : "=r" (condition)); + + /* Mask out spurious debug traps due to lazy DR7 setting */ + if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { + if (!tsk->thread.debugreg[7]) + goto clear_dr7; + } + if (regs->eflags & VM_MASK) goto debug_vm86; - __asm__ __volatile__("movl %%db6,%0" : "=r" (condition)); - /* Mask out spurious TF errors due to lazy TF clearing */ if (condition & DR_STEP) { /* @@ -373,12 +379,6 @@ */ if ((tsk->flags & (PF_DTRACE|PF_PTRACED)) == PF_DTRACE) goto clear_TF; - } - - /* Mask out spurious debug traps due to lazy DR7 setting */ - if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { - if (!tsk->thread.debugreg[7]) - goto clear_dr7; } /* If this is a kernel mode trap, we need to reset db7 to allow us to continue sanely */ @ And here... /* catch449.c */ #include <sys/vm86.h> #include <stdlib.h> #include <stdio.h> #include <asm/user.h> #define XFS(x) ((unsigned long)&(((struct user*)NULL)->x))
void main(int argc, char* argv) { int pid = fork(); if (pid) { sleep(1); ptrace(PTRACE_ATTACH, pid, 0, 0); perror("attach"); ptrace(PTRACE_POKEUSR, pid, XFS(u_debugreg[0]), 0x449); perror("pokeusr dr0"); ptrace(PTRACE_POKEUSR, pid, XFS(u_debugreg[7]), 0x00030003); perror("pokeusr dr7"); ptrace(PTRACE_CONT, pid, 0, 0); perror("cont"); while (1) { sleep(1000); } } else { printf("Traced..\n"); while (1) ; printf("End trace..\n"); } } /* end of catch449.c */ /* emu.c */ #include <asm/vm86.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/mman.h> #include <sys/fcntl.h> #include <sys/syscall.h> #include <errno.h> _syscall2(int,vm86,int,request,struct vm86plus_struct*,vm);
void main(void) { int i; unsigned char* m; struct vm86plus_struct vm; i = open("/dev/zero", O_RDONLY); if (i < 0) perror("open"); m = mmap(0, 1048576, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_FIXED|MAP_PRIVATE, i, 0); close(i); if ((caddr_t)m == (caddr_t)-1) perror("mmap"); vm.regs.ebx = 0x410; /* loops counter */ vm.regs.ecx = 0; vm.regs.edx = 0; vm.regs.esi = 0x420; /* exception counter */ vm.regs.edi = 0x449; /* breakpoint check */ vm.regs.ebp = 0; vm.regs.eax = 0; vm.regs.orig_eax = 0; vm.regs.eip = 0x400; /* code start is 0000:0400 */ vm.regs.cs = 0; vm.regs.eflags = VM_MASK | IF_MASK; vm.regs.esp = 0x800; vm.regs.ss = 0; vm.regs.es = 0; vm.regs.ds = 0; vm.regs.fs = 0; vm.regs.gs = 0; vm.flags = VM86_ENTER; vm.screen_bitmap = 0; vm.cpu_type = CPU_586; memset(&vm.int_revectored, 0, sizeof(vm.int_revectored)); memset(&vm.int21_revectored, 0, sizeof(vm.int21_revectored)); memset(&vm.vm86plus, 0, sizeof(vm.vm86plus)); for (i = 0; i < 1024; i+=4) { /* IDT points to 0000:0406, just in case... */ m[0] = 0x06; m[1] = 0x04; m[2] = 0; m[3] = 0; } /* l0400: inc word [bx]; mov al,[di]; jmp l0400; l0406: inc word [si]; jmp l0400; */ memcpy(m + 0x400, "\xFF\x07\x8A\x05\xEB\xFA\xFF\x04\xEB\xF6", 10); i = vm86(VM86_ENTER, &vm); printf("vm86 returned: %d (type=%d, arg=%d)\n", i, VM86_TYPE(i), VM86_ARG(i)); perror("errno"); printf("VM Info: Loops: %d, Except: %d, Var: %d\n", /* read 0x449 to clear DR7 for this CPU... otherwise you have to reboot to get access to 0x449 back to work (or run gdb) */ m[0x410], m[0x420], m[0x449]); }; /* end of emu.c */ Best regards, Petr Vandrovec vandrove@vc.cvut.cz
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/
| |