views:

81

answers:

4

If a function does all proper checks inside, should I check everything before calling it, or better not? Is security redundancy considered a good practice?

Example (in a sort of C#-like pseudocode with by-reference arguments passing):


doSomething(vector v) {
  ...;
  v.clear;
  useCleanVector(v)
}

useCleanVector(vector v) {
  if(!v.isClean) v.clear;
  ...
}

A: 

I'd say it's bad form. Not terrible form but the same kind of form that leads to this:

 v.clear(); // clear the vector to be safe. 
 v = v2;

If your manner is to reproduce the code that's already inside a function, then you're not saving time by having it in the function in the first place. You're violating the DRY concept if every call to a function has the same preamble.

It's best to understand what the function does check and what it doesn't and use it accordingly.

JoshD
I don't think this is what he means. What he means is stuff like checking function arguments for validity once when calling the function, and once inside it
Pekka
@Pekka, exactly. But from mathematical logic point of view, it's the same as @JoshD says.
Ivan
@Ivan yes, but your function may be used later in a context that *doesn't* do the first check. In that case, the second check *may* make sense (It could also be unnecessary. Really depends on your project)
Pekka
doSomething(vector v){...; v.clear; useCleanVector(v)} useCleanVector(vector v){v.clear; ...} // My case approximately
Ivan
@Pekka, @Ivan: Yeah that first example was a bit vague. I was going for the mindset of "I'd better check this just in case the function doesn't". The newer one might convey that point better. I think, though, we may all agree that it makes good sense for the function to do the checks in most cases so the programmer doesn't have to repeat it, right?
JoshD
+4  A: 

What matters most is that you document your preconditions, and exceptional conditions in an obvious way. Something like this seems sensible.

/**
 * precondition : id must be the id of a flarg.
 * 
 * myfunc will return -1 if value is outside the valid 0-10 range.
 */
int myfunc( int id, int value );

This lets me code something like this

 int flarg_id = ...
 if (! is_flarg( flarg_id ) ) { printf("Bad flarg"); exit(1); }
 int value = ...
 int rv = myfunc( flarg_id, value );
 if( rv == -1 )  { printf("Bad value"); exit(1); }
Michael Anderson
+1. As long as the docs tell clearly what the function is going to do under what circumstance, a huge part of uncertainty falls away
Pekka
A: 

Yes you should always perform the needed checks at that level of scope.

The reason? When someone comes in after you in n months, they are not going to follow the function calls down to the last item. Making sure that each function is protected in and of itself will help alleviate silly bugs or worse yet, bugs which are difficult to track down.

Aaron
It really depends how expensive the check is, though. If the code is well documented, being this strict may be more than necessary
Pekka
I think so mostly, but I am afraid this can even introduce difficultly trackable bugs in some cases.
Ivan
@Ivan not if your function is well documented, and every instance calling the function was written with that documentation in mind. Documentation is the key really - if it's good, it's possible to write code against it and trust that it'll feed the expected input
Pekka
@Pekka if the method you are in takes parameters you should check them. Assuming they are correct is asking for failure.
Aaron
@Aaron I'd say that really, really depends. In a closely knit, well-built Framework, not every private function needs to check what it gets from another. If the documentation defines what is valid and what is not, it can be perfectly acceptable to check only at the entry points where users / other classes / APIs send data unfiltered.
Pekka
@Pekka I've never experienced a dev group that tight knit.
Aaron
+1  A: 

There's redundancy (often good), and there's repeating yourself.

To borrow from Josh's example, if function Foo guarantees that it clears a vector, there's no reason to clear it beforehand. Trust-and-verify the guarantees your API provides.

On the other hand, even if you're confident that a data access surface is completely secured against any malicious activity (you checked every procedure pre- and post- conditions yourself!), there's no reason to expose that surface to unauthorized users. Find your bottlenecks, and secure those, just in case code deeper in has vulnerabilities you don't know about yet.

Michael Petrotta