Go vet报告了“可能误用reflect.SliceHeader”的问题。

huangapple go评论82阅读模式
英文:

Go vet reports "possible misuse of reflect.SliceHeader"

问题

我有以下代码片段,"go vet"对其发出了"possible misuse of reflect.SliceHeader"的警告。我找不到关于这个警告的很多信息,除了这个链接。阅读了之后,我对如何以一种让"go vet"满意且没有可能的垃圾回收问题的方式进行操作并不清楚。

这个代码片段的目标是让一个Go函数将数据复制到由一个不透明的C库管理的内存中。Go函数期望一个[]byte作为参数。

func Callback(ptr unsafe.Pointer, buffer unsafe.Pointer, size C.longlong) C.longlong {
        ...
        sh := &reflect.SliceHeader{
                Data: uintptr(buffer),
                Len:  int(size),
                Cap:  int(size),
        }
        buf := *(*[]byte)(unsafe.Pointer(sh))
        err := CopyToSlice(buf)
	    if err != nil {
		   log.Fatal("failed to copy to slice")
	    }
        ...
}
英文:

I have the following code snippet which "go vet" complains about with the warning "possible misuse of reflect.SliceHeader". I can not find very much information about this warning other then this. After reading that it is not very clear to me what is needed to do this in a way that makes go vet happy - and without possible gc issues.

The goal of the snippet is to have a go function copy data to memory which is managed by an opaque C library. The Go function expects a []byte as a parameter.

func Callback(ptr unsafe.Pointer, buffer unsafe.Pointer, size C.longlong) C.longlong {
        ...
        sh := &reflect.SliceHeader{
                Data: uintptr(buffer),
                Len:  int(size),
                Cap:  int(size),
        }
        buf := *(*[]byte)(unsafe.Pointer(sh))
        err := CopyToSlice(buf)
	    if err != nil {
		   log.Fatal("failed to copy to slice")
	    }
        ...
}

答案1

得分: 2

https://pkg.go.dev/unsafe@go1.19.4#Pointer

> Pointer表示对任意类型的指针。对于类型Pointer,有四个特殊操作是其他类型不可用的:
>
> - 任意类型的指针值可以转换为Pointer。
> - Pointer可以转换为任意类型的指针值。
> - uintptr可以转换为Pointer。
> - Pointer可以转换为uintptr。
>
> 因此,Pointer允许程序绕过类型系统读取和写入任意内存。应该非常小心地使用它。
>
> 以下涉及Pointer的模式是有效的。不使用这些模式的代码可能在今天无效或将来无效。即使是下面的有效模式也有重要的注意事项。
>
> 运行"go vet"可以帮助找到不符合这些模式的Pointer的使用,但是"go vet"的沉默并不保证代码是有效的。


> (6) 将reflect.SliceHeader或reflect.StringHeader的Data字段转换为Pointer,或将其从Pointer转换回来。
>
> 与前面的情况类似,reflect数据结构SliceHeader和StringHeader将字段Data声明为uintptr,以防止调用者在未导入"unsafe"的情况下将结果更改为任意类型。然而,这意味着SliceHeader和StringHeader仅在解释实际切片或字符串值的内容时才有效。
>
> go > var s string > hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1 > hdr.Data = uintptr(unsafe.Pointer(p)) // case 6 (this case) > hdr.Len = n >
>
> 在这种用法中,hdr.Data实际上是引用字符串头部中底层指针的一种替代方式,而不是uintptr变量本身。
>
> 通常情况下,应该只将reflect.SliceHeader和reflect.StringHeader用作指向实际切片或字符串的reflect.SliceHeader和reflect.StringHeader,而不是普通的结构体。程序不应该声明或分配这些结构体类型的变量。
>
> go > // 无效:直接声明的头部不会将Data作为引用。 > var hdr reflect.StringHeader > hdr.Data = uintptr(unsafe.Pointer(p)) > hdr.Len = n > s := *(*string)(unsafe.Pointer(&hdr)) // p可能已经丢失 >

英文:

https://pkg.go.dev/unsafe@go1.19.4#Pointer

> Pointer represents a pointer to an arbitrary type. There are four
> special operations available for type Pointer that are not available
> for other types:
>
> - A pointer value of any type can be converted to a Pointer.
> - A Pointer can be converted to a pointer value of any type.
> - A uintptr can be converted to a Pointer.
> - A Pointer can be converted to a uintptr.
>
> Pointer therefore allows a program to defeat the type system and read
> and write arbitrary memory. It should be used with extreme care.
>
> The following patterns involving Pointer are valid. Code not using
> these patterns is likely to be invalid today or to become invalid in
> the future. Even the valid patterns below come with important caveats.
>
> Running "go vet" can help find uses of Pointer that do not conform to
> these patterns, but silence from "go vet" is not a guarantee that the
> code is valid.


> (6) Conversion of a reflect.SliceHeader or reflect.StringHeader Data
> field to or from Pointer.
>
> As in the previous case, the reflect data structures SliceHeader and
> StringHeader declare the field Data as a uintptr to keep callers from
> changing the result to an arbitrary type without first importing
> "unsafe". However, this means that SliceHeader and StringHeader are
> only valid when interpreting the content of an actual slice or string
> value.
>
> go
> var s string
> hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1
> hdr.Data = uintptr(unsafe.Pointer(p)) // case 6 (this case)
> hdr.Len = n
>

>
> In this usage hdr.Data is really an alternate way to refer to the
> underlying pointer in the string header, not a uintptr variable
> itself.
>
> In general, reflect.SliceHeader and reflect.StringHeader should be used only as *reflect.SliceHeader and *reflect.StringHeader pointing at actual slices or strings, never as plain structs. A program should not declare or allocate variables of these struct types.
>
> go
> // INVALID: a directly-declared header will not hold Data as a reference.
> var hdr reflect.StringHeader
> hdr.Data = uintptr(unsafe.Pointer(p))
> hdr.Len = n
> s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost
>

答案2

得分: 0

看起来JimB(从评论中)暗示了最正确的答案,尽管他没有将其发布为答案,也没有包含示例。以下代码通过了go vet、staticcheck和golangci-lint的检查,并且不会导致段错误,所以我认为这是正确的答案。

func Callback(ptr unsafe.Pointer, buffer unsafe.Pointer, size C.longlong) C.longlong {
        ...
        buf := unsafe.Slice((*byte)(buffer), size)
        err := CopyToSlice(buf)
        if err != nil {
           log.Fatal("failed to copy to slice")
        }
        ...
}
英文:

It looks like JimB (from the comments) hinted upon the most correct answer, though he didn't post it as an answer and he didn't include an example. The following passes go vet, staticcheck, and golangci-lint - and doesn't segfault so I think it is the correct answer.

func Callback(ptr unsafe.Pointer, buffer unsafe.Pointer, size C.longlong) C.longlong {
        ...
        buf := unsafe.Slice((*byte)(buffer), size)
        err := CopyToSlice(buf)
        if err != nil {
           log.Fatal("failed to copy to slice")
        }
        ...
}

huangapple
  • 本文由 发表于 2022年12月14日 13:58:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/74794121.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定