Skip to main content
GameDev.net gamedev.net
🔒 Locked

safe snprintf

Started by aboeing Mar 19, 2005 at 12:23 AM 11 replies 6.2k views
Original Post
aboeing
aboeing
Hi, Just wondering whats the deal with all these unsafe printf related warnings in VC8, and how to "deal" with them, in a "safe" manner. For example, using snprintf is apparently unsafe because the last character in the buffer isn't gauranteed to be null. Does this mean all you need to do to make your code "safe" is:

char buffer[256];
_snprintf(buffer,256,"whatever");
buffer[255]=0;
So whats the point of _snprintf_s (new "secure" function), and why could the behaviour of snprintf not simply be redefined to do as above? (ie: and then I dont need to worry about those warnings anymore..) msvc8 headers btw:

_CRT_INSECURE_DEPRECATE _CRTIMP int __cdecl _snprintf(char *, size_t, const char *, ...);
_CRTIMP int __cdecl _snprintf_s(char *, size_t, size_t, const char *, ...);
DarkSlayer
DarkSlayer
either make your own string code stuff ....

or...

use the functions in strsafe.h ...

search for strsafe on msdn, or in the help file to platform sdk.
The clue is that printf and all related functions in c++ is open for abuse, so ms have a cure for that ... with the functions in strsafe.

If you don't have the library for it, just download the platform sdk from ms (the one for winXp).

doynax
doynax
MSVC's _snprintf function doesn't null terminate long strings (much like strncpy).
Perhaps the later versions of the compiler implement a C99 compatible implementation of snprintf.

Personally I'd implement a compatible replacement function instead of using the new (and uncompatible) safe functions.
#ifdef _MSC_VERint snprintf(char *buffer, size_t size, const char *format, ...) { va_list args; size_t result; va_start(args, format); result = _vsnprint(buffer, size, format, args); va_end(args, format); if(result < 0) {  buffer[size - 1] = '\0';  // Should return the number of characters that would be written if there had been enough buffer space to be C99 compatible. } return result;}#endif


edit: Fixed a stupid bug..

[Edited by - doynax on March 19, 2005 11:30:52 AM]
Anon Mike
Anon Mike
Quote:
why could the behaviour of snprintf not simply be redefined to do as above


Because snprintf has a specific, well-defined, and standardized function. It may not be the one you (or even most people) would like but you can't just go and change it because existing programs will break.
-Mike
doynax
doynax
Quote:
Original post by Anon Mike
Quote:
why could the behaviour of snprintf not simply be redefined to do as above
Because snprintf has a specific, well-defined, and standardized function. It may not be the one you (or even most people) would like but you can't just go and change it because existing programs will break.
But this isn't actually a problem since MSVC implements it in a different namespace (with a leading underscore).
So adding a standard compliant version really shouldn't break anything.
aboeing
aboeing
Thanks for the replies.
Quote:

But this isn't actually a problem since MSVC implements it in a different namespace (with a leading underscore).
So adding a standard compliant version really shouldn't break anything.

Personally I'd implement a compatible replacement function instead of using the new (and uncompatible) safe functions.

Yeah, thats what I was thinking, and was going to do, but then wasn't sure why MS didn't just do this in the first place, instead of making a "safer" version..

But thanks for your code snippet, I'm going to use it :)
MaulingMonkey
MaulingMonkey
Quote:
Original post by aboeing
Does this mean all you need to do to make your code "safe" is:
char buffer[256];_snprintf(buffer,256,"whatever");buffer[255]=0;

...assuming you can remember the last line every time, yes.

The single time you froget it will be the time you'll wish you'd used the safe version.
Sneftel
Sneftel
Keep in mind that functions such as snprintf, even when massaged to include the terminating null, are ONLY safe in that they won't overflow buffers. They still have the potential to end up as malformed data. This is probably okay as long as all you're using snprintf for is to display messages, in plain text through a connection that doesn't perform any sort of character translation, to the same user that generated all data forming part of the data set for snprintf. Otherwise, you should really look into using a safer string library, such as std::string.
Anon Mike
Anon Mike
Quote:
But this isn't actually a problem since MSVC implements it in a different namespace (with a leading underscore).
So adding a standard compliant version really shouldn't break anything.


Here's the deal as I understand it...

Until C99 snprintf was not an official standard function. It was however widely available non-standard extension. It, along with most of the other 'n' functions (e.g. strncpy), had this behavior of not zero-terminating if they end up filling the entire destination buffer. This goes way back and was done so that they could operate on fixed-length buffers and not "waste" a byte if they didn't have to. Raymond Chen has a good article on the history (and an excellent blog in general).

However widespread it may have been, it wasn't a standardized function and therefore was technically a "vender extension" and was supposed to begin with an underscore. MS did this (although you can use the underscore-free name if you know the right magic). Most *nix vendors did not (in my admitadly limited experience). This was actually one of those rare cases of MS being more standard compliant than others.

Now we move forward to C99 and snprintf is an official standard function. Apparently the semantics were also changed to make it NULL-terminate regardless (Herb Sutter discusses this). Why the standards committee chose to risk breaking old apps that might have relied on the old behavior I don't know.

MSVC however is not, nor does it claim to be, a C99-compliant compiler. MS is also notorious for going to incredible lengths to not break old apps (read Raymonds blog again, people literally *beg* him to break the worst sorts of abusive behavior slimely apps do to get rid of some of the hacks Windows has to go through to keep them running). Therefore they provided a brand-new function so that they could change the semantic without breaking old people.
-Mike
Zahlman
Zahlman
::coughs and points at sig::
dalleboy
dalleboy
You should be able to use sprintf_s as a drop in replacement for _snprintf. No need for _snprintf_s as it is slower than sprintf_s, as _snprintf_s and _snprintf fills the rest of the buffer with '\0', if I don't recall incorrectly.
aboeing
aboeing
Again thanks to the replies, and to the people advocating std::string, unfortunately this is a C program :(
Quote:

The single time you froget it will be the time you'll wish you'd used the safe version.

Yeah, but this is why you would want to either write a function/macro to call the appropriate version.

Quote:

use the functions in strsafe.h

Too much effort :P They are all different, and lack of portability?

Quote:

sprintf_s

Awesome! :) Thanks.

Quote:

They still have the potential to end up as malformed data.

This is a really good point btw..
But for this application im not too concerned about that happening.. It really shouldn't..

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.