Patchwork Implement -fsanitize=float-divide-by-zero

login
register
mail settings
Submitter Marek Polacek
Date April 29, 2014, 9:34 a.m.
Message ID <20140429093450.GA11802@redhat.com>
Download mbox | patch
Permalink /patch/343735/
State New
Headers show

Comments

Marek Polacek - April 29, 2014, 9:34 a.m.
On Tue, Apr 29, 2014 at 10:46:06AM +0200, Jakub Jelinek wrote:
> On Tue, Apr 29, 2014 at 10:27:58AM +0200, Tobias Burnus wrote:
> > Thus, I think Fortran users would also prefer not to have
> > -fsanitize=undefined implying trapping dividing by zero.
> > 
> > Thus, I wonder whether it wouldn't be more useful to provide a command-line option
> > to make the floating-point exceptions signalling - and to remind the users to link
> > libbacktrace to get the trace.
> 
> That can be done independently with a different option, but -fsanitize=...
> IMNSHO definitely shouldn't do anything with floating point control
> registers.
> 
> To my surprise, the wording in C99 or C++11 make even floating point
> division by zero undefined behavior, but I think generally at least for IEEE
> floating point semantics it is well defined, thus I think we shouldn't
> include it in -fsanitize=undefined.

Thanks for comments.  The following is thus implementation of
-fsanitize=float-divide-by-zero that is not enabled by default.  For
this to work well I had to uglify builtins.def and gcc.c a little bit
(hopefully this will be the sole ubsan option that is not included in
SANITIZE_UNDEFINED).

Ran ubsan testsuite (-m32/-m64) + bootstrap-ubsan on x86_64-linux, ok now?

2014-04-29  Marek Polacek  <polacek@redhat.com>

	* gcc.c (sanitize_spec_function): Handle SANITIZE_FLOAT_DIVIDE.
	* builtins.def: Initialize builtins even for SANITIZE_FLOAT_DIVIDE.
	* flag-types.h (enum sanitize_code): Add SANITIZE_FLOAT_DIVIDE.
	* opts.c (common_handle_option): Add -fsanitize=float-divide-by-zero.
c-family/
	* c-ubsan.c (ubsan_instrument_division): Handle REAL_TYPEs.  Perform
	INT_MIN / -1 sanitization only for integer types.
c/
	* c-typeck.c (build_binary_op): Call ubsan_instrument_division
	also when SANITIZE_FLOAT_DIVIDE is on.
cp/
	* typeck.c (cp_build_binary_op): Call ubsan_instrument_division
	even when SANITIZE_FLOAT_DIVIDE is on.  Set doing_div_or_mod even
	for non-integer types.
testsuite/
	* c-c++-common/ubsan/div-by-zero-5.c: Fix formatting.
	* c-c++-common/ubsan/float-div-by-zero-1.c: New test.


	Marek
Jakub Jelinek - April 30, 2014, 6:55 a.m.
On Tue, Apr 29, 2014 at 11:34:50AM +0200, Marek Polacek wrote:
> Ran ubsan testsuite (-m32/-m64) + bootstrap-ubsan on x86_64-linux, ok now?
> 
> 2014-04-29  Marek Polacek  <polacek@redhat.com>
> 
> 	* gcc.c (sanitize_spec_function): Handle SANITIZE_FLOAT_DIVIDE.
> 	* builtins.def: Initialize builtins even for SANITIZE_FLOAT_DIVIDE.
> 	* flag-types.h (enum sanitize_code): Add SANITIZE_FLOAT_DIVIDE.
> 	* opts.c (common_handle_option): Add -fsanitize=float-divide-by-zero.
> c-family/
> 	* c-ubsan.c (ubsan_instrument_division): Handle REAL_TYPEs.  Perform
> 	INT_MIN / -1 sanitization only for integer types.
> c/
> 	* c-typeck.c (build_binary_op): Call ubsan_instrument_division
> 	also when SANITIZE_FLOAT_DIVIDE is on.
> cp/
> 	* typeck.c (cp_build_binary_op): Call ubsan_instrument_division
> 	even when SANITIZE_FLOAT_DIVIDE is on.  Set doing_div_or_mod even
> 	for non-integer types.
> testsuite/
> 	* c-c++-common/ubsan/div-by-zero-5.c: Fix formatting.
> 	* c-c++-common/ubsan/float-div-by-zero-1.c: New test.

