Patchwork Implement -fsanitize=float-cast-overflow

login
register
mail settings
Submitter Marek Polacek
Date May 13, 2014, 5:08 p.m.
Message ID <20140513170801.GG2663@redhat.com>
Download mbox | patch
Permalink /patch/348477/
State New
Headers show

Comments

Marek Polacek - May 13, 2014, 5:08 p.m.
Here's an attempt to add the -fsanitize=float-cast-overflow
instrumentation.  It should issue a runtime error when a floating-point
to integer type conversion overflows.  Eventually it should instrument
even floating-point to floating-point conversions to detect e.g.
(float)1e39 overflow, but I'd like to settle first on float to int
before implementing that.

Current patch should detect e.g.
unsigned char uc = 250.0;
int i = (float) __INT_MAX__ + 1;
and similar.

In essence, the gist of this instrumentation is:
if (x u<= TYPE_MIN - 1.0 || x u>= TYPE_MAX + 1.0)
  __ubsan_builtin ();
this checks even +-Inf for free, and because the comparison is
unordered, it detects even NaNs.

The question is how this should interact with feenableexcept (FE_INVALID)
- currently it in some cases raises the Floating point exception, so I
didn't include that in the testsite...  I don't know floating-point
stuff well enough to judge what would be best.

Regtested/bootstrapped on x86_64.  Ubsan testsuite passes with
-m32/-m64 on x86_64/ppc64.  Ran bootstrap-ubsan.

2014-05-13  Marek Polacek  <polacek@redhat.com>

	* convert.c: Include "ubsan.h".
	(convert_to_integer): Instrument float to integer conversion.
	* flag-types.h (enum sanitize_code): Add SANITIZE_FLOAT_CAST and or
	it into SANITIZE_UNDEFINED.
	* opts.c (common_handle_option): Add -fsanitize=float-cast-overflow.
	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW,
	BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT): New built-ins.
	* ubsan.c (get_ubsan_type_info_for_type): Handle REAL_TYPEs.
	(ubsan_instrument_float_cast): New function.
	* ubsan.h (ubsan_instrument_float_cast): Declare.
	* doc/invoke.texi: Document -fsanitize=float-cast-overflow.

	* c-c++-common/ubsan/float-cast-overflow-1.c: New test.

	Marek
Jakub Jelinek - May 13, 2014, 5:38 p.m.
On Tue, May 13, 2014 at 07:08:01PM +0200, Marek Polacek wrote:
> In essence, the gist of this instrumentation is:
> if (x u<= TYPE_MIN - 1.0 || x u>= TYPE_MAX + 1.0)
>   __ubsan_builtin ();
> this checks even +-Inf for free, and because the comparison is
> unordered, it detects even NaNs.
> 
> The question is how this should interact with feenableexcept (FE_INVALID)
> - currently it in some cases raises the Floating point exception, so I
> didn't include that in the testsite...  I don't know floating-point
> stuff well enough to judge what would be best.

What kind of exceptions do you get and where?  For sNaN I guess an exception
should be expectable, not sure if we raise one when casting sNaN to integer
without instrumentation.

> --- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
> +++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
> @@ -0,0 +1,244 @@
> +/* { dg-do run } */

I'd say you should limit the test to ilp32 || lp64 targets.

> +/* { dg-options "-fsanitize=float-cast-overflow" } */
> +/* { dg-additional-options "-msse -mfpmath=sse" { target { { i?86-*-* x86_64-*-* } && ia32 } } } */

And this needs to be guarded by && sse2_runtime.  Supposedly
you should also pass -msse2 rather than -msse, the test uses double
rather than float.

	Jakub
Joseph S. Myers - May 13, 2014, 6:11 p.m.
On Tue, 13 May 2014, Marek Polacek wrote:

> Here's an attempt to add the -fsanitize=float-cast-overflow
> instrumentation.  It should issue a runtime error when a floating-point
> to integer type conversion overflows.  Eventually it should instrument

As with divide-by-zero, this should not be part of -fsanitize=undefined 
because under Annex F this is not undefined behavior (instead it raises 
"invalid" and returns an unspecified value, C11 F.4).

> even floating-point to floating-point conversions to detect e.g.
> (float)1e39 overflow, but I'd like to settle first on float to int
> before implementing that.

