From 9633cf022bdb3c73bf18d37c4e1c8d0d763bf1be Mon Sep 17 00:00:00 2001 From: jan grant <3430517+jan-g@users.noreply.github.com> Date: Tue, 3 Apr 2018 16:53:02 +0100 Subject: [PATCH] Bugfix: unsafeBytes slices were getting GCed (#913) There are alternative formulations of this, for instance see https://www.reddit.com/r/golang/comments/5zctpf/unsafe_conversion_between_strings_and_byte_slices/ The problem manifested in the returned values from unsafeBytes occasionally being broken. It's possible that by keeping a reference to the `a` parameter alive, the original code would still work - however, this definitely seems like a fix. (A cast to `[]byte(a)` looks increasingly attractive, for all that it'll perform small allocations and copies.) --- api/agent/slots.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/api/agent/slots.go b/api/agent/slots.go index cbe10943b..cf67920cb 100644 --- a/api/agent/slots.go +++ b/api/agent/slots.go @@ -5,6 +5,7 @@ import ( "crypto/sha1" "encoding/binary" "hash" + "reflect" "sort" "sync" "sync/atomic" @@ -332,5 +333,17 @@ func getSlotQueueKey(call *call) string { // WARN: this is read only func unsafeBytes(a string) []byte { - return *(*[]byte)(unsafe.Pointer(&a)) + strHeader := (*reflect.StringHeader)(unsafe.Pointer(&a)) + + var b []byte + byteHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + byteHeader.Data = strHeader.Data + + // need to take the length of `a` here to ensure it's alive until after we update b's Data + // field since the garbage collector can collect a variable once it is no longer used + // not when it goes out of scope, for more details see https://github.com/golang/go/issues/9046 + l := len(a) + byteHeader.Len = l + byteHeader.Cap = l + return b }