Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

industrial code (over flow...)

44 views
Skip to first unread message

G G

unread,
Jun 28, 2019, 10:46:54 PM6/28/19
to

are these checks still necessary or is there another way?

https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow

Like:

#include <limits.h>

void f(signed int si_a, signed int si_b)
{
signed int sum;

if (((si_b > 0) && (si_a > (INT_MAX - si_b))) ||
((si_b < 0) && (si_a < (INT_MIN - si_b))))
{
/* Handle error */
}
else
{
sum = si_a + si_b;
}

/* ... */
}

to protect against over flow like: (that may happen)

void func(signed int si_a, signed int si_b)
{
signed int sum = si_a + si_b;
/* ... */
}

the code above is taken from:

https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow

Alf P. Steinbach

unread,
Jun 28, 2019, 11:54:55 PM6/28/19
to
Checks are still necessary when there is a possibility of overflow that
one wishes to handle. C++ has not gained any support for overflow
handling of integer arithmetic.

However, the checking can be simplified, possibly more efficient, and
modulo late-late-night + full-of-painkillers I think this doesn't rely
on C++20 requirement of two's complement form:

inline auto sign( const int x ) -> bool { return x < 0; }

void func( const int a, const int b )
{
const int sum = static_cast<int>( 0u + a + b );
const bool overflow =
(sign( a ) == sign( b ) and sign( sum ) != sign( a );
// ...
}

Disclaimer: off the cuff code.

Also, formally the `static_cast` here has implementation defined
behavior, IIRC. But hey.

An alternative can be to use floating point and rely on NaN. But there
is danger in there. The standard library's facilities for checking NaN
are not good enough to deal with GCC and MSVC fast float optimizations.

Cheers!,

- Alf

David Brown

unread,
Jun 29, 2019, 5:56:19 PM6/29/19
to
Signed integer overflow is undefined behaviour in C++ (and C). There
are many ways to avoid overflowing, depending on the circumstances, the
operations you are doing, the target compiler (if portability is not a
concern), etc. In most cases, signed integer overflow is an error -
it's a case of garbage in, garbage out. And in most cases, a check like
you have made above is meaningless and inefficient because all it does
is check if the addition can be done with those types, without
overflowing. It is rare that you are adding two integers with unknown
origins, and unknown usage, and that adding them is all you are doing -
and thus rare that it makes sense to make a check like that. It is far
better to check for the validity of data when it comes in from the
outside, checking against sensible limits. And then make your
calculations valid for all sensible data (use bigger types if necessary).

G G

unread,
Jun 29, 2019, 7:03:08 PM6/29/19
to

> Signed integer overflow is undefined behaviour in C++ (and C). There
> are many ways to avoid overflowing, depending on the circumstances, the
> operations you are doing, the target compiler (if portability is not a
> concern), etc. In most cases, signed integer overflow is an error -
> it's a case of garbage in, garbage out. And in most cases, a check like
> you have made above is meaningless and inefficient because all it does
> is check if the addition can be done with those types, without
> overflowing. It is rare that you are adding two integers with unknown
> origins, and unknown usage, and that adding them is all you are doing -
> and thus rare that it makes sense to make a check like that. It is far
> better to check for the validity of data when it comes in from the
> outside, checking against sensible limits. And then make your
> calculations valid for all sensible data (use bigger types if necessary).

I'm sure you don't mean me per se , but only cause I'm asking and showing
the code. But to ensure I'm not taking undue credit, the code is from a CERT
website, www.securecoding.cert.org from a search INT32-CPP.
At that site, they show many other concerns and suggestions how to mitigate.

Jorgen Grahn

unread,
Jun 30, 2019, 1:59:02 AM6/30/19
to
On Sat, 2019-06-29, G G wrote:

[Attribution missing; I think David Brown wrote this]
I'm sure his advice was for you, me, and everyone, including the CERT
people. There are different schools of thought here, and has been for
as long as I can remember.

BTW, the code left out the most interesting part. It said:

/* Handle error */

If aborting isn't an option, it's really hard to fill in that part of
the function, and it's often impossible to get test coverage for it
in a full system test.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

G G

unread,
Jun 30, 2019, 5:43:46 AM6/30/19
to

>
> I'm sure his advice was for you, me, and everyone, including the CERT
> people.

I'm sure too.
I thank him, you, and everyone who helps me learn.

Chris M. Thomasson

unread,
Jun 30, 2019, 6:00:38 AM6/30/19
to
On 6/28/2019 7:46 PM, G G wrote:
>
> are these checks still necessary or is there another way?
[...]

Its been a while since I read the following, cannot remember if it goes
into overflows:

http://www.stroustrup.com/JSF-AV-rules.pdf

Öö Tiib

unread,
Jun 30, 2019, 10:19:42 AM6/30/19
to
On Saturday, 29 June 2019 05:46:54 UTC+3, G G wrote:
> are these checks still necessary or is there another way?
>
> https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow

Yes, quarter of possible signed addition or subtraction
operations are undefined behavior by standard.

There may be are non-standard compiler intrinsics that
help us to deal with overflows more simply or we can use
a type for result where every resulting value fits.

For example we can detect if distance between every two
signed longs fits into unsigned long with a compile-time
check:

static_assert(ULONG_MAX/2 >= LONG_MAX);

So we can luckily write a distance function that can never
fail if it compiles:

#include <iostream>
#include <limits.h>

unsigned long distance(long x, long y)
{
static_assert(ULONG_MAX/2 >= LONG_MAX);
return (x > y) ? (unsigned long)x - (unsigned long)y
: (unsigned long)y - (unsigned long)x;
}

int main()
{
std::cout << "Distance between 3 and -42 is "
<< distance(3, -42) << std::endl;
}

I prefer to use int64_t and uint64_t that guarantee that
UINT64_MAX/2 >= INT64_MAX so I don't need that
static_assert.

However quite common is that meaningful limits for a
number are rather different from what the integer type
of processor has.

For example we need to have variable that represents
reasonably reachable temperatures with accuracy of
one centi degree of Celsius.

The int32_t that we might want to use has range
-2147483648 to 2147483647 so most of that range will
be erroneous values.

Absolute zero is −273.15°C, can't go below that.
Temperatures within stars can reach millions of degrees
(thanks to pressure) but at surface of Sun we have "only"
5505°C. Therefore we should check limits like
-27315 to 600000 (far under INT32_MIN and INT32_MAX)
to figure if our values are outside of sanity limits or not.

Alf P. Steinbach

unread,
Jun 30, 2019, 10:44:53 AM6/30/19
to
On 30.06.2019 16:19, Öö Tiib wrote:
> On Saturday, 29 June 2019 05:46:54 UTC+3, G G wrote:
>> are these checks still necessary or is there another way?
>>
>> https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
>
> Yes, quarter of possible signed addition or subtraction
> operations are undefined behavior by standard.
>
> There may be are non-standard compiler intrinsics that
> help us to deal with overflows more simply or we can use
> a type for result where every resulting value fits.
>
> For example we can detect if distance between every two
> signed longs fits into unsigned long with a compile-time
> check:
>
> static_assert(ULONG_MAX/2 >= LONG_MAX);

Will always be true for two's complement form, by definition. floor(
(2^n)-1)/2 ) = floor( 2^(n-1) - 1/2 ) = 2^(n-1)-1, which is LONG_MAX.


