tags:

views:

140

answers:

4

Came across a line in OpenSSL that made me do a double take...

if (!*pos)
  return NULL;
if (!*pos || ((*pos)->flags == FLAGS))
  return blah;

Is there a (performance/security/concurrecy) difference in doing that instead of:

if (!*pos) 
  return NULL;
if (*pos && ((*pos)->flags == FLAGS))
  return blah;

Thanks, Chenz

+6  A: 

Why not:

if (!*pos) 
  return NULL;
if ((*pos)->flags == FLAGS)
  return blah;

The original code (comments removed) is:

if (!pos)
    return NULL;
if (!*pos)
    return BIO_new(BIO_s_null());
if (!*pos || ((*pos)->flags == ASN1_STRING_FLAG_CONT))
    return BIO_new(BIO_s_mem());
return BIO_new_mem_buf((*pos)->data, (*pos)->length);

If the tests were for some dubious security reason, shouldn't the third test be:

if ( pos && *pos && ((*pos)->flags == ASN1_STRING_FLAG_CONT))

which of course gets ridiculous the further into the function you get. Also, a cursory inspection reveals that this style is not used elsewhere in the file, so I think we can write this off to a minor programmers boo-boo.

anon
Agreed, but perhaps the !*pos || was left in so that the security would not be dependent on a previous if statement.
Crazy Chenz
@Crazy All programs depend on previous statements. And looking at the original code, there is another test before the ones you instanced, so I don't think that is the reason.
anon
Yes, but you didn't answer **"WHY not?"** . That's the gist of the question. Not "how you think it would look best in your opinion" but "what motives could have led the author to make it the way it is." Since it's OpenSSH, plain incompetence is much less likely than some important, hidden motive.
SF.
SF: .....Agreed
Crazy Chenz
Neil: Good Point.
Crazy Chenz
+2  A: 

Ignoring the first two lines of each which make the test redundant, the two forms don't do the same thing.

if (!*pos || ((*pos)->flags == FLAGS))
   // reached if *pos is NULL or the flags are set
   return blah;

if (*pos && ((*pos)->flags == FLAGS))
   // reached if *pos is not NULL and the flags are set 
   return blah;

The || form would cause the function to return blah if it was reached with *pos is NULL.

(since the code follows a statement which makes the function return NULL if *pos is NULL the test is probably redundant, assuming pos points at normal memory rather than a volatile)

Pete Kirkham
So you see what I see... perhaps it was simply the by product of patchsets. Thanks.
Crazy Chenz
+2  A: 

Seems like the first line obsoletes the first condition of the second, but it's still a good practice to leave checks for not-null before dereferencing, in case earlier code is ever removed, or if code that modifies pos (sets it to NULL) is ever placed between these sections. (one significant disadvantage of return in the middle of a function here...)

Other than that, there should be no difference with any optimizing compiler, but the code may convey the idea better: return blah either if *pos is NULL, or if flags are FLAGS; despite earlier condition that singles out pos==NULL, in this place of execution logic may allow it to be NULL as well and perform the return as described instead of skipping this place.

Imagine:

 if (!*pos)
   return NULL;

  /* code added later here */
  if(foo) 
    pos = baz;   // added latter; baz can be NULL
  /* more code... */

 if (!*pos || ((*pos)->flags == FLAGS))
   return blah;

This will still behave correctly, control returned if pos==NULL, only different return.

Any change to the later condition will modify the behavior:

 if (*pos && ((*pos)->flags == FLAGS))
   return blah;
 crash();
SF.
Even though, ATM, this answer is at the bottom of the pile, it makes the most sense to me. Anyway you could clear up your point SF?
Crazy Chenz
"Still a good practice" (in your first sentence)? If any piece of code is changed anywhere, then you better always check and modify all the following code, no matter what. If you want to secure any single command as if all the previous code never happened, why stop at pointer dereferencing? My guess is that it is simply a leftover from a functional extension, given Neil's cursory inspection that it is not found elsewhere.
Secure
@Secure: placing return anywhere but the last line of the function is a bad practice. Except it sometimes is worthwhile because it can save you several levels of nesting and quite a bit of calculation. But if you decide to place a return somewhere in the middle of your function, you'd better either write the remainder of the function very defensively, or really, really watch your steps when editing that function.
SF.
Just what I've said: You should always watch your steps when changing something. You've given an additional example of an inserted pointer assignment. So either you always watch your steps, or you write **all** of your code **very very** defensively. Why reduce it to the insertion of return, in case it could be removed later? It is only one of many possible code changes that affect the following code.
Secure
+1  A: 

Seeing such a code, efficiency is not the right question to ask. I don't like either of the versions. I'd go for

if (!*pos)
  return NULL;
else if((*pos)->flags == FLAGS)
  return blah;

Clean, unambiguous, efficient.

Jens Gustedt