Why doesn't strncpy terminate null?

strncpy() supposedly protects against buffer overflows. But if it prevents an overflow without a null termination, then most likely the subsequent row operation will overflow. Therefore, to protect against this, I find myself:

 strncpy( dest, src, LEN ); dest[LEN - 1] = '\0'; 



man strncpy gives:

The strncpy () function is similar, except that no more than n bytes of src are copied. Thus, if there is no null byte among the first n bytes of src, the result will not end with zero.

Without null termination, something kind of innocent like:

  printf( "FOO: %s\n", dest ); 

... may fail.




Are there any better, safer alternatives to strncpy() ?

+59
c strncpy
Sep 21 '09 at 10:38
source share
11 answers

strncpy not intended to be used as a more secure strcpy , it should be used to insert one line in the middle of another.

All of these "safe" string handling functions, such as snprintf and vsnprintf , are patches that have been added to later standards to mitigate buffer overflow exploits, etc.

Wikipedia mentions strncat as an alternative to writing its own secure strncpy :

 *dst = '\0'; strncat(dst, src, LEN); 

EDIT

I missed that strncat exceeds LEN characters when null terminates a string if it is longer or equal to LEN char.

In any case, the point of using strncat instead of any homegrown solution like memcpy (..., strlen (...)) / regardless of the fact that the strncat implementation can be optimized for the target / platform in the library.

Of course, you need to verify that dst contains at least nullchar, so using strncat correctly would be something like:

 if(LEN) { *dst = '\0'; strncat(dst, src, LEN-1); } 

I also assume that strncpy is not very useful for copying a substring to another string, if src is shorter than n char, the destination string will be truncated.

+34
Sep 21 '09 at 11:12
source share

There are already open source versions, such as strlcpy , that make safe copying.

http://en.wikipedia.org/wiki/Strlcpy

The links have links to sources.

+19
Sep 21 '09 at 11:32
source share

Initially, the 7th UNIX file system (see DIR (5)) had directory entries that limited file names to 14 bytes; each entry in the directory consisted of 2 bytes for the inode number plus 14 bytes for the name, zero was filled up to 14 characters, but not necessarily ended with zero. I believe strncpy() was designed to work with these directory structures - or at least it works fine for this structure.

Consider:

  • The 14-character file name was not terminated by zero.
  • If the name was shorter than 14 bytes, it was filled with zeros to the full length (14 bytes).

This is exactly what could be achieved:

 strncpy(inode->d_name, filename, 14); 

So strncpy() ideally matched to its original niche. It was just a coincidence about preventing line overflows with zero termination.

(Note that zero padding to length 14 is not a serious overhead - if the buffer is 4 KB long and all you need to do is safely copy 20 characters into it, then an extra 4075 zeros are serious excesses, and can easily result in quadratic behavior if you repeatedly add material to a long buffer.)

+17
Sep 21 '09 at 11:33
source share

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 information). Most of these functions accept an additional parameter that sets the maximum length of the target variable, ensures that all lines have zero termination and have names that end with _s (for "safe"?), To distinguish them from their earlier "unsafe" versions . one

Unfortunately, they still get support and may not be available with your toolbox. Later versions of Visual Studio will give warnings if you use old insecure functions.

If your tools do not support new functions, it is quite simple to create your own wrappers for old functions. Here is 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 many lines as possible without overflowing. In fact, a VC ++ implementation can do this if you pass _TRUNCATE as count .






1 Of course, you still need to be precise about the size of the destination buffer: if you order a 3-character buffer, but tell strcpy_s() , it has room for 25 characters, you're still in trouble.
+8
Sep 21 '09 at 10:57
source share

Strncpy is safer to attack from the user of your program, it does not protect you from errors which the programmer does, for example, a zero-terminated string, as you described.

You can avoid crashes from the described problem by limiting the number of characters printed by printf:

 char my_string[10]; //other code here printf("%.9s",my_string); //limit the number of chars to be printed to 9 
+6
Sep 21 '09 at 22:33
source share

Use strlcpy() specified here: http://www.courtesan.com/todd/papers/strlcpy.html

If your libc has no implementation, try the following:

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

+5
Sep 21 '09 at 12:18
source share

strncpy works directly with available line buffers, if you work directly with your memory, you MUST now have the size of the buffer, and you can manually set "\ 0".

I believe that in simple C there is no better alternative, but it is not so bad if you are as careful as when playing with raw memory.

+3
Sep 21 '09 at 10:45
source share

I always preferred:

  memset(dest, 0, LEN); strncpy(dest, src, LEN - 1); 

to fix it later, but it's really a matter of preference.

+3
Sep 21 '09 at 10:59
source share

Instead of strncpy() you can use

 snprintf(buffer, BUFFER_SIZE, "%s", src); 

Here is a single line that copies at most size-1 non-zero 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)); } 
+3
Sep 21 '09 at 14:41
source share

These functions have evolved more than they were developed, so there really is no “why.” You just need to know how. Unfortunately, the Linux manual pages are at least devoid of sharing examples for these functions, and I noticed a lot of misuse in the code that I reviewed. I took a few notes here: http://www.pixelbeat.org/programming/gcc/string_buffers.html

+2
Sep 21 '09 at 13:33
source share

Without relying on newer extensions, in the past I did something like this:

 /* 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 possibly 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 macros instead of newer "built-in" (?) Functions? Because before there were several different unions, as well as other non-unix (non-windows) environments that I had to migrate back when I was doing C on a daily basis.

+1
Sep 21 '09 at 23:04
source share



All Articles