Skip to content

NoCopyStringToBytes exposes undefined slice capacity #6783

@hsjsstn

Description

@hsjsstn

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))
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions