5 ms·
I used to work on compilers, C/C++, COBOL, PL/I, Java, a whole bunch of them. This is actually somewhat similar to my favorite bug I encountered while implement
by CountHackulus 13y ago
I used to work on compilers, C/C++, COBOL, PL/I, Java, a whole bunch of them. This is actually somewhat similar to my favorite bug I encountered while implementing some stack mapping optimizations.
The compiler itself was crashing out while compiling a SPEC2000 test case (perlbmk I think?) with an illegal instruction. This was already quite suspect since it was branching to somewhere WAY outside of where the program usually resides, and the compiler was compiled with a compiler that's known to be nearly rock solid. I got quite lucky in that I managed to find one level of the stack trace, and it pointed me towards sprintf. Using some awesome tools some coworkers and I had developed over the years, I managed to narrow down the test case to about 5 lines of code that involved long doubles. So I grepped the compiler source code for sprintf, set breakpoints on the ones that I thought would get called, and just kept stepping through them until it finally crashed hard. Then I just reran, and stopped at the final breakpoint and started stepping through the assembly. What I saw happen just blew my mind, the code was just a simple:
sprintf(buffer, "fold: %Lf", result);
But what was happening is that the buffer was only 200 characters long, and the long double was roughly 1000 characters long. It was just a buffer overflow, that was so long it ended up overwriting the register save area, and the return address pointer. So the sprintf completed, but when it went to branch back, it loaded some characters instead of the return address. Just hilarious, and good thing I was working on stack mapping and was familiar with the stack layout of this linkage convention.
The solution of course was to just use snprintf instead. No sorry, that's wrong since that platform doesn't have an snprintf (yay mainframes!), and so I had to use %0.6Lg instead of %Lf.
Compilers are fun!
- iso8859-1 13y agoWhy doesn't the standard library warn when it knows %Lf might be too long?
- asveikau 13y agosprintf does not know the size of the destination buffer. That's why the newer snprintf, which takes a size as a 2nd parameter, is recommended. (I say newer, but according to manpages snprintf is defined by SUSv2, from 1997, and C99, from 1999; so they're pretty old by now.) You could probably have the compiler warn, provided (1) the destination is still an array and not a pointer (2) the format string is known at compile time. But that's a very different thing. (Most compilers these days do warn about any calls to sprintf, recommending snprintf instead.)
- zwp 13y ago> newer snprintf Also see C11's sprintf_s() and similarly-named friends.
- asveikau 13y agoThis is one area where C11 seems like it solves pointless non-problems. snprintf() is not all that "unsafe" in that it does do a bounds check, and the C99 version is clear about saying that the null terminator is included in the count. I also like the error conditions of C99 snprintf() better - it's more interesting to know the exact size required than it is to know that my buffer is not big enough.
- asveikau 13y ago> that platform doesn't have an snprintf (yay mainframes!) One trick I did once when I wrote code somewhere that didn't have snprintf: create a pipe, fprintf into it, and only read at most N bytes back. It worked and was portable but I'm sure the performance was horrible; this wasn't anything professional, I was just messing around as a kid (back when it was more common to come across platforms that hadn't gotten to SUSv2 or C99 yet). Probably a better solution would be to steal an implementation from an open source libc.
- asveikau 13y agoNot sure why that was downvoted. I'm sorry I offended you, downvoter, but in my defense I've seen much more inane comments not downvoted here.