I think that should be a separate option.  If the type has infinities, 
it's not undefined even in the absence of Annex F (because infinities 
count as part of the range of the type).  And of course overflow depends 
on the rounding mode when the type of the result is a floating-point type.

(However, conversions of integers to floating point can probably count the 
same as conversions of floating point to floating point if you add such an 
option.  __int128 to float can overflow.)

It would be a good idea for the testcases to cover conversions to 
__int128 / unsigned __int128, where supported.

What do you do for overflowing conversions to bit-fields?  I think the 
correct rule is:

* For C, if the floating-point value, truncated toward 0, is outside the 
range of a signed or unsigned type of the specified number of bits, then 
you should get the (invalid, unspecified value (this isn't actually 
implemented in GCC)), and get a runtime error for the new option.

* For C++, bit-fields don't count as separate types, so it should act as 
converting to the declared type and then converting from that to the 
bit-field (as a modulo operation).

Thus, for an unsigned:1 bit-field, for example, values outside the 
interval (-1, 2) would produce the error for C, but only those outside 
(-1, 0x1p32) would do so for C++ (presuming 32-bit int).

> +  tree min = TYPE_MIN_VALUE (type);
> +  tree max = TYPE_MAX_VALUE (type);
> +  /* Add/subtract 1.0 so we can avoid truncating the value of EXPR.  */
> +  min = fold_build2 (MINUS_EXPR, expr_type,
> +		     build_real_from_int_cst (expr_type, min),
> +		     build_one_cst (expr_type));
> +  max = fold_build2 (PLUS_EXPR, expr_type,
> +		     build_real_from_int_cst (expr_type, max),
> +		     build_one_cst (expr_type));

It looks to me like this will first round the max value to the 
floating-point type, then add 1 to the rounded value and round again.  
Which I think is in fact safe at least for IEEE binary floating-point 
types, but that isn't immediately obvious.

Possible issues:

* Does the folding of the addition occur in all cases for IBM long double?

* Is this correct for decimal floating point?  There, the overflow 
condition (value >= max+1) should be using a value of (max+1) rounded 
upward rather than to-nearest, if max+1 isn't exactly representable (and 
in general it isn't - powers of two 0x1p24 and above aren't representable 
in decimal32, 0x1p54 and above in decimal64, 0x1p113 and above in 
decimal128, so you just need to find a case where the double-rounding 
computation you have produces the wrong value).

* Likewise, (value <= min-1) for both binary and decimal floating point - 
you need to round once, away from 0.  For float converted to signed int, 
the relevant condition is values < -0x1p31 - 1, i.e. <= 0x1.000002p31f 
once you allow for which values are representable as float, which is not 
min-1 (min-1 rounds to -0x1p31, but a conversion of that to signed int is 
fully defined with no exceptions).
Jakub Jelinek - May 14, 2014, 11:38 a.m.
On Tue, May 13, 2014 at 06:11:15PM +0000, Joseph S. Myers wrote:
> > +  tree min = TYPE_MIN_VALUE (type);
> > +  tree max = TYPE_MAX_VALUE (type);
> > +  /* Add/subtract 1.0 so we can avoid truncating the value of EXPR.  */
> > +  min = fold_build2 (MINUS_EXPR, expr_type,
> > +		     build_real_from_int_cst (expr_type, min),
> > +		     build_one_cst (expr_type));
> > +  max = fold_build2 (PLUS_EXPR, expr_type,
> > +		     build_real_from_int_cst (expr_type, max),
> > +		     build_one_cst (expr_type));
> 
> It looks to me like this will first round the max value to the 
> floating-point type, then add 1 to the rounded value and round again.  
> Which I think is in fact safe at least for IEEE binary floating-point 
> types, but that isn't immediately obvious.
> 
> Possible issues:
> 
> * Does the folding of the addition occur in all cases for IBM long double?
> 
> * Is this correct for decimal floating point?  There, the overflow 
> condition (value >= max+1) should be using a value of (max+1) rounded 
> upward rather than to-nearest, if max+1 isn't exactly representable (and 
> in general it isn't - powers of two 0x1p24 and above aren't representable 
> in decimal32, 0x1p54 and above in decimal64, 0x1p113 and above in 
> decimal128, so you just need to find a case where the double-rounding 
> computation you have produces the wrong value).
> 
> * Likewise, (value <= min-1) for both binary and decimal floating point - 
> you need to round once, away from 0.  For float converted to signed int, 
> the relevant condition is values < -0x1p31 - 1, i.e. <= 0x1.000002p31f 
> once you allow for which values are representable as float, which is not 
> min-1 (min-1 rounds to -0x1p31, but a conversion of that to signed int is 
> fully defined with no exceptions).

