compiler: pad structs ending with zero-sized field
For a struct with zero-sized last field, the address of the
field falls out of the object boundary, which confuses the
garbage collector. Pad an extra byte in this case.
Change-Id: I44c2b52f0353eff81725fe18084a229b44e0c344
Reviewed-on: https://go-review.googlesource.com/c/157557
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/go/expressions.cc b/go/expressions.cc
index 4854c3c..ed3863c 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -13082,6 +13082,12 @@
++pv;
}
}
+ if (this->type_->struct_type()->has_padding())
+ {
+ // Feed an extra value if there is a padding field.
+ Btype *fbtype = Type::lookup_integer_type("uint8")->get_backend(gogo);
+ init.push_back(gogo->backend()->zero_expression(fbtype));
+ }
return gogo->backend()->constructor_expression(btype, init, this->location());
}
diff --git a/go/types.cc b/go/types.cc
index a50156c..509be44 100644
--- a/go/types.cc
+++ b/go/types.cc
@@ -24,8 +24,7 @@
// backend.h.
static void
-get_backend_struct_fields(Gogo* gogo, const Struct_field_list* fields,
- bool use_placeholder,
+get_backend_struct_fields(Gogo* gogo, Struct_type* type, bool use_placeholder,
std::vector<Backend::Btyped_identifier>* bfields);
static void
@@ -1162,8 +1161,7 @@
// struct field.
{
std::vector<Backend::Btyped_identifier> bfields;
- get_backend_struct_fields(gogo, this->struct_type()->fields(),
- true, &bfields);
+ get_backend_struct_fields(gogo, this->struct_type(), true, &bfields);
bt = gogo->backend()->struct_type(bfields);
}
break;
@@ -6140,12 +6138,14 @@
// backend.h.
static void
-get_backend_struct_fields(Gogo* gogo, const Struct_field_list* fields,
- bool use_placeholder,
+get_backend_struct_fields(Gogo* gogo, Struct_type* type, bool use_placeholder,
std::vector<Backend::Btyped_identifier>* bfields)
{
+ const Struct_field_list* fields = type->fields();
bfields->resize(fields->size());
size_t i = 0;
+ int64_t lastsize = 0;
+ bool saw_nonzero = false;
for (Struct_field_list::const_iterator p = fields->begin();
p != fields->end();
++p, ++i)
@@ -6155,8 +6155,24 @@
? p->type()->get_backend_placeholder(gogo)
: p->type()->get_backend(gogo));
(*bfields)[i].location = p->location();
+ lastsize = gogo->backend()->type_size((*bfields)[i].btype);
+ if (lastsize != 0)
+ saw_nonzero = true;
}
go_assert(i == fields->size());
+ if (saw_nonzero && lastsize == 0)
+ {
+ // For nonzero-sized structs which end in a zero-sized thing, we add
+ // an extra byte of padding to the type. This padding ensures that
+ // taking the address of the zero-sized thing can't manufacture a
+ // pointer to the next object in the heap. See issue 9401.
+ size_t n = fields->size();
+ bfields->resize(n + 1);
+ (*bfields)[n].name = "_";
+ (*bfields)[n].btype = Type::lookup_integer_type("uint8")->get_backend(gogo);
+ (*bfields)[n].location = (*bfields)[n-1].location;
+ type->set_has_padding();
+ }
}
// Get the backend representation for a struct type.
@@ -6165,7 +6181,7 @@
Struct_type::do_get_backend(Gogo* gogo)
{
std::vector<Backend::Btyped_identifier> bfields;
- get_backend_struct_fields(gogo, this->fields_, false, &bfields);
+ get_backend_struct_fields(gogo, this, false, &bfields);
return gogo->backend()->struct_type(bfields);
}
@@ -10504,8 +10520,7 @@
case TYPE_STRUCT:
{
std::vector<Backend::Btyped_identifier> bfields;
- get_backend_struct_fields(gogo, base->struct_type()->fields(),
- true, &bfields);
+ get_backend_struct_fields(gogo, base->struct_type(), true, &bfields);
if (!gogo->backend()->set_placeholder_struct_type(bt, bfields))
bt = gogo->backend()->error_type();
}
diff --git a/go/types.h b/go/types.h
index 4898e67..9d79941 100644
--- a/go/types.h
+++ b/go/types.h
@@ -2432,7 +2432,7 @@
Struct_type(Struct_field_list* fields, Location location)
: Type(TYPE_STRUCT),
fields_(fields), location_(location), all_methods_(NULL),
- is_struct_incomparable_(false)
+ is_struct_incomparable_(false), has_padding_(false)
{ }
// Return the field NAME. This only looks at local fields, not at
@@ -2552,6 +2552,17 @@
set_is_struct_incomparable()
{ this->is_struct_incomparable_ = true; }
+ // Return whether this struct's backend type has padding, due to
+ // trailing zero-sized field.
+ bool
+ has_padding() const
+ { return this->has_padding_; }
+
+ // Record that this struct's backend type has padding.
+ void
+ set_has_padding()
+ { this->has_padding_ = true; }
+
// Write the hash function for this type.
void
write_hash_function(Gogo*, Named_type*, Function_type*, Function_type*);
@@ -2656,6 +2667,9 @@
// True if this is a generated struct that is not considered to be
// comparable.
bool is_struct_incomparable_;
+ // True if this struct's backend type has padding, due to trailing
+ // zero-sized field.
+ bool has_padding_;
};
// The type of an array.