From edb1c8215d849726adb8011a7dff9d38a73baa61 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 8 Nov 2024 18:35:52 +0900 Subject: [PATCH] Add integer overflow check macros for add/sub as well as mul --- configure.ac | 28 ++------- internal/bits.h | 82 ++++++++++++++++++++++++++ tool/m4/ruby_check_builtin_overflow.m4 | 28 +++++++++ 3 files changed, 114 insertions(+), 24 deletions(-) create mode 100644 tool/m4/ruby_check_builtin_overflow.m4 diff --git a/configure.ac b/configure.ac index d0745ea460..0128bf141b 100644 --- a/configure.ac +++ b/configure.ac @@ -20,6 +20,7 @@ RUBY_M4_INCLUDE([colorize_result.m4])dnl RUBY_M4_INCLUDE([ruby_append_option.m4])dnl RUBY_M4_INCLUDE([ruby_append_options.m4])dnl RUBY_M4_INCLUDE([ruby_check_builtin_func.m4])dnl +RUBY_M4_INCLUDE([ruby_check_builtin_overflow.m4])dnl RUBY_M4_INCLUDE([ruby_check_builtin_setjmp.m4])dnl RUBY_M4_INCLUDE([ruby_check_header.m4])dnl RUBY_M4_INCLUDE([ruby_check_printf_prefix.m4])dnl @@ -2297,10 +2298,6 @@ RUBY_CHECK_BUILTIN_FUNC(__builtin_clzl, [__builtin_clzl(0)]) RUBY_CHECK_BUILTIN_FUNC(__builtin_clzll, [__builtin_clzll(0)]) RUBY_CHECK_BUILTIN_FUNC(__builtin_ctz, [__builtin_ctz(0)]) RUBY_CHECK_BUILTIN_FUNC(__builtin_ctzll, [__builtin_ctzll(0)]) -RUBY_CHECK_BUILTIN_FUNC(__builtin_add_overflow, [int x;__builtin_add_overflow(0,0,&x)]) -RUBY_CHECK_BUILTIN_FUNC(__builtin_sub_overflow, [int x;__builtin_sub_overflow(0,0,&x)]) -RUBY_CHECK_BUILTIN_FUNC(__builtin_mul_overflow, [int x;__builtin_mul_overflow(0,0,&x)]) -RUBY_CHECK_BUILTIN_FUNC(__builtin_mul_overflow_p, [__builtin_mul_overflow_p(0,0,(int)0)]) RUBY_CHECK_BUILTIN_FUNC(__builtin_constant_p, [__builtin_constant_p(0)]) RUBY_CHECK_BUILTIN_FUNC(__builtin_choose_expr, [ [int x[__extension__(__builtin_choose_expr(1, 1, -1))]]; @@ -2316,26 +2313,9 @@ RUBY_CHECK_BUILTIN_FUNC(__builtin_types_compatible_p, [__builtin_types_compatibl RUBY_CHECK_BUILTIN_FUNC(__builtin_trap, [__builtin_trap()]) RUBY_CHECK_BUILTIN_FUNC(__builtin_expect, [__builtin_expect(0, 0)]) -AS_IF([test "$rb_cv_builtin___builtin_mul_overflow" != no], [ - AC_CACHE_CHECK(for __builtin_mul_overflow with long long arguments, rb_cv_use___builtin_mul_overflow_long_long, [ - AC_LINK_IFELSE([AC_LANG_SOURCE([[ -#pragma clang optimize off - -int -main(void) -{ - long long x = 0, y; - __builtin_mul_overflow(x, x, &y); - - return 0; -} -]])], - rb_cv_use___builtin_mul_overflow_long_long=yes, - rb_cv_use___builtin_mul_overflow_long_long=no)]) -]) -AS_IF([test "$rb_cv_use___builtin_mul_overflow_long_long" = yes], [ - AC_DEFINE(USE___BUILTIN_MUL_OVERFLOW_LONG_LONG, 1) -]) +RUBY_CHECK_BUILTIN_OVERFLOW(add) +RUBY_CHECK_BUILTIN_OVERFLOW(sub) +RUBY_CHECK_BUILTIN_OVERFLOW(mul) AS_IF([test "$ac_cv_func_qsort_r" != no], [ AC_CACHE_CHECK(whether qsort_r is GNU version, rb_cv_gnu_qsort_r, diff --git a/internal/bits.h b/internal/bits.h index 1fe98fa430..2b5aecf112 100644 --- a/internal/bits.h +++ b/internal/bits.h @@ -90,6 +90,7 @@ #define UNSIGNED_INTEGER_MAX(T) ((T)~(T)0) +#ifndef MUL_OVERFLOW_SIGNED_INTEGER_P #if __has_builtin(__builtin_mul_overflow_p) # define MUL_OVERFLOW_P(a, b) \ __builtin_mul_overflow_p((a), (b), (__typeof__(a * b))0) @@ -131,6 +132,87 @@ # define MUL_OVERFLOW_LONG_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, LONG_MIN, LONG_MAX) # define MUL_OVERFLOW_INT_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, INT_MIN, INT_MAX) #endif +#endif + +#ifndef ADD_OVERFLOW_SIGNED_INTEGER_P +#if __has_builtin(__builtin_add_overflow_p) +# define ADD_OVERFLOW_P(a, b) \ + __builtin_add_overflow_p((a), (b), (__typeof__(a * b))0) +#elif __has_builtin(__builtin_add_overflow) +# define ADD_OVERFLOW_P(a, b) \ + __extension__ ({ __typeof__(a) c; __builtin_add_overflow((a), (b), &c); }) +#endif + +#define ADD_OVERFLOW_SIGNED_INTEGER_P(a, b, min, max) ( \ + (a) > 0 ? (b) > (max) - (a) : (b) < (min) - (a)) + +#if __has_builtin(__builtin_add_overflow_p) +/* __builtin_add_overflow_p can take bitfield */ +/* and GCC permits bitfields for integers other than int */ +# define ADD_OVERFLOW_FIXNUM_P(a, b) \ + __extension__ ({ \ + struct { long fixnum : sizeof(long) * CHAR_BIT - 1; } c = { 0 }; \ + __builtin_add_overflow_p((a), (b), c.fixnum); \ + }) +#else +# define ADD_OVERFLOW_FIXNUM_P(a, b) \ + ADD_OVERFLOW_SIGNED_INTEGER_P(a, b, FIXNUM_MIN, FIXNUM_MAX) +#endif + +#if defined(ADD_OVERFLOW_P) && defined(USE___BUILTIN_ADD_OVERFLOW_LONG_LONG) +# define ADD_OVERFLOW_LONG_LONG_P(a, b) ADD_OVERFLOW_P(a, b) +#else +# define ADD_OVERFLOW_LONG_LONG_P(a, b) ADD_OVERFLOW_SIGNED_INTEGER_P(a, b, LLONG_MIN, LLONG_MAX) +#endif + +#ifdef ADD_OVERFLOW_P +# define ADD_OVERFLOW_LONG_P(a, b) ADD_OVERFLOW_P(a, b) +# define ADD_OVERFLOW_INT_P(a, b) ADD_OVERFLOW_P(a, b) +#else +# define ADD_OVERFLOW_LONG_P(a, b) ADD_OVERFLOW_SIGNED_INTEGER_P(a, b, LONG_MIN, LONG_MAX) +# define ADD_OVERFLOW_INT_P(a, b) ADD_OVERFLOW_SIGNED_INTEGER_P(a, b, INT_MIN, INT_MAX) +#endif +#endif + +#ifndef SUB_OVERFLOW_SIGNED_INTEGER_P +#if __has_builtin(__builtin_sub_overflow_p) +# define SUB_OVERFLOW_P(a, b) \ + __builtin_sub_overflow_p((a), (b), (__typeof__(a * b))0) +#elif __has_builtin(__builtin_sub_overflow) +# define SUB_OVERFLOW_P(a, b) \ + __extension__ ({ __typeof__(a) c; __builtin_sub_overflow((a), (b), &c); }) +#endif + +#define SUB_OVERFLOW_SIGNED_INTEGER_P(a, b, min, max) ( \ + (b) > 0 ? (a) < (min) + (b) : (a) > (max) + (b)) + +#if __has_builtin(__builtin_sub_overflow_p) +/* __builtin_sub_overflow_p can take bitfield */ +/* and GCC permits bitfields for integers other than int */ +# define SUB_OVERFLOW_FIXNUM_P(a, b) \ + __extension__ ({ \ + struct { long fixnum : sizeof(long) * CHAR_BIT - 1; } c = { 0 }; \ + __builtin_sub_overflow_p((a), (b), c.fixnum); \ + }) +#else +# define SUB_OVERFLOW_FIXNUM_P(a, b) \ + SUB_OVERFLOW_SIGNED_INTEGER_P(a, b, FIXNUM_MIN, FIXNUM_MAX) +#endif + +#if defined(SUB_OVERFLOW_P) && defined(USE___BUILTIN_SUB_OVERFLOW_LONG_LONG) +# define SUB_OVERFLOW_LONG_LONG_P(a, b) SUB_OVERFLOW_P(a, b) +#else +# define SUB_OVERFLOW_LONG_LONG_P(a, b) SUB_OVERFLOW_SIGNED_INTEGER_P(a, b, LLONG_MIN, LLONG_MAX) +#endif + +#ifdef SUB_OVERFLOW_P +# define SUB_OVERFLOW_LONG_P(a, b) SUB_OVERFLOW_P(a, b) +# define SUB_OVERFLOW_INT_P(a, b) SUB_OVERFLOW_P(a, b) +#else +# define SUB_OVERFLOW_LONG_P(a, b) SUB_OVERFLOW_SIGNED_INTEGER_P(a, b, LONG_MIN, LONG_MAX) +# define SUB_OVERFLOW_INT_P(a, b) SUB_OVERFLOW_SIGNED_INTEGER_P(a, b, INT_MIN, INT_MAX) +#endif +#endif #ifdef HAVE_UINT128_T # define bit_length(x) \ diff --git a/tool/m4/ruby_check_builtin_overflow.m4 b/tool/m4/ruby_check_builtin_overflow.m4 new file mode 100644 index 0000000000..8568d2c6d9 --- /dev/null +++ b/tool/m4/ruby_check_builtin_overflow.m4 @@ -0,0 +1,28 @@ +dnl -*- Autoconf -*- +AC_DEFUN([RUBY_CHECK_BUILTIN_OVERFLOW], [dnl +{ # $0($1) + RUBY_CHECK_BUILTIN_FUNC(__builtin_[$1]_overflow, [int x;__builtin_[$1]_overflow(0,0,&x)]) + RUBY_CHECK_BUILTIN_FUNC(__builtin_[$1]_overflow_p, [__builtin_[$1]_overflow_p(0,0,(int)0)]) + + AS_IF([test "$rb_cv_builtin___builtin_[$1]_overflow" != no], [ + AC_CACHE_CHECK(for __builtin_[$1]_overflow with long long arguments, rb_cv_use___builtin_[$1]_overflow_long_long, [ + AC_LINK_IFELSE([AC_LANG_SOURCE([[ +@%:@pragma clang optimize off + +int +main(void) +{ + long long x = 0, y; + __builtin_$1_overflow(x, x, &y); + + return 0; +} +]])], + rb_cv_use___builtin_[$1]_overflow_long_long=yes, + rb_cv_use___builtin_[$1]_overflow_long_long=no)]) + ]) + AS_IF([test "$rb_cv_use___builtin_[$1]_overflow_long_long" = yes], [ + AC_DEFINE(USE___BUILTIN_[]AS_TR_CPP($1)_OVERFLOW_LONG_LONG, 1) + ]) +} +])dnl