So what do you see as the way to handle this properly?
I mean, for REAL_MODE_FORMAT (TYPE_MODE (expr_type))->b == 2 supposedly to avoid issues with
rounding of the max we could just
  REAL_VALUE_TYPE maxval = dconst1;
  SET_REAL_EXP (&maxval, REAL_EXP (&maxval) + TYPE_PRECISION (type) - !TYPE_UNSIGNED (type));
  real_convert (&maxval, TYPE_MODE (expr_type), &maxval);
  max = build_real (expr_type, maxval);
or so, then supposedly max is always the smallest representable binary
floating point value above or equal to TYPE_MAX_VALUE + 1.0.
For the min value, if it is unsigned, then -1.0 is ok for all binary or
decimal floats, if it is signed, then supposedly we could do the above
with s/max/min/;s/dconst1/dconstm1/; and, after the real_convert
do inexact = real_arithmetic (&newminval, MINUS_EXPR, &minval, &dconst1);
if !inexact just min = build_real (expr_type, newminval); and be done with
it (the question is if for IBM double double this will DTRT for
LONG_LONG_MIN, which I think should be that the high double will contain
(double) LONG_LONG_MIN and the low double -1.0).  For inexact
(which should be the same thing as if result of real_arithmetic + real_convert
is the same as original minval) we need to subtract more than one, dunno if
we should just compute it from the REAL_EXP and precision, or just keep
subtracing powers of two until after real_convert it is no longer bitwise
identical to original minval.  We don't have anything close to
real_nextafter nor real_convert variant that can round for arbitrary
rounding modes.
Any preferences how to implement this?

For _Decimal*, no idea unfortunately, perhaps for the first iteration
ubsan should ignore decimal to int conversions.

	Jakub
Joseph S. Myers - May 14, 2014, 5:37 p.m.
On Wed, 14 May 2014, Jakub Jelinek wrote:

> So what do you see as the way to handle this properly?
> I mean, for REAL_MODE_FORMAT (TYPE_MODE (expr_type))->b == 2 supposedly to avoid issues with
> rounding of the max we could just
>   REAL_VALUE_TYPE maxval = dconst1;
>   SET_REAL_EXP (&maxval, REAL_EXP (&maxval) + TYPE_PRECISION (type) - !TYPE_UNSIGNED (type));
>   real_convert (&maxval, TYPE_MODE (expr_type), &maxval);
>   max = build_real (expr_type, maxval);
> or so, then supposedly max is always the smallest representable binary
> floating point value above or equal to TYPE_MAX_VALUE + 1.0.

Yes.  Either the power of 2 is exactly representable, or it rounds up to 
+Inf, and in either case >= (or unordered) is the right test.

> For the min value, if it is unsigned, then -1.0 is ok for all binary or
> decimal floats, if it is signed, then supposedly we could do the above

Yes.

> with s/max/min/;s/dconst1/dconstm1/; and, after the real_convert
> do inexact = real_arithmetic (&newminval, MINUS_EXPR, &minval, &dconst1);
> if !inexact just min = build_real (expr_type, newminval); and be done with
> it (the question is if for IBM double double this will DTRT for
> LONG_LONG_MIN, which I think should be that the high double will contain
> (double) LONG_LONG_MIN and the low double -1.0).  For inexact
> (which should be the same thing as if result of real_arithmetic + real_convert
> is the same as original minval) we need to subtract more than one, dunno if
> we should just compute it from the REAL_EXP and precision, or just keep
> subtracing powers of two until after real_convert it is no longer bitwise
> identical to original minval.  We don't have anything close to
> real_nextafter nor real_convert variant that can round for arbitrary
> rounding modes.
> Any preferences how to implement this?

