Re: Remove dependence on integer wrapping - Mailing list pgsql-hackers

From Joseph Koshakow
Subject Re: Remove dependence on integer wrapping
Date
Msg-id CAAvxfHdsdqgSoAbG-yMW186PcaicNQ2Dcr=7W8Misoy_y53fsg@mail.gmail.com
Whole thread Raw
In response to Re: Remove dependence on integer wrapping  (Andres Freund <andres@anarazel.de>)
Responses Re: Remove dependence on integer wrapping
List pgsql-hackers
>>               /* check the negative equivalent will fit without overflowing */
>>               if (unlikely(tmp > (uint16) (-(PG_INT16_MIN + 1)) + 1))
>>                       goto out_of_range;
>> +
>> +             /*
>> +              * special case the minimum integer because its negation cannot be
>> +              * represented
>> +              */
>> +             if (tmp == ((uint16) PG_INT16_MAX) + 1)
>> +                     return PG_INT16_MIN;
>>               return -((int16) tmp);
>
> My first impression is that there appears to be two overflow checks, one of
> which sends us to out_of_range, and another that just returns a special
> result.  Why shouldn't we add a pg_neg_s16_overflow() and replace this
> whole chunk with something like this?
>
>        if (unlikely(pg_neg_s16_overflow(tmp, &tmp)))
>                goto out_of_range;
>        else
>                return tmp;

tmp is an uint16 here, it seems like you might have read it as an
int16? We would need some helper method like

        static inline bool
        pg_neg_u16_overflow(uint16 a, int16 *result);

and then we could replace that whole chunk with something like

        if (unlikely(pg_neg_u16_overflow(tmp, &result)))
                goto out_of_range;
        else
                return result;


that pattern shows up a lot in this file, but I was worried that it
wasn't useful as a general purpose function. Happy to add it
though if you still feel otherwise.

>> +             return ((uint32) INT32_MAX) + 1;
>>
>> +             return ((uint64) INT64_MAX) + 1;
>
> nitpick: Any reason not to use PG_INT32_MAX/PG_INT64_MAX for these?

Carelessness, sorry about that, it's been fixed in the attached patch.

>> I believe this is a copy-and-paste from 841b4a2d5, which added this:
>>
>> +   *result = (date * INT64CONST(86400000000)) + time;
>> +   /* check for major overflow */
>> +   if ((*result - time) / INT64CONST(86400000000) != date)
>> +       return -1;
>> +   /* check for just-barely overflow (okay except time-of-day wraps) */
>> +   if ((*result < 0) ? (date >= 0) : (date < 0))
>> +       return -1;
>>
>> I think you could replace the whole thing by using overflow-aware
>> multiplication and addition primitives in the result calculation.
>> Lines 2-4 basically check for mult overflow and 5-7 for addition
>> overflow.
>
> Ah, I see.  Joe's patch does that in one place.  It's probably worth doing
> that in the other places this "just-barefly overflow" comment appears IMHO.
>
> I was still confused by the comment about 1999, but I tracked it down to
>commit 542eeba [0].  IIUC it literally means that we need special handling
>for that date because POSTGRES_EPOCH_JDATE is 2000-01-01.
>
> [0] https://postgr.es/m/CABUevEx5zUO%3DKRUg06a9qnQ_e9EvTKscL6HxAM_L3xj71R7AQw%40mail.gmail.com

> Yeah, I think so, and I think we probably don't need any special care
> if we switch to direct tests of overflow-aware primitives. (Though
>it'd be worth checking that '1999-12-31 24:00:00'::timestamp still
> works.  It doesn't look like I actually added a test case for that.)

The only other place I found this comment was in
`make_timestamp_internal`. I've updated that function and added some
tests. I also manually verified that the behavior matches before and
after this patch.

>> BTW, while I approve of trying to get rid of our need for -fwrapv,
>> I'm quite scared of actually doing it.
>
> I think that's a quite fair concern. One potentially relevant datapoint is
> that we actually don't have -fwrapv equivalent on all platforms, and I don't
>recall a lot of complaints from windows users. Of course it's quite possible
> that they'd never notice...
>
> I think this is a good argument for enabling -ftrapv in development
> builds. That gives us at least a *chance* of seeing these issues.

+1, I wouldn't recommend removing -fwrapv immediately after this
commit. However, if we can enable -ftrapv in development builds, then
we can find overflows much more easily.

> Whatever cases you may have discovered by running the regression tests will
> be at best the tip of the iceberg.

Agreed.

> Is there any chance of using static
> analysis to find all the places of concern?

I'm not personally familiar with any static analysis tools, but I can
try and do some research. Andres had previously suggested SQLSmith. I
think any kind of fuzz testing with -ftrapv enabled will reveal a lot
of issues. Honestly just grepping for +,-,* in certain directories
(like backend/utils/adt) would probably be fairly fruitful for anyone
with the patience. My previous overflow patch was the result of looking
through all the arithmetic in datetime.c.

Thanks,
Joe Koshakow
Attachment

pgsql-hackers by date:

Previous
From: Ashutosh Sharma
Date:
Subject: Re: Addressing SECURITY DEFINER Function Vulnerabilities in PostgreSQL Extensions
Next
From: Masahiko Sawada
Date:
Subject: Re: Revive num_dead_tuples column of pg_stat_progress_vacuum