.NET

Debugging with NTSD and Application Verifier

Source Code Accompanies This Article. Download It Now.

Did you know that Windows ships with built-in debugging tools--the Microsoft NT Symbolic Debugger and the Application Verifier?

Application Verifier Limitations

I designed the program in Example 5(a) to specifically use a 16-byte buffer to be sure that the buffer overrun was detected. That is, an apparent limitation of AppVerif revealed through experimentation is that when it intercepts an allocation, it returns a buffer with a start address that is 16-byte aligned. This means that if the code were to request only a 10-byte buffer, then AppVerif would not return a pointer to the last 10 bytes of the valid block. Instead, it would return a pointer to the last 16 bytes of the valid block. The remaining 6 bytes in the block are still valid memory and, unfortunately, the application can happily overrun the 10-byte buffer by up to 6 bytes despite being verified by AppVerif.

So, to automatically detect 1-byte overruns using AppVerif, I need to write wrappers around the malloc and free functions. Example 8 shows examples of suitable wrapper functions called AvrfAlloc and AvrfFree. AvrfAlloc ensures that it always asks malloc for a buffer with a size that is a multiple of 16 bytes. However, it then moves the pointer so that it is exactly the requested number of bytes before the end of the buffer. It also uses a single byte just before the start of the returned buffer to store the offset in bytes from the previous 16-byte alignment so that AvrfFree can later move the buffer pointer back before freeing it. AvrfAlloc effectively pushes the end of the returned buffer to be right before the next 16-byte boundary. Now when the application is being verified by AppVerif, the 1-byte overrun will always be into an inaccessible page of memory.

Example 8: Wrapper functions for malloc and free that compensate for the 16-byte alignment imposed by AppVerif.

Another coding method that helps spot buffer overruns regardless of the presence of AppVerif is to write code that simulates what AppVerif is doing here, but without its 16-byte buffer alignment restriction. Listing One (available electronically; see "Resource Center," page 5) contains the source code for two functions called MyAlloc and MyFree that do just this, and can replace malloc and free. The MyAlloc function calls the Windows Memory Management function VirtualAlloc to allocate enough pages to hold the requested buffer, plus an additional page called the guard page (one page is typically 4096 bytes). This means that each call to MyAlloc allocates at least two pages. It then calls VirtualProtect to mark the guard page as inaccessible. Finally, it returns a pointer to an address located exactly at the originally requested number of bytes before the beginning of the guard page. The corresponding MyFree function simply rounds the pointer value down to the nearest page boundary before passing it to VirtualFree. As with AppVerif, this uses a lot of extra memory, and can noticeably slow down the application.

Debugging Customer Crashes

Debugging crashes is fairly straightforward when you have physical access to the machine. However, if the crash occurs at customer sites, then you often cannot get access to the customer's machine. In these cases, I ask the customer to configure the built-in Dr. Watson for Windows tool to automatically generate a crash dump file when the access violation occurs. The customer then sends me the crash dump file so that I can load it into ntsd using the command ntsd -z user.dmp.

The Dr. Watson tool works okay, but in some cases, a customer's machine may not generate the required Dr. Watson crash dumps. As a work around, it is worthwhile to prepare a short note for the customer describing how to obtain a crash dump using ntsd.exe (or perhaps just point them to this article!). In the case where an application is crashing and the customer knows how to reproduce the crash (in Microsoft parlance, "the customer has a repro"), they can obtain a crash dump by running ntsd.exe from the command line, passing the application command-line as an argument (ntsd -g MyApp.exe, for instance). The customer then performs their repro and, when the crash occurs, the debugger breaks in. A crash dump can be generated using a Create Dump File command similar to .dump /m C:\Files\user.dmp. Similarly, to get a crash dump for a hung application, the customer can attach the ntsd debugger to the running application by specifying the process identifier for the application (for example, ntsd.exe -p <pid>). The debugger breaks into the application, and the customer can obtain a crash dump as previously described.

Conclusion

I've given here an overview of the Microsoft NT Symbolic Debugger (ntsd) and Microsoft Application Verifier, along with some useful code samples. For further information about the truly enormous array of ntsd debugger commands, I recommend reading the debugger.chm help file that ships with the Microsoft Debugging Tools for Windows package.

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!