可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Are functions like strcpy, gets, etc. always dangerous? What if I write a code like this:
int main(void)
{
char *str1 = "abcdefghijklmnop";
char *str2 = malloc(100);
strcpy(str2, str1);
}
This way the function doesn't accept arguments(parameters...) and the str variable will always be the same length...which is here 16 or slightly more depending on the compiler version...but yeah 100 will suffice as of march, 2011 :).
Is there a way for a hacker to take advantage of the code above?
10x!
回答1:
Absolutely not. Contrary to Microsoft's marketing campaign for their non-standard functions, strcpy
is safe when used properly.
The above is redundant, but mostly safe. The only potential issue is that you're not checking the malloc
return value, so you may be dereferencing null (as pointed out by kotlinski). In practice, this likely to cause an immediate SIGSEGV and program termination.
An improper and dangerous use would be:
char array[100];
// ... Read line into uncheckedInput
// Extract substring without checking length
strcpy(array, uncheckedInput + 10);
This is unsafe because the strcpy may overflow, causing undefined behavior. In practice, this is likely to overwrite other local variables (itself a major security breach). One of these may be the return address. Through a return to lib C attack, the attacker may be able to use C functions like system
to execute arbitrary programs. There are other possible consequences to overflows.
However, gets
is indeed inherently unsafe, and will be removed from the next version of C (C1X). There is simply no way to ensure the input won't overflow (causing the same consequences given above). Some people would argue it's safe when used with a known input file, but there's really no reason to ever use it. POSIX's getline
is a far better alternative.
Also, the length of str1
doesn't vary by compiler. It should always be 17, including the terminating NUL.
回答2:
yes, it is dangerous. After 5 years of maintenance, your code will look like this:
int main(void)
{
char *str1 = "abcdefghijklmnop";
{enough lines have been inserted here so as to not have str1 and str2 nice and close to each other on the screen}
char *str2 = malloc(100);
strcpy(str2, str1);
}
at that point, someone will go and change str1 to
str1 = "THIS IS A REALLY LONG STRING WHICH WILL NOW OVERRUN ANY BUFFER BEING USED TO COPY IT INTO UNLESS PRECAUTIONS ARE TAKEN TO RANGE CHECK THE LIMITS OF THE STRING. AND FEW PEOPLE REMEMBER TO DO THAT WHEN BUGFIXING A PROBLEM IN A 5 YEAR OLD BUGGY PROGRAM"
and forget to look where str1 is used and then random errors will start happening...
回答3:
You are forcefully stuffing completely different things into one category.
Functions gets
is indeed always dangerous. There's no way to make a safe call to gets
regardless of what steps you are willing to take and how defensive you are willing to get.
Function strcpy
is perfectly safe if you are willing to take the [simple] necessary steps to make sure that your calls to strcpy
are safe.
That already puts gets
and strcpy
in vastly different categories, which have nothing in common with regard to safety.
The popular criticisms directed at safety aspects of strcpy
are based entirely on anecdotal social observations as opposed to formal facts, e.g. "programmers are lazy and incompetent, so don't let them use strcpy
". Taken in the context of C programming, this is, of course, utter nonsense. Following this logic we should also declare the division operator exactly as unsafe for exactly the same reasons.
In reality, there are no problems with strcpy
whatsoever. gets
, on the other hand, is a completely different story, as I said above.
回答4:
strcpy
isn't dangerous as far as you know that the destination buffer is large enough to hold the characters of the source string; otherwise strcpy
will happily copy more characters than your target buffer can hold, which can lead to several unfortunate consequences (stack/other variables overwriting, which can result in crashes, stack smashing attacks & co.).
But: if you have a generic char *
in input which hasn't been already checked, the only way to be sure is to apply strlen
to such string and check if it's too large for your buffer; however, now you have to walk the entire source string twice, once for checking its length, once to perform the copy.
This is suboptimal, since, if strcpy
were a little bit more advanced, it could receive as a parameter the size of the buffer and stop copying if the source string were too long; in a perfect world, this is how strncpy
would perform (following the pattern of other strn***
functions). However, this is not a perfect world, and strncpy
is not designed to do this. Instead, the nonstandard (but popular) alternative is strlcpy
, which, instead of going out of the bounds of the target buffer, truncates.
Several CRT implementations do not provide this function (notably glibc), but you can still get one of the BSD implementations and put it in your application. A standard (but slower) alternative can be to use snprintf
with "%s"
as format string.
That said, since you're programming in C++ (edit I see now that the C++ tag has been removed), why don't you just avoid all the C-string nonsense (when you can, obviously) and go with std::string
? All these potential security problems vanish and string operations become much easier.
回答5:
The only way malloc may fail is when an out-of-memory error occurs, which is a disaster by itself. You cannot reliably recover from it because virtually anything may trigger it again, and the OS is likely to kill your process anyway.
回答6:
Your code is not safe. The return value of malloc
is unchecked, if it fails and returns 0 the strcpy
will give undefined behavior.
Besides that, I see no problem other than that the example basically does not do anything.
回答7:
As you point out, under constrained circumstances strcpy isn't dangerous. It is more typical to take in a string parameter and copy it to a local buffer, which is when things can get dangerous and lead to a buffer overrun. Just remember to check your copy lengths before calling strcpy and null terminate the string afterward.
回答8:
Aside for potentially dereferencing NULL (as you do not check the result from malloc) which is UB and likely not a security threat, there is no potential security problem with this.
回答9:
gets()
is always unsafe; the other functions can be used safely.
gets()
is unsafe even when you have full control on the input -- someday, the program may be run by someone else.
The only safe way to use gets()
is to use it for a single run thing: create the source; compile; run; delete the binary and the source; interpret results.