> +int
> +main (void)
> +{
> +  volatile float a = 1.3f;
> +  volatile double b = 0.0;
> +  volatile int c = 4;
> +
> +  a / b;
> +  a / 0.0;
> +  2.7f / b;
> +  3.6 / (b = 0.0, b);
> +  c / b;
> +  b / c;

Please assign the result of the divisions to some other volatile variables,
otherwise I don't see why the compiler couldn't optimize them away all.

Otherwise looks good to me.

	Jakub
Marek Polacek - April 30, 2014, 7:35 a.m.
On Wed, Apr 30, 2014 at 08:55:10AM +0200, Jakub Jelinek wrote:
> Please assign the result of the divisions to some other volatile variables,
> otherwise I don't see why the compiler couldn't optimize them away all.
> 
> Otherwise looks good to me.

Done, thanks.

	Marek

Patch

diff --git gcc/builtins.def gcc/builtins.def
index 5b902d8..d400ecb 100644
--- gcc/builtins.def
+++ gcc/builtins.def
@@ -176,7 +176,7 @@  along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
-				| SANITIZE_UNDEFINED)))
+				| SANITIZE_UNDEFINED | SANITIZE_FLOAT_DIVIDE)))
 
 #undef DEF_CILKPLUS_BUILTIN
 #define DEF_CILKPLUS_BUILTIN(ENUM, NAME, TYPE, ATTRS)  \
diff --git gcc/c-family/c-ubsan.c gcc/c-family/c-ubsan.c
index e4f6f32..a039792 100644
--- gcc/c-family/c-ubsan.c
+++ gcc/c-family/c-ubsan.c
@@ -46,15 +46,21 @@  ubsan_instrument_division (location_t loc, tree op0, tree op1)
   gcc_assert (TYPE_MAIN_VARIANT (TREE_TYPE (op0))
 	      == TYPE_MAIN_VARIANT (TREE_TYPE (op1)));
 
-  /* TODO: REAL_TYPE is not supported yet.  */
-  if (TREE_CODE (type) != INTEGER_TYPE)
+  if (TREE_CODE (type) == INTEGER_TYPE
+      && (flag_sanitize & SANITIZE_DIVIDE))
+    t = fold_build2 (EQ_EXPR, boolean_type_node,
+		     op1, build_int_cst (type, 0));
+  else if (TREE_CODE (type) == REAL_TYPE
+	   && (flag_sanitize & SANITIZE_FLOAT_DIVIDE))
+    t = fold_build2 (EQ_EXPR, boolean_type_node,
+		     op1, build_real (type, dconst0));
+  else
     return NULL_TREE;
 
-  t = fold_build2 (EQ_EXPR, boolean_type_node,
-		    op1, build_int_cst (type, 0));
-
   /* We check INT_MIN / -1 only for signed types.  */
