Patchwork [gccgo] Verify that array lengths are reasonable

login
register
mail settings
Submitter Ian Taylor
Date Aug. 31, 2010, 10:53 p.m.
Message ID <mcr7hj6cs27.fsf@google.com>
Download mbox | patch
Permalink /patch/63325/
State New
Headers show

Comments

Ian Taylor - Aug. 31, 2010, 10:53 p.m.
This patch adds support to gccgo for verifying that array lengths are
reasonable, rather than just accepting any old thing and crashing later.
Committed to gccgo branch.

Ian

Patch

diff -r c59e94784c63 go/types.cc
--- a/go/types.cc	Tue Aug 31 15:01:07 2010 -0700
+++ b/go/types.cc	Tue Aug 31 15:50:24 2010 -0700
@@ -3363,6 +3363,98 @@ 
   return TRAVERSE_CONTINUE;
 }
 
+// Check that the length is valid.
+
+bool
+Array_type::verify_length()
+{
+  if (this->length_ == NULL)
+    return true;
+  if (!this->length_->is_constant())
+    {
+      error_at(this->length_->location(), "array bound is not constant");
+      return false;
+    }
+
+  mpz_t val;
+
+  Type* t = this->length_->type();
+  if (t->integer_type() != NULL)
+    {
+      Type* vt;
+      mpz_init(val);
+      if (!this->length_->integer_constant_value(true, val, &vt))
+	{
+	  error_at(this->length_->location(),
+		   "array bound is not constant");
+	  mpz_clear(val);
+	  return false;
+	}
+    }
+  else if (t->float_type() != NULL)
+    {
+      Type* vt;
+      mpfr_t fval;
+      mpfr_init(fval);
+      if (!this->length_->float_constant_value(fval, &vt))
+	{
+	  error_at(this->length_->location(),
+		   "array bound is not constant");
+	  mpfr_clear(fval);
+	  return false;
+	}
+      if (!mpfr_integer_p(fval))
+	{
+	  error_at(this->length_->location(),
+		   "array bound truncated to integer");
+	  mpfr_clear(fval);
+	  return false;
+	}
+      mpz_init(val);
+      mpfr_get_z(val, fval, GMP_RNDN);
+      mpfr_clear(fval);
+    }
+  else
+    {
+      error_at(this->length_->location(), "array bound is not numeric");
+      return false;
+    }
+
+  if (mpz_sgn(val) < 0)
+    {
+      error_at(this->length_->location(), "negative array bound");
+      mpz_clear(val);
+      return false;
+    }
+
+  Type* int_type = Type::lookup_integer_type("int");
+  int tbits = int_type->integer_type()->bits();
+  int vbits = mpz_sizeinbase(val, 2);
+  if (vbits + 1 > tbits)
+    {
+      error_at(this->length_->location(), "array bound overflows");
+      mpz_clear(val);
+      return false;
+    }
+
+  mpz_clear(val);
+
+  return true;
+}
+
+// Verify the type.
+
+bool
+Array_type::do_verify()
+{
+  if (!this->verify_length())
+    {
+      this->length_ = Expression::make_error(this->length_->location());
+      return false;
+    }
+  return true;
+}
+
 // Array type hash code.
 
 unsigned int
diff -r c59e94784c63 go/types.h
--- a/go/types.h	Tue Aug 31 15:01:07 2010 -0700
+++ b/go/types.h	Tue Aug 31 15:50:24 2010 -0700
@@ -1944,6 +1944,9 @@ 
   do_traverse(Traverse* traverse);
 
   bool
+  do_verify();
+
+  bool
   do_has_pointer() const
   {
     return this->length_ == NULL || this->element_type_->has_pointer();
@@ -1978,6 +1981,9 @@ 
   do_export(Export*) const;
 
  private:
+  bool
+  verify_length();
+
   tree
   get_length_tree(Gogo*);