Clean 'make check' against gcc -fsanitize=address,undefined

Richard Kettlewell rjk at greenend.org.uk
Tue Jun 23 22:28:33 UTC 2015


On 23/06/2015 21:40, Julien ÉLIE wrote:
> Hi Richard,
> 
>> Make sure timer arithmetic is done in unsigned types
>>
>> Some care is required.  The divison must be done in the signed type
>> (otherwise the answer is wrong if usec<0) but any operation that
>> can overflow (+ and *) must be done in an unsigned type.
>>
>> +    long usec, msec;
>>
>> -    now = (tv.tv_sec - base.tv_sec) * 1000;
>> -    now += (tv.tv_usec - base.tv_usec) / 1000;
>> +    now = (unsigned long)(tv.tv_sec - base.tv_sec) * 1000u;
>> +    usec = tv.tv_usec - base.tv_usec; /* maybe -ve */
>> +    msec = usec / 1000;               /* still maybe -ve */
>> +    now += (unsigned long)msec;
> 
> Are we obliged to use both usec and msec?
> 
> As I understand the operation, there is an implicit conversion
> of signed long to unsigned long when the numerator and the denominator
> are not both signed or both unsigned.
> 
> The current code is:
> now = (tv.tv_sec - base.tv_sec) * 1000;
> now += (tv.tv_usec - base.tv_usec) / 1000;
> 
> As now is unsigned, the result of the * of the 2 signed numbers
> is cast to unsigned in the first line.

That doesn't help.  In the current code the multiplication is done in
the signed type.  If it overflows (as it will after a few weeks, on
32-bit platforms) then we are already into undefined behavior (C99 s6.5#5).

The subsequent conversion to an unsigned type does not make any
difference - the 'working type' for the multiplication is determined by
the operands alone, not by the treatment of the result.

> The / of the 2 signed numbers is a signed number that is then added
> to now.
> I do not understand what is wrong here.  Please tell where I am wrong.
> 
> 
> 
> If I follow your suggestion with tv=2,2s and base=1,4s:
> 
> now = (unsigned long)(tv.tv_sec - base.tv_sec) * 1000u;
> usec = tv.tv_usec - base.tv_usec; /* maybe -ve */
> msec = usec / 1000; /* still maybe -ve */
> now += (unsigned long)msec;
> I believe it would result in:
> now = 1000 + (-200+UINT_MAX+1) which would overflow because
> of the negative msec being converted to unsigned, instead of:

Overflow behavior for unsigned types (including conversion of negative
values to unsigned types) is defined: the result is the residue modulo
2^n (C99 s6.2.5#9).  The result with the patch is therefore 800.

ttfn/rjk



More information about the inn-workers mailing list