基本すぎてうっかりしてた。最も基本のメモリ境界越えパターン。
実行例 (1)
やられちゃう危ないパターン。
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が発生して停止してもらえたパターン。結果が安定しないのも曲者。
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
で長さを測って、アクセスして良い領域かどうか確認するのが基本。しかし次のようなミスは結構やりがち。
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)
}
ポインタのサイズを確認してどうする?ってやつですね。正しくはこう。
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
は別物であることによくよく注意すること。