diff --git a/types/address/hash.go b/types/address/hash.go index d6a249a8a..ddfec81df 100644 --- a/types/address/hash.go +++ b/types/address/hash.go @@ -64,11 +64,10 @@ func Module(moduleName string, key []byte) []byte { return Hash("module", append(mKey, key...)) } -// unsafeStrToByteArray uses unsafe to convert string into byte array. Returned array -// cannot be altered after this functions is called +// unsafeStrToByteArray uses unsafe to convert string into byte array. Returned bytes +// must not be altered after this function is called as it will cause a segmentation fault. func unsafeStrToByteArray(s string) []byte { - sh := *(*reflect.SliceHeader)(unsafe.Pointer(&s)) // nolint:govet - sh.Cap = sh.Len - bs := *(*[]byte)(unsafe.Pointer(&sh)) // nolint:govet - return bs + var buf = *(*[]byte)(unsafe.Pointer(&s)) + (*reflect.SliceHeader)(unsafe.Pointer(&buf)).Cap = len(s) + return buf } diff --git a/types/address/hash_test.go b/types/address/hash_test.go index be096c357..4cc6c6880 100644 --- a/types/address/hash_test.go +++ b/types/address/hash_test.go @@ -2,7 +2,9 @@ package address import ( "crypto/sha256" + "runtime" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -75,6 +77,23 @@ func (suite *AddressSuite) TestModule() { assert.NotEqual(addr2, addr3, "changing key must change address") } +func unsafeConvertABC() []byte { + return unsafeStrToByteArray("abc") +} + +func (suite *AddressSuite) TestUnsafeStrToBytes() { + // we convert in other function to trigger GC. We want to check that + // the underlying array in []bytes is accessible after GC will finish swapping. + for i := 0; i < 5; i++ { + b := unsafeConvertABC() + runtime.GC() + <-time.NewTimer(2 * time.Millisecond).C + b2 := append(b, 'd') + suite.Equal("abc", string(b)) + suite.Equal("abcd", string(b2)) + } +} + type addrMock struct { Addr []byte }