4

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!

Puppy
  • 144,682
  • 38
  • 256
  • 465
someoneyeah
  • 81
  • 1
  • 6
  • 2
    If it's in [Wikipedia](http://en.wikipedia.org/wiki/Strcpy) then it must be safe! – David Heffernan Mar 15 '11 at 20:16
  • 1
    On the other hand, perhaps they don't need any more motivation!! ;-) – David Heffernan Mar 15 '11 at 20:18
  • A perfect answer might have to give some background on why string functions could be considered unsafe to begin with. Not everyone knows what a buffer overrun exploit is... – Merlyn Morgan-Graham Mar 15 '11 at 20:24
  • Well...I guess everyone should know what is :). Even not so advanced programmers...like myself. Luckily, there are "things" like firewalls, dep, alsr, gcc warnings that protect even the most pathetic programmer from BOs these days...AND there si also c# :). And to D.Hefferman: Wikipedia works only 84% of the time :)...but in this example i think it's OK. – someoneyeah Mar 15 '11 at 20:38
  • For a safe version, you can check: https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/ – ch271828n Nov 07 '19 at 00:52

9 Answers9

14

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.

Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
  • +1, but - "Contract to Microsoft's" ... did you mean contrary? If not, I haven't heard the word "contract" used that way. – Merlyn Morgan-Graham Mar 15 '11 at 20:21
  • agree with mostly anything - only that you can't argue whether some people will prefer gets() over getline among others - so gets isn't always dangerous. Plus, the size will NOT always be 17 - for instance, after gcc 2.95 you have most likely 106 or something over 100 in a buffer defined to hold indeed 100 bytes - since the stack is aligned by 16 bytes not byte after byte. – someoneyeah Mar 15 '11 at 20:50
  • @someone, the length of the string is 17. The stack alignment is relevant, but it's internal to the implementation. I do think gets is always dangerous, simply because the input file can unexpectedly change. – Matthew Flaschen Mar 15 '11 at 21:05
7

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.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Check the size of the buffer and then IF the size is <...then call gets? Will it work? Or I gues something much more fancy...like asynchonous call to simulate keystroke pressing, so actually the system is using only get - and the system will have no malicious intention to provide more data to gets than posible. – someoneyeah Mar 15 '11 at 20:26
  • @someoneyeah: I don't understand what you mean. `gets` reads data from the outside world. There's no way to predict how much data will be there by the time `gets` will actually start reading it and filling the buffer with it. – AnT stands with Russia Mar 15 '11 at 20:28
  • @someone, you can't check the buffer size ahead of time, because you don't know the length of the input line ahead of time. – Matthew Flaschen Mar 15 '11 at 20:28
  • OK....then I guess I should have said not check the size of buffer but rather - define a size of a variable and then see if it's more then the defined. Anyways, it's a pretty simple concept. – someoneyeah Mar 15 '11 at 20:32
  • 1
    @someone, no, it's not. No amount of variables makes `gets` safe against unknown input. You simply can't know the length of the line ahead of time. – Matthew Flaschen Mar 15 '11 at 20:39
  • If you "check" how long the line is before using `gets()` - then you can just as well read the data in your "checking" function. That defeats the purpose of using `gets()` in the first place. ;) Again, you can't know the size beforehand with gets, and if you do know it - why isn't the data already stored? – Xupicor Jul 24 '15 at 15:36
5

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

ch271828n
  • 15,854
  • 5
  • 53
  • 88
csd
  • 934
  • 5
  • 12
  • 2
    That's about the OP's example code, which of course is not a real application, but doesn't answer the question. – Jim Balter Mar 16 '11 at 04:28
  • yes, it does answer the question. using strcpy is dangerous. No matter how careful you are at coding time, given the ease with which the api allows for introduction of bugs (since there is an implicit assumption between caller and calle that dest has room), and the dire consequences of a mistake (coredump at best, devolving into random memory errors and buffer overflow attacks at worst). – csd Mar 18 '11 at 18:04
  • 2
    Sigh. `p = malloc(strlen(s)+1); if(!p) error; strcpy(p, s)` is not dangerous. By your argument, all programming, especially in C, is dangerous, but that's clearly not the question. – Jim Balter Mar 19 '11 at 07:54
1

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.

Johan Kotlinski
  • 25,185
  • 9
  • 78
  • 101
  • OK, my bad - thanks for your answers and Peaker's one to point that out - however what you suggest here is not strictly related to strcpy and buffer overflows - which was actually the main motivation for me in the question. But anyway - you're right...my mistake. – someoneyeah Mar 15 '11 at 20:42
1

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.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • The c tag hasn't been removed by myself... the code mentioned should be simple enough to fit in c and c++, speaking of which I am not mentioning namespace std - though yes, I prefer c++ overall. – someoneyeah Mar 15 '11 at 20:35
1

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.

Azarien
  • 11
  • 1
0

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.

Avilo
  • 1,204
  • 7
  • 7
0

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.

Peaker
  • 2,354
  • 1
  • 14
  • 19
0

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.

pmg
  • 106,608
  • 13
  • 126
  • 198
  • why an answer to a question about strcpy() speaks only about gets()? I understand the connection between the 2 but... – BlackBear Mar 15 '11 at 20:32
  • @BlackBear: the first version of the answer was indeed only about gets, but the current version when you posted your comment says: "the other function can be used safely": meaning strcpy, etc – pmg Mar 15 '11 at 20:36
  • I don't think this is an answer, although I didn't downvote you. BTW strcpy() isn't always safe (see @Matthew Flaschen's answer) – BlackBear Mar 15 '11 at 20:39
  • 1
    I just said strcpy can be used safely. That's different than always safe. **Adding two unsigned values is always safe.** Adding two signed values is unsafe (less than strcpy, granted, but unsafe nonetheless). `i++` is unsafe -- whoever checks for overflow there?? – pmg Mar 15 '11 at 20:45
  • Actually I've seen `gets` used safely passing to it a pointer to a committed memory page followed by a [guard page](http://msdn.microsoft.com/en-us/library/aa366549%28v=vs.85%29.aspx). – Matteo Italia Mar 15 '11 at 20:45