Summary
NoCopyStringToBytes in pkg/strutil/convert.go reinterprets a string header as a []byte via unsafe.Pointer:
func NoCopyStringToBytes(s string) []byte {
return *(*[]byte)(unsafe.Pointer(&s))
}
string and []byte have different runtime layouts — a string has two fields (data pointer + length), while a byte slice has three (data pointer + length + capacity). Casting directly between them leaves the capacity field populated with whatever sits in adjacent memory at the time of the call.
The problem
string → [data ptr | len]
[]byte → [data ptr | len | cap]
The returned slice has an unpredictable capacity. If a caller appends to it or uses it as a write buffer, the write may go past the end of the original string:
b := NoCopyStringToBytes(s)
b = append(b, 'A') // may write outside the original string buffer if cap > len
On top of that, Go strings are immutable by contract. This function hands out a writable view into the same backing memory, so any write through the slice also mutates the original string data.
The function is exported, so nothing stops external callers from doing exactly this.
Fix (Go 1.20+)
Go 1.20 added unsafe.StringData and unsafe.Slice for this exact use case:
func NoCopyStringToBytes(s string) []byte {
return unsafe.Slice(unsafe.StringData(s), len(s))
}
Summary
NoCopyStringToBytesinpkg/strutil/convert.goreinterprets astringheader as a[]byteviaunsafe.Pointer:stringand[]bytehave different runtime layouts — a string has two fields (data pointer + length), while a byte slice has three (data pointer + length + capacity). Casting directly between them leaves the capacity field populated with whatever sits in adjacent memory at the time of the call.The problem
The returned slice has an unpredictable capacity. If a caller appends to it or uses it as a write buffer, the write may go past the end of the original string:
On top of that, Go strings are immutable by contract. This function hands out a writable view into the same backing memory, so any write through the slice also mutates the original string data.
The function is exported, so nothing stops external callers from doing exactly this.
Fix (Go 1.20+)
Go 1.20 added
unsafe.StringDataandunsafe.Slicefor this exact use case: