驗證golang中unsafe包不安全
在go中,uintptr不能持有物件,unsafe包不安全,但是我之前一直沒有時間驗證,今天寫了段程式碼驗證了一下。
程式碼
package main import ( "fmt" "unsafe" ) func main() { a := f() b := f2() fmt.Println(a) fmt.Println(b) } //go:noinline func f() unsafe.Pointer { d := 1 p := unsafe.Pointer(&d) return p } //go:noinline func f2() uintptr { d := 1 p := uintptr(unsafe.Pointer(&d)) return p }
逃逸分析
根據逃逸分析可以看出來f和f2這兩個函式中的d變數分別分配在哪裡:
編譯引數方法
$ go build -gcflags '-m -m' unsafe.go # command-line-arguments ./unsafe.go:16:6: cannot inline f: marked go:noinline ./unsafe.go:23:6: cannot inline f2: marked go:noinline ./unsafe.go:8:6: cannot inline main: function too complex: cost 260 exceeds budget 80 ./unsafe.go:11:13: inlining call to fmt.Println func(...interface {}) (int, error) { return fmt.Fprintln(io.Writer(os.Stdout), fmt.a...) } ./unsafe.go:12:13: inlining call to fmt.Println func(...interface {}) (int, error) { return fmt.Fprintln(io.Writer(os.Stdout), fmt.a...) } ./unsafe.go:18:22: &d escapes to heap ./unsafe.go:18:22: from p (assigned) at ./unsafe.go:18:4 ./unsafe.go:18:22: from ~r0 (return) at ./unsafe.go:19:2 ./unsafe.go:17:2: moved to heap: d ./unsafe.go:25:30: f2 &d does not escape ./unsafe.go:11:13: a escapes to heap ./unsafe.go:11:13: from ~arg0 (assign-pair) at ./unsafe.go:11:13 ./unsafe.go:11:13: io.Writer(os.Stdout) escapes to heap ./unsafe.go:11:13: from io.Writer(os.Stdout) (passed to call[argument escapes]) at ./unsafe.go:11:13 ./unsafe.go:12:13: io.Writer(os.Stdout) escapes to heap ./unsafe.go:12:13: from io.Writer(os.Stdout) (passed to call[argument escapes]) at ./unsafe.go:12:13 ./unsafe.go:12:13: b escapes to heap ./unsafe.go:12:13: from ~arg0 (assign-pair) at ./unsafe.go:12:13 ./unsafe.go:12:13: from []interface {} literal (slice-literal-element) at ./unsafe.go:12:13 ./unsafe.go:12:13: from fmt.a (assigned) at ./unsafe.go:12:13 ./unsafe.go:12:13: from *fmt.a (indirection) at ./unsafe.go:12:13 ./unsafe.go:12:13: from fmt.a (passed to call[argument content escapes]) at ./unsafe.go:12:13 ./unsafe.go:11:13: main []interface {} literal does not escape ./unsafe.go:12:13: main []interface {} literal does not escape <autogenerated>:1: os.(*File).close .this does not escape
可以看出來在函式f中,d逃逸到堆上;但是在函式f2中,d沒有發生逃逸,uintptr沒有持有物件。
彙編
再來看看彙編的結果:
$ go tool compile -S unsafe.go | grep unsafe.go:24 0x000e 00014 (unsafe.go:24)PCDATA$2, $0 0x000e 00014 (unsafe.go:24)PCDATA$0, $0 0x000e 00014 (unsafe.go:24)MOVQ$1, "".d(SP) $ go tool compile -S unsafe.go | grep unsafe.go:17 0x001d 00029 (unsafe.go:17)PCDATA$2, $1 0x001d 00029 (unsafe.go:17)PCDATA$0, $0 0x001d 00029 (unsafe.go:17)LEAQtype.int(SB), AX 0x0024 00036 (unsafe.go:17)PCDATA$2, $0 0x0024 00036 (unsafe.go:17)MOVQAX, (SP) 0x0028 00040 (unsafe.go:17)CALLruntime.newobject(SB) 0x002d 00045 (unsafe.go:17)PCDATA$2, $1 0x002d 00045 (unsafe.go:17)MOVQ8(SP), AX 0x0032 00050 (unsafe.go:17)MOVQ$1, (AX)
可以看出來,結果也是一樣的,f中的d呼叫了newobject,但是f2中沒有。
結論
所以為什麼說unsafe包不安全呢,原因之一就是因為go不保證地址一定是有效的,當然還有其它的原因,有時間再驗證分享。