欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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的安全性检查,做的都是一些不保证安全的工作。

(全文完)

参考资料:

https://mp.weixin.qq.com/s?__biz=MzAwMDAxNjU4Mg==&mid=2247483669&idx=1&sn=88f754ddabc04eb3f66ba8ac37ee1461&chksm=9aee28bcad99a1aa1ada41cfccaffc7ef4719a9bc11c1bef45b7d1b5427c1faa12d8d0c3156f&token=2092782362&lang=zh_CN&scene=21#wechat_redirect