-  if (!TYPE_UNSIGNED (type))
+  if (TREE_CODE (type) == INTEGER_TYPE
+      && (flag_sanitize & SANITIZE_DIVIDE)
+      && !TYPE_UNSIGNED (type))
     {
       tree x;
       tt = fold_build2 (EQ_EXPR, boolean_type_node, op1,
diff --git gcc/c/c-typeck.c gcc/c/c-typeck.c
index 62c72df..8df544b 100644
--- gcc/c/c-typeck.c
+++ gcc/c/c-typeck.c
@@ -10995,7 +10995,8 @@  build_binary_op (location_t location, enum tree_code code,
 	return error_mark_node;
     }
 
-  if ((flag_sanitize & (SANITIZE_SHIFT | SANITIZE_DIVIDE))
+  if ((flag_sanitize & (SANITIZE_SHIFT | SANITIZE_DIVIDE
+			| SANITIZE_FLOAT_DIVIDE))
       && current_function_decl != 0
       && !lookup_attribute ("no_sanitize_undefined",
 			    DECL_ATTRIBUTES (current_function_decl))
@@ -11006,7 +11007,8 @@  build_binary_op (location_t location, enum tree_code code,
       op1 = c_save_expr (op1);
       op0 = c_fully_fold (op0, false, NULL);
       op1 = c_fully_fold (op1, false, NULL);
-      if (doing_div_or_mod && (flag_sanitize & SANITIZE_DIVIDE))
+      if (doing_div_or_mod && (flag_sanitize & (SANITIZE_DIVIDE
+						| SANITIZE_FLOAT_DIVIDE)))
 	instrument_expr = ubsan_instrument_division (location, op0, op1);
       else if (doing_shift && (flag_sanitize & SANITIZE_SHIFT))
 	instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
diff --git gcc/cp/typeck.c gcc/cp/typeck.c
index 9a80727..99b4ce6 100644
--- gcc/cp/typeck.c
+++ gcc/cp/typeck.c
@@ -4112,10 +4112,7 @@  cp_build_binary_op (location_t location,
 	  enum tree_code tcode0 = code0, tcode1 = code1;
 	  tree cop1 = fold_non_dependent_expr_sfinae (op1, tf_none);
 	  cop1 = maybe_constant_value (cop1);
-
-	  if (tcode0 == INTEGER_TYPE)
-	    doing_div_or_mod = true;
-
+	  doing_div_or_mod = true;
 	  warn_for_div_by_zero (location, cop1);
 
 	  if (tcode0 == COMPLEX_TYPE || tcode0 == VECTOR_TYPE)
@@ -4155,9 +4152,7 @@  cp_build_binary_op (location_t location,
       {
 	tree cop1 = fold_non_dependent_expr_sfinae (op1, tf_none);
 	cop1 = maybe_constant_value (cop1);
-
-	if (code0 == INTEGER_TYPE)
-	  doing_div_or_mod = true;
+	doing_div_or_mod = true;
 	warn_for_div_by_zero (location, cop1);
       }
 
@@ -4904,7 +4899,8 @@  cp_build_binary_op (location_t location,
   if (build_type == NULL_TREE)
     build_type = result_type;
 
-  if ((flag_sanitize & (SANITIZE_SHIFT | SANITIZE_DIVIDE))
+  if ((flag_sanitize & (SANITIZE_SHIFT | SANITIZE_DIVIDE
+			| SANITIZE_FLOAT_DIVIDE))
       && !processing_template_decl
       && current_function_decl != 0
       && !lookup_attribute ("no_sanitize_undefined",
@@ -4918,7 +4914,8 @@  cp_build_binary_op (location_t location,
 								  tf_none));
       op1 = maybe_constant_value (fold_non_dependent_expr_sfinae (op1,
 								  tf_none));
-      if (doing_div_or_mod && (flag_sanitize & SANITIZE_DIVIDE))
+      if (doing_div_or_mod && (flag_sanitize & (SANITIZE_DIVIDE
+						| SANITIZE_FLOAT_DIVIDE)))
 	{
 	  /* For diagnostics we want to use the promoted types without
 	     shorten_binary_op.  So convert the arguments to the
diff --git gcc/flag-types.h gcc/flag-types.h
index fc3261b..caf4039 100644
--- gcc/flag-types.h
+++ gcc/flag-types.h
@@ -228,6 +228,7 @@  enum sanitize_code {
   SANITIZE_SI_OVERFLOW = 1 << 9,
   SANITIZE_BOOL = 1 << 10,
   SANITIZE_ENUM = 1 << 11,
+  SANITIZE_FLOAT_DIVIDE = 1 << 12,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
 		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
diff --git gcc/gcc.c gcc/gcc.c
index e5130d1..7bea6d7 100644
--- gcc/gcc.c
+++ gcc/gcc.c
@@ -8170,7 +8170,7 @@  sanitize_spec_function (int argc, const char **argv)
   if (strcmp (argv[0], "thread") == 0)
     return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
   if (strcmp (argv[0], "undefined") == 0)
-    return ((flag_sanitize & SANITIZE_UNDEFINED)
+    return ((flag_sanitize & (SANITIZE_UNDEFINED | SANITIZE_FLOAT_DIVIDE))
 	    && !flag_sanitize_undefined_trap_on_error) ? "" : NULL;
   if (strcmp (argv[0], "leak") == 0)
     return ((flag_sanitize
diff --git gcc/opts.c gcc/opts.c
index 1873b96..3c214f0 100644
--- gcc/opts.c
+++ gcc/opts.c
@@ -1461,6 +1461,8 @@  common_handle_option (struct gcc_options *opts,
 		sizeof "signed-integer-overflow" -1 },
 	      { "bool", SANITIZE_BOOL, sizeof "bool" - 1 },
 	      { "enum", SANITIZE_ENUM, sizeof "enum" - 1 },
+	      { "float-divide-by-zero", SANITIZE_FLOAT_DIVIDE,
+		sizeof "float-divide-by-zero" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
diff --git gcc/testsuite/c-c++-common/ubsan/div-by-zero-5.c gcc/testsuite/c-c++-common/ubsan/div-by-zero-5.c
index 7a28bac..bb391c5 100644
--- gcc/testsuite/c-c++-common/ubsan/div-by-zero-5.c
+++ gcc/testsuite/c-c++-common/ubsan/div-by-zero-5.c
@@ -1,4 +1,4 @@ 
-/* { dg-do compile} */
+/* { dg-do compile } */
 /* { dg-options "-fsanitize=integer-divide-by-zero" } */
 
 void
diff --git gcc/testsuite/c-c++-common/ubsan/float-div-by-zero-1.c gcc/testsuite/c-c++-common/ubsan/float-div-by-zero-1.c
index e69de29..30bc090 100644
--- gcc/testsuite/c-c++-common/ubsan/float-div-by-zero-1.c
+++ gcc/testsuite/c-c++-common/ubsan/float-div-by-zero-1.c
@@ -0,0 +1,25 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=float-divide-by-zero" } */
+
+int
+main (void)
+{
+  volatile float a = 1.3f;
+  volatile double b = 0.0;
+  volatile int c = 4;
+
+  a / b;
+  a / 0.0;
+  2.7f / b;
+  3.6 / (b = 0.0, b);
+  c / b;
+  b / c;
+
+  return 0;
+}
+
+/* { dg-output "division by zero\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*" } */