OSDN Git Service

PR fortran/28105 Overflow check for ALLOCATE statement
authorjb <jb@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 30 Nov 2010 21:33:32 +0000 (21:33 +0000)
committerjb <jb@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 30 Nov 2010 21:33:32 +0000 (21:33 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@167317 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/fortran/ChangeLog
gcc/fortran/trans-array.c

index 4f2d7c1..8b761ec 100644 (file)
@@ -1,3 +1,12 @@
+2010-11-30  Janne Blomqvist  <jb@gcc.gnu.org>
+
+       PR fortran/28105
+       * trans-array.c (gfc_unlikely): Helper function to mark boolean
+       expr as unlikely.
+       (gfc_array_index_size): Check whether the size overflows.
+       (gfc_array_allocate): Check whether size overflows and generate
+       error.
+
 2010-11-30  Joseph Myers  <joseph@codesourcery.com>
 
        * trans-common.c: Don't include toplev.h.
index 05ffef1..c768058 100644 (file)
@@ -3950,9 +3950,26 @@ gfc_conv_descriptor_size (tree desc, int rank)
 }
 
 
-/* Fills in an array descriptor, and returns the size of the array.  The size
-   will be a simple_val, ie a variable or a constant.  Also calculates the
-   offset of the base.  Returns the size of the array.
+/* Helper function for marking a boolean expression tree as unlikely.  */
+
+static tree
+gfc_unlikely (tree cond)
+{
+  tree tmp;
+
+  cond = fold_convert (long_integer_type_node, cond);
+  tmp = build_zero_cst (long_integer_type_node);
+  cond = build_call_expr_loc (input_location,
+                             built_in_decls[BUILT_IN_EXPECT], 2, cond, tmp);
+  cond = fold_convert (boolean_type_node, cond);
+  return cond;
+}
+
+/* Fills in an array descriptor, and returns the size of the array.
+   The size will be a simple_val, ie a variable or a constant.  Also
+   calculates the offset of the base.  The pointer argument overflow,
+   which should be of integer type, will increase in value if overflow
+   occurs during the size calculation.  Returns the size of the array.
    {
     stride = 1;
     offset = 0;
@@ -3964,8 +3981,13 @@ gfc_conv_descriptor_size (tree desc, int rank)
        a.ubound[n] = specified_upper_bound;
        a.stride[n] = stride;
        size = siz >= 0 ? ubound + size : 0; //size = ubound + 1 - lbound
+       overflow += size == 0 ? 0: (MAX/size < stride ? 1: 0);
        stride = stride * size;
       }
+    element_size = sizeof (array element);
+    stride = (size_t) stride;
+    overflow += element_size == 0 ? 0: (MAX/element_size < stride ? 1: 0);
+    stride = stride * element_size;
     return (stride);
    }  */
 /*GCC ARRAYS*/
