Home > Back-end >  On better understanding the strncpy() function behavior
On better understanding the strncpy() function behavior

Time:04-04

In the Linux manpage of strncpy I read:

If the length of src is less than n, strncpy() writes additional
null bytes to dest to ensure that a total of n bytes are written.

In this limit case (no \0 at the end of both strings) where n>4:

    char dest[8]="qqqqqqqq";
    char src[4] = "abcd";
    strncpy(dest, src, 5);

printing dest char by char gives "abcdqqqq". As src has no \0 in it, no \0 is copied from the src to dest, but if I understand correctly the man page, other 4 characters should be copied in any case, and they should be \0s. Moreover, if src is "abc" (so it is NUL terminated), dest contains "abc\0\0qqq". I add the whole code I used to test (yes it is going to the 8-th character to look at it also):

#include <stdio.h>
#include <string.h>

int main()
{
    char dest[8]="qqqqqqqq";
    char src[4] = "abcd"; //  "abc"
    strncpy(dest, src, 5);
    for (int i=0; i<9; i  )
        printf("%2x ", dest[i]);
    putchar('\n');

    return 0;
}

Is this a faulty implementation or do I miss something?

CodePudding user response:

If the length of src

That's the problem, your src is not a null terminated string so strncpy has no idea how long it is. The manpage also has a sample code showing how strncpy works: it stops at a null byte and if it doesn't find one it simply keeps reading until it reaches n. And in your case it reads the 4 characters of src and gets the fifth from dest because it's next in memory. (You can try printing src[4], nothing will stop you and you'll get the q from dest. Isn't C nice?)

CodePudding user response:

You seem to be using strncpy() with a source argument that is not a string but an array of characters. So why do you expect it to behave sensibly? The manual on Linux clearly says it takes a string as the source argument, and that it copies it "including the terminating null byte" (which presumes such a byte). Note that there is no such thing as a "non-terminated string". An array of characters is just an array of characters, and string functions are not guaranteed to work on it.

However, the specific behavior that you are observing is expected and documented.

The rationale for strncpy() in the POSIX standard contains the following sentence:

If there is no NUL character byte in the first n bytes of the array pointed to by s2, the result is not null-terminated.

... where s2 is what you call src.

The manual for strncpy() on at least Ubuntu and OpenBSD contains similar wordings.

CodePudding user response:

Let’s look at this logically, using quotes from the GNU man page dated 2017-09-15, as included in Debian 10:

If the length of src is less than n, strncpy() writes additional null bytes to dest to ensure that a total of n bytes are written.

As you correctly state (sort of), n == 5. So what is the “length of src”? As we should all know, C strings are null-terminated. The man page hints at this:

The strcpy() function copies the string pointed to by src, including the terminating null byte ('\0') … The strncpy() function is similar, except that at most n bytes of src are copied.

Your string is not null-terminated, though. So after reading the 4 bytes you defined, strncpy tries to read a fifth byte, and what does it find? The first byte of dest, apparently (actually, this is implementation-dependent, as far as I know). It still has not found a null to terminate the string, so does it keep reading? No, because as stated above:

a total of n bytes are written

and

at most n bytes of src are copied.

So it copies the 5 bytes "abcdq" into dest.

You said:

other 4 characters should be copied in any case, and they should be \0s

This implies a total length of 8 bytes. Where would you get this from? This is the “length” (or at least declared array length) of dest, not src, and in any case would be overridden by the fact that n < 8.

The case where src is null-terminated is straightforward.

So no, the implementation is not faulty.

Actually, you got lucky:

The strings may not overlap

They do here, so, technically, anything could happen. Indeed, a comment on the question says that Valgrind warns about this.

and the destination string dest must be large enough to receive the copy. Beware of buffer overruns!

This carelessness with string lengths should be a warning to take extra care, before you create a buffer overrun.

On another note, while it is possible that a function like strncpy would have a bug, it is extremely unlikely. Standard implementations have been thoroughly tested in a wide variety of environments. Any apparent bug is much more likely to a bug in your own code, or a lack of understanding of your own code, as is the case here.

  • Related