strncpy leading to segmentation fault

I could point you to man pages, websites, etc, but ultimately what matters is the C standard itself. As part of the standard runtime library, the usage and behavior is defined in C99-ยง7.23.2.4 as:

#include <string.h>
char *strncpy(char * restrict s1,
      const char * restrict s2,
      size_t n);

Description
The strncpy function copies not more than n characters (characters that follow a null character are not copied) from the array pointed to by s2 to the array pointed to by
s1. If copying takes place between objects that overlap, the behavior is undefined.
If the array pointed to by s2 is a string that is shorter than n characters, null characters are appended to the copy in the array pointed to by s1, until n characters in all have been written.

Returns
The strncpy function returns the value of s1.

There is significant implied information here, the most important being: strncpy() will NOT terminate your destination string with a null character if the source string length (not including its null character terminator) meets or exceeds the specified destination buffer length).

Furthermore, though clearly specified in the standard (see above), it continues to confound me how many engineers are NOT aware that strncpy() tail-fills the destination string buffer with null characters until the specified length n is reached when the source string length is less than the destination buffer size. This draws the following inescapable conclusion:

The strncpy() API will ALWAYS write n characters to the address referenced by the destination buffer.

In your case, because the target-buffer is only 10-chars wide, you’re writing 90 additional characters past the defined-end of writable memory, and thus walking into the land of undefined behavior.

At this point you have to be asking yourself “So whats the use?” There is an arguably fundamental use-case. It allows you to copy up to n chars to the target buffer with the predictability of knowing you won’t overrun past n chars. Period. Ultimately, though, you want a null-terminated string, so the proper usage is this:

char dst[ N ]; 
strncpy(dst, src, N-1);
dst[N-1] = 0;

where N is the hard-length of the dst buffer in chars and is greater-than-or-equal to 1. Note that dst could just-as-well be a dynamic-allocated memory pointer:

char *dst = malloc( N * sizeof(char) ); 
strncpy(dst, src, N-1);
dst[N-1] = 0;

With the above, you will always have a null-terminated string at dst. If the source string length is smaller than the specified target buffer length, strncpy() will tail-fill the rest of the buffer with null characters until a total of source-chars-copied + tail-filled-null-characters equals n, and the final statement is redundant. If the source string length is equal to or greater than the target buffer length, strncpy() will stop copying once N-1 chars are reached, and the final statement sets a null character at the end of the buffer. This results in a “cut-down” prefix string of the original source, but most important, it ensures you will NOT exceed the boundaries of your target buffer with a later string-API call that scans for a terminator.

The usefulness of the above technique is always debatable. I’m a C++ guy, so std::string saves my happy-self from all this insanity. But the reality is this: Sometimes you care if src isn’t copied in its entirety to dst; sometimes you don’t. The usefulness is very situationally dependent. For presenting string-data in a UI this won’t (likely) matter. For copying a string to be used for critical data, a partial-prefix-substring isn’t going to be acceptable. When the police issue an arrest warrant to “Joseph Johnson Jr.”, there will be some explaining to do when his father (“Joseph Johnson”) is hauled into jail because the name-buffer of the warrant-issuance software only held 15 chars.

All of that said, your segmentation fault comes down to this statement:

strncpy(s1.from_str,src, 100); // length parameter is wrong.

Recall the bold statement above: strncpy() will ALWAYS write n characters to the address referenced by the destination buffer.”. This means the above code will always write 100 chars to the target buffer, which in your case is only 10-chars wide, thus undefined behavior and likely ker-boom.

Rectify this by doing the following if the target buffer is a fixed-length character array:

strncpy(s1.from_str,src, sizeof(s1.from_str)/sizeof(s1.from_str[0])-1);
s1.from_str[ sizeof(s1.from_str)/sizeof(s1.from_str[0])-1 ] = 0;

See the prior usage for how to do this for dynamic string of length `N chars.

Leave a Comment