Search code examples
cunixlibcmanpagestrlcpy

What is the purpose of strlcpy and what was in the first version of its manual page?


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?


Solution

  • 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.