You be (UB) or not you be 
Author Message
 You be (UB) or not you be

I recently made the decision to invest some of my hard earned (?) money
into something useful, and spent a few bucks (yes, over there, in the
US) into C Unleashed.
After a week of reading I have reached Chapter 7, Code mending, where
among other useful advice, Richard Heathfield recommends the use of a
lint program.
So I did follow the recommendation, downloaded splint, and ran it on
some of my code (omigosh ! 90 warnings on only 300 lines of code...).
Among other things, splint was complaining about the statement marked in
the following function:

#include <math.h>

static double getEntropy(/*some param*/)
{
   double prob;

   /* code to assign prob an adequate value, i.e. strictly greater than
    * zero(or, at least, I assume so) goes here.
    */

   return - prob * log(prob) / log(2); /* <-- splint complains here */
   /* actually, it is right here ^ on the log(2), for those with a fixed
    * width font.
    */

Quote:
}

Now I first of all would like to know if someone is able to guess what
exactly splint is complaining about, because it seems very far-fetched
to me. Don't cheat, answer below... Tell me honestly,

The complaint is as follows: splint thinks that statement invokes
undefined behaviour, because "left operand uses errno, modified by right
operand". It goes on explaining that it thinks that there is undefined
behaviour because there is no intervening sequence point between
modifications on errno by log(prob) and log(2).

I am actually very surprised at this (wouldn't the log function call be
an intervening sequence point ? or maybe is it just a macro, or maybe we
don't know), so I'd like to have some enlightened opinion about it.

At any rate, if it is indeed a case of UB, I thought it worth
mentionning as a (IMHO) rather far-fetched example.

--
Bertrand Mollinier Toublet
Currently looking for employment in the San Francisco Bay area
http://www.*-*-*.com/ ~mollinie



Sun, 05 Jun 2005 12:49:35 GMT  
 You be (UB) or not you be
[snip]

Quote:
>   return - prob * log(prob) / log(2); /* <-- splint complains here */
>   /* actually, it is right here ^ on the log(2), for those with a fixed
>    * width font.
>    */
> }

> Now I first of all would like to know if someone is able to guess what
> exactly splint is complaining about, because it seems very far-fetched
> to me. Don't cheat, answer below... Tell me honestly,

> The complaint is as follows: splint thinks that statement invokes
> undefined behaviour, because "left operand uses errno, modified by right
> operand". It goes on explaining that it thinks that there is undefined
> behaviour because there is no intervening sequence point between
> modifications on errno by log(prob) and log(2).

You won't be able to catch an error generated by log() (it might set
errno).  The order of execution of each function call is implementation
defined as well...

Quote:
> I am actually very surprised at this (wouldn't the log function call be
> an intervening sequence point ? or maybe is it just a macro, or maybe we
> don't know), so I'd like to have some enlightened opinion about it.

> At any rate, if it is indeed a case of UB, I thought it worth
> mentionning as a (IMHO) rather far-fetched example.

It's only a problem if log(prob) raises an error (which you don't check
and couldn't reliably check w/o computing intermediary values).
;-)


Sun, 05 Jun 2005 14:50:30 GMT  
 You be (UB) or not you be

Quote:


> [snip]

>>   return - prob * log(prob) / log(2); /* <-- splint complains here */
>>   /* actually, it is right here ^ on the log(2), for those with a fixed
>>    * width font.
>>    */
>> }

>> The complaint is as follows: splint thinks that statement invokes
>> undefined behaviour, because "left operand uses errno, modified by
>> right operand". It goes on explaining that it thinks that there is
>> undefined behaviour because there is no intervening sequence point
>> between modifications on errno by log(prob) and log(2).

> You won't be able to catch an error generated by log() (it might set
> errno).  The order of execution of each function call is implementation
> defined as well...

Ok, but where is the UB ? Splint seems to indicate that it is caused by
errno being modified twice without an intervening sequence point, while
I argue that the intervening sequence point is the function call to log.

As I said:

