Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

debugging/ NTDLL.DLL/ and more

0 views
Skip to first unread message

marco 2

unread,
Jan 19, 2006, 5:22:12 PM1/19/06
to
hi everybody!

please help!

in my vstudio 2005 solution i recieve an error when i start
without debugging.
it's an unhandled excpetion - access voilation - reading from 0x0000
- inside NTDLL.DLL

1. what is/does ntdll.dll?
2. how should i resolve/debug this error???
i have little or no experience in debugging, especially
in vc++ and winXP, so please tell me how to debug this
and other things! years ago i worked in motorola and pentium assembly,
and i knew how to debug it, but not inside vstudio 2005.

regards,

marc k.


Ivan Brugiolo [MSFT]

unread,
Jan 19, 2006, 6:33:09 PM1/19/06
to
ntdll.dll is a core OS module responsible to implement
runtime functions (String Conversion, Heap management, Generic containers)
and thunk agaisnt the native calls implemented in the kernel.

In 99.99% of the cases, a problem in ntdll.dll is a consequence of
a programming error in the application, that causes the internal state
of the runtime in ntdll.dll to get corrupted.

First thing that would come to mind is a heap corruption.
If you could get good symbols for the OS binaries, and post a stack,
maybe few people could be able to help.

--
--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm


"marco 2" <rebel...@planet.nl> wrote in message
news:43d0111d$0$12831$ba62...@text.nova.planet.nl...

marco 2

unread,
Jan 19, 2006, 7:01:15 PM1/19/06
to
can you explain, please?

1. where/how do i get good symbols for the OS binaries?
i have installed windows symbols from microsoft.com (debugging),
but i don't see them in my disassembly.
2. and what means "posting a stack"? do you mean,
putting a message in the newsgroup with a copy of the call stack?
3. what means "thunk" in: "....and thunk against the native calls......"?

regards,
mk

"Ivan Brugiolo [MSFT]" <Ivan.B...@online.microsoft.com> schreef in
bericht news:%23VU91CV...@TK2MSFTNGP14.phx.gbl...

Ivan Brugiolo [MSFT]

unread,
Jan 19, 2006, 7:58:56 PM1/19/06
to
#1 Assuming you have the standard debugger package,
one possible method to get symbols for ntdll.dll is:

c:\debugger>symchk %windir%\system32\ntdll.dll /s
SRV*e:\temp*http://msdl.microsoft.com/download/symbols /os /od
SYMCHK: ntdll.dll PASSED - PDB:
e:\temp\ntdll.pdb\9385FE43F2EB4DCAAEB1EF2D2CF13E272\ntdll.pdb DBG:
<N/A>

SYMCHK: FAILED files = 0
SYMCHK: PASSED + IGNORED files = 1

#2 assuming you have cdb/ntsd/windbg, a stack would look like [see below]
I belice that that CallStack toolwindow in VS has a Select-All & Copy
function,
that would produce somewhat identical results.

#3 A think is normally a piece of code that calls one other.
Each CPU / architecture might have a different mechanism to call the kernl.
This abstraction (int 2e, sysenter, syscall) is kept in ntdll.dll

