0

By reading the man of strlcpy I met example:

Since it is known how many characters were copied the first time,
 things can be sped up a bit by using a copy instead of an append:

 char *dir, *file, pname[MAXPATHLEN];
 size_t n;

 ...

 n = strlcpy(pname, dir, sizeof(pname));

 if (n >= sizeof(pname))
   goto toolong;

 if (strlcpy(pname + n, file, sizeof(pname) - n) >= sizeof(pname) - n)
   goto toolong;

However, one may question the validity of such optimizations,
 as they defeat the whole purpose of strlcpy() and strlcat().
 As a matter of fact, the first version of this manual page got it wrong.

What is the whole purpose of strlcpy and how exactly the aforementioned example defeats it?

Also what was in the first version of this manual page and why it is still important to mention it in the new one?

Andrei
  • 55
  • 6
  • On freebsd git source code there is only this version https://github.com/freebsd/freebsd-src/blame/main/lib/libc/string/strlcpy.3 . You would have to download some ancient BSD and compare strlcpy.3 versions. – KamilCuk Jun 23 '23 at 11:53
  • One purpose of `strlcpy` is to be less error prone and the optimization in the example is arguably error prone. Possibly that is why the author says the example defeats the purpose. However, it is difficult to say for sure since they do not explain the point further. – nielsen Jun 23 '23 at 13:00
  • @nielsen: Yes, thanks, I have upgraded it to an answer. – Eric Postpischil Jun 23 '23 at 13:37
  • 1
    @KamilCuk I found the "first version" of the man. https://github.com/libressl/openbsd/commit/83b8cc1cd7642ed303f194fbdd37577149acfb3e Indeed there was an error in code: `strlcpy(pname + n, file, sizeof(pname)` vs `strlcpy(pname + n, file, sizeof(pname) - n)` – Andrei Jun 25 '23 at 09:48

2 Answers2

2

What is the purpose of strlcpy…?

Both strncpy and strlcpy guarantee not to attempt writing beyond length given by the third parameter, but they differ in how they handle the last character when the copy would go beyond the buffer:

  • strncpy copies a character from the source into the last byte of the destination.
  • strlcpy writes a null character into the last byte of the destination.

strncpy conceivably has uses such as completely filling a destination buffer and then, if the source was not completely copied, using realloc to allocate more memory for the destination and then completing the operation. However, it may be prone to misuse: Since it produces outputs that are not null-terminated, a programmer writing code using strings might inadvertently treat the destination as a null-terminated string, passing it to printf or other routines, resulting in a crash or other bugs.

strlcpy provides some safety from this sort of bug since its output is always a null-terminated string. (Of course, different types of bugs may occur, such as producing a string that is incorrect because an expected portion is missing or because it is shorter than expected.)

… and what was in the first version of it's manual page?

As the man page is making the point that replacing this code:

if (strlcpy(pname, dir, sizeof(pname)) >= sizeof(pname))
    goto toolong;
if (strlcat(pname, file, sizeof(pname)) >= sizeof(pname))
    goto toolong;

with this code:

n = strlcpy(pname, dir, sizeof(pname));
if (n >= sizeof(pname))
    goto toolong;
if (strlcpy(pname + n, file, sizeof(pname) - n) >= sizeof(pname) - n)
    goto toolong;

can improve efficiency but has a hazard of introducing bugs, we can presume the first version of the man page had a bug in the comparisons (n >= sizeof(pname) or strlcpy(…) >= sizeof(pname) - n) or the length and address calculations (pname + n, sizeof(pname) - n). Possibly > was used instead of >= or n-1 in place of n or some other off-by-one error. The specific error is inconsequential; the point is that more complicated code is more prone to human error.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
2

What is the whole purpose of strlcpy and how exactly the aforementioned example defeats it?

That question mistakes the focus of the manual's remark, which is suggesting that strlcpy() should be used together with strlcat() in this case (eschewing the posited optimization), as presented in a previous example. The quoted example does not defeat the purpose of strlcpy(). It defeats the purpose of strlcpy() and strlcat(), as a pair.

When used together for this purpose, every call to either strlcpy() or strlcat() would be passed a pointer to the overall start of the destination buffer, and the full size of the buffer. This relieves the programmer of the need to track and use information about the current length of the destination string, which can be a source of errors. It is providing the ability to do this that the manual is referring to as the whole purpose of this pair of functions, though that's a bit of an exaggeration.

Also what was in the first version of this manual page and why it is still important to mention it in the new one?

The specifics are unclear, but the general point is that the original version of the page contained an error in that example, of exactly the kind that the recommended usage mode of strlcat() together with strlcpy() is intended to avoid. That is, an error in maintaining the length of the destination string or in using that appropriately.

But as to the first question actually posed, the primary purpose of strlcpy() as an individual function is to provide a variation on strncpy() that guarantees to NUL-terminate the destination string. This is a bit of a red herring, however, because strncpy() itself is one. The uninitiated are prone to mistake it for a safer strcpy(), but in fact, it is appropriate for only certain very special purposes. It is actually strncat() that is the safer strcpy():

#define safer_strcpy(dest, src, size) (*(dest) = '\0', strncat(dest, src, size))
John Bollinger
  • 160,171
  • 8
  • 81
  • 157