1

I am trying create hardware watchpoint dynamically and for that in my program I am setting debug registers. I am using only dr0 and have chosen length as 3 (4 byte aligned) and type as 1 (break on write). My cpu is intel x86_it. However, in every run, I am seeing segfault whenever I try to write to db0/dr0. The offending function and it's assembly from gdb are shown below.

int watchpoint(void* addr)
{
    unsigned long value = (unsigned long) addr;
    asm("mov %0, %%dr0"     ::"r" (0));
    asm("mov %0, %%dr7"     ::"r" (0));
    asm("mov %0, %%dr0"     ::"r" (value));
    asm("mov %0, %%dr7"     ::"r" (851969));
    return 0;
}



   0x0832aa38 <+0>:     push   %ebp
   0x0832aa39 <+1>:     mov    %esp,%ebp
   0x0832aa3b <+3>:     sub    $0x10,%esp
   0x0832aa3e <+6>:     mov    0x8(%ebp),%eax
   0x0832aa41 <+9>:     mov    %eax,-0x4(%ebp)
   0x0832aa44 <+12>:    mov    $0x0,%eax
=> 0x0832aa49 <+17>:    mov    %eax,%db0  >>>>> Crashes here.
   0x0832aa4c <+20>:    mov    $0x0,%eax
   0x0832aa51 <+25>:    mov    %eax,%db7
   0x0832aa54 <+28>:    mov    -0x4(%ebp),%eax
   0x0832aa57 <+31>:    mov    %eax,%db0
   0x0832aa5a <+34>:    mov    $0xd0001,%eax
   0x0832aa5f <+39>:    mov    %eax,%db7
   0x0832aa62 <+42>:    mov    $0x0,%eax
   0x0832aa67 <+47>:    leave
   0x0832aa68 <+48>:    ret

Am I writing wrong register? Can someone please help?

Thank you.

NeilB
  • 347
  • 2
  • 16
  • BTW, you don't need to cast `addr` to `unsigned long`. You can use an `"r"` constraint for a `void*` operand. (That doesn't imply that the inline asm dereferences that pointer, so the compiler will still freely optimize loads/stores to that address around the asm, including optimizing them away, unless you use a `"memory"` clobber, if you were trying to avoid gimping the optimizer.) – Peter Cordes Oct 24 '18 at 13:26

1 Answers1

2

http://felixcloutier.com/x86/MOV-2.html (mov to debug register) says:

#GP(0) If the current privilege level is not 0.

User-space code runs in ring 3 (privilege level 3). root privilege is just a software thing; it's still purely ring 3.

A Linux iopl system call can raise the x86 hardware I/O privilege level, but that only affects a few instructions like in/out and cli/sti. MOV to debug registers isn't one of them.


Under Linux, you could either write your own kernel module, or use a ptrace system call to set breakpoints.

Under other OSes, you'll find other APIs that debuggers can use.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • So I must fork a child and then use ptrace with POKEUSER to set parent's debug registers accordingly. Is that the only option? My program is running with root privilege. – NeilB Oct 24 '18 at 12:03
  • In kgdb.c, I see that code is using similar asm intructions. – NeilB Oct 24 '18 at 12:07
  • @NeilB: root is still user-space (ring 3), not kernel. You can't run privileged instructions. You still can't take over the whole machine without making system calls. (Or accidentally crash it.) `kgdb.c` runs in kernel space, doesn't it? – Peter Cordes Oct 24 '18 at 12:12
  • @NeilB Typically, you can only debug your direct children with `ptrace` on Linux as the `ptrace_scope` sysctl normally forbids this. – fuz Oct 24 '18 at 12:17
  • @PeterCordes I think you can run privileged instructions if you first execute `iopl` to elevate yourself to ring 0. – fuz Oct 24 '18 at 12:18
  • @PeterCordes Yes, I just realized that root is not privilege level 0. I am able to program the debug register using ptrace from a child process. However I have many threads in the program and I noticed that I have to set the watchpoint from every thread for it work correctly. Then there are lots of issues when I fork a child process from a thread. and to get rid of that I think I will have to use exec. So to avoid all these I was trying to set the registers from the process itself. Thanks a lot for your prompt response. – NeilB Oct 24 '18 at 12:26
  • @fuz It seems ptrace is the only option. Actually using ptrace, child process can set registers in it's parent. Please see https://stackoverflow.com/questions/8941711/is-is-possible-to-set-a-gdb-watchpoint-programatically – NeilB Oct 24 '18 at 12:28
  • @NeilB As I tried to say before: yes, it is possible to do this, if the `ptrace_scope` sysctl is set to allow this. Many Linux distributions set up this sysctl to forbid debugging anything but your direct children, so don't expect this to work on other people's computers. – fuz Oct 24 '18 at 12:38
  • @fuz Thanks Fuz. I wan under the impression that it always works both ways but I will double check before porting it to other products. – NeilB Oct 24 '18 at 12:41
  • 1
    @fuz: No, `iopl` only sets your IO privilege level, allowing *some* instructions like `in`/`out` (to any port number, not just some in the low range which can be enabled in HW without full IOPL) and [`cli`](http://felixcloutier.com/x86/CLI.html)/`sti`. It doesn't put you in ring 0 so you still can't use other privileged instructions. (Yes, x86 really has hardware support for allowing IO privilege outside of ring 0 without full privs for system-breaking instructions like `invd`.) – Peter Cordes Oct 24 '18 at 12:41