-
Notifications
You must be signed in to change notification settings - Fork 18k
runtime: heapBitsSetTypeGCProg: total bits 0 but progSize 1193712 #30606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I've built a simpler testcase and published it here: https://gist.github.com/pwaller/a99e3836293e23677ad08052263b293a - it's >200kB. It eliminates the randomness. Trying to reduce it to a smaller program with goreduce, but noting my progress in case someone else is looking and/or I get sidetracked and fail at making something smaller.
|
Here is a Decreasing any of the array lengths by 1 or removing any field causes it to not crash. package main
import "reflect"
func main() {}
var typeUint64 = reflect.ValueOf(uint64(0)).Type()
var x = reflect.StructOf([]reflect.StructField{
{Name: "F1", Type: reflect.ArrayOf(4, typeUint64)},
{Name: "F5", Type: reflect.ArrayOf(7,
reflect.StructOf([]reflect.StructField{
{Name: "F1", Type: reflect.SliceOf(typeUint64)},
{Name: "F5", Type: reflect.StructOf([]reflect.StructField{
{Name: "F8", Type: reflect.ArrayOf(16,
reflect.ArrayOf(1,
reflect.StructOf([]reflect.StructField{
{Name: "F1", Type: reflect.ArrayOf(32,
reflect.StructOf([]reflect.StructField{
{Name: "F5", Type: reflect.StructOf([]reflect.StructField{
{Name: "F0", Type: typeUint64},
})},
}))},
{Name: "F6", Type: reflect.ArrayOf(115,
reflect.StructOf([]reflect.StructField{
{Name: "F8", Type: typeUint64},
}))},
})))},
})},
})),
},
})
|
|
Simplest crasher so far: package main
import "reflect"
func main() {}
func typ(x interface{}) reflect.Type { return reflect.ValueOf(x).Type() }
var z = reflect.StructOf([]reflect.StructField{
{Name: "F1", Type: reflect.ArrayOf(4, typ(uint64(0)))},
{Name: "F5", Type: reflect.ArrayOf(2,
typ(
struct {
F1 []uint64
F8 [8192 - 3 + 1]uint64
}{},
)),
},
}) If I remove the |
I can reproduce this on 1.11 so not a 1.12 regression. Milestoning as 1.13 |
The effect of changing the array size is to go from this branch: Lines 2883 to 2886 in 178a2c4
to this branch: Lines 2900 to 2906 in 178a2c4
I am guessing that there may be a bug in the latter program generation, and this bug may get lucky and not crash, except in circumstances that happen to be exercised in the minimal reproducer. |
The following patch appears to cure the segfault, which is because What I haven't yet understood is why the crash ceases in the minimal reproducer if you change the array constants, e.g. diff --git a/src/reflect/type.go b/src/reflect/type.go
index 5ce80c61dc..d452946553 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -2681,10 +2681,10 @@ func StructOf(fields []StructField) Type {
break
}
// FIXME(sbinet) handle padding, fields smaller than a word
- elemGC := (*[1 << 30]byte)(unsafe.Pointer(ft.typ.gcdata))[:]
elemPtrs := ft.typ.ptrdata / ptrSize
switch {
case ft.typ.kind&kindGCProg == 0 && ft.typ.ptrdata != 0:
+ elemGC := (*[1 << 30]byte)(unsafe.Pointer(ft.typ.gcdata))[:]
// Element is small with pointer mask; use as literal bits.
mask := elemGC
// Emit 120-bit chunks of full bytes (max is 127 but we avoid using partial bytes).
@@ -2697,6 +2697,7 @@ func StructOf(fields []StructField) Type {
prog = append(prog, byte(n))
prog = append(prog, mask[:(n+7)/8]...)
case ft.typ.kind&kindGCProg != 0:
+ elemGC := (*[1 << 30]byte)(unsafe.Pointer(ft.typ.gcdata))[:]
// Element has GC program; emit one element.
elemProg := elemGC[4 : 4+*(*uint32)(unsafe.Pointer(&elemGC[0]))-1]
prog = append(prog, elemProg...) |
The heapBitsSetTypeGCProg runtime panic ( (Edit: made reproducer smaller. Note that 5472*sizeof(slice) =16386.) package main
import "reflect"
func main() {}
func typ(x interface{}) reflect.Type { return reflect.ValueOf(x).Type() }
var x = reflect.New(reflect.StructOf([]reflect.StructField{
{Name: "F5", Type: reflect.StructOf([]reflect.StructField{
{Name: "F4", Type: reflect.ArrayOf(5462,
reflect.SliceOf(typ(uint64(0))))},
})},
})) Stack trace:
|
@sbinet This smells like a bug in the code that generates GC programs in The size is just big enough to force a GC program in the Looks like a missing stop mark at the end. That is ok by itself, but when incorporating a program in another program the code drops the last byte, and if the last byte isn't a stop mark then we throw away part of the program we actually need. |
Change https://golang.org/cl/165857 mentions this issue: |
Looks like this bug has existed since StructOf was introduced in 1.7. I don't think it is worth backporting. Counter argument though, by omitting the last byte of the program we're potentially using a random byte in the heap for the last byte, which could lead to arbitrarily weird things happening in the GC. It would be unlikely - the program (without the last byte) would have to be exactly the size of a sizeclass to have the missing byte be nonzero. |
@randall77 the master branch still crashes for me using my testcase in the original issue body ("main.go example program", which is click-to-expand). |
Change https://golang.org/cl/165859 mentions this issue: |
Indeed, I only fixed your last example. Both the original crashes are still happening. |
Change https://golang.org/cl/165860 mentions this issue: |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
Generate random struct types and random values for fuzz testing some serialization/de-serialization software.
I include the program in the
<details>
below.The go.mod file only has this one dependency, for generating random values:
main.go example program
What did you expect to see?
I expected that it should successfully generate random structure types filled with fields of random types (including nested structures, maps, etc).
What did you see instead?
The code does successfully generate random structures. It can run for quite a long time without issues. However, it also causes runtime crashes. I have chosen my variables to crash things quite quickly in the above example. The crashes manifest after generating 80-1000 structs for my given examples.
I found two different crashes by varying
NewWithSeed(1)
toNewWithSeed(2)
:NewWithSeed(1)
:panic: runtime error: invalid memory address or nil pointer dereference
NewWithSeed(2)
:runtime: heapBitsSetTypeGCProg: total bits 0 but progSize 1193712
Additional notes
This FIXME looks suspicious, since it is the site of the first crash in
reflect.StructOf
:go/src/reflect/type.go
Lines 2683 to 2684 in a563f2f
I have tried reproducing the crash by choosing specific examples of random structs taken from just before the crash and calling reflect.New() on them repeatedly. This hasn't been fruitful for me so far, so I haven't been able to get an example crasher which runs without generating random structs.
Different types can be commented out in the
var kinds
to change what types get produced.The following is the shortest crash length I have found, crashing after only 4 structs are made:
Click to expand configuration for shortest crash length
Output and stack trace
/cc @rsc @aclements @mwhudson @sbinet - cc derived from stacktrace since it points at the runtime and reflect package appearing at top of stack traces.
The text was updated successfully, but these errors were encountered: