From 54125d191d967da12c5aab957bf6ffcb222df9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=B6hrmann?= Date: Tue, 29 Aug 2017 13:31:45 +0200 Subject: [PATCH] runtime: move dynamic makemap checks into cmd/compile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check map invariants, type size and alignments during compile time. Keep runtime checks for reflect by adding them to reflect_makemap. Change-Id: Ia28610626591bf7fafb7d5a1ca318da272e54879 Reviewed-on: https://go-review.googlesource.com/59914 Run-TryBot: Martin Möhrmann TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/reflect.go | 62 +++++++++++++++++++--- src/runtime/hashmap.go | 72 ++++++++++++++------------ 2 files changed, 94 insertions(+), 40 deletions(-) diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index 845f7a6e31..a56edad3b4 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -121,11 +121,13 @@ func mapbucket(t *types.Type) *types.Type { arr = types.NewArray(keytype, BUCKETSIZE) arr.SetNoalg(true) - field = append(field, makefield("keys", arr)) + keys := makefield("keys", arr) + field = append(field, keys) arr = types.NewArray(valtype, BUCKETSIZE) arr.SetNoalg(true) - field = append(field, makefield("values", arr)) + values := makefield("values", arr) + field = append(field, values) // Make sure the overflow pointer is the last memory in the struct, // because the runtime assumes it can use size-ptrSize as the @@ -158,8 +160,8 @@ func mapbucket(t *types.Type) *types.Type { if !types.Haspointers(t.Val()) && !types.Haspointers(t.Key()) && t.Val().Width <= MAXVALSIZE && t.Key().Width <= MAXKEYSIZE { otyp = types.Types[TUINTPTR] } - ovf := makefield("overflow", otyp) - field = append(field, ovf) + overflow := makefield("overflow", otyp) + field = append(field, overflow) // link up fields bucket.SetNoalg(true) @@ -167,10 +169,51 @@ func mapbucket(t *types.Type) *types.Type { bucket.SetFields(field[:]) dowidth(bucket) + // Check invariants that map code depends on. + if BUCKETSIZE < 8 { + Fatalf("bucket size too small for proper alignment") + } + if keytype.Align > BUCKETSIZE { + Fatalf("key align too big for %v", t) + } + if valtype.Align > BUCKETSIZE { + Fatalf("value align too big for %v", t) + } + if keytype.Width > MAXKEYSIZE { + Fatalf("key size to large for %v", t) + } + if valtype.Width > MAXVALSIZE { + Fatalf("value size to large for %v", t) + } + if t.Key().Width > MAXKEYSIZE && !keytype.IsPtr() { + Fatalf("key indirect incorrect for %v", t) + } + if t.Val().Width > MAXVALSIZE && !valtype.IsPtr() { + Fatalf("value indirect incorrect for %v", t) + } + if keytype.Width%int64(keytype.Align) != 0 { + Fatalf("key size not a multiple of key align for %v", t) + } + if valtype.Width%int64(valtype.Align) != 0 { + Fatalf("value size not a multiple of value align for %v", t) + } + if bucket.Align%keytype.Align != 0 { + Fatalf("bucket align not multiple of key align %v", t) + } + if bucket.Align%valtype.Align != 0 { + Fatalf("bucket align not multiple of value align %v", t) + } + if keys.Offset%int64(keytype.Align) != 0 { + Fatalf("bad alignment of keys in mapbucket for %v", t) + } + if values.Offset%int64(valtype.Align) != 0 { + Fatalf("bad alignment of values in mapbucket for %v", t) + } + // Double-check that overflow field is final memory in struct, // with no padding at end. See comment above. - if ovf.Offset != bucket.Width-int64(Widthptr) { - Fatalf("bad math in mapbucket for %v", t) + if overflow.Offset != bucket.Width-int64(Widthptr) { + Fatalf("bad offset of overflow in mapbucket for %v", t) } t.MapType().Bucket = bucket @@ -218,6 +261,13 @@ func hmap(t *types.Type) *types.Type { hmap.SetLocal(t.Local()) hmap.SetFields(fields) dowidth(hmap) + + // The size of hmap should be 48 bytes on 64 bit + // and 28 bytes on 32 bit platforms. + if size := int64(8 + 5*Widthptr); hmap.Width != size { + Fatalf("hmap size not correct: got %d, want %d", hmap.Width, size) + } + t.MapType().Hmap = hmap hmap.StructType().Map = t return hmap diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index 422ccfd41a..37bf6e0aeb 100644 --- a/src/runtime/hashmap.go +++ b/src/runtime/hashmap.go @@ -285,7 +285,9 @@ func makemap64(t *maptype, hint int64, h *hmap) *hmap { // If h != nil, the map can be created directly in h. // If h.buckets != nil, bucket pointed to can be used as the first bucket. func makemap(t *maptype, hint int, h *hmap) *hmap { - if sz := unsafe.Sizeof(hmap{}); sz > 48 || sz != t.hmap.size { + // The size of hmap should be 48 bytes on 64 bit + // and 28 bytes on 32 bit platforms. + if sz := unsafe.Sizeof(hmap{}); sz != 8+5*sys.PtrSize { println("runtime: sizeof(hmap) =", sz, ", t.hmap.size =", t.hmap.size) throw("bad hmap size") } @@ -298,39 +300,6 @@ func makemap(t *maptype, hint int, h *hmap) *hmap { throw("runtime.makemap: unsupported map key type") } - // check compiler's and reflect's math - if t.key.size > maxKeySize && (!t.indirectkey || t.keysize != uint8(sys.PtrSize)) || - t.key.size <= maxKeySize && (t.indirectkey || t.keysize != uint8(t.key.size)) { - throw("key size wrong") - } - if t.elem.size > maxValueSize && (!t.indirectvalue || t.valuesize != uint8(sys.PtrSize)) || - t.elem.size <= maxValueSize && (t.indirectvalue || t.valuesize != uint8(t.elem.size)) { - throw("value size wrong") - } - - // invariants we depend on. We should probably check these at compile time - // somewhere, but for now we'll do it here. - if t.key.align > bucketCnt { - throw("key align too big") - } - if t.elem.align > bucketCnt { - throw("value align too big") - } - if t.key.size%uintptr(t.key.align) != 0 { - throw("key size not a multiple of key align") - } - if t.elem.size%uintptr(t.elem.align) != 0 { - throw("value size not a multiple of value align") - } - if bucketCnt < 8 { - throw("bucketsize too small for proper alignment") - } - if dataOffset%uintptr(t.key.align) != 0 { - throw("need padding in bucket (key)") - } - if dataOffset%uintptr(t.elem.align) != 0 { - throw("need padding in bucket (value)") - } if evacuatedX+1 != evacuatedY { // evacuate relies on this relationship throw("bad evacuatedN") @@ -1183,6 +1152,41 @@ func ismapkey(t *_type) bool { //go:linkname reflect_makemap reflect.makemap func reflect_makemap(t *maptype, cap int) *hmap { + // Check invariants and reflects math. + if sz := unsafe.Sizeof(hmap{}); sz != t.hmap.size { + println("runtime: sizeof(hmap) =", sz, ", t.hmap.size =", t.hmap.size) + throw("bad hmap size") + } + if t.key.size > maxKeySize && (!t.indirectkey || t.keysize != uint8(sys.PtrSize)) || + t.key.size <= maxKeySize && (t.indirectkey || t.keysize != uint8(t.key.size)) { + throw("key size wrong") + } + if t.elem.size > maxValueSize && (!t.indirectvalue || t.valuesize != uint8(sys.PtrSize)) || + t.elem.size <= maxValueSize && (t.indirectvalue || t.valuesize != uint8(t.elem.size)) { + throw("value size wrong") + } + if t.key.align > bucketCnt { + throw("key align too big") + } + if t.elem.align > bucketCnt { + throw("value align too big") + } + if t.key.size%uintptr(t.key.align) != 0 { + throw("key size not a multiple of key align") + } + if t.elem.size%uintptr(t.elem.align) != 0 { + throw("value size not a multiple of value align") + } + if bucketCnt < 8 { + throw("bucketsize too small for proper alignment") + } + if dataOffset%uintptr(t.key.align) != 0 { + throw("need padding in bucket (key)") + } + if dataOffset%uintptr(t.elem.align) != 0 { + throw("need padding in bucket (value)") + } + return makemap(t, cap, nil) }