0:000> r
rax=000000000000ff00 rbx=fffffffffffffff0 rcx=0000000000000000
rdx=0000000011000103 rsi=00000000019d0000 rdi=00000000019d02c8
rip=0000000078edcc6d rsp=00000000000ae280 rbp=0000000000000000
r8=0000000000000a58 r9=0000000000000a08 r10=0000000078ed6416
r11=0000000078fa55e0 r12=00000000019d0000 r13=00000000000000a8
r14=0000000000000000 r15=0000000001100003
iopl=0 nv up ei pl nz na pe nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b
efl=00010202
ntdll!RtlAllocateHeapSlowly+0xa50:
00000000`78edcc6d 0fb74308 movzx eax,word ptr [rbx+0x8]
ds:ffffffff`fffffff8=????
0:000> k
Child-SP RetAddr Call Site
00000000`000ae280 00000000`78f60ff7 ntdll!RtlAllocateHeapSlowly+0xa50
00000000`000ae560 00000000`78f3b758 ntdll!RtlDebugAllocateHeap+0xe7
00000000`000ae610 00000000`78edd036 ntdll!RtlAllocateHeapSlowly+0x74
00000000`000ae8f0 00000000`78f5e636 ntdll!RtlAllocateHeap+0x1292
00000000`000aeb40 00000000`78f5a985 ntdll!RtlpDphNormalHeapAllocate+0x36
00000000`000aeb70 00000000`78f60f50 ntdll!RtlpDebugPageHeapAllocate+0x6d8
00000000`000aee90 00000000`78f3b758 ntdll!RtlDebugAllocateHeap+0x40
00000000`000aef40 00000000`78edd036 ntdll!RtlAllocateHeapSlowly+0x74
00000000`000af220 000007ff`7fd34858 ntdll!RtlAllocateHeap+0x1292
00000000`000af470 000007ff`7fd34a3b RPCRT4!rc4_safe_startup+0x28
00000000`000af4b0 00000000`78edef73 RPCRT4!_InitializeDLL+0xb4
00000000`000af4e0 00000000`78ed6380 ntdll!LdrpRunInitializeRoutines+0x4d7
00000000`000af6c0 00000000`78ed6416 ntdll!LdrpInitializeProcess+0x1bb6
00000000`000af9d0 00000000`78ef3925 ntdll!_LdrpInitialize+0x18f
00000000`000afab0 00000000`78d59630 ntdll!KiUserApcDispatch+0x15
00000000`000affa8 00000000`00000000 kernel32!BaseProcessStart


--
--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm


"marco 2" <rebel...@planet.nl> wrote in message

news:43d02854$0$12834$ba62...@text.nova.planet.nl...

Scherbina Vladimir

unread,
Jan 20, 2006, 1:41:53 AM1/20/06
to
As Ivan wrote most likely that your application passed invalid params to
some API function (that probably resides in kernel32.dll, because
kernel32.dll in most cases redirects calls to ntdll.dll).

Why can't you simply debug your application in debug mode to see what's
wrong ?

--
Vladimir

"marco 2" <rebel...@planet.nl> wrote in message

news:43d02854$0$12834$ba62...@text.nova.planet.nl...

marco 2

unread,
Jan 20, 2006, 1:17:30 PM1/20/06
to
can somebody give me a
basic description of how to debug and resolve errors in the JIT debugger???
how do i basiccally start debugging/disassembling and resolve errors???

mk

"Scherbina Vladimir" <vladimir....@gmail.com> schreef in bericht
news:uwHdbyYH...@TK2MSFTNGP10.phx.gbl...

marco 2

unread,
Jan 20, 2006, 9:58:23 PM1/20/06
to
ok,

i have found the error.
it was loadbitmapfile().

1. the problem is the result of stepping up from iostream.h
to iostream. i haven't yet changed my original iostreamh-code.
for use with iostream. any suggestions????
2. can anyone explain to me the problems and ins and outs
of different working directories????

mk

"marco 2" <rebel...@planet.nl> schreef in bericht
news:43d12944$0$12839$ba62...@text.nova.planet.nl...

marco 2

unread,
Jan 21, 2006, 12:29:58 PM1/21/06
to
hi all,

i have upgraded my solution from vs6 to vs2005.
i had to replace iostream.h with iostream.
now it results in an error in LoadBitmapFile(),
(a Null Pointer or something like that.
JIT passes an access voilation reading from adress 0x0000.)
though it worked perfectly in vs6.
how do i solve this?
what should you change when upgrading
to <iostream> from <iostream.h>???

mk

"marco 2" <rebel...@planet.nl> schreef in bericht

news:43d1a35a$0$12837$ba62...@text.nova.planet.nl...

marco 2

