tags:

views:

324

answers:

9

Following is the most popular implementation of strcpy in traditional systems. Why dest and src are not checked for NULL in the start? I heard once that in old days the memory was limited so short code was always preferred. Will you implement strcpy and other similar functions with NULL pointer checks at the start now days? Why not?

char *strcpy(char *dest, const char *src)
{
   char *save = dest;
   while(*dest++ = *src++);
   return save;
}
+13  A: 

NULL is a bad pointer, but so is (char*)0x1. Should it also check for that? In my opinion (I don't know the definitive reason why), sanity checks in such a low-level operation are uncalled for. strcpy() is so fundamental that it should be treated something like as asm instruction, and you should do your own sanity checks in the caller if needed. Just my 2 cents :)

tenfour
I agree: low level routines should be implemented for efficiency and high level routines should add security when applicable.
Matthieu M.
+1 for pointing out that `NULL` is only one example of the 99.9% of pointer space that's likely also invalid.
R..
What makes you say (char*)0x1 is necessarily a bad pointer? In C99, the null pointer *is* a special case in that it "is guaranteed to compare unequal to a pointer to any object or function." (6.3.2.3).
JeremyP
i'm just illustrating a point, no need to be pedantic. On my machine, 0x1 is a bad pointer. Will you also criticize R for his inaccurate statistic of 99.9% as well? :P
tenfour
+3  A: 

The most likely reason is: Because strcpy is not specified to work with NULL inputs (i.e. its behaviour in this case is undefined).

So, what should a library implementer choose to do if a NULL is passed in? I would argue that the best thing do to is to let the application crash. Think of it this way: A crash is a fairly obvious sign that something has gone wrong... silently ignoring a NULL input, on the other hand, may mask a bug that will be much harder to detect.

Martin B
No. `strcpy` on NULL input is undefined behaviour, which may crash, or it may *silently do the right thing*. You certainly can't rely on a runtime error from using `strcpy` with NULL.
Philip Potter
It might do, but the reality is that it won't.
DeadMG
@Philip: Good point -- I've edited the answer to remove the erroneous statement that "the correct thing to do is to crash" (but I would still argue that it's the _best_ thing to do).
Martin B
+2  A: 

NULL checks were not implemented because C's earliest targets supported strong memory protections. When a process attempted to read from or write to NULL, the memory controller would signal the CPU that an out-of-range memory access was attempted (segmentation violation), and the kernel would kill the offending process.

This was an alright answer, because code attempting to read from or write to a NULL pointer is broken; the only answer is to re-write the code to check return values from malloc(3) and friends and take corrective action. By the time you're trying to use pointers to unallocated memory, it is too late to make a correct decision about how to fix the situation.

sarnold
+8  A: 

The whole C language is written with the motto "We'll behave correctly provided the programmer knows what he's doing." The programmer is expected to know to make all the checks he needs to make. It's not just checking for NULL, it's ensuring that dest points to enough allocated memory to hold src, it's checking the return value of fopen to make sure the file really did open successfully, knowing when memcpy is safe and when memmove is required, and so on.

Getting strcpy to check for NULL won't change the language paradigm. You will still need to ensure that dest points to enough space -- and this is something that strcpy can't check for without changing the interface. You will also need to ensure that src is '\0'-terminated, which again strcpy can't possibly check.

There are some C standard library functions which do check for NULL: for example, free(NULL) is always safe. But in general, C expects you to know what you're doing.

[C++ generally eschews the <cstring> library in favour of std::string and friends.]

Philip Potter
+1 for std::string
jk
..which is so utterly incompatible, it seems to be *designed to hurt*.
peterchen
+11  A: 

There are no sanity checks because one of the most important underlying ideologies of C is that the developer supplies the sanity. When you assume that the developer is sane, you end up with a language that can be used to do just about anything, anywhere.

This is not an explicitly stated goal — it's quite possible for someone to come up with an implementation that does check for this, and more. Maybe they have. But I doubt that many people used to C would clamour to use it, since they'd need to put the checks in anyway if there was any chance that their code would be ported to a more usual implementation.

detly
*...the developer supplies the sanity.* - I like that ;)
caf
A: 

You should think of the C standard library functions as the thinnest possible additional layer of abstraction above the assembly code that you don't want to churn out to get your stuff over the door. Everything beyond that, like error checking, is your responsibility.

Johann Gerell
A: 

According to me any function you would want to define would have a pre-condition and a post-condition. Taking care of the preconditions should never be part of a function. Following is a precondition to use strcpy taken from the man page.

The strcpy() function copies the string pointed to by src (including the terminating '\0' character) to the array pointed to by dest. The strings may not overlap, and the destination string dest must be large enough to receive the copy.

Now if the precondition is not met then things might be undefined.

Whether I would include a NULL check in my strcpy now. I would rather have another safe_strcpy, giving safety the priority I would definitely include NULL checks and handle overflow conditions. And accordingly my precondition gets modified.

aeh
+6  A: 
  1. It's usually better for the library to let the caller decide what it wants the failure semantics to be. What would you have strcpy do if either argument is NULL? Silently do nothing? Fail an assert (which isn't an option in non-debug builds)?

  2. It's easier to opt-in than it is to opt-out. It's trivial to write your own wrapper around strcpy that validates the inputs and to use that instead. If, however, the library did this itself, you would have no way of choosing not to perform those checks short of re-implementing strcpy. (For example, you might already know that the arguments you pass to strcpy aren't NULL, and it might be something you care about if you're calling it in a tight loop or are concerned about minimizing power usage.) In general, it's better to err on the side of granting more freedom (even if that freedom comes with additional responsibility).

jamesdlin
+1 for custom wrapper that implements your error handling policy.(Though I probably wouldn't wrap strcpy individually. I use a StrOnBuf class that wraps the core character buffer manipulation routines, and can be configured to truncate silently, truncate with debug assert, or throw).
peterchen
+1 Point 1 is the answer to OP's question.
Alexandre C.
A: 

There is simply no error semantic defined for it. In particular there is no way for strcpy to return an error value. C99 simply states:

The strcpy function returns the value of s1.

So for a conforming implementation there wouldn't even a possibility to return the information that something went wrong. So why bother with it.

All this is voluntary, I think, since strcpy is replaced by most compilers by very efficient assembler directly. Error checks are up to the caller.

Jens Gustedt
Since behavior is undefined if `NULL` is passed, `strcpy` could conceivably return something other than `s1` when `s1` is `NULL`. Or it could fail to return at all (crash or infinite loop).
R..
@Jens: What if you are the first one and were asked to design strcpy from scratch and also write the C99 standards youself. Would you change it to return some error value?
@user436748: unfortunately this is purely hypothetical, three options. For a design as "high level" I would just require it to return `NULL` if on error, to set `errno` with an indication and also that the original data is unchanged in such a case. this is done in several other places, but not here for `strcpy`. If I would design it as "low level" I'd go for "just do the right thing" but I would in addition require it to produce a segfault in case that one of the pointers is `NULL`. Then, you asked, for real C99 you could go for `char* strcpy(char s1[static 1], char const s2[static 1]);`
Jens Gustedt