Re: strncpy clarify result may not be null terminated

Jonny Grant
2023-11-04T21:18:15Z

On 04/11/2023 19:33, Alejandro Colomar wrote:
> Hi Jonny,
>
> On Sat, Nov 04, 2023 at 11:27:44AM +0000, Jonny Grant wrote:
>> Hello
>> I have a suggestion for strncpy.
>>
>> C23 draft states this caveat for strncpy.
>>
>> "373) Thus, if there is no null character in the first n characters of the array pointed to by s2, the result will not be null-
>> terminated."
>>
>>
>> https://man7.org/linux/man-pages/man3/strncpy.3.html
>>
>> "If the destination buffer, limited by its size, isn't large
>> enough to hold the copy, the resulting character sequence is
>> truncated. "
>
> The use of the term "character sequence" instead of "string" isn't
> casual. A "string" is a sequence of zero or more non-zero characters,
> followed by exactly one NUL. A "character sequence" is a sequence of
> zero or more non-zero characters, period.

Ok that's good to know. C23 calls it those "array", POSIX too. POSIX explains if the array is a string (ie null terminated) it pads with nulls, I'll paste it below:

https://pubs.opengroup.org/onlinepubs/009696899/functions/strncpy.html

"If the array pointed to by s2 is a string that is shorter than n bytes, null bytes shall be appended to the copy in the array pointed to by s1, until n bytes in all are written."

>
> To be clearer in that regard, the CAVEATS section of the same page says
> this:
>
> CAVEATS
> The name of these functions is confusing. These functions pro‐
> duce a null‐padded character sequence, not a string (see
> string_copying(7)).
>
> Saying that these functions don't produce a string should warn anyone
> thinking it would. The page string_copying(7) goes into more detail.
>
>>
>> How about clarifying this as:
>>
>>
>> "If the destination buffer, limited by its size, isn't large
>> enough to hold the copy, the resulting character sequence is
>> truncated; where there is no null terminating byte in the first n
>> characters the result will not be null terminated. "
>
> strncpy(3) should !*NEVER*! be used to produce a string.
> I don't think that should be conditional. Your suggested change could
> induce to the mistake of thinking that strncpy(3) is useful if the size
> of the buffer is enough. Do not ever use that function for producing
> strings. Use something else, like strlcpy(3), strcpy(3), or stpecpy(3).

Just documentation feedback based on C23, not writing code today.

Perhaps you may have seen Michael Kerrisk article about the risks with strlcpy.
https://lwn.net/Articles/507319/

re strcpy doesn't that risk buffer overruns? That's a surely a cyber security risk?
strlcpy is also bad in certain ways, it breaks ISO TR24731 "Do not unexpectedly truncate strings", can cause overruns and crashes.

I guess if you feel strncpy should "never be used to produce a string" you could describe that somewhere with an explanation in an article. You didn't mention why you feel it is not useful even if the size of the buffer is enough - including a null terminator I hope!

strncpy_s is a better solution, not widely available, and not part of glibc. That's another debate.

Is stpecpy standardised? If you can send me an online manual for it, I'll take a look.

Regards, Jonny