> So we can luckily write a distance function that can never
> fail if it compiles:
>
> #include <iostream>
> #include <limits.h>
>
> unsigned long distance(long x, long y)
> {
> static_assert(ULONG_MAX/2 >= LONG_MAX);
> return (x > y) ? (unsigned long)x - (unsigned long)y
> : (unsigned long)y - (unsigned long)x;
> }

Yes, but the assertion isn't necessary. ;-)

The distance between two numbers in a 2^n range can never be 2^n or higher.


> int main()
> {
> std::cout << "Distance between 3 and -42 is "
> << distance(3, -42) << std::endl;
> }
>
> I prefer to use int64_t and uint64_t that guarantee that
> UINT64_MAX/2 >= INT64_MAX so I don't need that
> static_assert.
>
> However quite common is that meaningful limits for a
> number are rather different from what the integer type
> of processor has.
>
> For example we need to have variable that represents
> reasonably reachable temperatures with accuracy of
> one centi degree of Celsius.
>
> The int32_t that we might want to use has range
> -2147483648 to 2147483647 so most of that range will
> be erroneous values.
>
> Absolute zero is −273.15°C, can't go below that.
> Temperatures within stars can reach millions of degrees
> (thanks to pressure) but at surface of Sun we have "only"
> 5505°C. Therefore we should check limits like
> -27315 to 600000 (far under INT32_MIN and INT32_MAX)
> to figure if our values are outside of sanity limits or not.

