Why is strcpy unsafe in C? [duplicate] - c

This question already has answers here:
Is the function strcpy always dangerous?
(9 answers)
Closed 8 years ago.
I am a beginner, and I am learning how to copy a string in C now.
Here is a problem I just met:
Every time I try to use "strcpy" command to copy from string 1 to string 2, Visual Studio 2013 will give me an error/warning message saying that "strcpy" is unsafe and suggest me to use strcpy_s instead.
Can you please explain why is strcpy unsafe to use? And what are the safer alternatives to strcpy?
Here is my code:
#include<stdio.h>
#include<string.h>
main()
{
char str1[] = "Copy a string.";
char str2[15];
char str3[15];
int i;
/* with strcpy() */
strcpy(str2, str1);
/* Without strcpy() */
for (i = 0; str1[i]; i++)
str3[i] = str1[i];
str3[i] = '\0';
/* Display str2 and str3 */
printf("The content of str2: %s\n", str2);
printf("The content of str3: %s\n", str3);
return 0;
}
P.S. I am using Visual Studio 2013 64-bit ultimate on Windows 7 64-bit ultimate.
Thank you for your time!

strcpy has no way of knowing how large the destination buffer is (i.e. there is no length parameter) so sloppy programming using it can lead to overrunning the buffer and corrupting other memory. Such an overrun can lead to crashes, odd behaviour and may be exploitable by malware authors.
BTW, look at strncpy that takes a length parameter. One problem to be aware of is that strncpy will not terminate the string if the buffer is too small so it is dangerous in its own way.
In answer to your comment/question - it will depend on context. If you know the buffer is large enough (for example, you allocated it in the previous line at the correct size), use strcpy. If there is any possibility of an overflow then use strncpy and make sure you set the last position in the buffer to null. Note that "any possibility" should be interpreted very generously - unless the previous five or six lines are enough to show safety then use strncpy.
Also see Andon's comment above about strncpy_s. Finally, if you do use strcpy, you might want to add a #pragma to suppress the warning at that location.

Here's your original code:
int main() {
char str1[] = "Copy a string.";
char str2[15];
strcpy(str2, str1);
}
Now, three day later, you go to edit this code. You've realized that you actually need to write the message "This code is copying a string." instead.
int main() {
char str1[] = "This code is copying a string.";
char str2[15];
strcpy(str2, str1);
}
However, you've now accidentally introduced a buffer overflow.
strcpy_s requires the extra argument of the length of str2. This way, while you may not get the whole string into your new buffer, you won't cause undefined behavior.

Unlike strncpy and strcpy_s, strcpy doesn't do any length checking of the destination buffer which may cause stack overflow allowing an attacker to execute malicious code if exploited or just crach your application

Related

