From 378b9abe569d030bbe33f74a55b55519da9bf4bb Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 11 Jan 2019 23:16:38 +0000 Subject: [PATCH] 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 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.cc | 6 ++++++ gcc/go/gofrontend/types.cc | 33 +++++++++++++++++++++++--------- gcc/go/gofrontend/types.h | 16 +++++++++++++++- 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 40e5a2e8649..ab0a2bd4623 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -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. diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 4854c3cb2a3..ed3863c31a9 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -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()); } diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index a50156c2a2b..509be44e028 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/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* bfields); static void @@ -1162,8 +1161,7 @@ Type::get_backend_placeholder(Gogo* gogo) // struct field. { std::vector 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* 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 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 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/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index 4898e67d7b8..9d7994106d8 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -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. -- 2.30.2