In the inexact case but where the power of 2 is representable, you could 
always handle it as < min rather than <= min-1 - although computing the 
actual nextafter value based on the precision of the floating-point type 
shouldn't be hard and would allow <= to be used everywhere.

(If min overflows to -Inf, then <= -Inf is correct, use of < would be an 
incorrect test.)

> For _Decimal*, no idea unfortunately, perhaps for the first iteration
> ubsan should ignore decimal to int conversions.

Yes, that seems reasonable.  (Computing the exact max+1 or min-1 as an 
MPFR value and then using mpfr_snprintf (then decimal_real_from_string) 
would be one way of converting to decimal with a controlled rounding 
direction.)

Patch

diff --git gcc/convert.c gcc/convert.c
index 91c1da2..de9e4b3 100644
--- gcc/convert.c
+++ gcc/convert.c
@@ -32,6 +32,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-core.h"
 #include "target.h"
 #include "langhooks.h"
+#include "ubsan.h"
 
 /* Convert EXPR to some pointer or reference type TYPE.
    EXPR must be pointer, reference, integer, enumeral, or literal zero;
@@ -394,6 +395,7 @@  convert_to_integer (tree type, tree expr)
   tree intype = TREE_TYPE (expr);
   unsigned int inprec = element_precision (intype);
   unsigned int outprec = element_precision (type);
+  location_t loc = EXPR_LOCATION (expr);
 
   /* An INTEGER_TYPE cannot be incomplete, but an ENUMERAL_TYPE can
      be.  Consider `enum E = { a, b = (enum E) 3 };'.  */
@@ -844,7 +846,15 @@  convert_to_integer (tree type, tree expr)
       return build1 (CONVERT_EXPR, type, expr);
 
     case REAL_TYPE:
-      return build1 (FIX_TRUNC_EXPR, type, expr);
+      if (flag_sanitize & SANITIZE_FLOAT_CAST)
+	{
+	  expr = save_expr (expr);
+	  tree check = ubsan_instrument_float_cast (loc, type, expr);
+	  expr = build1 (FIX_TRUNC_EXPR, type, expr);
+	  return fold_build2 (COMPOUND_EXPR, TREE_TYPE (expr), check, expr);
+	}
+      else
+	return build1 (FIX_TRUNC_EXPR, type, expr);
 
     case FIXED_POINT_TYPE:
       return build1 (FIXED_CONVERT_EXPR, type, expr);
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index da9694c..85d8a10 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -5426,6 +5426,13 @@  Detect floating-point division by zero.  Unlike other similar options,
 @option{-fsanitize=undefined}, since floating-point division by zero can
 be a legitimate way of obtaining infinities and NaNs.
 