Is this a good way to use strcpy? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 3 years ago.
Improve this question
I try to use strcpy; is it a good way to do so?
char *stringvar;
stringvar = malloc(strlen("mi string") + 1);
strcpy(stringvar, "mi string");
stringvar[strlen("mi string")] = '\0';
What do you think?
There is exactly one bug in it: You don't check for malloc-failure.
Aside from that, it's repetitive and error-prone.
And overwriting the terminator with the terminator is useless.
Also, the repeated recalculation of the string-length is expensive.
Anyway, as you already have to determine the length, prefer memcpy() over strcpy().
What you should do is extracting it into a function, let's call it strdup() (that is the name POSIX and the next C standard give it):
char* strdup(const char* s) {
size_t n = strlen(s) + 1;
char* r = malloc(n);
if (r)
memcpy(r, s, n);
return r;
}
char* stringvar = strdup("mi string");
if (!stringvar)
handle_error();
You don't need the last line
stringvar[strlen("mi string")] = '\0';
strcpy takes care of that for you.
In real code you absolutely must check malloc's return value to make sure it's not NULL.
Other than that your code is fine. In particular, you've got the vital + 1 in the call to malloc. strlen gives you the length of the string not including the terminating '\0' character, but strcpy is going to add it, so you absolutely need to allocate space for it.
The problem with strcpy -- the fatal problem, some people say -- is that at the moment you call strcpy, you have no way of telling strcpy how big the destination array is. It's your responsibility to make the array big enough, and avoid overflow. strcpy is unable, by itself, to prevent buffer overflow -- and of course if the destination does overflow, that's a big problem.
So then the question is, how can you ensure -- absolutely ensure -- that all the calls to strcpy in all the code you write are correct? And how can you ensure that later, someone modifying your program won't accidentally mess things up?
Basically, if you use strcpy at all, you want to arrange that two things are right next to each other:
the code that arranges that the pointer variable points to enough space for the string you're about to copy into it, and
the actual call to strcpy that copies the string into that pointer.
So your code
stringvar = malloc(strlen("mi string") + 1);
strcpy(stringvar, "mi string");
comes pretty close to that ideal.
I know your code is only an example, but it does let us explore the concern, what if later, someone modifying your program accidentally messes things up? What if someone changes it to
stringvar = malloc(strlen("mi string") + 1);
strcpy(stringvar, "mi string asombroso");
Obviously we've got a problem. So to make absolutely sure that there's room for the string we're copying, it's even better, I think, if the string we're copying is in a variable, so it's patently obvious that the string we're copying is the same string we allocated space for.
So here's my improved version of your code:
char *inputstring = "mi string";
char *stringvar = malloc(strlen(inputstring) + 1);
if(stringvar == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
strcpy(stringvar, inputstring);
(Unfortunately, the check for a NULL return from malloc gets in the way of the goal of having the strcpy call right next to the malloc call.)
Basically your code is an implementation of the C library function strdup, which takes a string you give it and returns a copy in malloc'ed memory.
One more thing. You were worried about the + 1 in the all to malloc, and as I said, it's correct. A pretty common error is
stringvar = malloc(strlen(inputstring));
strcpy(stringvar, inputstring);
This fails to allocate space for the \0, so when strcpy adds the \0 it overflows the allocated space, so it's a problem.
And with that said, make sure you don't accidentally write
stringvar = malloc(strlen("mi string" + 1));
Do you see the error? This is a surprisingly easy mistake to make, but obviously it doesn't do what you want it to do.
There are some issues with the code posted:
you do not check if malloc() succeeded: if malloc() fails and returns NULL, passing this null pointer to strcpy has undefined behavior.
the last statement stringvar[strlen("mi string")] = '\0'; is useless: strcpy does copy the null terminator at the end of the source string, making the destination array a proper C string.
Here is a corrected version:
#include <stdlib.h>
#include <string.h>
...
char *stringvar;
if ((stringvar = malloc(strlen("mi string") + 1)) != NULL)
strcpy(stringvar, "mi string");
Note that is would be slightly better to store the allocation size and not use strcpy:
char *stringvar;
size_t size = strlen("mi string") + 1;
if ((stringvar = malloc(size)) != NULL)
memcpy(stringvar, "mi string", size);
Indeed it would be even simpler and safer to use strdup(), available on POSIX compliant systems, that performs exactly the above steps:
char *stringvar = strdup("mi string");

Why does passing right values through pointers results in segfault? [duplicate]

This question already has answers here:
Is it possible to modify a string of char in C?
(9 answers)
Closed 3 years ago.
Im currently teaching myself c, and during exercising pointers use I bumped into this problem-
I'm trying to replace a substring in a string that is pointed to (meaning,not a char array), with a substring from another pointed string.
char *str1="I like pizza!";
char *str2="love";
printf("%s\n", str1);
for (int i=2, j=0; j<4; i++, j++) {
*(str1+i)=*(str2+j);
}
printf("%s\n", str1);
Result should be- the way I see it- output of "I like pizza", followed by output of "I love pizza".
Instead, I get a segfault (error 139).
I scraped the web for a solution but couldn't find what is the problem.
(I am aware that the for loop is not perfect, to say the least, but that's not the issue here).
Please help me out :)
Because those are pointers to a read-only part in you're program's binary. You can't change the content. Try this instead:
char str1[] = "I like pizza!";
char str2[] = "love"; // actually, this one can stay as a pointer as we're only reading
Now the strings are copied to the stack and the program works as intended.
The strings pointed to by str1 and str2 are read-only.
The behaviour on attempting to change their contents is undefined.
Use char str1[] = "I like pizza!"; &c. instead, which copies the read-only string to str1[], which you are then free to modify.

Why does the following c code work in turbo c and not in devc++?

I have the following piece of code:
char *str;
gets(str);
Now it works in turbo c but fails to in devc.
I have to add the following in order to make it work in devC++.
char *str = malloc(5);
Can anyone explain, why is it so?
Also, which one is authentic and a more correct form of coding.
that
char *str;
gets(str);
just cannot be right. str is not initialized, and gets recieves the pointer by value so it cannot allocate it internally. You're just lucky/unlucky with undefined behaviour.
which one is authentic and a more correct form of coding ?
None of the above. don't use gets. It's unsafe because you cannot limit the input size. Use fgets with specified size (and an allocated buffer of course!)
#include <stdio.h>
char buffer[20];
fgets(buffer, sizeof(buffer), stdin); // reads at most 19 chars + null-termination
or scanf with size limit (note the -1): scanf("%19s",buffer);
gets(str);
It shouldnt work even in turbo c++ because you have not allocated the space to str.
correct way
str = (char *)malloc(sizeof(char) * (length+1));

My program is crashing when it calls `strcpy()' [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *str1 = "Bangladesh Bogus Party";
char *str2 = "Sad is a beautiful Country";
strcpy(str1 + 11, str2 + 4);
strcat(str1, "! !! !!!");
printf("\n%s", str1);
return 0;
}
In the code blocks ,it has found....
-------------- Build: Debug in catcpy (compiler: GNU GCC Compiler)---------------
Target is up to date.
Nothing to be done (all items are up-to-date).
-------------- Run: Debug in catcpy (compiler: GNU GCC Compiler)---------------
Checking for existence: E:\Sobar Jonno C (Niton)\String\catcpy\bin\Debug\catcpy.exe
Executing: "C:\Program Files (x86)\CodeBlocks/cb_console_runner.exe" "E:\Sobar Jonno C (Niton)\String\catcpy\bin\Debug\catcpy.exe" (in E:\Sobar Jonno C (Niton)\String\catcpy\.)
You can't overwrite your str1 pointer to a string literal because they are read only memory and your program is trying to write to the memory the pointer points to which causes Undefined Behavior most of the time it causes a Segmentation Fault. And even worst you can't concatenate without previously allocating the necessary space.
If you want your code to work, you need something like this
#include <stdio.h>
#include <string.h>
int main(void)
{
char str1[42] = "Bangladesh Bogus Party";
const char *str2 = "Sad is a beautiful country";
strcpy(str1 + 11, str2 + 4);
strcat(str1, "! !! !!!");
printf("%s\n", str1);
return 0;
}
Note that str1 is no longer a string literal, whil str2 is. That is because str2 is never modified, and I added the const specifier to prevent from accidentally doing it. It's impossible to completely prevent this but const can help you notice when you do it unintentionally.
The main issue is that str1 points to a string literal. These live in a read-only section of memory, so attempting to change them will typically cause your program to crash. Rather than defining str1 as a pointer, define it as an array initialized with the literal in question:
char str1[] = "Bangladesh Bogus Party";
If you do this, however, the array doesn't have enough room for the extra characters to be added. So make the array larger so that is has enough space:
char str1[50] = "Bangladesh Bogus Party";
strcpy and strcat both require that the first operand points to memory that is both writable and big enough to hold the result.
In your case neither of these requirements is fulfilled.
String literals are often placed in write-protected memory by the compiler. gcc places string literals in write-protected memory. Other compilers can do it differently.
The problem can be solved by creating str1 in writable memory and increasing its size so that it is big enough to hold the entire string.
The resulting string will be 41 characters + 1 for the string terminator.
Here are a couple of different ways to do it:
char str1[42] = "Bangladesh Bogus Party"; // Declare str1 as an array
or
char *str1 = malloc(42); // Allocate str1 dynamically
strcpy(str1, "Bangladesh Bogus Party"); // and copy the string to it
or
char *str1 = strdup("Bangladesh Bogus Party"); // Allocate str1 dynamically and copy the string to it. It will only be just big enough to hold the initial data
str1 = realloc(str1, 42); // Increase the size to 42.

C: String manipulation adding more characters without causing a buffer overflow

In C I have a path in one of my strings
/home/frankv/
I now want to add the name of files that are contained in this folder - e.g. file1.txt file123.txt etc.
Having declared my variable either like this
char pathToFile[strlen("/home/frankv/")+1]
or
char *pathToFile = malloc(strlen("/home/frankv/")+1)
My problem is that I cannot simply add more characters because it would cause a buffer overflow. Also, what do I do in case I do not know how long the filenames will be?
I've really gotten used to PHP lazy $string1.$string2 .. What is the easiest way to do this in C?
If you've allocated a buffer with malloc(), you can use realloc() to expand it:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char *buf;
const char s1[] = "hello";
const char s2[] = ", world";
buf = malloc(sizeof s1);
strcpy(buf, s1);
buf = realloc(buf, sizeof s1 + sizeof s2 - 1);
strcat(buf, s2);
puts(buf);
return 0;
}
NOTE: I have omitted error checking. You shouldn't. Always check whether malloc() returns a null pointer; if it does, take some corrective action, even if it's just terminating the program. Likewise for realloc(). And if you want to be able to recover from a realloc() failure, store the result in a temporary so you don't clobber your original pointer.
Use std::string, if possible. Else, reallocate another block of memory and use strcpy and strcat.
You have a couple options, but, if you want to do this dynamically using no additional libraries, realloc() is the stdlib function you're looking for:
char *pathToFile = malloc(strlen("/home/frankv/")+1);
char *string_to_add = "filename.txt";
char *p = realloc(pathToFile, strlen(pathToFile) + strlen(string_to_add) + 1);
if (!p) abort();
pathToFile = p;
strcat(p, string_to_add);
Note: you should always assign the result of realloc to a new pointer first, as realloc() returns NULL on failure. If you assign to the original pointer, you are begging for a memory leak.
If you're going to be doing much string manipulation, though, you may want to consider using a string library. Two I've found useful are bstring and ustr.
In case you can use C++, use the std::string. In case you must to use pure C, use what's call doubling - i.e. when out of space in the string - double the memory and copy the string into the new memory. And you'll have to use the second syntax:
char *pathToFile = malloc(strlen("/home/frankv/")+1);
You have chosen the wrong language for manipulating strings!
The easy and conventional way out is to do something like:
#define MAX_PATH 260
char pathToFile[MAX_PATH+1] = "/home/frankv/";
strcat(pathToFile, "wibble/");
Of course, this is error prone - if the resulting string exceeds MAX_PATH characters, anything can happen, and it is this sort of programming which is the route many trojans and worms use to penetrate security (by corrupting memory in a carefully defined way). Hence my deliberate choice of 260 for MAX_PATH, which is what it used to be in Windows - you can still make Windows Explorer do strange things to your files with paths over 260 characters, possibly because of code like this!
strncat may be a small help - you can at least tell it the maximum size of the destination, and it won't copy beyond that.
To do it robustly you need a string library which does variable length strings correctly. But I don't know if there is such a thing for C (C++ is a different matter, of course).

Resources