What are the problems with using
Approach 1:
func Encode(v *float32) []byte {
return unsafe.Slice((*byte)(unsafe.Pointer(v)), 4)
}
over
Approach 2
func Encode(v float32) []byte {
return unsafe.Slice((*byte)(unsafe.Pointer(&v)), 4)
}
Both are run on Little Endian System. The only difference is that Approach 1 accepts a pointer as an argument, while Approach 2 accepts a value as an argument.
I was debugging an issue related to
fatal error: found bad pointer in Go heap (incorrect use of unsafe or cgo?)
found pointer to free object | bad use of unsafe.Pointer?
and I finally found the root cause could be due to Approach 1 usage. Could someone please let me know what could be the issue with Approach 1? Is it generally recommended to use Approach 2?
Please note that float32
is one of the functions. There are other encode functions that accept byte arrays
, int8
etc as well.
type Uuid [16]byte
func EncodeUuid(v *Uuid) []byte {
return unsafe.Slice((*byte)(unsafe.Pointer(v)), UuidSize)
}
This SO answer here mentioned that the original variable could be GC'ed before the Encode()
function is called. Attempting to access the memory pointed to by an already freed pointer can result in a "found pointer to free object" error.
Below are the error logs found during execution.
Error Log 1
runtime: marked free object in span 0x7fef20c4c8a8, elemsize=24 freeindex=0 (bad use of unsafe.Pointer? try -d=checkptr)
0xc14a028000 free unmarked
0xc14a028018 free unmarked
0xc14a028030 free unmarked
0xc14a028048 free unmarked
0xc14a028060 free unmarked
0xc14a028078 free marked zombie
0x000000c14a028078: 0x000000c0003f2800 0x0000000000000800
0x000000c14a028088: 0x0000000000000800
0xc14a028090 free unmarked
0xc14a029fc8 free unmarked
0xc14a029fe0 free unmarked
fatal error: found pointer to free object
goroutine 1482447 [running]:
runtime.throw({0x29b9a01?, 0xc14a028090?})
/usr/lib/golang/src/runtime/panic.go:1047 +0x5d fp=0xc2555a2ae0 sp=0xc2555a2ab0 pc=0x44775d
runtime.(*mspan).reportZombies(0x7fef20c4c8a8)
/usr/lib/golang/src/runtime/mgcsweep.go:788 +0x2e5 fp=0xc2555a2b60 sp=0xc2555a2ae0 pc=0x435dc5
Error Log 2
runtime: pointer 0xc079e0001d to unallocated span span.base()=0xc079dfe000 span.limit=0xc079e2e010 span.state=0
runtime: found in object at *(0xc032b5c180+0x28)
object=0xc032b5c180 s.base()=0xc032b5c000 s.limit=0xc032b5e000 s.spanclass=20 s.elemsize=128 s.state=mSpanInUse
*(object+0) = 0x0
*(object+8) = 0x400000016
*(object+16) = 0xffffffff00000020
*(object+24) = 0x3a665e0
*(object+32) = 0xc0dde3d110
*(object+40) = 0xc079e0001d <==
*(object+48) = 0x8000
*(object+56) = 0x8008
*(object+64) = 0x0
*(object+72) = 0x0
*(object+80) = 0x0
*(object+88) = 0x2
*(object+96) = 0x2000
*(object+104) = 0x6174620
*(object+112) = 0x101
*(object+120) = 0x0
fatal error: found bad pointer in Go heap (incorrect use of unsafe or cgo?)
Error Log 3
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x2 addr=0x450608 pc=0x468c46]
goroutine 1202 [running]:
runtime.throw({0x3f96b81?, 0x0?})
/usr/local/go/src/runtime/panic.go:1047 +0x5d fp=0xc006433cd0 sp=0xc006433ca0 pc=0x454c7d
runtime.sigpanic()
/usr/local/go/src/runtime/signal_unix.go:819 +0x369 fp=0xc006433d20 sp=0xc006433cd0 pc=0x46ce09
runtime.(*waitq).enqueue(...)
/usr/local/go/src/runtime/chan.go:766
runtime.selectgo(0xc006433fb0, 0xc006433ed8, 0x2710?, 0x0, 0x0?, 0x1)
/usr/local/go/src/runtime/select.go:317 +0x7c6 fp=0xc006433e80 sp=0xc006433d20 pc=0x468c46
Any suggestion would be really helpful!
PS: The issue occurs rarely when the GC kicks in. So in order to have aggressive GC to test the scenario, I am currently using
GODEBUG=invalidptr=1,cgocheck=1,madvdontneed=1 GOGC=2 GOMEMLIMIT=10MiB ./my-executable