mirror of
https://github.com/golang/go
synced 2024-11-19 03:54:42 -07:00
cmd/gc: pad structs which end in zero-sized fields
For a non-zero-sized struct with a final zero-sized field, add a byte to the size (before rounding to alignment). This change ensures that taking the address of the zero-sized field will not incorrectly leak the following object in memory. reflect.funcLayout also needs this treatment. Fixes #9401 Change-Id: I1dc503dc5af4ca22c8f8c048fb7b4541cc957e0f Reviewed-on: https://go-review.googlesource.com/2452 Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
654a185f20
commit
6f07ac2f28
@ -48,10 +48,13 @@ widstruct(Type *errtype, Type *t, vlong o, int flag)
|
|||||||
Type *f;
|
Type *f;
|
||||||
int64 w;
|
int64 w;
|
||||||
int32 maxalign;
|
int32 maxalign;
|
||||||
|
vlong starto, lastzero;
|
||||||
|
|
||||||
|
starto = o;
|
||||||
maxalign = flag;
|
maxalign = flag;
|
||||||
if(maxalign < 1)
|
if(maxalign < 1)
|
||||||
maxalign = 1;
|
maxalign = 1;
|
||||||
|
lastzero = 0;
|
||||||
for(f=t->type; f!=T; f=f->down) {
|
for(f=t->type; f!=T; f=f->down) {
|
||||||
if(f->etype != TFIELD)
|
if(f->etype != TFIELD)
|
||||||
fatal("widstruct: not TFIELD: %lT", f);
|
fatal("widstruct: not TFIELD: %lT", f);
|
||||||
@ -80,22 +83,28 @@ widstruct(Type *errtype, Type *t, vlong o, int flag)
|
|||||||
} else
|
} else
|
||||||
f->nname->xoffset = o;
|
f->nname->xoffset = o;
|
||||||
}
|
}
|
||||||
|
if(w == 0)
|
||||||
|
lastzero = o;
|
||||||
o += w;
|
o += w;
|
||||||
if(o >= MAXWIDTH) {
|
if(o >= MAXWIDTH) {
|
||||||
yyerror("type %lT too large", errtype);
|
yyerror("type %lT too large", errtype);
|
||||||
o = 8; // small but nonzero
|
o = 8; // small but nonzero
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 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.
|
||||||
|
if(flag == 1 && o > starto && o == lastzero)
|
||||||
|
o++;
|
||||||
|
|
||||||
// final width is rounded
|
// final width is rounded
|
||||||
if(flag)
|
if(flag)
|
||||||
o = rnd(o, maxalign);
|
o = rnd(o, maxalign);
|
||||||
t->align = maxalign;
|
t->align = maxalign;
|
||||||
|
|
||||||
// type width only includes back to first field's offset
|
// type width only includes back to first field's offset
|
||||||
if(t->type == T)
|
t->width = o - starto;
|
||||||
t->width = 0;
|
|
||||||
else
|
|
||||||
t->width = o - t->type->width;
|
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1527,9 +1527,10 @@ func isReflexive(t *rtype) bool {
|
|||||||
|
|
||||||
// gcProg is a helper type for generatation of GC pointer info.
|
// gcProg is a helper type for generatation of GC pointer info.
|
||||||
type gcProg struct {
|
type gcProg struct {
|
||||||
gc []byte
|
gc []byte
|
||||||
size uintptr // size of type in bytes
|
size uintptr // size of type in bytes
|
||||||
hasPtr bool
|
hasPtr bool
|
||||||
|
lastZero uintptr // largest offset of a zero-byte field
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gc *gcProg) append(v byte) {
|
func (gc *gcProg) append(v byte) {
|
||||||
@ -1542,6 +1543,9 @@ func (gc *gcProg) appendProg(t *rtype) {
|
|||||||
gc.align(uintptr(t.align))
|
gc.align(uintptr(t.align))
|
||||||
if !t.pointers() {
|
if !t.pointers() {
|
||||||
gc.size += t.size
|
gc.size += t.size
|
||||||
|
if t.size == 0 {
|
||||||
|
gc.lastZero = gc.size
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
@ -1566,11 +1570,15 @@ func (gc *gcProg) appendProg(t *rtype) {
|
|||||||
gc.appendWord(bitsPointer)
|
gc.appendWord(bitsPointer)
|
||||||
gc.appendWord(bitsPointer)
|
gc.appendWord(bitsPointer)
|
||||||
case Struct:
|
case Struct:
|
||||||
|
oldsize := gc.size
|
||||||
c := t.NumField()
|
c := t.NumField()
|
||||||
for i := 0; i < c; i++ {
|
for i := 0; i < c; i++ {
|
||||||
gc.appendProg(t.Field(i).Type.common())
|
gc.appendProg(t.Field(i).Type.common())
|
||||||
}
|
}
|
||||||
gc.align(uintptr(t.align))
|
if gc.size > oldsize + t.size {
|
||||||
|
panic("reflect: struct components are larger than the struct itself")
|
||||||
|
}
|
||||||
|
gc.size = oldsize + t.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1595,6 +1603,9 @@ func (gc *gcProg) finalize() (unsafe.Pointer, bool) {
|
|||||||
if gc.size == 0 {
|
if gc.size == 0 {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
if gc.lastZero == gc.size {
|
||||||
|
gc.size++
|
||||||
|
}
|
||||||
ptrsize := unsafe.Sizeof(uintptr(0))
|
ptrsize := unsafe.Sizeof(uintptr(0))
|
||||||
gc.align(ptrsize)
|
gc.align(ptrsize)
|
||||||
nptr := gc.size / ptrsize
|
nptr := gc.size / ptrsize
|
||||||
|
@ -247,3 +247,44 @@ func TestEqString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTrailingZero(t *testing.T) {
|
||||||
|
// make sure we add padding for structs with trailing zero-sized fields
|
||||||
|
type T1 struct {
|
||||||
|
n int32
|
||||||
|
z [0]byte
|
||||||
|
}
|
||||||
|
if unsafe.Sizeof(T1{}) != 8 {
|
||||||
|
t.Errorf("sizeof(%#v)==%d, want 8", T1{}, unsafe.Sizeof(T1{}))
|
||||||
|
}
|
||||||
|
type T2 struct {
|
||||||
|
n int64
|
||||||
|
z struct{}
|
||||||
|
}
|
||||||
|
if unsafe.Sizeof(T2{}) != 16 {
|
||||||
|
t.Errorf("sizeof(%#v)==%d, want 16", T2{}, unsafe.Sizeof(T2{}))
|
||||||
|
}
|
||||||
|
type T3 struct {
|
||||||
|
n byte
|
||||||
|
z [4]struct{}
|
||||||
|
}
|
||||||
|
if unsafe.Sizeof(T3{}) != 2 {
|
||||||
|
t.Errorf("sizeof(%#v)==%d, want 2", T3{}, unsafe.Sizeof(T3{}))
|
||||||
|
}
|
||||||
|
// make sure padding can double for both zerosize and alignment
|
||||||
|
type T4 struct {
|
||||||
|
a int32
|
||||||
|
b int16
|
||||||
|
c int8
|
||||||
|
z struct{}
|
||||||
|
}
|
||||||
|
if unsafe.Sizeof(T4{}) != 8 {
|
||||||
|
t.Errorf("sizeof(%#v)==%d, want 8", T4{}, unsafe.Sizeof(T4{}))
|
||||||
|
}
|
||||||
|
// make sure we don't pad a zero-sized thing
|
||||||
|
type T5 struct {
|
||||||
|
}
|
||||||
|
if unsafe.Sizeof(T5{}) != 0 {
|
||||||
|
t.Errorf("sizeof(%#v)==%d, want 0", T5{}, unsafe.Sizeof(T5{}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user