Which of sprintf/snprintf is more secure?

CSecurityUnixPrintfSecure Coding

C Problem Overview


I wish to know which of these two options is the more secure one to use:

#define MAXLEN 255
char buff[MAXLEN + 1]
  1. sprintf(buff, "%.*s", MAXLEN, name)

  2. snprintf(buff, MAXLEN, "%s", name)

My understanding is that both are same. Please suggest.

C Solutions


Solution 1 - C

The two expressions you gave are not equivalent: sprintf takes no argument specifying the maximum number of bytes to write; it simply takes a destination buffer, a format string, and a bunch of arguments. Therefore, it may write more bytes than your buffer has space for, and in so doing write arbitrary code. The %.*s is not a satisfactory solution because:

  1. When the format specifier refers to length, it's referring to the equivalent of strlen; this is a measure of the number of characters in the string, not its length in memory (i.e. it doesn't count the null terminator).
  2. Any change in the format string (adding a newline, for example) will change the behavior of the sprintf version with respect to buffer overflows. With snprintf, a fixed, clear maximum is set regardless of changes in the format string or input types.

Solution 2 - C

For the simple example in the question, there might not be much difference in the security between the two calls. However, in the general case snprintf() is probably more secure. Once you have a more complex format string with multiple conversion specifications it can be difficult (or near impossible) to ensure that you have the buffer length accounted for accurately across the different conversions - especially since a previous conversions don't necessarily produce a fixed number of output characters.

So, I'd stick with snprintf().

Another small advantage to snprintf() (though not security related) is that it'll tell you how big of a buffer you need.

A final note - you should specify the actual buffer size in the snprintf() call - it'll handle accounting for the null terminator for you:

snprintf(buff, sizeof(buff), "%s", name);

Solution 3 - C

I would say snprintf() is much more better until I read this passage:

https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/838-BSI.html

Short summary is: snprintf() not portable its behaviour change from system to system. The most serious problem with snprintf() can occur when snprintf() is implemented simply by calling sprintf().You may think it protects you from buffer overflow and let your guard down, but it may not.

So now I am still saying snprintf() safer but also being cautious when I use it.

Solution 4 - C

The best and most flexible way would be to use snprintf!

size_t nbytes = snprintf(NULL, 0, "%s", name) + 1; /* +1 for the '\0' */
char *str = malloc(nbytes);
snprintf(str, nbytes, "%s", name);

In C99, snprintf returns the number of bytes written to the string excluding the '\0'. If there were less than the necessary amount of bytes, snprintf returns the number of bytes that would have been necessary to expand the format (still excluding the '\0'). By passing snprintf a string of 0 length, you can find out ahead of time how long the expanded string would have been, and use it to allocate the necessary memory.

Solution 5 - C

There's an important difference between these two -- the snprintf call will scan the name argument to the end (terminating NUL) in order to figure out the correct return value. The sprintf call on the other hand will read AT MOST 255 characters from name.

So if name is a pointer to a non-NUL terminated buffer with at least 255 characters, the snprintf call might run off the end of the buffer and trigger undefined behavior (such as crashing), while the sprintf version will not.

Solution 6 - C

Your sprintf statement is correct, but I'd not be self-confident enough to use that for safety purpose (e.g. missing one cryptic char and you're shieldless) while there is snprintf around that can be applied to any format ... oh wait snprintf is not in ANSI C. It is (only?) C99. That could be a (weak) reason to prefer the other one.

Well. You could use strncpy, too, couldn't you ?

e.g.

  char buffer[MAX_LENGTH+1];
  buffer[MAX_LENGTH]=0;             // just be safe in case name is too long
  strncpy(buffer,MAX_LENGTH,name);  // strncpy will never overwrite last byte

Solution 7 - C

Both will give the result you want, but snprintf is more generic, and will protect your string from overruns no matter the format string given.

In addition, because snprintf (or sprintf for that matter) adds a final \0, you should make the string buffer one byte bigger, char buff[MAXLEN + 1].

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionArpitView Question on Stackoverflow
Solution 1 - CazernikView Answer on Stackoverflow
Solution 2 - CMichael BurrView Answer on Stackoverflow
Solution 3 - CKadir Erdem DemirView Answer on Stackoverflow
Solution 4 - CGiorgian Borca-TasciucView Answer on Stackoverflow
Solution 5 - CChris DoddView Answer on Stackoverflow
Solution 6 - CPypeBrosView Answer on Stackoverflow
Solution 7 - CEli IserView Answer on Stackoverflow