unread,
Jan 21, 2006, 5:22:09 PM1/21/06
to

BELOW THE FUNCTION AND THE CALLSTACK
WHERE THINGS GO WRONG

------------

THIS IS THE FUCTION:
unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER
*_bitmapInfoHeader)

{

FILE *filePtr; // the file pointer

BITMAPFILEHEADER bitmapFileHeader; // bitmap file header

unsigned char *bitmapImage; // bitmap image data

int imageIdx = 0; // image index counter

unsigned char tempRGB; // swap variable

// open filename in "read binary" mode

filePtr = fopen(filename, "rb");

if (filePtr == NULL)

return NULL;

// read the bitmap file header

fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);


// verify that this is a bitmap by checking for the universal bitmap id

if (bitmapFileHeader.bfType != BITMAP_ID)

{

fclose(filePtr);

return NULL;

}

// read the bitmap information header

fread(_bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);

// move file pointer to beginning of bitmap data

fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);

// allocate enough memory for the bitmap image data

THIS IS WHERE THE ERROR OCCURS:

bitmapImage = (unsigned char*)malloc(_bitmapInfoHeader->biSizeImage);

--------

THE CALLSTACK:

ntdll.dll!_RtlpCoalesceFreeBlocks@16() + 0x124e bytes
ntdll.dll!_RtlpExtendHeap@8() + 0xa1 bytes
ntdll.dll!_RtlAllocateHeap@12() + 0x16a2 bytes
prophecy city.exe!malloc(unsigned int size=196608) Line 163 + 0x63 bytes
C
> prophecy city.exe!LoadBitmapFile(char * filename=0x00000024,
> tagBITMAPINFOHEADER * _bitmapInfoHeader=0x00364d42) Line 3498 + 0x9 bytes
> C++
user32.dll!77d21324()
[Frames below may be incorrect and/or missing, no symbols loaded for
user32.dll]
prophecy city.exe!int_textures() Line 8696 + 0xd bytes C++
user32.dll!77d21324()
prophecy city.exe!main_int() Line 8730 C++
prophecy city.exe!WinMain(HINSTANCE__ * hInstance=0x00400000, HINSTANCE__
* hPrevInstance=0x00000000, char * lpCmdLine=0x00162354, int nShowCmd=5)
Line 8862 C++
prophecy city.exe!__tmainCRTStartup() Line 315 + 0x1c bytes C
kernel32.dll!_BaseProcessStart@4() + 0x23 bytes

----------------------------

"marco 2" <rebel...@planet.nl> schreef in bericht

news:43d26fa1$0$12840$ba62...@text.nova.planet.nl...

marco 2

unread,
Jan 22, 2006, 12:31:00 PM1/22/06
to
i know now where things go wrong
but i can't actually solve the error?!?!?!
can anyone help?
see former messages......

mk


"marco 2" <rebel...@planet.nl> schreef in bericht

news:43d2b412$0$12842$ba62...@text.nova.planet.nl...

Pavel Lebedinsky [MSFT]

unread,
Jan 22, 2006, 6:59:24 PM1/22/06
to
You have a heap corruption. The best way to debug this is to
enable full pageheap and run the program under windbg/cdb.

See this thread for more details:

http://groups.google.com/group/microsoft.public.windbg/browse_frm/thread/66a2a7d95008e556/5d90e8381346d600#5d90e8381346d600

--
This posting is provided "AS IS" with no warranties, and confers no
rights.

"marco 2" wrote:

>i know now where things go wrong
> but i can't actually solve the error?!?!?!
> can anyone help?
> see former messages......
>

marco 2

unread,
Jan 22, 2006, 7:25:16 PM1/22/06
to
i have windbg/cdb.
but what is the easiest/fastest way to enable full page heap???

marc


"Pavel Lebedinsky [MSFT]" <pa...@online.microsoft.com> schreef in bericht
news:%23SsKY$6HGHA...@TK2MSFTNGP12.phx.gbl...

