views:

342

answers:

10

Hi,

I have the following C code fragment and have to identify the error and suggest a way of writing it more safely:

char somestring[] = "Send money!\n";
char *copy;

copy = (char *) malloc(strlen(somestring));

strcpy(copy, somestring);
printf(copy);

So the error is that strlen ignores the trailing '\0' of a string and therefore it is not going to be allocated enough memory for the copy but I'm not sure what they're getting at about writing it more safely?

I could just use malloc(strlen(somestring)+1)) I assume but I'm thinking there must be a better way than that?

EDIT: OK, I've accepted an answer, I suspect that the strdup solution would not be expected from us as it's not part of ANSI C. It seems to be quite a subjective question so I'm not sure if what I've accepted is actually the best. Thanks anyway for all the answers.

Thanks,

Adam

+5  A: 
char somestring[] = "Send money!\n";
char *copy = strdup(something);

if (copy == NULL) {
    // error
}

or just put this logic in a separate function xstrdup:

char * xstrdup(const char *src) 
{
    char *copy = strdup(src);

    if (copy == NULL) {
       abort();
    }

    return copy;
}
dfa
It should be noted that strdup() is not a part of ANSI C.
Marc W
it should be: if (copy == NULL) {
hiena
If strdup() does not exist for your C library, then you should write it. That means you have a single point where a bug can occur instead of hundreds.
Aaron Digulla
Replaced null with NULL - hope you don't mind, dfa :)
schnaader
oops sorry! I'm programming mostly in Java these days...
dfa
A: 

The safer way would be to use strncpy instead of strcpy. That function takes a third argument: the length of the string to copy. This solution doesn't stretch beyond ANSI C, so this will work under all environments (whereas other methods may only work under POSIX-compliant systems).

char somestring[] = "Send money!\n";
char *copy;

copy = (char *) malloc(strlen(somestring));

strncpy(copy, somestring, strlen(somestring));
printf(copy);
Marc W
+1 for strncpy; more than anything else, that makes this code safer. At least in the malloc failure case, you'll segfault; strcpy() allows for insane security holes that are HARD to find.
McWafflestix
-1 for one-off error and memory corruption
Aaron Digulla
@McWafflestix: no, in this case, the strncpy() is misused and the result is a mess. As Aaron Digulla points out, the call to strncpy() needs the length 'strlen(somestring)+1'; as it is written, the copy is guaranteed not to be null terminated. The printf() is dangerous in general - using a random string as the format to printf() is bad (puts() is probably the nearest to sane choice here). (Imagine "Send 20% extra money!" instead!)
Jonathan Leffler
+3  A: 
  1. strlen + 1, for the \0 terminator
  2. malloc may fail; always check malloc return value
John Pirie
+3  A: 
char   somestring[] = "Send money!\n";
char   *copy;
size_t copysize;

copysize = strlen(somestring)+1;
copy = (char *) malloc(copysize);
if (copy == NULL)
    bail("Oh noes!\n");

strncpy(copy, somestring, copysize);
printf("%s", copy);

Noted differences above:

  • Result of malloc() must be checked!
  • Compute and store the memory size!
  • Use strncpy() because strcpy() is naughty. In this contrived example it won't hurt, but don't get into the habit of using it.

EDIT:

To those thinking I should be using strdup()... that only works if you take the very narrowest view of the question. That's not only silly, it's overlooking an even better answer:

char somestring[] = "Send money!\n";
char *copy = somestring;
printf(copy);

If you're going to be obtuse, at least be good at it.

dwc
+1 for use of strncpy. That's the source of so many security holes, it's not funny.
McWafflestix
Using strncpy() badly is worse than using strcpy() badly. If you use strncpy(), you are *NOT* guaranteed null terminated output (this example is OK, but not in general). Also, if you use over-size buffers and sizeof(buffer) for the third parameter to strncpy(), you have a performance mini-disaster on your hands; strncpy() zealously zero-pads the copied data to full length. *YES* to knowing length of source and target strings; if you do that correctly, using strcpy() is safe and safer than blind use of strncpy(). (If it is any help, strncat() is far, far worse than strncpy(); never use it!)
Jonathan Leffler
Jonathan, strlcpy() is much more sane. I use it whenever it's available, and sometime I bring along a version when it's not. Too bad Drepper is such a dork.
dwc
strncpy() will _always_ write N characters, and should be used only when manipulating length-limited not-necessarily-null-terminated arrays-of-char, as for example filenames in old-style Unix directory entries, the application for which it was originally designed.
mlp
-1 for using malloc() to in a string copy when you have strdup().
Aaron Digulla
A: 

The best way to write it more safely, if one were to be truly interested in such a thing, would be to write it in Ada.

somestring : constant string := "Send money!";
declare
   copy : constant string := somestring;
begin
   put_line (somestring);
end;

Same result, so what are the differences?

  • The whole thing is done on the stack (no pointers). Deallocation is automatic and safe.
  • Everything is automaticly range-checked so there's no chance of buffer-overflow exploits
  • Both strings are constants, so there's no chance of screwing up and modifying them.
  • It will probably be way faster than the C, not only because of the lack of dynamic allocation, but because there isn't that extra scan through the string required by strlen().

Note that in Ada "string" is not some special dynamic construct. It's the built-in array of characters. However, Ada arrays can be sized at declaration by the array you assign into them.

T.E.D.
+4  A: 

Ick... use strdup() like everyone else said and write it yourself if you have to. Since you have time to think about this now... check out the 25 Most Dangerous Programming Errors at Mitre, then consider why the phrase printf(copy) should never appear in code. That is right up there with malloc(strlen(str)) in terms of utter badness not to mention the headache of tracking down why it causes lots of grief when copy is something like "%s%n"...

D.Shawley
+5  A: 

I can't comment on the responses above, but in addition to checking the return code and using strncpy, you should never do:

printf(string)

But use:

printf("%s", string);

ref: http://en.wikipedia.org/wiki/Format_string_attack

Sucuri
+1 for unexpected security hole in most common code
Aaron Digulla
+1  A: 

I would comment to previous solutions but I do not have enough rep. Using strncpy here is as wrong as using strcpy(As there is absolutely no risk of overflow). There is a function called memcpy in < string.h > and it is meant exactly for this. It is not only significantly faster, but also the correct function to use to copy strings of known length in standard C.

From the accepted answer:

char   somestring[] = "Send money!\n";
char   *copy;
size_t copysize;

copysize = strlen(somestring)+1;
copy = (char *) malloc(copysize);
if (copy == NULL)
    bail("Oh noes!\n");

memcpy(copy, somestring, copysize); /* You don't use str* functions for this! */
printf("%s", copy);
jbcreix
-1 for using malloc() to in a string copy when you have strdup(). Don't reinvent the wheel when you have a library function.
Aaron Digulla
@Aaron As mentioned before, strdup() is not ANSI, it will not exist on all systems. The point of this answer can be applied when you write your own version of strdup().
Trent
+1  A: 

Ways to make the code safer (and more correct).

  1. Don't make an unnecessary copy. From the example, there's no apparent requirement that you actually need to copy somestring. You can output it directly.
  2. If you have to make a copy of a string, write a function to do it (or use strdup if you have it). Then you only have to get it right in one place.
  3. Whenever possible, initialize the pointer to the copy immediately when you declare it.
  4. Remember to allocate space for the null terminator.
  5. Remember to check the return value from malloc.
  6. Remember to free the malloc'ed memory.
  7. Don't call printf with an untrusted format string. Use printf("%s", copy) or puts(copy).
  8. Use an object-oriented language with a string class or any language with built-in string support to avoid most of these problems.
Adrian McCarthy
+1  A: 

to add more to Adrian McCarthy's ways to make safer code,

Use a static code analyzer, they are very good at finding this kind of errors

Chintan