5

Does this code cause undefined behaviour? Because the buffer is only 128 byte long but i tell snprintf() that it is longer. However, the resulting string is shorter than 128 byte.

#include <stdio.h> int main(void) { char buffer[128]; snprintf(buffer,294201,"%s","ABC"); puts(buffer); return 0; } 
7
  • 1
    As "The snprintf function is equivalent to fprintf ...", we need to look at fprintf(). fprintf() does not write past what is needs. I suppose the question is, may it access beyond, hmmm. I think 294201 being more than 128 is not UB, but we are on thin ice. Good luck. Commented Apr 21, 2022 at 8:21
  • @chux-ReinstateMonica fprintf() does not write a '\0' character to the file, snprintf() does write one. Maybe an implementation writes '\0' at the end (so there is one in case the result is too long) and then writes the data. Commented Apr 21, 2022 at 8:31
  • There is no reason any sane snprintf implementation would write further into the buffer than past the terminating null character. Any writing beyond the null character would be completely useless and just waste CPU cycles. But you never know. But for me the code you show is broken, even if most likely nothing bad is going to happen. Commented Apr 21, 2022 at 8:31
  • @12431234123412341234123, Yes the steam vs. buffer difference is in the ... . Full spec covers more detail. Commented Apr 21, 2022 at 8:34
  • 2
    I am concerned about snprintf() attempting to do a buffer + 294201 addition that is invalid. I'm leaning toward UB now. Commented Apr 21, 2022 at 8:47

1 Answer 1

5

C 2018 7.21.6.5 2 says:

The snprintf function is equivalent to fprintf, except that the output is written into an array (specified by argument s) rather than to a stream. If n is zero, nothing is written, and s may be a null pointer. Otherwise, output characters beyond the n-1st are discarded rather than being written to the array, and a null character is written at the end of the characters actually written into the array.

Note this does not say snprintf is passed an array of n or more characters. So snprintf is not given any license to assume it may write to s[n-1] unless the fprintf that it is equivalent to would write n characters (including the terminating null character).

Looking at this another way, suppose we define an array buffer of 294,201 characters, fill it with data, and call snprintf(buffer,294201,"%s","ABC");. Would we expect nothing beyond the first four characters to change? If some other byte in the buffer changed, then this snprintf call would not be “equivalent to fprintf, except that the output is written into an array…” I would deem it a violation of this specification if it changed anything further in the buffer.

Sign up to request clarification or add additional context in comments.

2 Comments

UB does not have to be just writing issue. It could be an address calculation one. I reviewed a sample snprintf() source looking for possible code doing something like buffer + 294201 to form a "do not write pass this pointer". Since that addition is possible UB, that would be a problem. But source code does not do that - so perhaps speaks to implementation's good quality. Accounting for a possible overvalued size_t n makes for an interesting coding restriction.
@chux-ReinstateMonica: The issue calculating the address would be the same as the one for accessing memory: The specification for snprintf does not specify it is passed an array of size at least n or of any size except that needed to write the characters the specification says snprintf writes. So doing the address calculation in any way that has undefined behavior according to both the C standard and the implementation would not conform to the specification of the snprintf routine. (The implementation could do the calculation if it pleases and it guarantees no adverse effects.)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.