+@item -fsanitize=float-cast-overflow
+@opindex fsanitize=float-cast-overflow
+
+This option enables floating-point type to integer conversion checking.
+We check that the result of the conversion does not overflow.
+This option does not work well with @code{FE_INVALID} exceptions enabled.
+
 @item -fsanitize-recover
 @opindex fsanitize-recover
 By default @option{-fsanitize=undefined} sanitization (and its suboptions
diff --git gcc/flag-types.h gcc/flag-types.h
index caf4039..2965734 100644
--- gcc/flag-types.h
+++ gcc/flag-types.h
@@ -229,9 +229,11 @@  enum sanitize_code {
   SANITIZE_BOOL = 1 << 10,
   SANITIZE_ENUM = 1 << 11,
   SANITIZE_FLOAT_DIVIDE = 1 << 12,
+  SANITIZE_FLOAT_CAST = 1 << 13,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
 		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
+		       | SANITIZE_FLOAT_CAST
 };
 
 /* flag_vtable_verify initialization levels. */
diff --git gcc/opts.c gcc/opts.c
index f15852d..9d38690 100644
--- gcc/opts.c
+++ gcc/opts.c
@@ -1463,6 +1463,8 @@  common_handle_option (struct gcc_options *opts,
 	      { "enum", SANITIZE_ENUM, sizeof "enum" - 1 },
 	      { "float-divide-by-zero", SANITIZE_FLOAT_DIVIDE,
 		sizeof "float-divide-by-zero" - 1 },
+	      { "float-cast-overflow", SANITIZE_FLOAT_CAST,
+		sizeof "float-cast-overflow" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
diff --git gcc/sanitizer.def gcc/sanitizer.def
index 6184b5a..a2f7ff0 100644
--- gcc/sanitizer.def
+++ gcc/sanitizer.def
@@ -371,3 +371,11 @@  DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE_ABORT,
 		      "__ubsan_handle_load_invalid_value_abort",
 		      BT_FN_VOID_PTR_PTR,
 		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW,
+		      "__ubsan_handle_float_cast_overflow",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT,
+		      "__ubsan_handle_float_cast_overflow_abort",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
diff --git gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
index e69de29..a184541 100644
--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
@@ -0,0 +1,244 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+/* { dg-additional-options "-msse -mfpmath=sse" { target { { i?86-*-* x86_64-*-* } && ia32 } } } */
+
+#include <limits.h>
+
+int
+main (void)
+{
+  const double inf = __builtin_inf ();
+  const double nan = __builtin_nan ("");
+  volatile double d;
+
+#define CHECK_BOUNDARY(VAR, VAL)	\
+  (VAR) = (VAL) - 5.0;			\
+  (VAR) = (VAL) - 1.5;			\
+  (VAR) = (VAL) - 1.0;			\
+  (VAR) = (VAL) - 0.9999999;		\
+  (VAR) = (VAL) - 0.5;			\
+  (VAR) = (VAL) - 0.0000001;		\
+  (VAR) = (VAL) - 0.0;			\
+  (VAR) = (VAL);			\
+  (VAR) = (VAL) + 0.0;			\
+  (VAR) = (VAL) + 0.0000001;		\
+  (VAR) = (VAL) + 0.5;			\
+  (VAR) = (VAL) + 0.9999999;		\
+  (VAR) = (VAL) + 1.0;			\
+  (VAR) = (VAL) + 1.5;			\
+  (VAR) = (VAL) + 5.0;
+
+#define CHECK_NONNUMBERS(VAR)		\
+  (VAR) = nan;				\
+  (VAR) = -nan;				\
+  (VAR) = inf;				\
+  (VAR) = -inf;
+
+  volatile signed char sc;
+  d = SCHAR_MIN;
+  CHECK_BOUNDARY (sc, d);
+  d = 0.0;
+  CHECK_BOUNDARY (sc, d);
+  d = SCHAR_MAX;
+  CHECK_BOUNDARY (sc, d);
+  CHECK_NONNUMBERS (sc);
+
+  volatile unsigned char uc;
+  d = UCHAR_MAX;
+  CHECK_BOUNDARY (uc, d);
+  d = 0.0;
+  CHECK_BOUNDARY (uc, d);
+  CHECK_NONNUMBERS (uc);
+
+  volatile short int s;
+  d = SHRT_MIN;
+  CHECK_BOUNDARY (s, d);
+  d = 0.0;
+  CHECK_BOUNDARY (s, d);
+  d = SHRT_MAX;
+  CHECK_BOUNDARY (s, d);
+  CHECK_NONNUMBERS (s);
+
+  volatile unsigned short int us;
+  d = USHRT_MAX;
+  CHECK_BOUNDARY (us, d);
+  d = 0.0;
+  CHECK_BOUNDARY (us, d);
+  CHECK_NONNUMBERS (us);
+
+  volatile int i;
+  d = INT_MIN;
+  CHECK_BOUNDARY (i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (i, d);
+  d = INT_MAX;
+  CHECK_BOUNDARY (i, d);
+  CHECK_NONNUMBERS (i);
+
+  volatile unsigned int u;
+  d = UINT_MAX;
+  CHECK_BOUNDARY (u, d);
+  d = 0.0;
+  CHECK_BOUNDARY (u, d);
+  CHECK_NONNUMBERS (u);
+
+  volatile long l;
+  /* 64-bit vs 32-bit longs matter causes too much of a headache.  */
+  d = 0.0;
+  CHECK_BOUNDARY (l, d);
+  CHECK_NONNUMBERS (l);
+
+  volatile unsigned long ul;
+  d = 0.0;
+  CHECK_BOUNDARY (ul, d);
+  CHECK_NONNUMBERS (ul);
+
+  volatile long long ll;
+  d = LLONG_MIN;
+  CHECK_BOUNDARY (ll, d);
+  d = 0.0;
+  CHECK_BOUNDARY (ll, d);
+  d = LLONG_MAX;
+  CHECK_BOUNDARY (ll, d);
+  CHECK_NONNUMBERS (ll);
+
+  volatile unsigned long long ull;
+  d = ULLONG_MAX;
+  CHECK_BOUNDARY (ull, d);
+  d = 0.0;
+  CHECK_BOUNDARY (ull, d);
+  CHECK_NONNUMBERS (ull);
+
+  return 0;
+}
+
+/* { dg-output "value -133 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129.5 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128.5 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 132 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256.5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 260 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32773 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32769.5 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32769 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32768 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32768.5 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32772 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65536 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65536.5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65540 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/ubsan.c gcc/ubsan.c
index d9d740c..b8d724b 100644
--- gcc/ubsan.c
+++ gcc/ubsan.c
@@ -267,9 +267,14 @@  static unsigned short
 get_ubsan_type_info_for_type (tree type)
 {
   gcc_assert (TYPE_SIZE (type) && tree_fits_uhwi_p (TYPE_SIZE (type)));
-  int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
-  gcc_assert (prec != -1);
-  return (prec << 1) | !TYPE_UNSIGNED (type);
+  if (TREE_CODE (type) == REAL_TYPE)
+    return tree_to_uhwi (TYPE_SIZE (type));
+  else
+    {
+      int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
+      gcc_assert (prec != -1);
+      return (prec << 1) | !TYPE_UNSIGNED (type);
+    }
 }
 
 /* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type
@@ -891,6 +896,52 @@  instrument_bool_enum_load (gimple_stmt_iterator *gsi)
   gsi_insert_before (&gsi2, g, GSI_SAME_STMT);
 }
 
+/* Instrument float point-to-integer conversion.  TYPE is an integer type of
+   destination, EXPR is floating-point expression.  */
+
+tree
+ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
+{
+  tree expr_type = TREE_TYPE (expr);
+  tree t, tt, fn;
+
+  tree min = TYPE_MIN_VALUE (type);
+  tree max = TYPE_MAX_VALUE (type);
+  /* Add/subtract 1.0 so we can avoid truncating the value of EXPR.  */
+  min = fold_build2 (MINUS_EXPR, expr_type,
+		     build_real_from_int_cst (expr_type, min),
+		     build_one_cst (expr_type));
+  max = fold_build2 (PLUS_EXPR, expr_type,
+		     build_real_from_int_cst (expr_type, max),
+		     build_one_cst (expr_type));
+
+  if (flag_sanitize_undefined_trap_on_error)
+    fn = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
+  else
+    {
+      /* Create the __ubsan_handle_float_cast_overflow fn call.  */
+      tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data", NULL,
+				     NULL,
+				     ubsan_type_descriptor (expr_type, false),
+				     ubsan_type_descriptor (type, false),
+				     NULL_TREE);
+      enum built_in_function bcode
+	= flag_sanitize_recover
+	  ? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW
+	  : BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT;
+      fn = builtin_decl_explicit (bcode);
+      fn = build_call_expr_loc (loc, fn, 2,
+				build_fold_addr_expr_loc (loc, data),
+				ubsan_encode_value (expr, false));
+    }
+
+  t = fold_build2 (UNLE_EXPR, boolean_type_node, expr, min);
+  tt = fold_build2 (UNGE_EXPR, boolean_type_node, expr, max);
+  return fold_build3 (COND_EXPR, void_type_node,
+		      fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, tt),
+		      fn, integer_zero_node);
+}
+
 namespace {
 
 const pass_data pass_data_ubsan =
diff --git gcc/ubsan.h gcc/ubsan.h
index 67cc6e9..b008419 100644
--- gcc/ubsan.h
+++ gcc/ubsan.h
@@ -44,6 +44,7 @@  extern tree ubsan_type_descriptor (tree, bool);
 extern tree ubsan_encode_value (tree, bool = false);
 extern bool is_ubsan_builtin_p (tree);
 extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree);
+extern tree ubsan_instrument_float_cast (location_t, tree, tree);
 
 #endif  /* GCC_UBSAN_H  */