And always think of Ariane 5.


Cheers!,

- Alf

Öö Tiib

unread,
Jun 30, 2019, 11:18:36 AM6/30/19
to
On Sunday, 30 June 2019 17:44:53 UTC+3, Alf P. Steinbach wrote:
> On 30.06.2019 16:19, Öö Tiib wrote:
> > For example we can detect if distance between every two
> > signed longs fits into unsigned long with a compile-time
> > check:
> >
> > static_assert(ULONG_MAX/2 >= LONG_MAX);
>
> Will always be true for two's complement form, by definition. floor(
> (2^n)-1)/2 ) = floor( 2^(n-1) - 1/2 ) = 2^(n-1)-1, which is LONG_MAX.

Yes but two's complement is not guaranteed for long.
Basically if we assume two's complement then we better
use types that guarantee it like I said later:

> > I prefer to use int64_t and uint64_t that guarantee that
> > UINT64_MAX/2 >= INT64_MAX so I don't need that
> > static_assert.

> And always think of Ariane 5.

You mean partially failed launch 25 January 2018? I
wasn't involved. :D Incorrect value in specifications.
That is hard to avoid in practice because infallible and
omnipotent humans are impossible to hire for whatever
money.

For example if to take and to let three separate teams to
write same software and then let the resulting programs to
vote each decision ... if the specs were defective then
these would likely suggest same, incorrect decision
unanimously.

Alf P. Steinbach

unread,
Jun 30, 2019, 12:22:27 PM6/30/19
to
On 30.06.2019 17:18, Öö Tiib wrote:
> On Sunday, 30 June 2019 17:44:53 UTC+3, Alf P. Steinbach wrote:
>> On 30.06.2019 16:19, Öö Tiib wrote:
>>> For example we can detect if distance between every two
>>> signed longs fits into unsigned long with a compile-time
>>> check:
>>>
>>> static_assert(ULONG_MAX/2 >= LONG_MAX);
>>
>> Will always be true for two's complement form, by definition. floor(
>> (2^n)-1)/2 ) = floor( 2^(n-1) - 1/2 ) = 2^(n-1)-1, which is LONG_MAX.
>
> Yes but two's complement is not guaranteed for long.

Oh it is.

No extant compiler uses anything else, and next year you get it
guaranteed from the standard too.


> Basically if we assume two's complement then we better
> use types that guarantee it like I said later:
>
>>> I prefer to use int64_t and uint64_t that guarantee that
>>> UINT64_MAX/2 >= INT64_MAX so I don't need that
>>> static_assert.
>
>> And always think of Ariane 5.
>
> You mean partially failed launch 25 January 2018? I
> wasn't involved. :D Incorrect value in specifications.
> That is hard to avoid in practice because infallible and
> omnipotent humans are impossible to hire for whatever
> money.
>
> For example if to take and to let three separate teams to
> write same software and then let the resulting programs to
> vote each decision ... if the specs were defective then
> these would likely suggest same, incorrect decision
> unanimously.
>

Cheers!

- Alf

David Brown

unread,
Jun 30, 2019, 4:23:05 PM6/30/19
to
Yes, I meant "you" in general, not just you personally.

I've seen that page. The sample code is silly and pointless, because it
solves a problem that doesn't exist while failing to address the problem
that /does/ exist.

Overflows (including unsigned overflows, which are also usually errors)
occur because people don't do a good enough job of checking data coming
in from outside, and don't check that their calculations will work over
the full range of the data they are using. Checking specifically over
the range of "int" is pointless.

David Brown

unread,
Jun 30, 2019, 4:24:28 PM6/30/19
to
(/Please/ use appropriate attributions. Even if you have to use the
appalling google groups interface instead of a proper newsreader and
newsserver, you should make the effort to keep attributions.)



David Brown

unread,
Jun 30, 2019, 4:25:42 PM6/30/19
to
> in a full system test.NoNoNo
>
> /Jorgen
>

That is a very important point - thanks for bringing it up. You (yous,
or y'all) should not include code that can't be tested.

0 new messages