diff mbox

[gccgo] Check make arguments

Message ID mcrpqwyb1pq.fsf@google.com
State New
Headers show

Commit Message

Ian Lance Taylor Sept. 1, 2010, 3:08 a.m. UTC
This gccgo patch checks the arguments to the predeclared function make
to make sure they are in-range and do not overflow.  Committed to gccgo
branch.

Ian
diff mbox

Patch

diff -r 5ee6d7ed6e13 go/expressions.cc
--- a/go/expressions.cc	Tue Aug 31 16:14:52 2010 -0700
+++ b/go/expressions.cc	Tue Aug 31 19:48:00 2010 -0700
@@ -9886,10 +9886,11 @@ 
 {
   if (this->args_ != NULL)
     {
+      Type_context context(Type::lookup_integer_type("int"), false);
       for (Expression_list::const_iterator pe = this->args_->begin();
 	   pe != this->args_->end();
 	   ++pe)
-	(*pe)->determine_type_no_context();
+	(*pe)->determine_type(&context);
     }
 }
 
diff -r 5ee6d7ed6e13 go/types.cc
--- a/go/types.cc	Tue Aug 31 16:14:52 2010 -0700
+++ b/go/types.cc	Tue Aug 31 19:48:00 2010 -0700
@@ -3652,6 +3652,46 @@ 
     }
 }
 
+// Check that LEN, which has an arbitrary integer type, is
+// non-negative and not more than the maximum value of BOUND_TYPE.
+// Return a tree which performs this check.  The return value may be
+// NULL_TREE.  CHECK is a set of checks so far which are or'ed into
+// the result.
+
+tree
+Array_type::check_make_arg(tree len, tree check, tree bound_type,
+			   source_location location)
+{
+  tree len_type = TREE_TYPE(len);
+  tree ret = NULL_TREE;
+  if (!TYPE_UNSIGNED(len_type))
+    ret = fold_build2_loc(location, LT_EXPR, boolean_type_node,
+			  len,
+			  fold_convert_loc(location, len_type,
+					   integer_zero_node));
+  if ((TYPE_UNSIGNED(len_type) && !TYPE_UNSIGNED(bound_type))
+      || TYPE_SIZE(len_type) > TYPE_SIZE(bound_type))
+    {
+      tree max = TYPE_MAX_VALUE(bound_type);
+      tree big = fold_build2_loc(location, GT_EXPR, boolean_type_node,
+				 len,
+				 fold_convert_loc(location, len_type, max));
+      if (ret == NULL_TREE)
+	ret = big;
+      else
+	ret = fold_build2_loc(location, TRUTH_OR_EXPR, boolean_type_node,
+			      ret, big);
+    }
+
+  if (ret == NULL_TREE)
+    return check;
+  else if (check == NULL_TREE)
+    return ret;
+  else
+    return fold_build2_loc(location, TRUTH_OR_EXPR, boolean_type_node,
+			   ret, check);
+}
+
 // Handle the builtin make function for a slice.
 
 tree
@@ -3678,8 +3718,6 @@ 
   if (element_type_tree == error_mark_node)
     return error_mark_node;
   tree element_size_tree = TYPE_SIZE_UNIT(element_type_tree);
-  element_size_tree = fold_convert_loc(location, TREE_TYPE(count_field),
-				       element_size_tree);
 
   tree value = this->element_type_->get_init_tree(gogo, true);
 
@@ -3690,30 +3728,76 @@ 
   tree length_tree = args->front()->get_tree(context);
   if (length_tree == error_mark_node)
     return error_mark_node;
