5

As multiple questions on here also point out, you can printf a nonterminated string by formatting with a precision as maximum length to print. Something like

printf("%.*s\n", length, str);

will print length chars starting at str (or until the first 0 byte).

As pointed out here by jonathan-leffler, this is specified by posix here. And when reading the doc I discovered it actually never states this should work (or I couldn't find it) , as "The ‘%s’ conversion prints a string." and "A string is a null-terminated array of bytes [...] ". The regard about the precision states "A precision can be specified to indicate the maximum number of characters to write;".

My interpretation would be that the line above is actually undefined behavior, but because printf's implementation is efficient it doesn't read more than it writes.

So my question is: Is this interpretation correct and

TLDR: Should I stop using this printf trick when trying to be posix compliant as there exists an implementation where this might cause a buffer-overrun?

Poohl
  • 1,932
  • 1
  • 9
  • 22
  • I would say it is rather an omission than an "intended" undefined behavior. But strictly speaking one can think of an implementation that would invoke UB. – Eugene Sh. Mar 17 '21 at 18:13
  • If you know the data is not null-terminated, then you can use `fwrite` without any additional logic. – William Pursell Mar 17 '21 at 18:13
  • Suppose you specify 8 as the maximum length to printf, but the unterminated array has only 4 valid characters? Seems a bit dangerous 'hoping' there won't be an overrun. – Weather Vane Mar 17 '21 at 18:15
  • @WeatherVane Isn't it *on par* with the aforementioned `fwrite` ? – Eugene Sh. Mar 17 '21 at 18:18
  • @EugeneSh. perhaps, but the binary file write statements are usually focused on the source size, whereas text limits are often focused on the output constraints. – Weather Vane Mar 17 '21 at 18:21

1 Answers1

5

What you're reading isn't the actual POSIX spec, but the GNU libc manual, which tends to be a little less precise for the sake of readability. The actual spec can be found at https://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html (it's even linked from Jonathan Leffler's answer which you link to), and it makes it clear that your code is fine:

s The argument shall be a pointer to an array of char. Bytes from the array shall be written up to (but not including) any terminating null byte. If the precision is specified, no more than that many bytes shall be written. If the precision is not specified or is greater than the size of the array, the application shall ensure that the array contains a null byte.

Note that they are careful not to use the word "string" for exactly the reason you point out.

The ISO C17 standard uses almost identical language, so your code is even portable to non-POSIX standard C implementations. (POSIX generally incorporates ISO C and many parts of the POSIX spec are copy/pasted from the C standard.)

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82