{
char bufBef[32];
char buf[8];
char bufAfter[32];
sprintf(buf,"AAAAAAA\0");
buf[8]='\0';
printf("%s\n",buf);
}
On Windows 7, I compiled the program with Visual Studio 2008 as debug project. 3 buffers are adjacent. I find their addresses with a debugger, as followed:
bufBef 0x001afa50 开发者_开发百科
buf 0x001afa40
bufAfter 0x001afa18
The statement "buf[8]='\0'" writes the address out of buf. When I run the program, Operation System reported " Debug Error: Run-Time Check Failure #2 - Stack around the variable 'buf' was corrupted."
Then I compiled it as a release project. It run quietly, no error report raised.
My question is how run-time detect buffer overflow?
What you see if the effect of the /RTCs switch.
John Robbins' book Debugging Applications for Microsoft .NET and Microsoft Windows talks about this in depth.
Relevant excerpt:
Fortunately for us, Microsoft extended the /RTCs switch to also do overrun and underrun checking of all multibyte local variables such as arrays. It does this by adding four bytes to the front and end of those arrays and checking them at the end of the function to ensure those extra bytes are still set to 0xCC.
Note that this switch only works in an unoptimized build (debug build).
In general, you don't. You should write defensive code that does the proper checks to ensure that it never overruns a buffer.
The debug runtime adds a large number of checks to help find this sort of bug (and all sorts of other common memory-related bugs); these checks are often very expensive, so they are only included in debug builds or when running attached to a debugger. They also can't detect every possible error, so they aren't foolproof; they are just debugging aids.
The Wikipedia article on Electric Fence explains how buffer overruns are caught, and why you should not use such mechanisms in production code.
Typically, the run-time will detect overflows like that by allocating some extra space between the variables, and filling that space with a known bit pattern. After your code runs, it looks at the bit pattern in that space. Since it's outside any variable, it should retain the same bit pattern. If the content has changed, you wrote somewhere you shouldn't have.
The three buffers are not adjacent. The difference between the start of buf
and the start of bufBef
(the following item on the stack) is 16 bytes, but buf
is only 8 bytes long.
The 8 bytes in between is presumably filled with an 8 byte "canary" value. When the runtime detects that the canary has been changed by your wild write, it raises the error you have seen.
(Your write to buf[8]
writes to address 0x001afa48
, which is in between buf
and bufBef
).
Compiler in debug mode put additional range checks for operations.
You need to understand the stack structure. Usually compiler places extra guard bytes with random cookie around arrays, if the value at the end of the function doesn't match, there is an overflow.
Well 0x001afa50 - 0x001afa40 = 0x10 = 16
, and 0x001afa40 - 0x001afa18 = 0x28 = 40
, so there's some space between the buffers for it to leave some known dummy data. If that's changed by the time the function ends, it knows you went beyond the end of the buffer. I'm just speculating -- they may have done it another way, but that seems one possibility.
C explicitly permits you to over-run (and under-run) your buffers, at your own peril.
There is no short-n-simple way to detect at run-time (in release builds) buffer overflows.
You're looking for a language different from C. Some languages define the behavior of ever possible program, defining specific error behavior for doing things that are "wrong". C on the other hand leaves the behavior of "wrong" code undefined, which means it's up to the programmer to ensure that he/she never uses the language in ways that result in undefined behavior. Some implementations are debugging-oriented or have debugging modes that assist you in finding errors, which you absolutely need to fix before deploying the code in release/production use.
精彩评论