Golang的unsafe.Pointer是真的不安全
程序员文章站
2022-06-22 20:02:13
...
今天看了一篇文章,介绍通过指针的方式在类型强转的过程中避免数据拷贝。代码如下:
a :="aaa"
ssh := *(*reflect.StringHeader)(unsafe.Pointer(&a))
b := *(*[]byte)(unsafe.Pointer(&ssh))
fmt.Printf("%T",b)
fmt.Printf("%v",b)
原理就是,内置string类型其实本质是个reflect.StringHeader,内置切片类型其本质其实是reflect.SliceHeader,他们的定义分别如下:
type StringHeader struct {
Data uintptr
Len int
}
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
这样在进行指针强转的过程中,其实是把指向reflect.StringHeader结构体的指针赋值给指向reflect.SliceHeader结构体的指针。表面上看打印的结果没问题,但其实有大问题。在程序里如果增加下面这条语句就能看出端倪。
fmt.Printf("len(b)=%d,cap(b)=%d\n",len(b),cap(b))
在我的电脑上运行结果如下:
len(b)=3,cap(b)=4833819
可以看到len(b)结果是正确的,但是cap(b)结果看上去让人感觉莫名其妙。这是为什么呢?
这是因为,在读取b这个reflect.SliceHeader类型数据的时候,它其实是强制套在一个reflect.StringHeader数据上的。这样第一个指针Data内容是相同的,第二个Len字段数据也是相同的,这导致len(b)的结果和len(a)的结果是相同的。但有安全隐患的是cap(b),也就是b的第三个字段Cap,它在reflect.StringHeader里是没有数据跟它对应的,所以访问这个字段的数据其实是内存越界访问了reflect.StringHeader结构体外面的数据,这是不安全的。
所以强烈不建议用类似的奇技淫巧工作,还是写一些安全点的代码好。unsafe.Pointer从名字上也警告我们,这是不安全的,它绕开了golang的安全性检查,做的都是一些不保证安全的工作。
(全文完)
参考资料: