Reputation: 10662
strncpy()
supposedly protects from buffer overflows. But if it prevents an overflow without null terminating, in all likelihood a subsequent string operation is going to overflow. So to protect against this I find myself doing:
strncpy( dest, src, LEN );
dest[LEN - 1] = '\0';
man strncpy
gives:
The
strncpy()
function is similar, except that not more thann
bytes ofsrc
are copied. Thus, if there is no null byte among the firstn
bytes ofsrc
, the result will not be null-terminated.
Without null terminating something seemingly innocent like:
printf( "FOO: %s\n", dest );
...could crash.
Are there better, safer alternatives to strncpy()
?
Upvotes: 102
Views: 134368
Reputation: 4040
strncpy()
is not intended to be used as a safer strcpy()
, it is supposed to be used to insert one string in the middle of another.
All those "safe" string handling functions such as snprintf()
and vsnprintf()
are fixes that have been added in later standards to mitigate buffer overflow exploits etc.
Wikipedia mentions strncat()
as an alternative to writing your own safe strncpy()
:
*dst = '\0';
strncat(dst, src, LEN);
EDIT
I missed that strncat()
exceeds LEN characters when null terminating the string if it is longer or equal to LEN char's.
Anyway, the point of using strncat()
instead of any homegrown solution such as memcpy(..., strlen(...))
/whatever is that the implementation of strncat()
might be target/platform optimized in the library.
Of course you need to check that dst holds at least the nullchar, so the correct use of strncat()
would be something like:
if (LEN) {
*dst = '\0'; strncat(dst, src, LEN-1);
}
I also admit that strncpy()
is not very useful for copying a substring into another string, if the src is shorter than n char's, the destination string will be truncated.
Upvotes: 63
Reputation: 6208
I have always preferred:
memset(dest, 0, LEN);
strncpy(dest, src, LEN - 1);
to the fix it up afterwards approach, but that is really just a matter of preference.
Upvotes: 4
Reputation: 3144
Without relying on newer extensions, I have done something like this in the past:
/* copy N "visible" chars, adding a null in the position just beyond them */
#define MSTRNCPY( dst, src, len) ( strncpy( (dst), (src), (len)), (dst)[ (len) ] = '\0')
and perhaps even:
/* pull up to size - 1 "visible" characters into a fixed size buffer of known size */
#define MFBCPY( dst, src) MSTRNCPY( (dst), (src), sizeof( dst) - 1)
Why the macros instead of newer "built-in" (?) functions? Because there used to be quite a few different unices, as well as other non-unix (non-windows) environments that I had to port to back when I was doing C on a daily basis.
Upvotes: 3
Reputation: 4903
Strncpy is safer against stack overflow attacks by the user of your program, it doesn't protect you against errors you the programmer do, such as printing a non-null-terminated string, the way you've described.
You can avoid crashing from the problem you've described by limiting the number of chars printed by printf:
char my_string[10];
//other code here
printf("%.9s",my_string); //limit the number of chars to be printed to 9
Upvotes: 8
Reputation: 753475
Originally, the 7th Edition UNIX file system (see DIR(5)) had directory entries that limited file names to 14 bytes; each entry in a directory consisted of 2 bytes for the inode number plus 14 bytes for the name, null padded to 14 characters, but not necessarily null-terminated. It's my belief that strncpy()
was designed to work with those directory structures - or, at least, it works perfectly for that structure.
Consider:
This is exactly what would be achieved by:
strncpy(inode->d_name, filename, 14);
So, strncpy()
was ideally fitted to its original niche application. It was only coincidentally about preventing overflows of null-terminated strings.
(Note that null padding up to the length 14 is not a serious overhead - if the length of the buffer is 4 KB and all you want is to safely copy 20 characters into it, then the extra 4075 nulls is serious overkill, and can easily lead to quadratic behaviour if you are repeatedly adding material to a long buffer.)
Upvotes: 40
Reputation: 169533
Instead of strncpy()
, you could use
snprintf(buffer, BUFFER_SIZE, "%s", src);
Here's a one-liner which copies at most size-1
non-null characters from src
to dest
and adds a null terminator:
static inline void cpystr(char *dest, const char *src, size_t size)
{ if(size) while((*dest++ = --size ? *src++ : 0)); }
Upvotes: 6
Reputation: 31708
These functions have evolved more than being designed, so there really is no "why". You just have to learn "how". Unfortunately the linux man pages at least are devoid of common use case examples for these functions, and I've noticed lots of misuse in code I've reviewed. I've made some notes here: http://www.pixelbeat.org/programming/gcc/string_buffers.html
Upvotes: 2
Reputation: 7221
Use strlcpy()
, specified here: http://www.courtesan.com/todd/papers/strlcpy.html
If your libc doesn't have an implementation, then try this one:
size_t strlcpy(char* dst, const char* src, size_t bufsize)
{
size_t srclen =strlen(src);
size_t result =srclen; /* Result is always the length of the src string */
if(bufsize>0)
{
if(srclen>=bufsize)
srclen=bufsize-1;
if(srclen>0)
memcpy(dst,src,srclen);
dst[srclen]='\0';
}
return result;
}
(Written by me in 2004 - dedicated to the public domain.)
Upvotes: 5
Reputation: 2805
There are already open source implementations like strlcpy that do safe copying.
http://en.wikipedia.org/wiki/Strlcpy
In the references there are links to the sources.
Upvotes: 30
Reputation: 48280
Some new alternatives are specified in ISO/IEC TR 24731 (Check https://buildsecurityin.us-cert.gov/daisy/bsi/articles/knowledge/coding/317-BSI.html for info). Most of these functions take an additional parameter that specifies the maximum length of the target variable, ensure that all strings are null-terminated, and have names that end in _s
(for "safe" ?) to differentiate them from their earlier "unsafe" versions.1
Unfortunately, they're still gaining support and may not be available with your particular tool set. Later versions of Visual Studio will throw warnings if you use the old unsafe functions.
If your tools don't support the new functions, it should be fairly easy to create your own wrappers for the old functions. Here's an example:
errCode_t strncpy_safe(char *sDst, size_t lenDst,
const char *sSrc, size_t count)
{
// No NULLs allowed.
if (sDst == NULL || sSrc == NULL)
return ERR_INVALID_ARGUMENT;
// Validate buffer space.
if (count >= lenDst)
return ERR_BUFFER_OVERFLOW;
// Copy and always null-terminate
memcpy(sDst, sSrc, count);
*(sDst + count) = '\0';
return OK;
}
You can change the function to suit your needs, for example, to always copy as much of the string as possible without overflowing. In fact, the VC++ implementation can do this if you pass _TRUNCATE
as the count
.
strcpy_s()
it has space for 25 chars, you're still in trouble.
Upvotes: 8
Reputation: 23168
strncpy works directly with the string buffers available, if you are working directly with your memory, you MUST now buffer sizes and you could set the '\0' manually.
I believe there is no better alternative in plain C, but its not really that bad if you are as careful as you should be when playing with raw memory.
Upvotes: 3