marco 2

unread,
Jan 22, 2006, 7:44:34 PM1/22/06
to
i believe you can enable full pageheap by making a registry
setting.
how do i do this?

marc k.

"marco 2" <rebel...@planet.nl> schreef in bericht

news:43d4226f$0$12833$ba62...@text.nova.planet.nl...

marco 2

unread,
Jan 22, 2006, 7:46:49 PM1/22/06
to
1. see former question
2. is it a good idea to use pageheap.exe???

m.

"marco 2" <rebel...@planet.nl> schreef in bericht

news:43d4226f$0$12833$ba62...@text.nova.planet.nl...

Pavel Lebedinsky [MSFT]

unread,
Jan 24, 2006, 12:09:57 AM1/24/06
to
1. See the thread I mentioned below.
2. No.

Ben Voigt

unread,
Jan 26, 2006, 10:04:01 AM1/26/06
to
Reordered for better debugging

>> ntdll.dll!_RtlpCoalesceFreeBlocks@16() + 0x124e bytes
>> ntdll.dll!_RtlpExtendHeap@8() + 0xa1 bytes
>> ntdll.dll!_RtlAllocateHeap@12() + 0x16a2 bytes
>> prophecy city.exe!malloc(unsigned int size=196608) Line 163 + 0x63
>> bytes C
>>> prophecy city.exe!LoadBitmapFile(char * filename=0x00000024,
>>> tagBITMAPINFOHEADER * _bitmapInfoHeader=0x00364d42) Line 3498 + 0x9
>>> bytes C++

Here filename is not a valid pointer, however the failure indicates that
fopen succeeded, therefore it seems the arguments area of your stack has
been overwritten.... this will lead to failures accessing _bitmapInfoHeader

I don't see anything wrong with the code after a cursory inspection, so you
will be debugging...

>> ------------
>>
>> THIS IS THE FUCTION:
>> unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER
>> *_bitmapInfoHeader)

Use this prototype instead (safer!)
unsigned char *LoadBitmapFile(const char* const filename, BITMAPINFOHEADER*
const _bitmapInfoHeader)
>>
>> {

Put a breakpoint [here]!

Check your arguments. Add the following code (use your favorite
logging/reporting mechanism in place of MessageBox):

if ((NULL == filename) || IsBadStringPtr(filename, _MAX_PATH)) {
MessageBox(...);
}
if ((NULL == _bitmapInfoHeader) || IsBadWritePtr(_bitmapInfoHeader, sizeof
*_bitmapInfoHeader)) {
MessageBox(...);
}

Then recompile and launch in debug mode. When the debugger breaks, visually
inspect the arguments (make sure the filename is reasonable, etc.).

Now added both arguments to the watch list and begin stepping through the
function (use step over).

Find out what line causes filename to get corrupted. Watch to see if
_bitmapInfoHeader also changes.


>>
>> FILE *filePtr; // the file pointer
>>
>> BITMAPFILEHEADER bitmapFileHeader; // bitmap file header
>>
>> unsigned char *bitmapImage; // bitmap image data
>>
>> int imageIdx = 0; // image index counter
>>
>> unsigned char tempRGB; // swap variable
>>
>> // open filename in "read binary" mode
>>
>> filePtr = fopen(filename, "rb");
>>
>> if (filePtr == NULL)
>>
>> return NULL;

tested return value.... check

>>
>> // read the bitmap file header
>>
>> fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);

Check return value!

>>
>>
>> // verify that this is a bitmap by checking for the universal bitmap id
>>
>> if (bitmapFileHeader.bfType != BITMAP_ID)
>>
>> {
>>


tested value.... check

>> fclose(filePtr);
>>
>> return NULL;
>>
>> }
>>
>> // read the bitmap information header
>>
>> fread(_bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);
>>

Check return value!

>> // move file pointer to beginning of bitmap data
>>
>> fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);
>>

Check return value!

