unsafe が unsafe なケース (2)


基本すぎてうっかりしてた。最も基本のメモリ境界越えパターン。

実行例 (1)
----------
やられちゃう危ないパターン。

```go
package main

import (
        "log"
        "unsafe"
)

type A struct {
        a uint8
}

func main() {
        a := &A{19}
        b := (*[32]byte)(unsafe.Pointer(a))
        log.Print(b)
        b[31] = 19
        log.Print(b)
}
```

実行結果。

```
$ go run a.go
2015/08/12 09:09:29 &[19 0 0 0 0 0 0 38 91 49 57 32 48 32 48 240 231 80 0 0 0 0 0 36 0 0 0 0 0 0 0 240]
2015/08/12 09:09:29 &[19 0 0 0 0 0 0 38 91 49 57 32 48 32 48 240 231 80 0 0 0 0 0 36 0 0 0 0 0 0 0 19]
```

実行例 (2)
----------
Windows環境。幸いにもsignalが発生して停止してもらえたパターン。結果が安定しないのも曲者。

```go
package main

import (
        "log"
        "unsafe"
)

type A struct {
        a uint8
}

func main() {
        a := &A{19}
        b := (*[32]byte)(unsafe.Pointer(a))
        b[31] = 19
        log.Print(b)
}
```

実行結果。

```
$ go run a.go
unexpected fault address 0xffffffffffffffff
fatal error: fault
[signal 0xc0000005 code=0x0 addr=0xffffffffffffffff pc=0x40b701]

goroutine 1 [running]:
runtime.gothrow(0x4eee30, 0x5)
        C:/prog/Go/src/runtime/panic.go:503 +0x8e fp=0xc0820489b8 sp=0xc0820489a0
runtime.sigpanic()
        C:/prog/Go/src/runtime/os_windows.go:45 +0x118 fp=0xc0820489d8 sp=0xc0820489b8
runtime.mallocgc(0x1, 0x4b0780, 0x1, 0xc082008622)
        C:/prog/Go/src/runtime/malloc.go:152 +0x201 fp=0xc082048a88 sp=0xc0820489d8
runtime.newobject(0x4b0780, 0xc082008622)
        C:/prog/Go/src/runtime/malloc.go:353 +0x50 fp=0xc082048ab0 sp=0xc082048a88
runtime.convT2E(0x4b0780, 0xc082048b2f, 0x0, 0x0)
        C:/prog/Go/src/runtime/iface.go:141 +0xd9 fp=0xc082048ae8 sp=0xc082048ab0
fmt.(*pp).fmtBytes(0xc0820200d0, 0xc082008251, 0x400, 0x400, 0x76, 0x291800, 0x4ad080, 0x1)
        C:/prog/Go/src/fmt/print.go:552 +0xb5e fp=0xc082048d18 sp=0xc082048ae8
fmt.(*pp).printReflectValue(0xc0820200d0, 0x4ad080, 0xc082008251, 0xd1, 0xc000000076, 0x1, 0x0)
        C:/prog/Go/src/fmt/print.go:954 +0x77c fp=0xc082049498 sp=0xc082048d18
fmt.(*pp).printValue(0xc0820200d0, 0x4ad080, 0xc082008251, 0xd1, 0x76, 0x1, 0x0)
        C:/prog/Go/src/fmt/print.go:836 +0x483 fp=0xc082049588 sp=0xc082049498
fmt.(*pp).printReflectValue(0xc0820200d0, 0x4a69c0, 0xc082008251, 0x16, 0x76, 0x0, 0x284400)
        C:/prog/Go/src/fmt/print.go:991 +0x39db fp=0xc082049d08 sp=0xc082049588
fmt.(*pp).printArg(0xc0820200d0, 0x4a69c0, 0xc082008251, 0xc000000076, 0x0, 0x100000000)
        C:/prog/Go/src/fmt/print.go:798 +0x456 fp=0xc082049dd8 sp=0xc082049d08
fmt.(*pp).doPrint(0xc0820200d0, 0xc082049f68, 0x1, 0x1, 0x0)
        C:/prog/Go/src/fmt/print.go:1218 +0x2b3 fp=0xc082049eb0 sp=0xc082049dd8
fmt.Sprint(0xc082049f68, 0x1, 0x1, 0x0, 0x0)
        C:/prog/Go/src/fmt/print.go:239 +0x72 fp=0xc082049ef8 sp=0xc082049eb0
log.Print(0xc082049f68, 0x1, 0x1)
        C:/prog/Go/src/log/log.go:270 +0x43 fp=0xc082049f40 sp=0xc082049ef8
main.main()
```

ちなみに上のトレースは Windows の場合。同じコードは Linux x86_64 では止めてくれなかった。

実行例 (3)
--------------
必ず `unsafe.Sizeof` で長さを測って、アクセスして良い領域かどうか確認するのが基本。しかし次のようなミスは結構やりがち。

```go
package main

import (
        "log"
        "unsafe"
)

type A struct {
        a uint8
}

func main() {
        a := &A{19}
        b := (*[unsafe.Sizeof(a)]byte)(unsafe.Pointer(a))
        log.Print(b)
}
```

ポインタのサイズを確認してどうする？ってやつですね。正しくはこう。

```go
package main

import (
        "log"
        "unsafe"
)

type A struct {
        a uint8
}

func main() {
        a := A{19}
        b := (*[unsafe.Sizeof(a)]byte)(unsafe.Pointer(&a))
        log.Print(b)
}
```

関連するものを軽く補足しておくと：

`uintptr` へのキャストを使うとアドレス計算もできてしまうけど、アドレス計算は*決してやらない*こと。

`cgo` も絡んでいるときは、`C.sizeof` と `unsafe.Sizeof` は*別物*であることによくよく注意すること。