>From c3545bdddacc4974fff87f3f2632c03e96ebbf05 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 5 Dec 2020 15:54:22 -0800 Subject: [PATCH] intprops: Add INT_ADD_OK etc. * doc/intprops.texi (Checking Integer Overflow): New section. * lib/intprops.h: From a suggestion by Bruno Haible in: https://lists.gnu.org/r/bug-gnulib/2020-12/msg00051.html (SAFE_INT_ADD, SAFE_INT_SUBTRACT, SAFE_INT_MULTIPLY): New macros. --- ChangeLog | 6 +++ doc/intprops.texi | 109 +++++++++++++++++++++++++++++++++++++++++++++- lib/intprops.h | 29 ++++++++++++ 3 files changed, 143 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 8db9975f5..72895ac78 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2020-12-05 Paul Eggert + intprops: Add INT_ADD_OK etc. + * doc/intprops.texi (Checking Integer Overflow): New section. + * lib/intprops.h: From a suggestion by Bruno Haible in: + https://lists.gnu.org/r/bug-gnulib/2020-12/msg00051.html + (SAFE_INT_ADD, SAFE_INT_SUBTRACT, SAFE_INT_MULTIPLY): New macros. + doc: move exotic platfroms to Target Platforms * doc/gnulib-intro.texi (Supported Platforms) (Formerly Supported Platforms, Unsupported Platforms): diff --git a/doc/intprops.texi b/doc/intprops.texi index 294c235f5..f3a958a75 100644 --- a/doc/intprops.texi +++ b/doc/intprops.texi @@ -49,7 +49,8 @@ but it does not assume that signed integer arithmetic wraps around. @menu * Arithmetic Type Properties:: Determining properties of arithmetic types. * Integer Bounds:: Bounds on integer values and representations. -* Wraparound Arithmetic:: Well-defined behavior on signed overflow. +* Checking Integer Overflow:: Checking for overflow while computing integers. +* Wraparound Arithmetic:: Well-defined behavior on integer overflow. * Integer Type Overflow:: General integer overflow checking. * Integer Range Overflow:: Integer overflow checking if bounds are known. @end menu @@ -150,6 +151,107 @@ in_off_t_range (intmax_t a) @} @end example +@node Checking Integer Overflow +@subsection Checking Integer Overflow + +@cindex integer overflow checking + +Signed integer arithmetic has undefined behavior on overflow in C@. +Although almost all modern computers use two's complement signed +arithmetic that is well-defined to wrap around, C compilers routinely +optimize assuming that signed integer overflow cannot occur, which +means that a C program cannot easily get at the underlying machine +arithmetic. For example: + +@example +if ((a + b < b) == (a < 0)) + a += b; +else + print ("overflow"); +@end example + +@noindent +might not work as expected if @code{a} and @code{b} are signed, +because a compiler can assume that signed overflow cannot occur and +treat the entire @code{if} expression as if it were true. And even if +@code{a} is unsigned, the expression might not work as expected if +@code{b} is negative or is wider than @code{a}. + +The following macros work around this problem by returning an overflow +indication while computing the sum, difference, or product of two +integers. For example, if @code{i} is of type @code{int}, +@code{INT_ADD_OK (INT_MAX - 1, 1, &i)} sets @code{i} to +@code{INT_MAX} and returns true, whereas @code{INT_ADD_OK (INT_MAX, 1, +&i)} returns false. + +Example usage: + +@example +#include +#include + +/* Compute A * B, reporting whether overflow occurred. */ +void +print_product (long int a, long int b) +@{ + long int r; + if (INT_MULTIPLY_OK (a, b, r)) + printf ("result is %ld\n", r); + else + printf ("overflow\n"); +@} +@end example + +These macros work for both signed and unsigned integers, so they can +be used with integer types like @code{time_t} that may or may not be +signed, depending on the platform. + +These macros have the following restrictions: + +@itemize @bullet +@item +Their first two arguments must be integer expressions. + +@item +Their last argument must be a non-null pointer to an integer. + +@item +They may evaluate their arguments zero or multiple times, so the +arguments should not have side effects. + +@item +They are not necessarily constant expressions, even if all their +arguments are constant expressions. +@end itemize + +@table @code +@item INT_ADD_OK (@var{a}, @var{b}, @var{r}) +@findex INT_ADD_OK +Compute the sum of @var{a} and @var{b}. If it fits into +@code{*@var{r}}, store it there and return true. Otherwise return +false, possibly modifying @code{*@var{r}} to an unspecified value. +See above for restrictions. + +@item INT_SUBTRACT_OK (@var{a}, @var{b}, @var{r}) +@findex INT_SUBTRACT_OK +Compute the difference between @var{a} and @var{b}. If it fits into +@code{*@var{r}}, store it there and return true. Otherwise return +false, possibly modifying @code{*@var{r}} to an unspecified value. +See above for restrictions. + +@item INT_MULTIPLY_OK (@var{a}, @var{b}, @var{r}) +@findex INT_MULTIPLY_OK +Compute the product of @var{a} and @var{b}. If it fits into +@code{*@var{r}}, store it there and return true. Otherwise return +false, possibly modifying @code{*@var{r}} to an unspecified value. +See above for restrictions. +@end table + +Other macros are available if you need wrapped-around results when +overflow occurs (@pxref{Wraparound Arithmetic}), or if you need to +check for overflow in operations other than addition, subtraction, and +multiplication (@pxref{Integer Type Overflow}). + @node Wraparound Arithmetic @subsection Wraparound Arithmetic with Integers @@ -238,6 +340,11 @@ low-order bits are the mathematically-correct product. See above for restrictions. @end table +Other macros are available if you do not need wrapped-around results +when overflow occurs (@pxref{Checking Integer Overflow}), or if you +need to check for overflow in operations other than addition, +subtraction, and multiplication (@pxref{Integer Type Overflow}). + @node Integer Type Overflow @subsection Integer Type Overflow diff --git a/lib/intprops.h b/lib/intprops.h index cb086715b..8cc8138f9 100644 --- a/lib/intprops.h +++ b/lib/intprops.h @@ -591,4 +591,33 @@ : (tmin) / (a) < (b)) \ : (tmax) / (b) < (a))) +/* The following macros compute A + B, A - B, and A * B, respectively. + If no overflow occurs, they set *R to the result and return 1; + otherwise, they return 0 and may modify *R. + + Example usage: + + long int result; + if (INT_ADD_OK (a, b, &result)) + printf ("result is %ld\n", result); + else + printf ("overflow\n"); + + A, B, and *R should be integers; they need not be the same type, + and they need not be all signed or all unsigned. + + These macros work correctly on all known practical hosts, and do not rely + on undefined behavior due to signed arithmetic overflow. + + These macros are not constant expressions. + + These macros may evaluate their arguments zero or multiple times, so the + arguments should not have side effects. + + These macros are tuned for B being a constant. */ + +#define INT_ADD_OK(a, b, r) ! INT_ADD_WRAPV (a, b, r) +#define INT_SUBTRACT_OK(a, b, r) ! INT_SUBTRACT_WRAPV (a, b, r) +#define INT_MULTIPLY_OK(a, b, r) ! INT_MULTIPLY_WRAPV (a, b, r) + #endif /* _GL_INTPROPS_H */ -- 2.27.0