strncpy
doesn't add a terminating the '\0' character to truncated strings which causes problems like you experienced. When the string is not terminated correctly then it looks like it is longer but what you are actually seeing is the data that is placed after your buffer in memory. It can cause to serious problems.
Instead of strncpy
you should use strlcpy
which does terminate the string correctly and returns the length of the source string which you can compare to the length of your buffer to know if the string was truncated or not. strncpy
returns the pointer to the pointer to your buffer (which is not very useful since you already know it - you passed it as the first argument) and doesn't tell you whether any truncation has taken place.
See man strlcpy:
The strlcpy() and strlcat() functions copy and concatenate strings
with the same input parameters and output result as snprintf(3). They
are designed to be safer, more consistent, and less error prone
replacements for the easily misused functions strncpy(3) and
strncat(3). strlcpy() and strlcat() take the full size of the
destination buffer and guarantee NUL-termination if there is room.
Note that room for the NUL should be included in dstsize.
and C string handling - Replacements on Wikipedia:
The most popular[a] replacement are the strlcat and strlcpy functions,
which appeared in OpenBSD 2.4 in December, 1998.[84] These functions
always write one NUL to the destination buffer, truncating the result
if necessary, and return the size of buffer that would be needed,
which allows detection of the truncation and provides a size for
creating a new buffer that will not truncate.
Unfortunately it is not included in glibc - see the Secure Portability paper by
Damien Miller (PDF):
The strlcpy and strlcat API properly check the target buffer’s bounds,
nul-terminate in all cases and return the length of the source string,
allowing detection of truncation. This API has been adopted by most
modern operating systems and many standalone software packages,
including OpenBSD (where it originated), Sun Solaris, FreeBSD, NetBSD,
the Linux kernel, rsync and the GNOME project. The notable exception
is the GNU standard C library, glibc [12], whose maintainer
steadfastly refuses to include these improved APIs, labelling them
“horribly inefficient BSD crap” [4], despite prior evidence that they
are faster is most cases than the APIs they replace [13]. As a result,
over 100 of the software packages present in the OpenBSD ports tree
maintain their own strlcpy and/or strlcat replacements or equivalent
APIs - not an ideal state of affairs.
It is available for Linux in the libbsd library:
There are packages in Debian and Ubuntu and other distros:
Even if you don't want to depend on anything other than glibc it is very easy to add to your project since the entire source is short ana available under a permissive license:
/*
* Copyright (c) 1998 Todd C. Miller <[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0) {
while (--n != 0) {
if ((*d++ = *s++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}
Source: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcpy.c?rev=1.11