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.
    
    Reviewed-on: https://go-review.googlesource.com/c/157557

From-SVN: r267861
This commit is contained in:
Ian Lance Taylor 2019-01-11 23:16:38 +00:00
parent 3ddf08b3b8
commit 378b9abe56
4 changed files with 46 additions and 11 deletions

View File

@ -1,4 +1,4 @@
960637781ca9546ea2db913e48afd7eccbdadfa9
0d64279c01a37b2579c0c62ca4f2c3e3f81de07c
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.

View File

@ -13082,6 +13082,12 @@ Struct_construction_expression::do_get_backend(Translate_context* context)
++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());
}

View File

@ -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 @@ Type::get_backend_placeholder(Gogo* gogo)
// 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 @@ Struct_type::interface_method_table(Interface_type* interface,
// 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 @@ get_backend_struct_fields(Gogo* gogo, const Struct_field_list* fields,
? 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 @@ Btype*
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 @@ Named_type::convert(Gogo* gogo)
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();
}

View File

@ -2432,7 +2432,7 @@ class Struct_type : public Type
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 @@ class Struct_type : public Type
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 @@ class Struct_type : public Type
// 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.