-  length_tree = ::convert(TREE_TYPE(count_field), length_tree);
-  length_tree = save_expr(length_tree);
+  if (!DECL_P(length_tree))
+    length_tree = save_expr(length_tree);
+  if (!INTEGRAL_TYPE_P(TREE_TYPE(length_tree)))
+    length_tree = convert_to_integer(TREE_TYPE(count_field), length_tree);
+
+  tree bad_index = Array_type::check_make_arg(length_tree, NULL_TREE,
+					      TREE_TYPE(count_field),
+					      location);
+
+  length_tree = fold_convert_loc(location, TREE_TYPE(count_field), length_tree);
   tree capacity_tree;
   if (args->size() == 1)
     capacity_tree = length_tree;
   else
     {
       capacity_tree = args->back()->get_tree(context);
-      capacity_tree = ::convert(TREE_TYPE(count_field), capacity_tree);
-      capacity_tree = save_expr(capacity_tree);
-      capacity_tree = fold_build3_loc(location, COND_EXPR,
-				      TREE_TYPE(count_field),
-				      fold_build2_loc(location,
-						      LT_EXPR,
-						      boolean_type_node,
-						      capacity_tree,
-						      length_tree),
-				      length_tree,
-				      capacity_tree);
-      capacity_tree = save_expr(capacity_tree);
-    }
-
-  tree size_tree = fold_build2_loc(location, MULT_EXPR, TREE_TYPE(count_field),
-				   element_size_tree, capacity_tree);
+      if (capacity_tree == error_mark_node)
+	return error_mark_node;
+      if (!DECL_P(capacity_tree))
+	capacity_tree = save_expr(capacity_tree);
+      if (!INTEGRAL_TYPE_P(TREE_TYPE(capacity_tree)))
+	capacity_tree = convert_to_integer(TREE_TYPE(count_field),
+					   capacity_tree);
+
+      bad_index = Array_type::check_make_arg(capacity_tree, bad_index,
+					     TREE_TYPE(count_field),
+					     location);
+
+      tree chktype = (((TYPE_SIZE(TREE_TYPE(capacity_tree))
+			> TYPE_SIZE(TREE_TYPE(length_tree)))
+		       || ((TYPE_SIZE(TREE_TYPE(capacity_tree))
+			    == TYPE_SIZE(TREE_TYPE(length_tree)))
+			   && TYPE_UNSIGNED(TREE_TYPE(capacity_tree))))
+		      ? TREE_TYPE(capacity_tree)
+		      : TREE_TYPE(length_tree));
+      tree chk = fold_build2_loc(location, LT_EXPR, boolean_type_node,
+				 fold_convert_loc(location, chktype,
+						  capacity_tree),
+				 fold_convert_loc(location, chktype,
+						  length_tree));
+      if (bad_index == NULL_TREE)
+	bad_index = chk;
+      else
+	bad_index = fold_build2_loc(location, TRUTH_OR_EXPR, boolean_type_node,
+				    bad_index, chk);
+
+      capacity_tree = fold_convert_loc(location, TREE_TYPE(count_field),
+				       capacity_tree);
+    }
+
+  tree size_tree = fold_build2_loc(location, MULT_EXPR, sizetype,
+				   element_size_tree,
+				   fold_convert_loc(location, sizetype,
+						    capacity_tree));
+
+  tree chk = fold_build2_loc(location, TRUTH_AND_EXPR, boolean_type_node,
+			     fold_build2_loc(location, GT_EXPR,
+					     boolean_type_node,
+					     fold_convert_loc(location,
+							      sizetype,
+							      capacity_tree),
+					     size_zero_node),
+			     fold_build2_loc(location, LT_EXPR,
+					     boolean_type_node,
+					     size_tree, element_size_tree));
+  if (bad_index == NULL_TREE)
+    bad_index = chk;
+  else
+    bad_index = fold_build2_loc(location, TRUTH_OR_EXPR, boolean_type_node,
+				bad_index, chk);
 
   tree space = context->gogo()->allocate_memory(this->element_type_,
 						size_tree, location);
@@ -3723,6 +3807,22 @@ 
 
   space = fold_convert(TREE_TYPE(values_field), space);
 
