LoginSignup
5
4

More than 5 years have passed since last update.

unsafe が unsafe なケース (2)

Last updated at Posted at 2015-08-12

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

実行例 (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.sizeofunsafe.Sizeof別物であることによくよく注意すること。

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4