golang unsafe 包修改私有属性没加偏移量会有什么隐患?

2023-09-20 17:15:18 +08:00
 Nazz
type Buffer struct {
	buf      []byte
	off      int
	lastRead int8
}

//go:nosplit
func BufferReset(b *bytes.Buffer, p []byte) {
	buffer := (*Buffer)(unsafe.Pointer(b))
	buffer.off = 0
}
944 次点击
所在节点    程序员
11 条回复
Thiece
2023-09-20 17:45:30 +08:00
如果你错误地修改了结构体的私有字段,可能会导致数据状态的不一致。例如,可能会影响缓冲区( buffer )的长度和容量,使其不匹配。

bytes.Buffer 的实现在未来版本中发生变化也会出现问题。
Nazz
2023-09-20 17:47:28 +08:00
@Thiece 目前没发现影响正确性(后面两个字段的值本来就是 0), 但是增加了一次 allocs 影响了性能
Nazz
2023-09-20 17:48:34 +08:00
@Thiece 你能给出一个影响数据长度或内容的 demo 吗
Thiece
2023-09-21 10:13:26 +08:00
```
package main

import (
"fmt"
"unsafe"
)

type MyData struct {
length int
data int
}

func main() {
data := MyData{
length: 5,
data: 100,
}

fmt.Println("Before:", data)

// 获取 MyData 结构体的地址,并转换为通用指针
ptr := unsafe.Pointer(&data)

// 模拟不加偏移量的修改操作
*(*int)(ptr) = 200

fmt.Println("After:", data)
}
```
Nazz
2023-09-21 10:27:57 +08:00
@Thiece 不加偏移量取的就是首地址, 等价于第一个字段
Nazz
2023-09-21 10:36:01 +08:00
@Thiece 这个例子里面,
Nazz
2023-09-21 10:36:21 +08:00
Nazz
2023-09-21 10:38:09 +08:00
@Thiece 这个例子里面, BufferReset2 比 BufferReset1 多一次 allocs

https://gist.github.com/lxzan/8f491e332a77ea6a049a78074799e9d7
Thiece
2023-09-21 17:13:14 +08:00
@Nazz
Go 语言中的结构体是将 Field 按照声明的顺序进行内存布局。
如果没有使用 unsafe.Offsetof 之类的方法,unsafe.Pointer 获取到的地址就是该结构体的首地址。
结构体的首地址就是其第一个字段的地址,等同于访问该结构体的第一个字段。

BufferReset2 不仅重置了 buf 和 off ,还重置了 lastRead 。
Nazz
2023-09-21 17:23:58 +08:00
@Thiece BufferReset2 有内存安全方面的隐患吗?
很好奇为什么多了一次 allocs, 而且 go bench 特别慢, 但是 qps 又挺正常的
Thiece
2023-09-22 14:22:55 +08:00
@Nazz
在你的 github 这个代码中,BufferReset2 虽然用了 unsafe 但是操作的都是 Buffer 结构体内定义的字段,没有越界访问或对未初始化内存的操作,所以现在没有安全上的问题。
BufferReset2 中,更改了 buf 、off 和 lastRead 字段,在 BufferReset1 中仅更改了 off 字段。可能是更多字段引起额外的内存分配导致的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/975604

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX