Skip to content

Incorrect handling of typed array → unsafe.Pointer → typed array conversion. #1001

Open
@nevkontakte

Description

@nevkontakte

Consider the following snippet:

package main

import (
	"unsafe"
)

func main() {
	bytes := [4]byte{1, 2, 3, 4}
	ptr := unsafe.Pointer(&bytes[0])
	ints := (*[1]int32)(ptr)
	want := &[1]int32{0x04030201} // Assuming little endian.
	println("Should be the same:", ints, want)

	// Try modifying the original bytes.
	ints[0] = 0x05060708
	println("Got:", bytes[0], bytes[1], bytes[2], bytes[3])
	println("Want:", 8, 7, 6, 5) // Assuming little endian.
}

When executed under native Go:

$ go run main.go
Should be the same: 0xc000034750 0xc000034754
Got: 8 7 6 5
Want: 8 7 6 5

When executed under GopherJS:

$ gopherjs run main.go
Should be the same: Uint8Array [ 1, 2, 3, 4 ] Int32Array [ 67305985 ]
Got: 8 2 3 4
Want: 8 7 6 5

The most important issue is in the last two lines: assigning the full int should have modified all 4 bytes, but only the least significant byte was actually changed. This can lead to significant correctness problems with the code which does low-level byte manipulation.

I think the root cause here is that the compiler provides special handling for converting an array-like type into unsafe.Pointer, but it only returns the underlying typed array (e.g. Uint8Array). It doesn't seem to provide complimentary handling of the opposite conversion. As a result, [n]byteunsafe.Pointer[n]byte works ok, but [n]byteunsafe.Pointer[m]int32 does not, leading to obscure runtime errors.

A better solution is to extract the underlying ArrayBuffer when converting to unsafe.Pointer and wrap it into the correct typed array when converting back.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions