summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJamie Liu <jamieliu@google.com>2021-02-17 17:39:24 -0800
committergVisor bot <gvisor-bot@google.com>2021-02-17 17:41:10 -0800
commitf051ec64639b83faabcfe766ff078072def3c2aa (patch)
tree892c0fc2f4fd4138299c3c0d5a5836a940a670b5
parent4bc7daf91a0d9102fa477b199964e7db45066da1 (diff)
Add gohacks.Slice/StringHeader.
See https://github.com/golang/go/issues/19367 for rationale. Note that the upstream decision arrived at in that thread, while useful for some of our use cases, doesn't account for all of our SliceHeader use cases (we often use SliceHeader to extract pointers from slices in a way that avoids bounds checking and/or handles nil slices correctly) and also doesn't exist yet. PiperOrigin-RevId: 358071574
-rw-r--r--pkg/fdchannel/BUILD7
-rw-r--r--pkg/fdchannel/fdchannel_unsafe.go9
-rw-r--r--pkg/flipcall/flipcall_unsafe.go13
-rw-r--r--pkg/gohacks/gohacks_unsafe.go38
-rw-r--r--pkg/safemem/BUILD5
-rw-r--r--pkg/safemem/block_unsafe.go13
-rw-r--r--pkg/safemem/seq_unsafe.go7
-rw-r--r--pkg/sentry/arch/stack_unsafe.go23
-rw-r--r--pkg/sentry/vfs/mount_unsafe.go5
-rw-r--r--pkg/sync/generic_atomicptrmap_unsafe.go7
-rw-r--r--pkg/usermem/addr_range_seq_unsafe.go7
11 files changed, 72 insertions, 62 deletions
diff --git a/pkg/fdchannel/BUILD b/pkg/fdchannel/BUILD
index d9104ef02..0b34cef03 100644
--- a/pkg/fdchannel/BUILD
+++ b/pkg/fdchannel/BUILD
@@ -6,6 +6,9 @@ go_library(
name = "fdchannel",
srcs = ["fdchannel_unsafe.go"],
visibility = ["//visibility:public"],
+ deps = [
+ "//pkg/gohacks",
+ ],
)
go_test(
@@ -13,5 +16,7 @@ go_test(
size = "small",
srcs = ["fdchannel_test.go"],
library = ":fdchannel",
- deps = ["//pkg/sync"],
+ deps = [
+ "//pkg/sync",
+ ],
)
diff --git a/pkg/fdchannel/fdchannel_unsafe.go b/pkg/fdchannel/fdchannel_unsafe.go
index b253a8fdd..0ebdedf26 100644
--- a/pkg/fdchannel/fdchannel_unsafe.go
+++ b/pkg/fdchannel/fdchannel_unsafe.go
@@ -20,9 +20,10 @@ package fdchannel
import (
"fmt"
- "reflect"
"syscall"
"unsafe"
+
+ "gvisor.dev/gvisor/pkg/gohacks"
)
// int32 is the real type of a file descriptor.
@@ -53,10 +54,10 @@ func (ep *Endpoint) Init(sockfd int) {
// sendmsg+recvmsg for a zero-length datagram is slightly faster than
// sendmsg+recvmsg for a single byte over a stream socket.
cmsgSlice := make([]byte, syscall.CmsgSpace(sizeofInt32))
- cmsgReflect := (*reflect.SliceHeader)(unsafe.Pointer(&cmsgSlice))
+ cmsgSliceHdr := (*gohacks.SliceHeader)(unsafe.Pointer(&cmsgSlice))
ep.sockfd = int32(sockfd)
- ep.msghdr.Control = (*byte)(unsafe.Pointer(cmsgReflect.Data))
- ep.cmsg = (*syscall.Cmsghdr)(unsafe.Pointer(cmsgReflect.Data))
+ ep.msghdr.Control = (*byte)(cmsgSliceHdr.Data)
+ ep.cmsg = (*syscall.Cmsghdr)(cmsgSliceHdr.Data)
// ep.msghdr.Controllen and ep.cmsg.* are mutated by recvmsg(2), so they're
// set before calling sendmsg/recvmsg.
}
diff --git a/pkg/flipcall/flipcall_unsafe.go b/pkg/flipcall/flipcall_unsafe.go
index 580bf23a4..613ed8943 100644
--- a/pkg/flipcall/flipcall_unsafe.go
+++ b/pkg/flipcall/flipcall_unsafe.go
@@ -61,13 +61,12 @@ func (ep *Endpoint) dataLen() *uint32 {
// - Writers must not assume that they will read back the same data that they
// have written. In other words, writers should avoid reading from Data() at
// all.
-func (ep *Endpoint) Data() []byte {
- var bs []byte
- bsReflect := (*reflect.SliceHeader)(unsafe.Pointer(&bs))
- bsReflect.Data = ep.packet + PacketHeaderBytes
- bsReflect.Len = int(ep.dataCap)
- bsReflect.Cap = int(ep.dataCap)
- return bs
+func (ep *Endpoint) Data() (bs []byte) {
+ bshdr := (*reflect.SliceHeader)(unsafe.Pointer(&bs))
+ bshdr.Data = ep.packet + PacketHeaderBytes
+ bshdr.Len = int(ep.dataCap)
+ bshdr.Cap = int(ep.dataCap)
+ return
}
// ioSync is a dummy variable used to indicate synchronization to the Go race
diff --git a/pkg/gohacks/gohacks_unsafe.go b/pkg/gohacks/gohacks_unsafe.go
index aad675172..d26c1ac05 100644
--- a/pkg/gohacks/gohacks_unsafe.go
+++ b/pkg/gohacks/gohacks_unsafe.go
@@ -12,14 +12,35 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// +build go1.13
+// +build !go1.17
+
+// Check type signatures when updating Go version.
+
// Package gohacks contains utilities for subverting the Go compiler.
package gohacks
import (
- "reflect"
"unsafe"
)
+// SliceHeader is equivalent to reflect.SliceHeader, but represents the pointer
+// to the underlying array as unsafe.Pointer rather than uintptr, allowing
+// SliceHeaders to be directly converted to slice objects.
+type SliceHeader struct {
+ Data unsafe.Pointer
+ Len int
+ Cap int
+}
+
+// StringHeader is equivalent to reflect.StringHeader, but represents the
+// pointer to the underlying array as unsafe.Pointer rather than uintptr,
+// allowing StringHeaders to be directly converted to strings.
+type StringHeader struct {
+ Data unsafe.Pointer
+ Len int
+}
+
// Noescape hides a pointer from escape analysis. Noescape is the identity
// function but escape analysis doesn't think the output depends on the input.
// Noescape is inlined and currently compiles down to zero instructions.
@@ -36,22 +57,21 @@ func Noescape(p unsafe.Pointer) unsafe.Pointer {
// ImmutableBytesFromString is equivalent to []byte(s), except that it uses the
// same memory backing s instead of making a heap-allocated copy. This is only
// valid if the returned slice is never mutated.
-func ImmutableBytesFromString(s string) []byte {
- shdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
- var bs []byte
- bshdr := (*reflect.SliceHeader)(unsafe.Pointer(&bs))
+func ImmutableBytesFromString(s string) (bs []byte) {
+ shdr := (*StringHeader)(unsafe.Pointer(&s))
+ bshdr := (*SliceHeader)(unsafe.Pointer(&bs))
bshdr.Data = shdr.Data
bshdr.Len = shdr.Len
bshdr.Cap = shdr.Len
- return bs
+ return
}
// StringFromImmutableBytes is equivalent to string(bs), except that it uses
// the same memory backing bs instead of making a heap-allocated copy. This is
// only valid if bs is never mutated after StringFromImmutableBytes returns.
func StringFromImmutableBytes(bs []byte) string {
- // This is cheaper than messing with reflect.StringHeader and
- // reflect.SliceHeader, which as of this writing produces many dead stores
- // of zeroes. Compare strings.Builder.String().
+ // This is cheaper than messing with StringHeader and SliceHeader, which as
+ // of this writing produces many dead stores of zeroes. Compare
+ // strings.Builder.String().
return *(*string)(unsafe.Pointer(&bs))
}
diff --git a/pkg/safemem/BUILD b/pkg/safemem/BUILD
index 68ed074f8..d3b9b0ca9 100644
--- a/pkg/safemem/BUILD
+++ b/pkg/safemem/BUILD
@@ -11,7 +11,10 @@ go_library(
"seq_unsafe.go",
],
visibility = ["//:sandbox"],
- deps = ["//pkg/safecopy"],
+ deps = [
+ "//pkg/gohacks",
+ "//pkg/safecopy",
+ ],
)
go_test(
diff --git a/pkg/safemem/block_unsafe.go b/pkg/safemem/block_unsafe.go
index 7857f5853..93879bb4f 100644
--- a/pkg/safemem/block_unsafe.go
+++ b/pkg/safemem/block_unsafe.go
@@ -16,9 +16,9 @@ package safemem
import (
"fmt"
- "reflect"
"unsafe"
+ "gvisor.dev/gvisor/pkg/gohacks"
"gvisor.dev/gvisor/pkg/safecopy"
)
@@ -148,12 +148,11 @@ func (b Block) TakeFirst64(n uint64) Block {
// ToSlice returns a []byte equivalent to b.
func (b Block) ToSlice() []byte {
- var bs []byte
- hdr := (*reflect.SliceHeader)(unsafe.Pointer(&bs))
- hdr.Data = uintptr(b.start)
- hdr.Len = b.length
- hdr.Cap = b.length
- return bs
+ return *(*[]byte)(unsafe.Pointer(&gohacks.SliceHeader{
+ Data: b.start,
+ Len: b.length,
+ Cap: b.length,
+ }))
}
// Addr returns b's start address as a uintptr. It returns uintptr instead of
diff --git a/pkg/safemem/seq_unsafe.go b/pkg/safemem/seq_unsafe.go
index fc4049eeb..b315b0e5a 100644
--- a/pkg/safemem/seq_unsafe.go
+++ b/pkg/safemem/seq_unsafe.go
@@ -17,9 +17,10 @@ package safemem
import (
"bytes"
"fmt"
- "reflect"
"syscall"
"unsafe"
+
+ "gvisor.dev/gvisor/pkg/gohacks"
)
// A BlockSeq represents a sequence of Blocks, each of which has non-zero
@@ -184,8 +185,8 @@ func (bs BlockSeq) Tail() BlockSeq {
return BlockSeq{}
}
var extSlice []Block
- extSliceHdr := (*reflect.SliceHeader)(unsafe.Pointer(&extSlice))
- extSliceHdr.Data = uintptr(bs.data)
+ extSliceHdr := (*gohacks.SliceHeader)(unsafe.Pointer(&extSlice))
+ extSliceHdr.Data = bs.data
extSliceHdr.Len = bs.length
extSliceHdr.Cap = bs.length
tailSlice := skipEmpty(extSlice[1:])
diff --git a/pkg/sentry/arch/stack_unsafe.go b/pkg/sentry/arch/stack_unsafe.go
index a90d297ee..0e478e434 100644
--- a/pkg/sentry/arch/stack_unsafe.go
+++ b/pkg/sentry/arch/stack_unsafe.go
@@ -15,8 +15,6 @@
package arch
import (
- "reflect"
- "runtime"
"unsafe"
"gvisor.dev/gvisor/pkg/marshal/primitive"
@@ -33,35 +31,22 @@ import (
// On error, the contents of the stack and the bottom cursor are undefined.
func (s *Stack) pushAddrSliceAndTerminator(src []usermem.Addr) (int, error) {
// Note: Stack grows upwards, so push the terminator first.
- srcHdr := (*reflect.SliceHeader)(unsafe.Pointer(&src))
switch s.Arch.Width() {
case 8:
nNull, err := primitive.CopyUint64Out(s, StackBottomMagic, 0)
if err != nil {
return 0, err
}
- var dst []uint64
- dstHdr := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
- dstHdr.Data = srcHdr.Data
- dstHdr.Len = srcHdr.Len
- dstHdr.Cap = srcHdr.Cap
- n, err := primitive.CopyUint64SliceOut(s, StackBottomMagic, dst)
- // Ensures src doesn't get GCed until we're done using it through dst.
- runtime.KeepAlive(src)
+ srcAsUint64 := *(*[]uint64)(unsafe.Pointer(&src))
+ n, err := primitive.CopyUint64SliceOut(s, StackBottomMagic, srcAsUint64)
return n + nNull, err
case 4:
nNull, err := primitive.CopyUint32Out(s, StackBottomMagic, 0)
if err != nil {
return 0, err
}
- var dst []uint32
- dstHdr := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
- dstHdr.Data = srcHdr.Data
- dstHdr.Len = srcHdr.Len
- dstHdr.Cap = srcHdr.Cap
- n, err := primitive.CopyUint32SliceOut(s, StackBottomMagic, dst)
- // Ensure src doesn't get GCed until we're done using it through dst.
- runtime.KeepAlive(src)
+ srcAsUint32 := *(*[]uint32)(unsafe.Pointer(&src))
+ n, err := primitive.CopyUint32SliceOut(s, StackBottomMagic, srcAsUint32)
return n + nNull, err
default:
panic("Unsupported arch width")
diff --git a/pkg/sentry/vfs/mount_unsafe.go b/pkg/sentry/vfs/mount_unsafe.go
index 0df023713..c7a78d8f8 100644
--- a/pkg/sentry/vfs/mount_unsafe.go
+++ b/pkg/sentry/vfs/mount_unsafe.go
@@ -17,7 +17,6 @@ package vfs
import (
"fmt"
"math/bits"
- "reflect"
"sync/atomic"
"unsafe"
@@ -153,8 +152,8 @@ func (mt *mountTable) Init() {
func newMountTableSlots(cap uintptr) unsafe.Pointer {
slice := make([]mountSlot, cap, cap)
- hdr := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
- return unsafe.Pointer(hdr.Data)
+ hdr := (*gohacks.SliceHeader)(unsafe.Pointer(&slice))
+ return hdr.Data
}
// Lookup returns the Mount with the given parent, mounted at the given point.
diff --git a/pkg/sync/generic_atomicptrmap_unsafe.go b/pkg/sync/generic_atomicptrmap_unsafe.go
index c70dda6dd..3e98cb309 100644
--- a/pkg/sync/generic_atomicptrmap_unsafe.go
+++ b/pkg/sync/generic_atomicptrmap_unsafe.go
@@ -17,8 +17,6 @@
package atomicptrmap
import (
- "reflect"
- "runtime"
"sync/atomic"
"unsafe"
@@ -372,9 +370,8 @@ func (shard *apmShard) rehash(oldSlots unsafe.Pointer) {
// Allocate the new table.
newSlotsSlice := make([]apmSlot, newSize)
- newSlotsReflect := (*reflect.SliceHeader)(unsafe.Pointer(&newSlotsSlice))
- newSlots := unsafe.Pointer(newSlotsReflect.Data)
- runtime.KeepAlive(newSlotsSlice)
+ newSlotsHeader := (*gohacks.SliceHeader)(unsafe.Pointer(&newSlotsSlice))
+ newSlots := newSlotsHeader.Data
newMask := newSize - 1
// Start a writer critical section now so that racing users of the old
diff --git a/pkg/usermem/addr_range_seq_unsafe.go b/pkg/usermem/addr_range_seq_unsafe.go
index 495896ded..c9a1415a0 100644
--- a/pkg/usermem/addr_range_seq_unsafe.go
+++ b/pkg/usermem/addr_range_seq_unsafe.go
@@ -17,8 +17,9 @@ package usermem
import (
"bytes"
"fmt"
- "reflect"
"unsafe"
+
+ "gvisor.dev/gvisor/pkg/gohacks"
)
// An AddrRangeSeq represents a sequence of AddrRanges.
@@ -163,8 +164,8 @@ func (ars AddrRangeSeq) externalTail() AddrRangeSeq {
tailLimit = int64(ars.limit - headLen)
}
var extSlice []AddrRange
- extSliceHdr := (*reflect.SliceHeader)(unsafe.Pointer(&extSlice))
- extSliceHdr.Data = uintptr(ars.data)
+ extSliceHdr := (*gohacks.SliceHeader)(unsafe.Pointer(&extSlice))
+ extSliceHdr.Data = ars.data
extSliceHdr.Len = ars.length
extSliceHdr.Cap = ars.length
return addrRangeSeqFromSliceLimited(extSlice[1:], tailLimit)