>> // allocate enough memory for the bitmap image data
>>
>> THIS IS WHERE THE ERROR OCCURS:
>>
>> bitmapImage = (unsigned char*)malloc(_bitmapInfoHeader->biSizeImage);
>>

If any of the above reads fail then _bitmapInfoHeader->biSizeImage is
meaningless.


Alex Blekhman

unread,
Jan 26, 2006, 10:36:11 AM1/26/06
to
Ben Voigt wrote:
> [...]

> Check your arguments. Add the following code (use your
> favorite logging/reporting mechanism in place of
> MessageBox):
> if ((NULL == filename) || IsBadStringPtr(filename,
> _MAX_PATH)) { MessageBox(...);
> }
> if ((NULL == _bitmapInfoHeader) ||
> IsBadWritePtr(_bitmapInfoHeader, sizeof
> *_bitmapInfoHeader)) { MessageBox(...);
> }
>

Don't use IsBadWritePtr, it's considered both pointless and
harmfull.

"Should I check the parameters to my function?"
http://blogs.msdn.com/larryosterman/archive/2004/05/18/134471.aspx


Ben Voigt

unread,
Feb 16, 2006, 10:40:03 AM2/16/06
to

"Alex Blekhman" <tkfx....@yahoo.com> wrote in message
news:eMaC%234oIG...@tk2msftngp13.phx.gbl...

Sounds like bugs in IsBadWritePtr... it should be a little smarter and not
extend the stack. And that's not an expensive check! You don't have to
check for guard pages or anything like that... check against ESP and EBP --
no user buffer should be allowed to overlap with the scratch space of
IsBadWritePtr. Basically.... if ptr < ESP && ptr + size > EBP then bad
pointer. Or maybe I've got the stack registers backwards. Of course
IsBadWritePtr checks for pointer overflow.... right? If I have

char* ptr = NULL;
IsBadWritePtr(&ptr[-10], 500);

I would hope it fails *without* needing to touch memory.

For a debug build, it would even be worthwhile to virtually unwind the stack
and make sure the buffer doesn't overlap any reserved stack areas in any
frame (i.e. return address, saved registers, etc.)


>


Alex Blekhman

unread,
Feb 16, 2006, 11:50:49 AM2/16/06
to
Ben Voigt wrote:
>> Don't use IsBadWritePtr, it's considered both pointless
>> and harmfull. "Should I check the parameters to my
>> function?"
>> http://blogs.msdn.com/larryosterman/archive/2004/05/18/134471.aspx
>
> Sounds like bugs in IsBadWritePtr... it should be a
> little smarter and not extend the stack. And that's not
> an expensive check! You don't have to check for guard
> pages or anything like that... check against ESP and EBP
> -- no user buffer should be allowed to overlap with the
> scratch space of IsBadWritePtr. Basically.... if ptr <
> ESP && ptr + size > EBP then bad pointer. Or maybe I've
> got the stack registers backwards. Of course
> IsBadWritePtr checks for pointer overflow.... right? If
> I have
> char* ptr = NULL;
> IsBadWritePtr(&ptr[-10], 500);
>
> I would hope it fails *without* needing to touch memory.
>
> For a debug build, it would even be worthwhile to
> virtually unwind the stack and make sure the buffer
> doesn't overlap any reserved stack areas in any frame
> (i.e. return address, saved registers, etc.)

IsBad<Read|Write>Ptr does exactlty what is says - it
reads/writes memory starting with specified pointer for
specified length. I can't see how otherwise you would be
able to check readability/writability of memory if not by
actually reading or writing. The point of Larry Osterman's
blog article is: if you're going to crash anyway, then at
least do it explicitly and at right moment, so developer
will be able to debug it at once.


Ivan Brugiolo [MSFT]

unread,
Feb 16, 2006, 3:16:29 PM2/16/06
to
Using the IsBadWritePtr is not recomended, and it's flagged by AppVerifier
as well.
There are knonw bad effect if you are touching the guard page
of a different thread by passing to the API random pointers.
You should use VirtualQuery to check what the OS knows about a random
address.