+  if (bad_index != NULL_TREE && bad_index != boolean_false_node)
+    {
+      static tree bad_index_fndecl;
+      tree crash = Gogo::call_builtin(&bad_index_fndecl,
+				      location,
+				      "__go_bad_makeslice",
+				      0,
+				      void_type_node);
+      TREE_NOTHROW(bad_index_fndecl) = 0;
+      TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
+      space = build2(COMPOUND_EXPR, TREE_TYPE(space),
+		     build3(COND_EXPR, void_type_node,
+			    bad_index, crash, NULL_TREE),
+		     space);
+    }
+
   tree constructor = gogo->slice_constructor(type_tree, space, length_tree,
 					     capacity_tree);
 
diff -r 5ee6d7ed6e13 go/types.h
--- a/go/types.h	Tue Aug 31 16:14:52 2010 -0700
+++ b/go/types.h	Tue Aug 31 19:48:00 2010 -0700
@@ -1987,6 +1987,9 @@ 
   tree
   get_length_tree(Gogo*);
 
+  static tree
+  check_make_arg(tree, tree, tree, source_location);
+
   // A mapping from Type to tree, used to ensure that arrays of
   // identical types are identical.
   typedef std::tr1::unordered_map<const Type*, tree, Type_hash_identical,
diff -r 5ee6d7ed6e13 libgo/Makefile.am
--- a/libgo/Makefile.am	Tue Aug 31 16:14:52 2010 -0700
+++ b/libgo/Makefile.am	Tue Aug 31 19:48:00 2010 -0700
@@ -295,6 +295,7 @@ 
 	runtime/go-assert.c \
 	runtime/go-assert-interface.c \
 	runtime/go-bad-index.c \
+	runtime/go-bad-makeslice.c \
 	runtime/go-byte-array-to-string.c \
 	runtime/go-breakpoint.c \
 	runtime/go-caller.c \
diff -r 5ee6d7ed6e13 libgo/runtime/go-bad-makeslice.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgo/runtime/go-bad-makeslice.c	Tue Aug 31 19:48:00 2010 -0700
@@ -0,0 +1,15 @@ 
+/* go-bad-makeslice.c -- bad call to make for a slice in Go.
+
+   Copyright 2010 The Go Authors. All rights reserved.
+   Use of this source code is governed by a BSD-style
+   license that can be found in the LICENSE file.  */
+
+#include "go-panic.h"
+
+extern void __go_bad_makeslice () __attribute__ ((noreturn));
+
+void
+__go_bad_makeslice ()
+{
+  __go_panic_msg ("slice len or cap out of range");
+}
diff -r 5ee6d7ed6e13 libgo/runtime/go-new-channel.c
--- a/libgo/runtime/go-new-channel.c	Tue Aug 31 16:14:52 2010 -0700
+++ b/libgo/runtime/go-new-channel.c	Tue Aug 31 19:48:00 2010 -0700
@@ -8,6 +8,7 @@ 
 
 #include "go-alloc.h"
 #include "go-assert.h"
+#include "go-panic.h"
 #include "channel.h"
 
 struct __go_channel*
@@ -17,6 +18,9 @@ 
   size_t alloc_size;
   int i;
 
+  if ((size_t) (int) entries != entries || entries > (size_t) -1 / element_size)
+    __go_panic_msg ("chan size out of range");
+
   alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t);
 
   /* We use a circular buffer which means that when next_fetch ==
diff -r 5ee6d7ed6e13 libgo/runtime/go-new-map.c
--- a/libgo/runtime/go-new-map.c	Tue Aug 31 16:14:52 2010 -0700
+++ b/libgo/runtime/go-new-map.c	Tue Aug 31 19:48:00 2010 -0700
@@ -5,6 +5,7 @@ 
    license that can be found in the LICENSE file.  */
 
 #include "go-alloc.h"
+#include "go-panic.h"
 #include "map.h"
 
 /* List of prime numbers, copied from libstdc++/src/hashtable.c.  */
@@ -107,6 +108,9 @@ 
 {
   struct __go_map *ret;
 
+  if ((size_t) (int) entries != entries)
+    __go_panic_msg ("map size out of range");
+
   if (entries == 0)
     entries = 5;
   else