在Go中使用reflect.StringHeader安全吗?

问题描述 投票:0回答:1

我有一个小函数,它将 Go 字符串数据的指针传递给 C(Lua 库):

func (L *C.lua_State) pushLString(s string) {
    gostr := (*reflect.StringHeader)(unsafe.Pointer(&s))
    C.lua_pushlstring(L, (*C.char)(unsafe.Pointer(gostr.Data)), C.ulong(gostr.Len))
    // lua_pushlstring copies the given string, not keeping the original pointer.
}

它可以在简单的测试中工作,但从文档来看,尚不清楚这是否安全。

根据Go文档

reflect.StringHeader
的内存应该固定为gostr,但是
Stringheader.Data
已经是一个
uintptr
“没有指针语义的整数值” - 这本身就是奇怪的因为如果它没有指针语义,那么该字段是否完全无用,因为内存可能会在读取值后立即移动?还是像
reflect.Value.Pointer
一样对字段进行了特殊处理?或者也许有不同的方法从字符串获取 C 指针?

go cgo
1个回答
1
投票

尚不清楚这是否安全。

2022 年第 4 季度:Tapir Liui (https://twitter.com/TapirLiu/) 和 Go101 (https://github.com/go101/go101) 给出了关于 reflect 的“安全性”的线索。 此推文中的 StringHeader

从 Go 1.20 开始,

reflect.StringHeader
reflect.SliceHeader
类型将被贬值
,不建议使用。

因此,Go 1.20 中将引入两个函数

unsafe.StringData
unsafe.SliceData
来接管两个旧反射类型的用例。

这最初在 CL 401434 中讨论,然后在 issue 53003 中讨论。

弃用的原因是

reflect.SliceHeader
reflect.StringHeader
经常被误用
此外,这些类型一直被记录为不稳定且不值得依赖。

我们可以在 Github code search 中看到这些类型的使用无处不在。
我见过的最常见的用例是:

  • []byte
    转换为
    string
    :
    相当于
    *(*string)(unsafe.Pointer(&mySlice))
    ,实际上从未在任何地方正式记录为可以信赖的东西。
    在引擎盖下,
    string
    的形状小于切片,因此根据不安全规则这似乎是有效的。
  • string
    转换为
    []byte
    :
    通常被视为
    *(*[]byte)(unsafe.Pointer(&string))
    ,默认情况下会被破坏,因为
    Cap
    字段可能会超出页面边界的末尾(此处的示例,在广泛使用的代码中)——这违反了不安全规则。
  • 获取 ffi 的数据指针字段或其他一些利基使用将一种类型的切片转换为另一种类型的切片

伊恩·兰斯·泰勒添加

unsafe.Slice 的主要用例之一是创建一个切片,其支持数组是从 C 代码或 syscall.MMap 等调用返回的内存缓冲区。
我同意它可以用于(不安全地)从一种类型的切片转换为另一种类型的切片。


2023 年第 4 季度:此问题已从提案中关闭/删除,问题 53003 的结论是:

为什么最初提出的函数

StringToBytes
BytesToString
没有添加到不安全的包中,所以人们一次又一次地停止编写各种正确性有问题的实现,这超出了我的理解。

回复:

因为:

  • StringToBytes
    现在只是
    unsafe.Slice(unsafe.StringData(s), len(s))
  • BytesToString
    就是
    unsafe.String(unsafe.SliceData(b), len(b))
    ,

这两者都非常简单,并且不应该有任何正确性值得怀疑的实现。

© www.soinside.com 2019 - 2024. All rights reserved.