--
--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm

"Alex Blekhman" <tkfx....@yahoo.com> wrote in message

news:uMrtlkxM...@TK2MSFTNGP10.phx.gbl...

Ben Voigt

unread,
Feb 20, 2006, 11:05:52 AM2/20/06
to

"Alex Blekhman" <tkfx....@yahoo.com> wrote in message
news:uMrtlkxM...@TK2MSFTNGP10.phx.gbl...

I actually suggested two other ways...please comment.

By my definition, IsBadReadPtr/IsBadWritePtr shouldn't be limited to
indicating whether the CPU MMU will permit the operation, they should
indicate whether the memory lies within a user buffer.

First, if the pointer overflows.


char* ptr = NULL;
IsBadWritePtr(&ptr[-10], 500);

This is clearly a bad pointer, and shouldn't SEH to determine that.

Secondly, since stack grows downward any access to the stack region below
EBP/ESP is invalid. If I'm not mistaken, this check will eliminate the
"guard page" bug, because the guard page is an unused stack page strictly
below EBP and ESP.

(Isn't ESP the stack pointer on entry to the function, and EBP the base of
the function's stack space? Or do I have that reversed, or is one of them
adjusted by argument space, etc? And is this behavior different between
i686 vs x86_64? Of course that's why APIs are OS-provided, so they can be)

Someone might rationally want to overwrite several stack variables at once.
But no one (except maybe an exception handler) should ever be writing into
the areas managed by prolog/epilog, i.e. return address and saved registers.
This information is available to a debugger, so why not check that too?
Although a stack walk might be slow enough you would want to explicitly
enable it.

Of course, these features (knowing the stack base and maximum stack size,
advanced knowledge about the composition of stack frames) would require
compiler support. There might have to be different keywords depending on
whether the buffer can lie in the local stack frame, i.e. no caller buffer
should overlap my stack frame, but if I declare an object, and get a pointer
returned from one of its member functions, it should be allowed to lie in my
stack frame. Then the following compiler primitives:

__isbadwriteptr
__isbadreadptr
__isbadstringptr
__isbadcallerwriteptr
__isbadcallerreadptr
__isbadcallerstringptr
__isbadcodeptr

And a compiler option to enable stack-walking instead of checking against
the current frame.

Alex Blekhman

unread,
Feb 20, 2006, 1:26:11 PM2/20/06
to
Ben Voigt wrote:
>> IsBad<Read|Write>Ptr does exactlty what is says - it
>> reads/writes memory starting with specified pointer for
>> specified length. I can't see how otherwise you would be
>> able to check readability/writability of memory if not
>> by actually reading or writing.
>
> I actually suggested two other ways...please comment.

OK. Let's assume that IsBadReadPtr is written in
straightforward manner like this:

BOOL IsBadReadPtr(const VOID* lp, UINT_PTR ucb)
{
__try
{
for(UINT_PTR i = 0; i < ucb; ++i)
{
BYTE bt = *(((PBYTE)lp) + i);
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
return FALSE;
}

return TRUE;
}

> By my definition, IsBadReadPtr/IsBadWritePtr shouldn't be
> limited to indicating whether the CPU MMU will permit the
> operation, they should indicate whether the memory lies
> within a user buffer.
> First, if the pointer overflows.
> char* ptr = NULL;
> IsBadWritePtr(&ptr[-10], 500);
>
> This is clearly a bad pointer, and shouldn't SEH to
> determine that.

So, now we have something like that inside:

BOOL IsBadReadPtr(const VOID* lp, UINT_PTR ucb)
{
if(IsInBadRange(lp)) // Platform specific!
return FALSE;

// rest of function...
}

And we have to implement platform specific IsInBadRange.

> Secondly, since stack grows downward any access to the
> stack region below EBP/ESP is invalid. If I'm not
> mistaken, this check will eliminate the "guard page" bug,
> because the guard page is an unused stack page strictly
> below EBP and ESP.

So, IsBadReadPtr becomes:

BOOL IsBadReadPtr(const VOID* lp, UINT_PTR ucb)
{
// 2 - check up to 2 levels upward:
// 1st - IsBadReadPtr itself
// 2nd - caller of IsBadReadPtr
if(IsBadStack(lp, ucb, 2)) // Platform specific!
return FALSE;

if(IsInBadRange(lp)) // Platform specific!
return FALSE;

// rest of function...
}

And we have to implement platform specific IsBadStack, too.
(Mind you that we don't discuss here another whole issue of
what the bad stack is under different platforms. For
example, look for Itanium's stack madness:

"The Itanium's so-called stack"
http://blogs.msdn.com/oldnewthing/archive/2005/04/21/410397.aspx)

Now, we also should handle correctly __declspec( naked ) and
__declspec( noreturn ) functions. Also, IsBadStack should
know about its callers stack frames to check them, i.e.
should know how to walk stack upwards.

> Someone might rationally want to overwrite several stack
> variables at once. But no one (except maybe an exception
> handler) should ever be writing into the areas managed by
> prolog/epilog, i.e. return address and saved registers.

You cannot assume it with __declspec( naked ) functions.

> This information is available to a debugger, so why not
> check that too? Although a stack walk might be slow
> enough you would want to explicitly enable it.

This will require additional parameter for IsBadReadPtr. So,
IsBadReadPtr cannot be used anymore due to backward
compatibility. We'll need IsBadReadPtrEx, for instance. Now
we have:

BOOL IsBadReadPtrEx(const VOID* lp, UINT_PTR ucb, DWORD
dwFlags)
{
// 2 - check up to 2 levels upward:
// 1st - IsBadReadPtr itself
// 2nd - caller of IsBadReadPtr
if((dwFlags & IBP_CHECKSTACK) && IsBadStack(lp, ucb, 2))
return FALSE;

if(IsInBadRange(lp)) // Platform specific!
return FALSE;

// rest of function...
}

> Of course, these features (knowing the stack base and
> maximum stack size, advanced knowledge about the
> composition of stack frames) would require compiler
> support. There might have to be different keywords
> depending on whether the buffer can lie in the local
> stack frame, i.e. no caller buffer should overlap my
> stack frame, but if I declare an object, and get a
> pointer returned from one of its member functions, it
> should be allowed to lie in my stack frame.

Many of these checks already exist. See:

"Compiler Security Checks In Depth"
http://msdn.microsoft.com/library/en-us/dv_vstechart/html/vctchCompilerSecurityChecksInDepth.asp

Now, let's summarize what we have after refurbishing
IsBadReadPtr:

1. Two platform specific and non-trivial functions should be
written.
2. Additional parameter should be added for IsBadReadPtr,
making it IsBadReadPtrEx. (I.e., documentation updates,
support, marking IsBadReadPtr as deprecated, etc..).
3. IsBadReadPtrEx is heavy and complex stack-walking hog. To
make it light you need to combine flags for it, then you'll
get equivalent of old IsBadReadPtr.
4. You gained exactly nothing considering pointers security.
That's why:

a) The address you pass to IsBadReadPtrEx can be
unmapped by other thread immediately after you return from
the function. So, you checked nothing.
b) Making IsBadReadPtrEx walking your stack guarantees
safety for your thread only. Nothing prevents me to write
code like that:

char* p = 0x00BAAADD; // arbitrary address
IsBadReadPtrEx(p, 1024);

There is no way to know for IsBadReadPtrEx where `p'
comes from. It can be on heap, it can be other thread's
guard page, too. Oops! Back to guard page problem again.

Well, we made so many drastic changes to gain tiny ammount
of additional cases. Was it worthwhile? I don't think so.


0 new messages