Skip to content

Instantly share code, notes, and snippets.

@aktau
Created January 10, 2025 14:47
Show Gist options
  • Save aktau/357a08a0d1b67f6bc3766feb59136a16 to your computer and use it in GitHub Desktop.
Save aktau/357a08a0d1b67f6bc3766feb59136a16 to your computer and use it in GitHub Desktop.
// $ go build crasher.go
// $ ( ulimit -c unlimited ; GOMAXPROCS=2 GOTRACEBACK=crash ./crasher )
// $ viewcore core --exe crasher html --port 3981
package main
import (
"fmt"
"runtime"
"unsafe"
)
type nonTinyComposite1 struct {
a uint64
b uint32
c uint16
d uint16
f complex128
}
// Same layout as nonTinyComposite1, but different type.
type nonTinyComposite2 struct {
a uint64
b uint32
c uint16
d uint16
f complex128
}
type Large struct {
ptr *uint8 // Object must contain a pointer to trigger code path.
arr [32768 - 8]uint8
}
func assert(pred bool, msg string) {
if !pred {
panic(msg)
}
}
func create[T any](v T, isTiny bool) (*T, []T) {
backing := make([]T, 3) // 12 bytes, fits in tinyalloc (<16B).
objsize := len(backing) * int(unsafe.Sizeof(v))
assert((isTiny && objsize < 16) || (!isTiny && objsize >= 16), "allocation is not tiny/large")
backing[1] = v
return &backing[1], backing
}
func escape1[T any](o *T) (rendezvous func()) {
ready := make(chan struct{})
block := make(chan struct{})
go func() {
fmt.Printf("starting obj len %v\n", unsafe.Sizeof(*o))
ready <- struct{}{} // Ensure the goroutine has scheduled.
<-block
fmt.Printf("finishing escape obj len %v\n", unsafe.Sizeof(*o))
}()
return func() { block <- struct{}{} }
}
func escape2[T any](o *T, ready chan<- struct{}, block <-chan struct{}) {
fmt.Printf("starting obj len %v\n", unsafe.Sizeof(*o))
ready <- struct{}{} // Ensure the goroutine has scheduled.
<-block
fmt.Printf("finishing escape obj len %v\n", unsafe.Sizeof(*o))
}
func main() {
elTinyLostBacking, _ := create(uint32(0xDEAD), true)
defer runtime.KeepAlive(elTinyLostBacking)
elTinyKeepBacking, tinyKeepBacking := create(uint32(0xBEEF), true)
defer runtime.KeepAlive(elTinyKeepBacking)
defer runtime.KeepAlive(tinyKeepBacking)
elBigLostBacking, _ := create(uint64(0xCAFECAFE), false)
defer runtime.KeepAlive(elBigLostBacking)
elBigKeepBacking, bigKeepBacking := create(uint64(0xBABEBABE), false)
defer runtime.KeepAlive(elBigKeepBacking)
defer runtime.KeepAlive(bigKeepBacking)
plainUint16 := new(uint16)
*plainUint16 = 0xFEFE
defer runtime.KeepAlive(plainUint16)
nonTinyCompositeObj := new(nonTinyComposite1)
nonTinyCompositeObj.c = 0xCACA
defer runtime.KeepAlive(nonTinyCompositeObj)
const (
escapeStyle1 = false
escapeStyle2 = false
)
// The following block is important and load-boarding, even if escapeStyle1/2
// are both false.
//
// If uncommented, there is we see (truncated):
//
// Output: pre nonTinyCompositeObj: 0xc0000a6000
//
// field | type | value
// ----- | ---- | ----
// nonTinyCompositeObj | *main.nonTinyComposite1 | dead
// unk | unsafe.Pointer | object c0000a6000
//
// If commented, we see:
//
// Output: pre nonTinyCompositeObj: 0xc0000a6000
//
// field | type | value
// ----- | ---- | ----
// unk | unsafe.Pointer | object c0000a6000
//
// It's unclear to me why c0000a6000 isn't being typed correctly in either
// case, and especially not why there is a dead nonTinyCompositeObj if the
// channel is created in the block below.
if escapeStyle1 {
var nonTinyCompositeObj2 nonTinyComposite2
nonTinyCompositeObj2.c = 0xBABA
defer escape1(&nonTinyCompositeObj2)
}
if escapeStyle2 {
var nonTinyCompositeObj2 nonTinyComposite2
nonTinyCompositeObj2.c = 0xBABA
ready := make(chan struct{})
<-ready
}
pri := func(prefix string) {
fmt.Printf("%s tiny lost : %x\n", prefix, elTinyLostBacking)
fmt.Printf("%s tiny keep: %x\n", prefix, elTinyKeepBacking)
fmt.Printf("%s big lost : %x\n", prefix, elBigLostBacking)
fmt.Printf("%s big keep: %x\n", prefix, elBigKeepBacking)
fmt.Printf("%s plain uint16: %x\n", prefix, plainUint16)
fmt.Printf("%s nonTinyCompositeObj: %p\n", prefix, nonTinyCompositeObj)
}
runtime.GC() // Force cleanup, likely unnecessary.
pri("pre")
_ = *(*int)(nil)
pri("post")
if escapeStyle2 {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment