views:

430

answers:

2

There is a post by Raymond Chen, where he tells how bad IsBadXxxPtr function is by eating guard page exception.

I don't quite understand how it is applied to Delphi. Who and how should normally (i.e. without call to IsBadXxxPtr) process this exception? I do know that Delphi inserts a code, which (for example) access a memory for large static arrays - exactly for this reason: to expand stack.

But if guard page exception is raised: who will handle it in a Delphi application? Can't I accidentally mess with it by using try/except in inappropriate way? Will Delphi's debugger notify me about these exceptions?

+2  A: 

From looking at the comments, it looks to me like the "guard page exception" mess takes place entirely within the kernel, and is not something that you need to be worrying about from user space.

You've gotta remember that this article was written for C++, which is nowhere near as advanced as Delphi on the memory management front. The uninitialized pointers issue is a lot less of a mess in Delphi than in C/C++ for two reasons:

  1. Delphi checks for uninitialized variables at compile time, which (for whatever reason) a lot of C compilers tend to have trouble with.
  2. Delphi initializes all of its dynamic memory to 0, so you don't have random heap garbage to deal with that might look like a good pointer when it's really not. This means that most bad pointers give you access violations, which are easy to debug, instead of silently failing and corrupting memory.
Mason Wheeler
+12  A: 

Windows structured exception handling (SEH) is has a two-phase structure. When an exception occurs, Windows first looks for a handler for the exception by following the registered exception handler chain (the head of which is stored in fs:[0] on x86, i.e. the first dword in the segment pointed to by the FS segment register - all that ugly 16-bit segment-offset logic didn't go away in 32-bit, it just became less relevant).

The search is done by calling a function with a particular flag, a pointer to which is stored in each exception frame on the stack. fs:[0] points to the topmost frame. Each frame points to the previous frame. Ultimately, the last frame on the list is one that has been provided by the OS (this handler will pop up a app-crash dialog if an unhandled exception reaches it).

These functions normally check the type of the exception, and return a code to indicate what to do. One of the codes that can be returned is basically, "ignore this exception and continue". If Windows sees this, it will reset the instruction pointer to the point of the exception and resume execution. Another code indicates that this exception frame should handle the given exception. A third code is "I'm not going to catch this exception, keep searching". Windows keeps on calling these exception filter functions until it finds one that handles the exception one way or the other.

If Windows finds one that handles the exception by catching it, then it will proceed to unwind the stack back to that handler, which consists of calling all the functions again, only passing in a different flag. It's at this point that the functions execute the finally logic, up until the handler which executes the except logic.

However, with the stack page guard exception, the process is different. None of the language's exception handlers will elect to handle this exception, because otherwise the stack growth mechanism would break. Instead, the filter search filters all the way through to the base exception handler provided by the OS, which grows the stack allocation by committing the appropriate memory, and then returns the appropriate return code to indicate that the OS should continue where it left off, rather than unwind the stack.

The tool and debugging infrastructure are designed to let these particular exceptions play out correctly, so you don't need to worry about handling them.

You can read more about SEH in Matt Pietrek's excellent article in MSJ from over a decade ago.

Barry Kelly
In Delphi, it's impossible to specify the "ignore this and continue" result, isn't it? So you couldn't set up a guard page in Delphi even if you wanted to, separate from whatever the kernel provides. The language doesn't really expose everything that SEH provides, and if it did, Linux and .Net probably would have been that much more difficult to port to, no?
Rob Kennedy
Rob - you can specify anything you like if you set up the exception frame yourself, which requires a little assembler. .NET exceptions support something called exception filters, which map to the search phase of SEH, but they are only exposed in ILASM syntax and Visual Basic for .NET, to my knowledge. WRT Linux, I haven't looked in depth into the mechanism used by dcc, but it is a PC-mapped mechanism, and for simplicity I suspect it isn't two-phase.
Barry Kelly
(The PC-mapped exception mechanism in Kylix was entirely implemented by the compiler and RTL, since Linux doesn't have an exception mechanism outside the very inferior reenter-on-the-same-thread signal mechanism.)
Barry Kelly
Barry, thanks for the detailed answer. But this leave me with another question: why IsBadXxxPtr handle this exception?
Alexander
Alexander, it HAS to handle that exception. That's the very basis for how it works. IsBadReadPtr attempts to read from the given address. If that attempt raises an exception, it catches that exception and returns true. But if the address is for a guard page, then there is already another exception handler that's supposed to catch the exception and handle it differently. IsBadReadPtr breaks that functionality by triggering the exception prematurely. The intended handler never sees the exception.
Rob Kennedy
Alexander, the stack expansion method is designed for the thread that triggered the guard page only, i.e. it has thread affinity. IsBadReadPtr can trigger a guard page for a *different* thread, so letting the exception leak through to the filter at the end of the current thread's exception handler chain wouldn't work; that handler would try to expand the wrong thread's stack.
Barry Kelly
Thanks, Barry. It is clear now.
Alexander