tags:

views:

338

answers:

6

Hello,

gcc 4.4.1 C99

I am using snprintf like this to avoid a buffer overrun.

char err_msg[32] = {0};
snprintf(err_msg, sizeof(err_msg) - 1, "[ ST_ENGINE_FAILED ]");

I have added the minus 1 incase the string is over 32 bytes long and I want to include the null terminator.

Am I correct in my thinking?

Many thanks for any suggestions,

A: 

sizeof will return the number of bytes the datatype will use in memory, not the length of the string. E.g. sizeof(int) returns '4' bytes on a 32-bit system (well, depending on the implementation I guess). Since you use a constant in your array, you can happily pass that to the printf.

TheGrandWazoo
He's applying `sizeof` to the destination array, which is completely correct.
caf
nope, `sizeof(err_msg)` will give 32.
Matt Joiner
+5  A: 

You don't need the -1, as the reference states:

The functions snprintf() and vsnprintf() do not write more than size bytes (including the trailing '\0').

Note the "including the trailing '\0'" part

Eli Bendersky
+1  A: 

According to snprintf(3):

The functions snprintf() and vsnprintf() do not write more than size bytes (including the trailing '\0').

Andrey Vlasovskikh
+2  A: 

No need for -1. C99 snprintf always zero-terminates. Size argument specifies the size of output buffer including zero terminator. The code, thus, becomes

char err_msg[32];
int ret = snprintf(err_msg, sizeof err_msg, "[ ST_ENGINE_FAILED ]");

ret contains actual number of characters printed (excluding zero terminator).

However, do not confuse with Microsoft's _snprintf (pre-C99), which does not null-terminate, and, for that matter, has completely different behaviour (e.g. returning -1 instead of would-be printed length in case if buffer is not big enough). If using _snprintf, you should be using the same code as in your question.

Alex B
+3  A: 

As others have said, you do not need the -1 in this case. If the array is fixed size, I would use strncpy instead. It was made for copying strings - sprintf was made for doing difficult formatting. However, if the size of the array is unknown or you are trying to determine how much storage is necessary for a formatted string. This is what I really like about the Standard specified version of snprintf:

char* get_error_message(char const *msg) {
    size_t needed = snprintf(NULL, 0, "%s: %s (%d)", msg, strerror(errno), errno);
    char  *buffer = malloc(needed);
    snprintf(buffer, needed, "%s: %s (%d)", msg, strerror(errno), errno);
    return buffer;
}

Combine this feature with va_copy and you can create very safe formatted string operations.

D.Shawley
Don't use strncpy() if there string might be too big to fit into the target; strncpy() does **NOT** null-terminate what it copies if it is too long. Further, it always copies N characters - even if the source is 1 byte and the target is 1 megabyte.
Jonathan Leffler
@Jonathan: Nope. while you are right that strncpy does not terminate with \0 it *does* stop if it does find a string terminator in the source string.
Nicholaz
For fixed length buffers, I usually use `strncpy(dest, src, sizeof(dest)); dest[sizeof(dest)-1] = '\0';` That guarantees NULL termination and is just less hassle than `snprintf` not to mention that a lot of people use `snprintf(dest, sizeof(dest), src);` instead and are very surprised when their programs crash arbitrarily.
D.Shawley
+1  A: 

For the example given, you should be doing this instead:

char err_msg[32];
strncpy(err_msg, "[ ST_ENGINE_FAILED ]", sizeof(err_msg));
err_msg[sizeof(err_msg) - 1] = '\0';

or even better:

char err_msg[32] = "[ ST_ENGINE_FAILED ]";
Matt Joiner
As noted in a comment to another answer: Don't use strncpy() if there string might be too big to fit into the target; strncpy() does NOT null-terminate what it copies if it is too long. Further, it always copies N characters - even if the source is 1 byte and the target is 1 megabyte.
Jonathan Leffler
@Jonathan Leffler, your description of how many bytes `strncpy()` is incorrect. "At most n bytes of src are copied." I've adjusted the example to fix the null-termination.
Matt Joiner
@anacrolix: your example does not guarantee NULL termination. It does guarantee that `strncpy()` will never overwrite the last character in the buffer. You have to explicitly do `err_msg[sizeof(err_msg)-1] = '\0';` to get termination.
D.Shawley
@D.Shawley, thx for pointing that out.
Matt Joiner