Interrupt code for detecting memory corruption

Started by
7 comments, last by TheAdmiral 16 years, 11 months ago
I used to have a piece of code that would let me set a kind of "watch" on a variable, and any time any part of the app would try to modify the variable's contents, it would use an interrupt to stop the debugger and let you see what was trying to modify it. It was a single page of code, written by a guy whose name I think was Mike something, but my memory is foggy. Anyone have a clue what it was called and where to get it? It was really useful, and I think it could help me track down a minor corruption bug I'm experiencing now. edit: I should probably mention that it was in C/C++
------------------------------------------[New Delta Games] | [Sliders]
Advertisement
It would help to know what platform you're on. Aside from that, you can use a debugger (MSVC, WinDbg, GDB, DBX, etc) to do this at runtime instead of programmatically, it's probably much more convenient that way.
Under Windows, right?

As outRider says, your debugger is designed for this sort of task - use it if possible. Otherwise, the only way I can think to emulate such functionality is to use hardware breakpoints. I haven't tested this very much, but here is something I've adapted from a user-mode debugger I wrote a while back:

#include <Windows.h>enum BP_ACCESS{	BP_WRITE,	BP_READWRITE};int CreateHWBP(void* const address, int index, BP_ACCESS access) {	if (index < 0 || index > 3) return -1; // Minimal error-handling	CONTEXT context;	context.ContextFlags = CONTEXT_DEBUG_REGISTERS;	DWORD thread_id = GetCurrentThreadId();	HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, false, thread_id);	GetThreadContext(thread_handle, &context);	// Reset	context.Dr7 &= ~(3 << (2 * index));	context.Dr7 &= ~(3 << (16 + 2 * index));	context.Dr7 &= ~(3 << (24 + 2 * index));	// Enable	context.Dr7 |= (1 << (2 * index));	// Address	switch (index) {		case 0:			context.Dr0 = reinterpret_cast<DWORD> (address);			break;		case 1:			context.Dr1 = reinterpret_cast<DWORD> (address);			break;		case 2:			context.Dr2 = reinterpret_cast<DWORD> (address);			break;		case 3:			context.Dr3 = reinterpret_cast<DWORD> (address);			break;	}	// Access	switch (access) {		case BP_WRITE:			context.Dr7 |= (1 << (16 + 2 * index));			break;		case BP_READWRITE:			context.Dr7 |= (3 << (16 + 2 * index));			break;		default:			break;	}	SetThreadContext(thread_handle, &context);	CloseHandle(thread_handle);	return 0; // Success}int DestroyHWBP(int index) {	if (index < 0 || index > 3) return -1;	CONTEXT context;	context.ContextFlags = CONTEXT_DEBUG_REGISTERS;	DWORD thread_id = GetCurrentThreadId();	HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, false, thread_id);	GetThreadContext(thread_handle, &context);	switch (index) {		case 0:			context.Dr0 = 0;			break;		case 1:			context.Dr1 = 0;			break;		case 2:			context.Dr2 = 0;			break;		case 3:			context.Dr3 = 0;			break;	}	SetThreadContext(thread_handle, &context);	CloseHandle(thread_handle);	return 0; // Success}


It isn't very portable, and certainly not 64-bit-compliant. Neither is it compatible with Visual Studio's tracing.

Call CreateHWBP with a pointer to the watch variable, passing zero as the index and BP_WRITE or BP_READWRITE as appropriate. If the local process violates the break condition, a EXCEPTION_SINGLE_STEP is raised. Without a debugger, this will fall through to whatever handlers you have installed, probably resulting in a crash. Under Visual Studio, the exception will be caught and handled internally. By the looks of things, VS uses the same technique for its own breakpoints. Moreover, it ignores any single-step errors in places it isn't using itself. This has a few consequences:

1. Setting any breakpoints in the debugger will overwrite your user-breakpoint.
2. Calling the function after having set a breakpoint in the IDE will similarly result in an overwrite.
3. You may only have one such breakpoint going at a time, with index = 0. Putting index = 1, 2, 3 doesn't seem to do anything.

You can remove the breakpoint by calling DestroyHWBP with the corresponding index.

Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.
I'm using VS2005 - what's the command(s) to setup a variable break in that IDE? I know you can setup breaks with equation checks, but how do I set it up to break any time a variable is altered?
------------------------------------------[New Delta Games] | [Sliders]
Debug -> New Breakpoint -> New Data Breakpoint
Enter the address of the variable to watch (E.g. &m_theVariableName) and the number of bytes to watch (E.g. 4 for an int, 2 for a short, etc), then hit "OK"
Hardware supported breakpoints use... hardware... so you only can watch so many bytes. You can have either one or two, each 4 bytes long, on most hardware. If you go outside of this, it has the enumate the code. This is extremely slow.

HW breakpoints are a great tool - everyone should know about them. :)
Ah see, those data breakpoints are not much use for what I was after - for starters they can only be set once the program is running, and then you have to set them again every time you run it.

TheAdmiral has posted some nice code there - it wasn't the exact same code I had before, but it looks like it does the same kinda thing - you add a call in your code to set a HW breakpoint on the variable space you're interested in, and hey presto.

Thanks Admiral.
------------------------------------------[New Delta Games] | [Sliders]
If you're only watching pointers, it might be better to create a 'DebugPointer' template class with overloaded operators. You can call 'DebugBreak()' from the windows API to break at any time inside the various operators. Optionally, use IsDebuggerPresent() so that you only break when a debugger is watching, and you could use #ifdef blocks to entirely remove the test in release builds.
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk
Quote:Original post by BrianL
Hardware supported breakpoints use... hardware... so you only can watch so many bytes. You can have either one or two, each 4 bytes long, on most hardware.

For the sake of correctness, I'm not sure how things used to be, but since the 486, Intel set the standard as four hardware breakpoints per task (hence DR1 through DR4 being address registers). And although it's most common to use dword breakpoints, the processor supports HWBPs of size 1, 2, or 4 bytes.

Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.

This topic is closed to new replies.

Advertisement