views:

406

answers:

7

I am trying to understand the difference/disadvantages of strcpy and strncpy. Can somebody please help:

void main()
{
char src[] = "this is a long string";
char dest[5];

strcpy(dest,src) ;
printf("%s \n", dest);
printf("%s \n", src);

}

The output is:

this is a long string 
a long string

QUESTION: I dont understand, how the source sting got modified. As per explanation, strcpy should keep copying till it encounters a '\0', so it does, but how come "src' string got modified.

Please explain.

+4  A: 

This is a buffer overflow, and undefined behavior.

In your case, it appears that the compiler has placed dest and src sequentially in memory. When you copy from src to dest, it continues copying past the end of dest and overwrites part of src.

ephemient
Can you please be more descriptive. Let us just consider my dest and src are sequential in memory. So they should start from location 1000 (say).. So dest takes 1000-1004 (5 bytes) and src takes 22 bytes (21 characters + null termination). So we have used total of 27 bytes 1000 till 1026. If what I have mentioned (in terms of memory) is correct, can you please explain in detail how the src got overwritten?
And src and dst appear to be aligned to multiples of 4 chars, so with dest being 5 chars long, the new mangled src will start 8 chars into the original src (which is now dest).
ndim
Can you please explain the memory map so it becomes easier to understand? just assume that all memory allocated starts from location 1000.. can you explain the spaces occupied by dest and src before and after the strcpy.
The space occupied by dest and src doesn't change. The amount of space allocated for them is constant. What changes is the actual contents of the memory. Because you're causing a buffer overrun in your copy you're not just changing the area of memory that is allocated for dest, but ALSO some of the memory allocated for src.
Herms
I have added a graphic explanation to my answer below.
ndim
+1  A: 

with high likliness the string are exact neighbours. So in your case you may have this picture

dst | | | | |src | | | | | |

so you start writing and it happens that the fields of src are overwritten.

Howerver you can surely not rely on it. Everything could happen what you have is undefined behaviour. So something else can happen on another computer another time and/or other options.

Regards Friedrich

Friedrich
A: 

I suggest a quick read of:

http://en.wikipedia.org/wiki/Strncpy#strncpy

which shows you the differences. Essentially strncpy lets you specify a number of bytes to copy, which means the resultant string isn't necessarily nullterminated.

Now when you use strcpy to copy one string over another, it doesn't check the resultant area of memory to see if it's big enough - it doesn't hold your hand in that regard. It checks up to the null character in the src string.

Of course, dst in this example is only 5 bytes. So what happens? It keeps on writing, to the end of dest and onwards past it in memory. And in this case, the next part of memory on the stack is your src string. So while your code isn't intentionally copying it, the layout of bytes in memory coupled with the writing past the end of dst has caused this.

Hope that helps!

Mark Mayo
+9  A: 

The easy answer is that you have (with that strcpy() call) done something outside the specifications of the system, and thus deservedly suffer from undefined behaviour.

The more difficult answer involves examining the concrete memory layout on your system, and how strcpy() works internally. It probably goes something like this:

     N+28 "g0PP"
     N+24 "trin"
     N+20 "ng s"
     N+16 "a lo"
     N+12 " is "
src  N+08 "this"
     N+04 "DPPP"
dest N+00 "DDDD"

The letters D stand for bytes in dest, the letters P are padding bytes, the 0 characters are ASCII NUL characters used as string terminators.

Now strcpy(dest,src) will change the memory content somewhat (presuming it correctly handles the overlapping memory areas):

     N+28 "g0PP"
     N+24 "trin"
     N+20 "g0 s"
     N+16 "trin"
     N+12 "ng s"
src  N+08 "a lo"
     N+04 " is "
dest N+00 "this"

I.e. while dest now "contains" the full string "this is a long string" (if you count the overflowed memory), src now contains a completely different NUL-terminated string "a long string".

ndim
+1  A: 

Your code caused a buffer overflow - copying to dest more characters than it can hold. The additional characters were written on another place on the stack, in your case, where src was pointing to.

You need to use strncpy() function.

eyalm
A: 

Either I'm misunderstanding your question, or you're misunderstanding strcpy:

QUESTION: I dont understand, how the source sting got modified. As per explanation, strcpy should keep copying till it encounters a '\0', so it does, but how come "src' string got modified.

It sounds to me like you're expecting strcpy to stop copying into dest when it reaches the end of dest, based on seeing a \0 character. This isn't what it does. strcpy copies into the destination until it reaches the end of the source string, delimited by a \0 character. It assumes you allocated enough memory for the copy. Before the copy the dest buffer could have anything in it, including all nulls.

strncpy solves this by having you actually tell it how big the buffer you're copying into is, so you can avoid cases where it copies more than can fit.

Herms
A: 

As an additional note, please keep in mind that strncpy function is not the right function to use when you need to perform copying with buffer overrun protection. This function is not intended for that purpose and has never been intended for that purpose. strncpy is a function that was created long time ago to perform some very application-specific string copying within some very specific filesystem in some old version of UNIX. Unfortunately, the authors of the library managed to "highjack" the generic-sounding name strncpy to use for that very narrow and specific purpose. It was then preserved for backward compatibility purposes. And now, we have a generation or two of programmers who make ther assumptions about strncpy's purpose based solely on its name, and consequently use it improperly. In reality, strncpy has very little or no meaningful uses at all.

C standard library (at least its C89/90 version) offers no string copying function with buffer overrrun protection. In order to perform such protected copying, you have to use either some platform-specific function, like strlcpy, strcpy_s or write one yourself.

P.S. This thread on StackOverflow contains a good discussion about the real purpose strncpy was developed for. See this post specifically for the precise explanation of its role in UNIX file system. Also, see here for a good article on how strncpy came to be.

Once again, strncpy is a function for copying a completely different kind of string - fixed length string. It is not even intended to be used with traditional C-style null-terminated strings.

AndreyT
I find strncpy useful say if I have a buffer and am reading lines from a text file which I know will be < BUF_LEN unless some malicious user changed them, so I pass BUF_LEN to strncpy. Anything wrong with that?
James Morris
@James Morris: I don't see what exactly you are trying to achieve by using `strncpy` there. Which feature of `strncpy` is important to you in this case?
AndreyT
This answer in that way is not correct. Strncpy will surely not overruns a buffer if youa) have the proper space for the "String"b) you get it right with the length. For that all you probably best use the following code (pseudo-C, partly taken from Code Complete II)char buf[MAX_LEN+1];strncpy(buf, src, MAX_LEN);In that case strncpy is safer then strcpy. But you are right there is not one function in Standard C which "buffer" overrun protection. This is simply how C was implemented, speed, speed, speed
Friedrich
@Friedrich: The answer is correct. `strncpy` does a lot of useless work when the string is shorter than the buffer (fills the tail part with zeroes), and doesn't do what has to be done when the string is longer than the buffer (doesn't add the terminating zero). These are consequences of a simple fact that `strncpy` was originally developed for a completely different purpose. True, it is possible to jump through certain hoops to make `strncpy` "work" here as well, but it is still not the right tool for the job. (You in your "pseudocode" forgot to initialize the terminating zero as well.)
AndreyT
I added a P.S. to my reply that contains some useful links that can help one to understand the real purpose of `strncpy` function.
AndreyT
@AndreyT: Quoting the man page, "except that at most n bytes of src are copied" would be the feature of strncpy that is important. I did not realize strncpy did all that padding though. Cheers for P.S.
James Morris