}
func BenchmarkRead(b *testing.B) {
+ b.Run("4", func(b *testing.B) {
+ benchmarkRead(b, 4)
+ })
b.Run("32", func(b *testing.B) {
benchmarkRead(b, 32)
})
"unsafe"
)
+//go:linkname vgetrandom runtime.vgetrandom
+func vgetrandom(p []byte, flags uint32) (ret int, supported bool)
+
var getrandomUnsupported atomic.Bool
// GetRandomFlag is a flag supported by the getrandom system call.
// GetRandom calls the getrandom system call.
func GetRandom(p []byte, flags GetRandomFlag) (n int, err error) {
+ ret, supported := vgetrandom(p, uint32(flags))
+ if supported {
+ if ret < 0 {
+ return 0, syscall.Errno(-ret)
+ }
+ return ret, nil
+ }
if getrandomUnsupported.Load() {
return 0, syscall.ENOSYS
}
// needPerThreadSyscall indicates that a per-thread syscall is required
// for doAllThreadsSyscall.
needPerThreadSyscall atomic.Uint8
+
+ // This is a pointer to a chunk of memory allocated with a special
+ // mmap invocation in vgetrandomGetState().
+ vgetrandomState uintptr
}
//go:noescape
ncpu = getproccount()
physHugePageSize = getHugePageSize()
osArchInit()
+ vgetrandomInit()
}
var urandom_dev = []byte("/dev/urandom\x00")
// Called from exitm, but not from drop, to undo the effect of thread-owned
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
func mdestroy(mp *m) {
+ if mp.vgetrandomState != 0 {
+ vgetrandomPutState(mp.vgetrandomState)
+ mp.vgetrandomState = 0
+ }
}
// #ifdef GOARCH_386
SYSCALL
MOVQ AX, ret+0(FP)
RET
+
+// func vgetrandom1(buf *byte, length uintptr, flags uint32, state uintptr, stateSize uintptr) int
+TEXT runtime·vgetrandom1<ABIInternal>(SB),NOSPLIT,$16-48
+ MOVQ SI, R8 // stateSize
+ MOVL CX, DX // flags
+ MOVQ DI, CX // state
+ MOVQ BX, SI // length
+ MOVQ AX, DI // buf
+
+ MOVQ SP, R12
+
+ MOVQ runtime·vdsoGetrandomSym(SB), AX
+ MOVQ g_m(R14), BX
+
+ MOVQ m_vdsoPC(BX), R9
+ MOVQ R9, 0(SP)
+ MOVQ m_vdsoSP(BX), R9
+ MOVQ R9, 8(SP)
+ LEAQ buf+0(FP), R9
+ MOVQ R9, m_vdsoSP(BX)
+ MOVQ -8(R9), R9
+ MOVQ R9, m_vdsoPC(BX)
+
+ ANDQ $~15, SP
+
+ CALL AX
+
+ MOVQ R12, SP
+ MOVQ 8(SP), R9
+ MOVQ R9, m_vdsoSP(BX)
+ MOVQ 0(SP), R9
+ MOVQ R9, m_vdsoPC(BX)
+ RET
SVC
MOVD R0, ret+0(FP)
RET
+
+// func vgetrandom1(buf *byte, length uintptr, flags uint32, state uintptr, stateSize uintptr) int
+TEXT runtime·vgetrandom1<ABIInternal>(SB),NOSPLIT,$16-48
+ MOVD RSP, R20
+
+ MOVD runtime·vdsoGetrandomSym(SB), R8
+ MOVD g_m(g), R21
+
+ MOVD m_vdsoPC(R21), R9
+ MOVD R9, 8(RSP)
+ MOVD m_vdsoSP(R21), R9
+ MOVD R9, 16(RSP)
+ MOVD LR, m_vdsoPC(R21)
+ MOVD $buf-8(FP), R9
+ MOVD R9, m_vdsoSP(R21)
+
+ MOVD RSP, R9
+ BIC $15, R9
+ MOVD R9, RSP
+
+ MOVBU runtime·iscgo(SB), R9
+ CBNZ R9, nosaveg
+ MOVD m_gsignal(R21), R9
+ CBZ R9, nosaveg
+ CMP g, R9
+ BEQ nosaveg
+ MOVD (g_stack+stack_lo)(R9), R22
+ MOVD g, (R22)
+
+ BL (R8)
+
+ MOVD ZR, (R22)
+ B restore
+
+nosaveg:
+ BL (R8)
+
+restore:
+ MOVD R20, RSP
+ MOVD 16(RSP), R1
+ MOVD R1, m_vdsoSP(R21)
+ MOVD 8(RSP), R1
+ MOVD R1, m_vdsoPC(R21)
+ NOP R0 // Satisfy go vet, since the return value comes from the vDSO function.
+ RET
MOVV R0, 2(R0) // unimplemented, only needed for android; declared in stubs_linux.go
MOVW R0, ret+16(FP) // for vet
RET
+
+// func vgetrandom1(buf *byte, length uintptr, flags uint32, state uintptr, stateSize uintptr) int
+TEXT runtime·vgetrandom1<ABIInternal>(SB),NOSPLIT,$16-48
+ MOVV R3, R23
+
+ MOVV runtime·vdsoGetrandomSym(SB), R12
+
+ MOVV g_m(g), R24
+
+ MOVV m_vdsoPC(R24), R13
+ MOVV R13, 8(R3)
+ MOVV m_vdsoSP(R24), R13
+ MOVV R13, 16(R3)
+ MOVV R1, m_vdsoPC(R24)
+ MOVV $buf-8(FP), R13
+ MOVV R13, m_vdsoSP(R24)
+
+ AND $~15, R3
+
+ MOVBU runtime·iscgo(SB), R13
+ BNE R13, nosaveg
+ MOVV m_gsignal(R24), R13
+ BEQ R13, nosaveg
+ BEQ g, R13, nosaveg
+ MOVV (g_stack+stack_lo)(R13), R25
+ MOVV g, (R25)
+
+ JAL (R12)
+
+ MOVV R0, (R25)
+ JMP restore
+
+nosaveg:
+ JAL (R12)
+
+restore:
+ MOVV R23, R3
+ MOVV 16(R3), R25
+ MOVV R25, m_vdsoSP(R24)
+ MOVV 8(R3), R25
+ MOVV R25, m_vdsoPC(R24)
+ NOP R4 // Satisfy go vet, since the return value comes from the vDSO function.
+ RET
MOVD R0, 0(R0) // unimplemented, only needed for android; declared in stubs_linux.go
MOVW R0, ret+16(FP) // for vet
RET
+
+// func vgetrandom1(buf *byte, length uintptr, flags uint32, state uintptr, stateSize uintptr) int
+TEXT runtime·vgetrandom1<ABIInternal>(SB),NOSPLIT,$16-48
+ MOVD R1, R15
+
+ MOVD runtime·vdsoGetrandomSym(SB), R12
+ MOVD R12, CTR
+ MOVD g_m(g), R21
+
+ MOVD m_vdsoPC(R21), R22
+ MOVD R22, 32(R1)
+ MOVD m_vdsoSP(R21), R22
+ MOVD R22, 40(R1)
+ MOVD LR, m_vdsoPC(R21)
+ MOVD $buf-FIXED_FRAME(FP), R22
+ MOVD R22, m_vdsoSP(R21)
+
+ RLDICR $0, R1, $59, R1
+
+ MOVBZ runtime·iscgo(SB), R22
+ CMP R22, $0
+ BNE nosaveg
+ MOVD m_gsignal(R21), R22
+ CMP R22, $0
+ BEQ nosaveg
+ CMP R22, g
+ BEQ nosaveg
+ MOVD (g_stack+stack_lo)(R22), R22
+ MOVD g, (R22)
+
+ BL (CTR)
+
+ MOVD $0, (R22)
+ JMP restore
+
+nosaveg:
+ BL (CTR)
+
+restore:
+ MOVD $0, R0
+ MOVD R15, R1
+ MOVD 40(R1), R22
+ MOVD R22, m_vdsoSP(R21)
+ MOVD 32(R1), R22
+ MOVD R22, m_vdsoPC(R21)
+
+ BVC out
+ NEG R3, R3
+out:
+ RET
MOVD $0, 2(R0) // unimplemented, only needed for android; declared in stubs_linux.go
MOVW R0, ret+16(FP)
RET
+
+// func vgetrandom1(buf *byte, length uintptr, flags uint32, state uintptr, stateSize uintptr) int
+TEXT runtime·vgetrandom1(SB),NOSPLIT,$16-48
+ MOVD buf+0(FP), R2
+ MOVD length+8(FP), R3
+ MOVW flags+16(FP), R4
+ MOVD state+24(FP), R5
+ MOVD stateSize+32(FP), R6
+
+ MOVD R15, R7
+
+ MOVD runtime·vdsoGetrandomSym(SB), R1
+ MOVD g_m(g), R9
+
+ MOVD m_vdsoPC(R9), R12
+ MOVD R12, 8(R15)
+ MOVD m_vdsoSP(R9), R12
+ MOVD R12, 16(R15)
+ MOVD R14, m_vdsoPC(R9)
+ MOVD $buf+0(FP), R12
+ MOVD R12, m_vdsoSP(R9)
+
+ SUB $160, R15
+ MOVD $~7, R12
+ AND R12, R15
+
+ MOVB runtime·iscgo(SB), R12
+ CMPBNE R12, $0, nosaveg
+ MOVD m_gsignal(R9), R12
+ CMPBEQ R12, $0, nosaveg
+ CMPBEQ g, R12, nosaveg
+ MOVD (g_stack+stack_lo)(R12), R12
+ MOVD g, (R12)
+
+ BL R1
+
+ MOVD $0, (R12)
+ JMP restore
+
+nosaveg:
+ BL R1
+
+restore:
+ MOVD R7, R15
+ MOVD 16(R15), R12
+ MOVD R12, m_vdsoSP(R9)
+ MOVD 8(R15), R12
+ MOVD R12, m_vdsoPC(R9)
+ MOVD R2, ret+40(FP)
+ RET
var vdsoSymbolKeys = []vdsoSymbolKey{
{"__vdso_gettimeofday", 0x315ca59, 0xb01bca00, &vdsoGettimeofdaySym},
{"__vdso_clock_gettime", 0xd35ec75, 0x6e43a318, &vdsoClockgettimeSym},
+ {"__vdso_getrandom", 0x25425d, 0x84a559bf, &vdsoGetrandomSym},
}
var (
vdsoGettimeofdaySym uintptr
vdsoClockgettimeSym uintptr
+ vdsoGetrandomSym uintptr
)
// vdsoGettimeofdaySym is accessed from the syscall package.
var vdsoSymbolKeys = []vdsoSymbolKey{
{"__kernel_clock_gettime", 0xb0cd725, 0xdfa941fd, &vdsoClockgettimeSym},
+ {"__kernel_getrandom", 0x9800c0d, 0x540d4e24, &vdsoGetrandomSym},
}
-// initialize to fall back to syscall
-var vdsoClockgettimeSym uintptr = 0
+var (
+ vdsoClockgettimeSym uintptr
+ vdsoGetrandomSym uintptr
+)
var vdsoSymbolKeys = []vdsoSymbolKey{
{"__vdso_clock_gettime", 0xd35ec75, 0x6e43a318, &vdsoClockgettimeSym},
+ {"__vdso_getrandom", 0x25425d, 0x84a559bf, &vdsoGetrandomSym},
}
-// initialize to fall back to syscall
var (
- vdsoClockgettimeSym uintptr = 0
+ vdsoClockgettimeSym uintptr
+ vdsoGetrandomSym uintptr
)
var vdsoSymbolKeys = []vdsoSymbolKey{
{"__kernel_clock_gettime", 0xb0cd725, 0xdfa941fd, &vdsoClockgettimeSym},
+ {"__kernel_getrandom", 0x9800c0d, 0x540d4e24, &vdsoGetrandomSym},
}
-// initialize with vsyscall fallbacks
var (
- vdsoClockgettimeSym uintptr = 0
+ vdsoClockgettimeSym uintptr
+ vdsoGetrandomSym uintptr
)
var vdsoSymbolKeys = []vdsoSymbolKey{
{"__kernel_clock_gettime", 0xb0cd725, 0xdfa941fd, &vdsoClockgettimeSym},
+ {"__kernel_getrandom", 0x9800c0d, 0x540d4e24, &vdsoGetrandomSym},
}
-// initialize with vsyscall fallbacks
var (
- vdsoClockgettimeSym uintptr = 0
+ vdsoClockgettimeSym uintptr
+ vdsoGetrandomSym uintptr
)
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && (amd64 || arm64 || arm64be || ppc64 || ppc64le || loong64 || s390x)
+
+package runtime
+
+import "unsafe"
+
+func vgetrandom1(buf *byte, length uintptr, flags uint32, state uintptr, stateSize uintptr) int
+
+var vgetrandomAlloc struct {
+ states []uintptr
+ statesLock mutex
+ stateSize uintptr
+ mmapProt int32
+ mmapFlags int32
+}
+
+func vgetrandomInit() {
+ if vdsoGetrandomSym == 0 {
+ return
+ }
+
+ var params struct {
+ SizeOfOpaqueState uint32
+ MmapProt uint32
+ MmapFlags uint32
+ reserved [13]uint32
+ }
+ if vgetrandom1(nil, 0, 0, uintptr(unsafe.Pointer(¶ms)), ^uintptr(0)) != 0 {
+ return
+ }
+ vgetrandomAlloc.stateSize = uintptr(params.SizeOfOpaqueState)
+ vgetrandomAlloc.mmapProt = int32(params.MmapProt)
+ vgetrandomAlloc.mmapFlags = int32(params.MmapFlags)
+
+ lockInit(&vgetrandomAlloc.statesLock, lockRankLeafRank)
+}
+
+func vgetrandomGetState() uintptr {
+ lock(&vgetrandomAlloc.statesLock)
+ if len(vgetrandomAlloc.states) == 0 {
+ num := uintptr(ncpu) // Just a reasonable size hint to start.
+ allocSize := (num*vgetrandomAlloc.stateSize + physPageSize - 1) &^ (physPageSize - 1)
+ num = (physPageSize / vgetrandomAlloc.stateSize) * (allocSize / physPageSize)
+ p, err := mmap(nil, allocSize, vgetrandomAlloc.mmapProt, vgetrandomAlloc.mmapFlags, -1, 0)
+ if err != 0 {
+ unlock(&vgetrandomAlloc.statesLock)
+ return 0
+ }
+ newBlock := uintptr(p)
+ if vgetrandomAlloc.states == nil {
+ vgetrandomAlloc.states = make([]uintptr, 0, num)
+ }
+ for i := uintptr(0); i < num; i++ {
+ if (newBlock&(physPageSize-1))+vgetrandomAlloc.stateSize > physPageSize {
+ newBlock = (newBlock + physPageSize - 1) &^ (physPageSize - 1)
+ }
+ vgetrandomAlloc.states = append(vgetrandomAlloc.states, newBlock)
+ newBlock += vgetrandomAlloc.stateSize
+ }
+ }
+ state := vgetrandomAlloc.states[len(vgetrandomAlloc.states)-1]
+ vgetrandomAlloc.states = vgetrandomAlloc.states[:len(vgetrandomAlloc.states)-1]
+ unlock(&vgetrandomAlloc.statesLock)
+ return state
+}
+
+func vgetrandomPutState(state uintptr) {
+ lock(&vgetrandomAlloc.statesLock)
+ vgetrandomAlloc.states = append(vgetrandomAlloc.states, state)
+ unlock(&vgetrandomAlloc.statesLock)
+}
+
+// This is exported for use in internal/syscall/unix as well as x/sys/unix.
+//
+//go:linkname vgetrandom
+func vgetrandom(p []byte, flags uint32) (ret int, supported bool) {
+ if vgetrandomAlloc.stateSize == 0 {
+ return -1, false
+ }
+
+ mp := acquirem()
+ if mp.vgetrandomState == 0 {
+ state := vgetrandomGetState()
+ if state == 0 {
+ releasem(mp)
+ return -1, false
+ }
+ mp.vgetrandomState = state
+ }
+
+ ret = vgetrandom1(unsafe.SliceData(p), uintptr(len(p)), flags, mp.vgetrandomState, vgetrandomAlloc.stateSize)
+ supported = true
+
+ releasem(mp)
+ return
+}
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !(linux && (amd64 || arm64 || arm64be || ppc64 || ppc64le || loong64 || s390x))
+
+package runtime
+
+import _ "unsafe"
+
+//go:linkname vgetrandom
+func vgetrandom(p []byte, flags uint32) (ret int, supported bool) {
+ return -1, false
+}
+
+func vgetrandomPutState(state uintptr) {}
+
+func vgetrandomInit() {}