@@ -3973,13 +3995,14 @@ gfc_conv_descriptor_size (tree desc, int rank)
 static tree
 gfc_array_init_size (tree descriptor, int rank, int corank, tree * poffset,
                     gfc_expr ** lower, gfc_expr ** upper,
-                    stmtblock_t * pblock)
+                    stmtblock_t * pblock, tree * overflow)
 {
   tree type;
   tree tmp;
   tree size;
   tree offset;
   tree stride;
+  tree element_size;
   tree or_expr;
   tree thencase;
   tree elsecase;
@@ -4056,7 +4079,33 @@ gfc_array_init_size (tree descriptor, int rank, int corank, tree * poffset,
 
       /* Calculate size and check whether extent is negative.  */
       size = gfc_conv_array_extent_dim (conv_lbound, conv_ubound, &or_expr);
-
+      size = gfc_evaluate_now (size, pblock);
+
+      /* Check whether multiplying the stride by the number of
+        elements in this dimension would overflow. We must also check
+        whether the current dimension has zero size in order to avoid
+        division by zero. 
+      */
+      tmp = fold_build2_loc (input_location, TRUNC_DIV_EXPR, 
+                            gfc_array_index_type, 
+                            fold_convert (gfc_array_index_type, 
+                                          TYPE_MAX_VALUE (gfc_array_index_type)),
+                                          size);
+      tmp = fold_build3_loc 
+       (input_location, COND_EXPR, integer_type_node,
+        gfc_unlikely (fold_build2_loc (input_location, LT_EXPR, 
+                                       boolean_type_node, tmp, stride)),
+        integer_one_node, integer_zero_node);
+      tmp = fold_build3_loc 
+       (input_location, COND_EXPR, integer_type_node,
+        gfc_unlikely (fold_build2_loc (input_location, EQ_EXPR,
+                                       boolean_type_node, size, 
+                                       build_zero_cst (gfc_array_index_type))),
+        integer_zero_node, tmp);
+      tmp = fold_build2_loc (input_location, PLUS_EXPR, integer_type_node,
+                            *overflow, tmp);
+      *overflow = gfc_evaluate_now (tmp, pblock);
+      
       /* Multiply the stride by the number of elements in this dimension.  */
       stride = fold_build2_loc (input_location, MULT_EXPR,
                                gfc_array_index_type, stride, size);
@@ -4104,8 +4153,33 @@ gfc_array_init_size (tree descriptor, int rank, int corank, tree * poffset,
   /* The stride is the number of elements in the array, so multiply by the
      size of an element to get the total size.  */
   tmp = TYPE_SIZE_UNIT (gfc_get_element_type (type));
-  size = fold_build2_loc (input_location, MULT_EXPR, gfc_array_index_type,
-                         stride, fold_convert (gfc_array_index_type, tmp));
+  /* Convert to size_t.  */
+  element_size = fold_convert (sizetype, tmp);
+  stride = fold_convert (sizetype, stride);
+
+  /* First check for overflow. Since an array of type character can
+     have zero element_size, we must check for that before
+     dividing.  */
+  tmp = fold_build2_loc (input_location, TRUNC_DIV_EXPR, 
+                        sizetype, 
+                        TYPE_MAX_VALUE (sizetype), element_size);
+  tmp = fold_build3_loc (input_location, COND_EXPR, integer_type_node,
+                        gfc_unlikely (fold_build2_loc (input_location, LT_EXPR, 
+                                                       boolean_type_node, tmp, 
+                                                       stride)),
+                        integer_one_node, integer_zero_node);
+  tmp = fold_build3_loc (input_location, COND_EXPR, integer_type_node,
+                        gfc_unlikely (fold_build2_loc (input_location, EQ_EXPR,
+                                                       boolean_type_node, 
+                                                       element_size, 
+                                                       size_zero_node)),
+                        integer_zero_node, tmp);
+  tmp = fold_build2_loc (input_location, PLUS_EXPR, integer_type_node,
+                        *overflow, tmp);
+  *overflow = gfc_evaluate_now (tmp, pblock);
+
+  size = fold_build2_loc (input_location, MULT_EXPR, sizetype,
+                         stride, element_size);
 
   if (poffset != NULL)
     {
@@ -4120,7 +4194,7 @@ gfc_array_init_size (tree descriptor, int rank, int corank, tree * poffset,
 
   var = gfc_create_var (TREE_TYPE (size), "size");
   gfc_start_block (&thenblock);
-  gfc_add_modify (&thenblock, var, gfc_index_zero_node);
+  gfc_add_modify (&thenblock, var, size_zero_node);
   thencase = gfc_finish_block (&thenblock);
 
   gfc_start_block (&elseblock);
@@ -4146,6 +4220,12 @@ gfc_array_allocate (gfc_se * se, gfc_expr * expr, tree pstat)
   tree pointer;
   tree offset;
   tree size;
+  tree msg;
+  tree error;
+  tree overflow; /* Boolean storing whether size calculation overflows.  */
+  tree var_overflow;
+  tree cond;
+  stmtblock_t elseblock;
   gfc_expr **lower;
   gfc_expr **upper;
   gfc_ref *ref, *prev_ref = NULL;
@@ -4213,21 +4293,60 @@ gfc_array_allocate (gfc_se * se, gfc_expr * expr, tree pstat)
       break;
     }
 
+  overflow = integer_zero_node;
   size = gfc_array_init_size (se->expr, ref->u.ar.as->rank,
                              ref->u.ar.as->corank, &offset, lower, upper,
-                             &se->pre);
+                             &se->pre, &overflow);
+
+  var_overflow = gfc_create_var (integer_type_node, "overflow");
+  gfc_add_modify (&se->pre, var_overflow, overflow);
 
+  /* Generate the block of code handling overflow.  */
+  msg = gfc_build_addr_expr (pchar_type_node, gfc_build_localized_cstring_const
+                       ("Integer overflow when calculating the amount of "
+                        "memory to allocate"));
+  error = build_call_expr_loc (input_location,
+                          gfor_fndecl_runtime_error, 1, msg);
+
+  if (pstat != NULL_TREE && !integer_zerop (pstat))
+    {
+      /* Set the status variable if it's present.  */
+      stmtblock_t set_status_block;
+      tree status_type = pstat ? TREE_TYPE (TREE_TYPE (pstat)) : NULL_TREE;
+
+      gfc_start_block (&set_status_block);
+      gfc_add_modify (&set_status_block,
+                     fold_build1_loc (input_location, INDIRECT_REF,
+                                      status_type, pstat),
+                          build_int_cst (status_type, LIBERROR_ALLOCATION));
+
+      tmp = fold_build2_loc (input_location, EQ_EXPR, boolean_type_node,
+                            pstat, build_int_cst (TREE_TYPE (pstat), 0));
+      error = fold_build3_loc (input_location, COND_EXPR, void_type_node, tmp,
+                              error, gfc_finish_block (&set_status_block));
+    }
+
+  gfc_start_block (&elseblock);
+  
   /* Allocate memory to store the data.  */
   pointer = gfc_conv_descriptor_data_get (se->expr);
   STRIP_NOPS (pointer);
 
   /* The allocate_array variants take the old pointer as first argument.  */
   if (allocatable_array)
-    tmp = gfc_allocate_array_with_status (&se->pre, pointer, size, pstat, expr);
+    tmp = gfc_allocate_array_with_status (&elseblock, pointer, size, pstat, expr);
   else
-    tmp = gfc_allocate_with_status (&se->pre, size, pstat);
+    tmp = gfc_allocate_with_status (&elseblock, size, pstat);
   tmp = fold_build2_loc (input_location, MODIFY_EXPR, void_type_node, pointer,
                         tmp);
+
+  gfc_add_expr_to_block (&elseblock, tmp);
+
+  cond = gfc_unlikely (fold_build2_loc (input_location, NE_EXPR, boolean_type_node,
+                                       var_overflow, integer_zero_node));
+  tmp = fold_build3_loc (input_location, COND_EXPR, void_type_node, cond, 
+                        error, gfc_finish_block (&elseblock));
+
   gfc_add_expr_to_block (&se->pre, tmp);
 
   gfc_conv_descriptor_offset_set (&se->pre, se->expr, offset);