Breaking on Data Read

Edit: As of windows 10 the details and code below do not work. A working alternative is detailed at a newer post.


 

You’re probably familiar with Data Breakpoints, and rightfully so: It’s extremely useful to know where a value changes. But did you know that with a little help VS can break when a value is used?

Usage

By ‘little help’ I mean some code. Plenty of free implementations are available: 1, 2, 3, 4. All are very similar (one notable difference discussed below), but I’m used to the first and will use it below.

First, a toy example:

#include "Breakpoint.h"
...
CBreakpoint g_bp;
…
void Whateva()
{
int a = 3, b;
g_bp.Set(&a, 4, CBreakpoint::Read);
b = a; // g_bp breaks!
g_bp.Clear();
}

The link does mention that you can call CBreakpoint::Clear() from a QuickWatch window (== from anywhere the Expression Evaluator lives, for that matter). What’s even more useful – you can call CBreakpoint::Set() from the debugger with a minor additional cast. While debugging the code above, evaluate the following in any watch window:

g_bp.Set(&a, 4, (CBreakpoint::Condition)3)

image

Internals (well, some, anyway)

Both read and write breakpoints are implemented via debug registers: special registers on a x86 CPU which trigger an ‘int 1’ interrupt (‘debug step’) whenever a pre-specified virtual address is accessed. Debug Register Dr7 is set to activate any hardware breakpoint, Dr0-Dr3 determine the type (11b means read/write).

All implementations linked above modify the debug registers via SetThreadContext. The documentation includes a grave warning, that only implementations 3 & 4 seem to respect:

Do not try to set the context for a running thread; the results are unpredictable. Use the SuspendThread function to suspend the thread before calling SetThreadContext.

However I’ve never had issues with implementation 1, so I assume in practice usage of SetThreadContext with this particular mask (CONTEXT_DEBUG_REGISTERS) is safe.

This usage makes one wonder – are debug registers indeed part of a thread context? Are they reset on every context switch?

The intel manuals, vol 3A, section 16.4.2 details the contents of DR7:

The debug control register (DR7) enables or disables breakpoints and sets breakpoint conditions … The flags and fields in this register control the following things:

• L0 through L3 (local breakpoint enable) flags (bits 0, 2, 4, and 6) — Enables (when set) the breakpoint condition for the associated breakpoint for the current task. When a breakpoint condition is detected and its associated Ln flag is set, a debug exception is generated. The processor automatically clears these flags on every task switch to avoid unwanted breakpoint conditions in the new task.

Oh dear. Are hardware breakpoints indeed that useless? Are they indeed blind to reads/writes by other threads?

Well obviously, no. It’s a 1 minute test to set a HW-Bp, modify its address from a different thread and watch it trigger.

It all boils down to a nuance in x86 terminology: tasks are not threads. Windows does not use the task context switching hardware apparatus that x86 offers, so it really is an OS decision whether to store debug registers per thread – and the obvious choice seems to store them per process. That is probably the reason calling SetThreadContext with CONTEXT_DEBUG_REGISTERS mask is safe also for non-suspended threads.

This entry was posted in Debugging, VC++, Win32. Bookmark the permalink.

6 Responses to Breaking on Data Read

  1. bobcat says:

    “All implementations linked above modify the debug registers via SetThreadContext. The documentation includes a grave warning, that only implementations 3 & 4 seem to respect:

    Do not try to set the context for a running thread; the results are unpredictable. Use the SuspendThread function to suspend the thread before calling SetThreadContext.

    However I’ve never had issues with implementation 1, so I assume in practice usage of SetThreadContext with this particular mask (CONTEXT_DEBUG_REGISTERS) is safe.”

    You’re accessing the context of your own thread, as such certain registers are ignored (they’re in use) and for most parts you can safely get and set the context with reliable results.

    Apply the same thing to another thread, one which you have gained a handle with OpenThread (from the same *Thread* function family, used primarily for debugging) but which you haven’t suspended.

    Get the context and it is a snapshot of when the context was taken, not what is actually there…

    Set the context and you may find yourself setting the EIP to somewhere it shouldn’t be, or modifying the stack pointer (ESP) so that the thread accesses invalid memory and causes an access violation exception…

    Unless it is unavoidable or unnecessary (due to the thread already being suspened as it is upon returning from a call to WaitForDebugEvent which is actually reporting an event), it is always advisable to suspend a remote thread before calling SetThreadContext and GetThreadContext.

    They might make people jump through hoops but its better to follow Microsoft’s advice for the majority of coding decisions when coding for Windows, after all they did write the thing and have half an idea of what is going on… o0

    G out.

    • Ofek Shilon says:

      @bobcat: Thanks. Obviously setting general thread context on a live thread is suicidal. However, EIP/ESP/ all other general-purpose registers are *not* part of the CONTEXT_DEBUG_REGISTERS mask, and as noted the real CONTEXT_DEBUG_REGISTERS are not really per-thread, which is why I dared say that in this particular usage (and in it alone), SetThreadContext on a non-suspended thread is probably safe.

  2. So just to clarify, when debug registers are changed every time there is a swap context? In other words, debug registers are thread specific as opposed to process specific?

    Thanks.

    • Ofek Shilon says:

      Debug register contents are definitely *not* part of thread context. They seem to be part of ‘task’ context, where ‘task’ is a hardware apparatus for context switching that it seems windows doesn’t use – or at least, doesn’t use for regular thread switching. Maybe just for cross-process thread switching?
      Anyway – bottom line, you can safely use debug registers to set process-wide breakpoints.

  3. Pingback: .mischief.mayhem.soap. » Blog Archive » Whac-A-Mole

  4. Pingback: Data Read Breakpoints – Redux | 神刀安全网

Leave a comment