Quote:
>> I am actually very surprised at this (wouldn't the log function call
>> be an intervening sequence point ? or maybe is it just a macro, or
>> maybe we don't know), so I'd like to have some enlightened opinion
>> about it.

--
Bertrand Mollinier Toublet
Currently looking for employment in the San Francisco Bay area
http://www-eleves.enst-bretagne.fr/~mollinie


Sun, 05 Jun 2005 14:53:49 GMT  
 You be (UB) or not you be

Quote:
> Ok, but where is the UB ? Splint seems to indicate that it is caused
> by errno being modified twice without an intervening sequence point,
> while I argue that the intervening sequence point is the function call
> to log.

You missed footnote 156 in C99, which talks about macro
replacements for library functions:

     156) Such macros might not contain the sequence points that
          the corresponding function calls do.

(I know that footnotes are not normative.  I assume that this
footnote is properly implied by the normative text; I see no
reason that this is not true.)

You can avoid the problem by undefining any library functions
that might have macro replacements:

        #undef log
--
"A lesson for us all: Even in trivia there are traps."
--Eric Sosman



Sun, 05 Jun 2005 14:59:39 GMT  
 You be (UB) or not you be

Quote:


>> Ok, but where is the UB ? Splint seems to indicate that it is caused
>> by errno being modified twice without an intervening sequence point,
>> while I argue that the intervening sequence point is the function call
>> to log.

> You missed footnote 156 in C99, which talks about macro
> replacements for library functions:

>     156) Such macros might not contain the sequence points that
>          the corresponding function calls do.

> (I know that footnotes are not normative.  I assume that this
> footnote is properly implied by the normative text; I see no
> reason that this is not true.)

I think that splint is wrong. The `log' implementation, no matter if
it is a function or a macro, is *not allowed* to modify `errno' unless
there really is an error (see 7.12.1). Therefore, the expression

  log (prob) / log (2)

modifies `errno' at most once. OTOH,

  log (foo) / log (bar)

might (to my understanding of the standard) indeed cause UB, if and
only if both `foo' and `bar' have values that cause an error in the
evaluation of the `log' function/macro.

Martin



Sun, 05 Jun 2005 21:30:58 GMT  
 You be (UB) or not you be

Quote:
> You won't be able to catch an error generated by log() (it might set
> errno). The order of execution of each function call is
> implementation defined as well...

Minor point: it is not implementation-defined, but unspecified
(see 6.5 #3).

Martin



Sun, 05 Jun 2005 21:37:59 GMT  
 You be (UB) or not you be
Quote:



>> [snip]

>>>   return - prob * log(prob) / log(2); /* <-- splint complains here */
>>>   /* actually, it is right here ^ on the log(2), for those with a fixed
>>>    * width font.
>>>    */
>>> }

>>> The complaint is as follows: splint thinks that statement invokes
>>> undefined behaviour, because "left operand uses errno, modified by
>>> right operand". It goes on explaining that it thinks that there is
>>> undefined behaviour because there is no intervening sequence point
>>> between modifications on errno by log(prob) and log(2).

>> You won't be able to catch an error generated by log() (it might set
>> errno).  The order of execution of each function call is implementation
>> defined as well...

> Ok, but where is the UB ? Splint seems to indicate that it is caused by
> errno being modified twice without an intervening sequence point, while
> I argue that the intervening sequence point is the function call to log.

It is not defined which one of the two calls to log() gets the final
word on what errno should be, because as Eric pointed out, the order of
execution of the calls is implementation defined

To quote K&R (pg. 52 buttom):
C, like most languages, does not specify the order in which the operands
of an operator are evaluated. (The exceptions are &&, ||, ?;, and ','.)

Apparantly there is no sequence point between the operands of an
operator even if the operands contain function calls.



Sun, 05 Jun 2005 22:23:52 GMT  
 You be (UB) or not you be

Quote:



> >> Ok, but where is the UB ? Splint seems to indicate that it is caused
> >> by errno being modified twice without an intervening sequence point,
> >> while I argue that the intervening sequence point is the function call
> >> to log.

> > You missed footnote 156 in C99, which talks about macro
> > replacements for library functions:

> >     156) Such macros might not contain the sequence points that
> >          the corresponding function calls do.

> I think that splint is wrong. The `log' implementation, no matter if
> it is a function or a macro, is *not allowed* to modify `errno' unless
> there really is an error (see 7.12.1). [...]

Really?  I don't see where 7.12.1 says that.  Ordinarily, library
functions are allowed to modify errno whether there is an error
or not, as 7.5#3 says:

     The value of errno may be set to nonzero by a library
     function call whether or not there is an error, provided the
     use of errno is not documented in the description of the
     function in this International Standard.

I can see where 7.12.1 specifies particular errors that can
occur, and what happens in those cases, but I don't see it
prohibiting other errors and I don't see it prohibiting the
setting of errno should no error have occurred at all.
--
"When I have to rely on inadequacy, I prefer it to be my own."
--Richard Heathfield



Mon, 06 Jun 2005 00:43:07 GMT  
 You be (UB) or not you be

Quote:


>> I think that splint is wrong. The `log' implementation, no matter if
>> it is a function or a macro, is *not allowed* to modify `errno' unless
>> there really is an error (see 7.12.1). [...]

> Really?  I don't see where 7.12.1 says that.  Ordinarily, library
> functions are allowed to modify errno whether there is an error
> or not, as 7.5#3 says:

>     The value of errno may be set to nonzero by a library
>     function call whether or not there is an error, provided the
>     use of errno is not documented in the description of the
>     function in this International Standard.

Doesn't the last part (`provided the use of errno is not documented in
the description of the function') apply here, because 7.12.1 /does/
document the use of errno for the math functions?

