strcpy when dest buffer is smaller than src buffer
Asked Answered
H

7

8

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.

Hare answered 21/10, 2009 at 15:53 Comment(5)
This is called buffer Overun.Infanticide
This buffer overflow problem illustrates why you should choose to use strncopy over strcpy.Amply
I couldn't reproduce your code. I get tring in the dest array.Guibert
@Spidey, this is undefined behavior. c-faq.com/ansi/undef.html "undefined: Anything at all can happen; the Standard imposes no requirements. The program may fail to compile, or it may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended."Crumpton
Thanks all. Especially because you not only explained the answer, but went on to show graphically how it works in the memory (for the behavior shown in my case). Also noted that this could throw up different results as it is undefined behavior.Hare
S
12

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 complex 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".

Sauerbraten answered 21/10, 2009 at 16:0 Comment(0)
C
7

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.

Crumpton answered 21/10, 2009 at 15:57 Comment(5)
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?Hare
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).Sauerbraten
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.Hare
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.Melodrama
I have added a graphic explanation to my answer below.Sauerbraten
M
2

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.

Mesial answered 21/10, 2009 at 17:34 Comment(7)
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?Photoflood
@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?Mesial
This answer in that way is not correct. Strncpy will surely not overruns a buffer if you a) 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, speedMixed
@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.)Mesial
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.Mesial
@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.Photoflood
Zero-padded "strings" are not as common today as they used to be, but they're hardly "application-specific". The function strncpy is the proper thing to use if e.g. a char[8] is used to hold up to eight (rather than seven) characters, and code which uses the contents thereof will know the maximum length. Note that either memcpy or strncpy could be used if both source and destination are zero-padded buffers of the same length, but memcpy would likely be faster. The time to use strncpy is when the source is zero-terminated and the destination is zero-padded.Tideway
M
1

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

Mixed answered 21/10, 2009 at 15:58 Comment(0)
H
1

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.

Hilel answered 21/10, 2009 at 16:1 Comment(0)
H
0

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!

Howse answered 21/10, 2009 at 15:58 Comment(0)
M
0

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.

Melodrama answered 21/10, 2009 at 16:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.