If I'm wrong, then what is the meaning of this provision?

Martin



Mon, 06 Jun 2005 06:03:55 GMT  
 You be (UB) or not you be

Quote:



> >> I think that splint is wrong. The `log' implementation, no matter if
> >> it is a function or a macro, is *not allowed* to modify `errno' unless
> >> there really is an error (see 7.12.1). [...]

> > Really?  I don't see where 7.12.1 says that.  Ordinarily, library
> > functions are allowed to modify errno whether there is an error
> > or not, as 7.5#3 says:

> >     The value of errno may be set to nonzero by a library
> >     function call whether or not there is an error, provided the
> >     use of errno is not documented in the description of the
> >     function in this International Standard.

> Doesn't the last part (`provided the use of errno is not documented in
> the description of the function') apply here, because 7.12.1 /does/
> document the use of errno for the math functions?

Hmm.  You may be right.  I had interpreted that to mean that
other errno values were allowed as long as the function
description didn't say that they weren't, but come to think of
it, your interpretation seems more likely.  If you're concerned
about it, you may want to ask the real experts in comp.std.c.
--
"When in doubt, treat ``feature'' as a pejorative.
 (Think of a hundred-bladed Swiss army knife.)"
--Kernighan and Plauger, _Software Tools_


Mon, 06 Jun 2005 06:07:52 GMT  
 You be (UB) or not you be

Quote:

>    return - prob * log(prob) / log(2); /* <-- splint complains here */

> The complaint is as follows: splint thinks that statement invokes
> undefined behaviour, because "left operand uses errno, modified by right
> operand". It goes on explaining that it thinks that there is undefined
> behaviour because there is no intervening sequence point between
> modifications on errno by log(prob) and log(2).

     This complaint is completely bogus, because it is the
implementation that modifies errno, not the C program.  And a
conforming implementation needs not be written in portable C.

Tak-Shing



Mon, 06 Jun 2005 06:13:10 GMT  
 
 [ 11 post ] 

 Relevant Pages 

1. I am new to programming and am lost

2. I am not quite understand the program asks for

3. I am not quite understand the program asks for

4. Why am I not getting correct position?

5. I still am not sure what pragma is

6. Why am I not intercepting Enter key (VK_RETURN)

7. I am not able to see my database class members in class view - Please help

8. Why am I not getting a LIB?

9. What message am I not handling?

10. Hey Friends I am Not Able To View the Messages Posted

11. Help a Newbie at Visual C++ (I am not a newbie at C++)

12. how good am I? Am I Good Enough????

 

 
Powered by phpBB® Forum Software