snapshot: sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1

Содержимое пина, зафиксированного в go.mod sing-box-lx, одним коммитом
без истории. Полная история SagerNet/gvisor — 1.45 ГБ и клонируется в
каждой CI-джобе; наша дельта — одна вставка в одну функцию, история для
неё не нужна.

Module path github.com/sagernet/gvisor сохранён намеренно: на него
опирается replace-директива суперпроекта.

Патч поверх — отдельным коммитом, чтобы дельта читалась одним git show
и переносилась на новый пин копированием.

SPECS/TASKS/048-GVISOR_HANDSHAKE_NIL_CRASH
This commit is contained in:
Leadaxe 2026-08-04 15:50:08 +03:00
commit 2c4ae3b0a4
712 changed files with 185689 additions and 0 deletions

160
pkg/safecopy/atomic_amd64.s Normal file
View file

@ -0,0 +1,160 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "textflag.h"
// handleSwapUint32Fault returns the value stored in DI. Control is transferred
// to it when swapUint32 below receives SIGSEGV or SIGBUS, with the signal
// number stored in DI.
//
// It must have the same frame configuration as swapUint32 so that it can undo
// any potential call frame set up by the assembler.
TEXT handleSwapUint32Fault(SB), NOSPLIT|NOFRAME, $0-24
MOVL DI, sig+20(FP)
RET
// swapUint32 atomically stores new into *ptr and returns (the previous ptr*
// value, 0). If a SIGSEGV or SIGBUS signal is received during the swap, the
// value of old is unspecified, and sig is the number of the signal that was
// received.
//
// Preconditions: ptr must be aligned to a 4-byte boundary.
//
//func swapUint32(ptr unsafe.Pointer, new uint32) (old uint32, sig int32)
TEXT ·swapUint32(SB), NOSPLIT|NOFRAME, $0-24
// Store 0 as the returned signal number. If we run to completion,
// this is the value the caller will see; if a signal is received,
// handleSwapUint32Fault will store a different value in this address.
MOVL $0, sig+20(FP)
MOVQ ptr+0(FP), DI
MOVL new+8(FP), AX
XCHGL AX, 0(DI)
MOVL AX, old+16(FP)
RET
// func addrOfSwapUint32() uintptr
TEXT ·addrOfSwapUint32(SB), $0-8
MOVQ $·swapUint32(SB), AX
MOVQ AX, ret+0(FP)
RET
// handleSwapUint64Fault returns the value stored in DI. Control is transferred
// to it when swapUint64 below receives SIGSEGV or SIGBUS, with the signal
// number stored in DI.
//
// It must have the same frame configuration as swapUint64 so that it can undo
// any potential call frame set up by the assembler.
TEXT handleSwapUint64Fault(SB), NOSPLIT|NOFRAME, $0-28
MOVL DI, sig+24(FP)
RET
// swapUint64 atomically stores new into *ptr and returns (the previous *ptr
// value, 0). If a SIGSEGV or SIGBUS signal is received during the swap, the
// value of old is unspecified, and sig is the number of the signal that was
// received.
//
// Preconditions: ptr must be aligned to a 8-byte boundary.
//
//func swapUint64(ptr unsafe.Pointer, new uint64) (old uint64, sig int32)
TEXT ·swapUint64(SB), NOSPLIT|NOFRAME, $0-28
// Store 0 as the returned signal number. If we run to completion,
// this is the value the caller will see; if a signal is received,
// handleSwapUint64Fault will store a different value in this address.
MOVL $0, sig+24(FP)
MOVQ ptr+0(FP), DI
MOVQ new+8(FP), AX
XCHGQ AX, 0(DI)
MOVQ AX, old+16(FP)
RET
// func addrOfSwapUint64() uintptr
TEXT ·addrOfSwapUint64(SB), NOSPLIT|NOFRAME, $0-8
MOVQ $·swapUint64(SB), AX
MOVQ AX, ret+0(FP)
RET
// handleCompareAndSwapUint32Fault returns the value stored in DI. Control is
// transferred to it when swapUint64 below receives SIGSEGV or SIGBUS, with the
// signal number stored in DI.
//
// It must have the same frame configuration as compareAndSwapUint32 so that it
// can undo any potential call frame set up by the assembler.
TEXT handleCompareAndSwapUint32Fault(SB), NOSPLIT|NOFRAME, $0-24
MOVL DI, sig+20(FP)
RET
// compareAndSwapUint32 is like sync/atomic.CompareAndSwapUint32, but returns
// (the value previously stored at ptr, 0). If a SIGSEGV or SIGBUS signal is
// received during the operation, the value of prev is unspecified, and sig is
// the number of the signal that was received.
//
// Preconditions: ptr must be aligned to a 4-byte boundary.
//
//func compareAndSwapUint32(ptr unsafe.Pointer, old, new uint32) (prev uint32, sig int32)
TEXT ·compareAndSwapUint32(SB), NOSPLIT|NOFRAME, $0-24
// Store 0 as the returned signal number. If we run to completion, this is
// the value the caller will see; if a signal is received,
// handleCompareAndSwapUint32Fault will store a different value in this
// address.
MOVL $0, sig+20(FP)
MOVQ ptr+0(FP), DI
MOVL old+8(FP), AX
MOVL new+12(FP), DX
LOCK
CMPXCHGL DX, 0(DI)
MOVL AX, prev+16(FP)
RET
// func addrOfCompareAndSwapUint32() uintptr
TEXT ·addrOfCompareAndSwapUint32(SB), NOSPLIT|NOFRAME, $0-8
MOVQ $·compareAndSwapUint32(SB), AX
MOVQ AX, ret+0(FP)
RET
// handleLoadUint32Fault returns the value stored in DI. Control is transferred
// to it when LoadUint32 below receives SIGSEGV or SIGBUS, with the signal
// number stored in DI.
//
// It must have the same frame configuration as loadUint32 so that it can undo
// any potential call frame set up by the assembler.
TEXT handleLoadUint32Fault(SB), NOSPLIT|NOFRAME, $0-16
MOVL DI, sig+12(FP)
RET
// loadUint32 atomically loads *ptr and returns it. If a SIGSEGV or SIGBUS
// signal is received, the value returned is unspecified, and sig is the number
// of the signal that was received.
//
// Preconditions: ptr must be aligned to a 4-byte boundary.
//
//func loadUint32(ptr unsafe.Pointer) (val uint32, sig int32)
TEXT ·loadUint32(SB), NOSPLIT|NOFRAME, $0-16
// Store 0 as the returned signal number. If we run to completion,
// this is the value the caller will see; if a signal is received,
// handleLoadUint32Fault will store a different value in this address.
MOVL $0, sig+12(FP)
MOVQ ptr+0(FP), AX
MOVL (AX), BX
MOVL BX, val+8(FP)
RET
// func addrOfLoadUint32() uintptr
TEXT ·addrOfLoadUint32(SB), NOSPLIT|NOFRAME, $0-8
MOVQ $·loadUint32(SB), AX
MOVQ AX, ret+0(FP)
RET

152
pkg/safecopy/atomic_arm64.s Normal file
View file

@ -0,0 +1,152 @@
// Copyright 2014 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 or at
// https://developers.google.com/open-source/licenses/bsd.
#include "textflag.h"
// handleSwapUint32Fault returns the value stored in R1. Control is transferred
// to it when swapUint32 below receives SIGSEGV or SIGBUS, with the signal
// number stored in R1.
//
// It must have the same frame configuration as swapUint32 so that it can undo
// any potential call frame set up by the assembler.
TEXT handleSwapUint32Fault(SB), NOSPLIT, $0-24
MOVW R1, sig+20(FP)
RET
// See the corresponding doc in safecopy_unsafe.go
//
// The code is derived from Go source runtime/internal/atomic.Xchg.
//
//func swapUint32(ptr unsafe.Pointer, new uint32) (old uint32, sig int32)
TEXT ·swapUint32(SB), NOSPLIT, $0-24
// Store 0 as the returned signal number. If we run to completion,
// this is the value the caller will see; if a signal is received,
// handleSwapUint32Fault will store a different value in this address.
MOVW $0, sig+20(FP)
again:
MOVD ptr+0(FP), R0
MOVW new+8(FP), R1
LDAXRW (R0), R2
STLXRW R1, (R0), R3
CBNZ R3, again
MOVW R2, old+16(FP)
RET
// func addrOfSwapUint32() uintptr
TEXT ·addrOfSwapUint32(SB), $0-8
MOVD $·swapUint32(SB), R0
MOVD R0, ret+0(FP)
RET
// handleSwapUint64Fault returns the value stored in R1. Control is transferred
// to it when swapUint64 below receives SIGSEGV or SIGBUS, with the signal
// number stored in R1.
//
// It must have the same frame configuration as swapUint64 so that it can undo
// any potential call frame set up by the assembler.
TEXT handleSwapUint64Fault(SB), NOSPLIT, $0-28
MOVW R1, sig+24(FP)
RET
// See the corresponding doc in safecopy_unsafe.go
//
// The code is derived from Go source runtime/internal/atomic.Xchg64.
//
//func swapUint64(ptr unsafe.Pointer, new uint64) (old uint64, sig int32)
TEXT ·swapUint64(SB), NOSPLIT, $0-28
// Store 0 as the returned signal number. If we run to completion,
// this is the value the caller will see; if a signal is received,
// handleSwapUint64Fault will store a different value in this address.
MOVW $0, sig+24(FP)
again:
MOVD ptr+0(FP), R0
MOVD new+8(FP), R1
LDAXR (R0), R2
STLXR R1, (R0), R3
CBNZ R3, again
MOVD R2, old+16(FP)
RET
// func addrOfSwapUint64() uintptr
TEXT ·addrOfSwapUint64(SB), $0-8
MOVD $·swapUint64(SB), R0
MOVD R0, ret+0(FP)
RET
// handleCompareAndSwapUint32Fault returns the value stored in R1. Control is
// transferred to it when compareAndSwapUint32 below receives SIGSEGV or SIGBUS,
// with the signal number stored in R1.
//
// It must have the same frame configuration as compareAndSwapUint32 so that it
// can undo any potential call frame set up by the assembler.
TEXT handleCompareAndSwapUint32Fault(SB), NOSPLIT, $0-24
MOVW R1, sig+20(FP)
RET
// See the corresponding doc in safecopy_unsafe.go
//
// The code is derived from Go source runtime/internal/atomic.Cas.
//
//func compareAndSwapUint32(ptr unsafe.Pointer, old, new uint32) (prev uint32, sig int32)
TEXT ·compareAndSwapUint32(SB), NOSPLIT, $0-24
// Store 0 as the returned signal number. If we run to completion, this is
// the value the caller will see; if a signal is received,
// handleCompareAndSwapUint32Fault will store a different value in this
// address.
MOVW $0, sig+20(FP)
MOVD ptr+0(FP), R0
MOVW old+8(FP), R1
MOVW new+12(FP), R2
again:
LDAXRW (R0), R3
CMPW R1, R3
BNE done
STLXRW R2, (R0), R4
CBNZ R4, again
done:
MOVW R3, prev+16(FP)
RET
// func addrOfCompareAndSwapUint32() uintptr
TEXT ·addrOfCompareAndSwapUint32(SB), $0-8
MOVD $·compareAndSwapUint32(SB), R0
MOVD R0, ret+0(FP)
RET
// handleLoadUint32Fault returns the value stored in DI. Control is transferred
// to it when LoadUint32 below receives SIGSEGV or SIGBUS, with the signal
// number stored in DI.
//
// It must have the same frame configuration as loadUint32 so that it can undo
// any potential call frame set up by the assembler.
TEXT handleLoadUint32Fault(SB), NOSPLIT, $0-16
MOVW R1, sig+12(FP)
RET
// loadUint32 atomically loads *ptr and returns it. If a SIGSEGV or SIGBUS
// signal is received, the value returned is unspecified, and sig is the number
// of the signal that was received.
//
// Preconditions: ptr must be aligned to a 4-byte boundary.
//
//func loadUint32(ptr unsafe.Pointer) (val uint32, sig int32)
TEXT ·loadUint32(SB), NOSPLIT, $0-16
// Store 0 as the returned signal number. If we run to completion,
// this is the value the caller will see; if a signal is received,
// handleLoadUint32Fault will store a different value in this address.
MOVW $0, sig+12(FP)
MOVD ptr+0(FP), R0
LDARW (R0), R1
MOVW R1, val+8(FP)
RET
// func addrOfLoadUint32() uintptr
TEXT ·addrOfLoadUint32(SB), $0-8
MOVD $·loadUint32(SB), R0
MOVD R0, ret+0(FP)
RET

155
pkg/safecopy/memclr_amd64.s Normal file
View file

@ -0,0 +1,155 @@
// Copyright 2014 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 or at
// https://developers.google.com/open-source/licenses/bsd.
#include "textflag.h"
// handleMemclrFault returns (the value stored in AX, the value stored in DI).
// Control is transferred to it when memclr below receives SIGSEGV or SIGBUS,
// with the faulting address stored in AX and the signal number stored in DI.
//
// It must have the same frame configuration as memclr so that it can undo any
// potential call frame set up by the assembler.
TEXT handleMemclrFault(SB), NOSPLIT|NOFRAME, $0-28
MOVQ AX, addr+16(FP)
MOVL DI, sig+24(FP)
RET
// memclr sets the n bytes following ptr to zeroes. If a SIGSEGV or SIGBUS
// signal is received during the write, it returns the address that caused the
// fault and the number of the signal that was received. Otherwise, it returns
// an unspecified address and a signal number of 0.
//
// Data is written in order, such that if a fault happens at address p, it is
// safe to assume that all data before p-maxRegisterSize has already been
// successfully written.
//
// The code is derived from runtime.memclrNoHeapPointers.
//
// func memclr(ptr unsafe.Pointer, n uintptr) (fault unsafe.Pointer, sig int32)
TEXT ·memclr(SB), NOSPLIT|NOFRAME, $0-28
// Store 0 as the returned signal number. If we run to completion,
// this is the value the caller will see; if a signal is received,
// handleMemclrFault will store a different value in this address.
MOVL $0, sig+24(FP)
MOVQ ptr+0(FP), DI
MOVQ n+8(FP), BX
XORQ AX, AX
// MOVOU seems always faster than REP STOSQ.
tail:
TESTQ BX, BX
JEQ _0
CMPQ BX, $2
JBE _1or2
CMPQ BX, $4
JBE _3or4
CMPQ BX, $8
JB _5through7
JE _8
CMPQ BX, $16
JBE _9through16
PXOR X0, X0
CMPQ BX, $32
JBE _17through32
CMPQ BX, $64
JBE _33through64
CMPQ BX, $128
JBE _65through128
CMPQ BX, $256
JBE _129through256
// TODO: use branch table and BSR to make this just a single dispatch
// TODO: for really big clears, use MOVNTDQ, even without AVX2.
loop:
MOVOU X0, 0(DI)
MOVOU X0, 16(DI)
MOVOU X0, 32(DI)
MOVOU X0, 48(DI)
MOVOU X0, 64(DI)
MOVOU X0, 80(DI)
MOVOU X0, 96(DI)
MOVOU X0, 112(DI)
MOVOU X0, 128(DI)
MOVOU X0, 144(DI)
MOVOU X0, 160(DI)
MOVOU X0, 176(DI)
MOVOU X0, 192(DI)
MOVOU X0, 208(DI)
MOVOU X0, 224(DI)
MOVOU X0, 240(DI)
SUBQ $256, BX
ADDQ $256, DI
CMPQ BX, $256
JAE loop
JMP tail
_1or2:
MOVB AX, (DI)
MOVB AX, -1(DI)(BX*1)
RET
_0:
RET
_3or4:
MOVW AX, (DI)
MOVW AX, -2(DI)(BX*1)
RET
_5through7:
MOVL AX, (DI)
MOVL AX, -4(DI)(BX*1)
RET
_8:
// We need a separate case for 8 to make sure we clear pointers atomically.
MOVQ AX, (DI)
RET
_9through16:
MOVQ AX, (DI)
MOVQ AX, -8(DI)(BX*1)
RET
_17through32:
MOVOU X0, (DI)
MOVOU X0, -16(DI)(BX*1)
RET
_33through64:
MOVOU X0, (DI)
MOVOU X0, 16(DI)
MOVOU X0, -32(DI)(BX*1)
MOVOU X0, -16(DI)(BX*1)
RET
_65through128:
MOVOU X0, (DI)
MOVOU X0, 16(DI)
MOVOU X0, 32(DI)
MOVOU X0, 48(DI)
MOVOU X0, -64(DI)(BX*1)
MOVOU X0, -48(DI)(BX*1)
MOVOU X0, -32(DI)(BX*1)
MOVOU X0, -16(DI)(BX*1)
RET
_129through256:
MOVOU X0, (DI)
MOVOU X0, 16(DI)
MOVOU X0, 32(DI)
MOVOU X0, 48(DI)
MOVOU X0, 64(DI)
MOVOU X0, 80(DI)
MOVOU X0, 96(DI)
MOVOU X0, 112(DI)
MOVOU X0, -128(DI)(BX*1)
MOVOU X0, -112(DI)(BX*1)
MOVOU X0, -96(DI)(BX*1)
MOVOU X0, -80(DI)(BX*1)
MOVOU X0, -64(DI)(BX*1)
MOVOU X0, -48(DI)(BX*1)
MOVOU X0, -32(DI)(BX*1)
MOVOU X0, -16(DI)(BX*1)
RET
// func addrOfMemclr() uintptr
TEXT ·addrOfMemclr(SB), $0-8
MOVQ $·memclr(SB), AX
MOVQ AX, ret+0(FP)
RET

View file

@ -0,0 +1,82 @@
// Copyright 2014 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 or at
// https://developers.google.com/open-source/licenses/bsd.
#include "textflag.h"
// handleMemclrFault returns (the value stored in R0, the value stored in R1).
// Control is transferred to it when memclr below receives SIGSEGV or SIGBUS,
// with the faulting address stored in R0 and the signal number stored in R1.
//
// It must have the same frame configuration as memclr so that it can undo any
// potential call frame set up by the assembler.
TEXT handleMemclrFault(SB), NOSPLIT, $0-28
MOVD R0, addr+16(FP)
MOVW R1, sig+24(FP)
RET
// See the corresponding doc in safecopy_unsafe.go
//
// The code is derived from runtime.memclrNoHeapPointers.
//
// func memclr(ptr unsafe.Pointer, n uintptr) (fault unsafe.Pointer, sig int32)
TEXT ·memclr(SB), NOSPLIT, $0-28
// Store 0 as the returned signal number. If we run to completion,
// this is the value the caller will see; if a signal is received,
// handleMemclrFault will store a different value in this address.
MOVW $0, sig+24(FP)
MOVD ptr+0(FP), R0
MOVD n+8(FP), R1
// If size is less than 16 bytes, use tail_zero to zero what remains
CMP $16, R1
BLT tail_zero
// Get buffer offset into 16 byte aligned address for better performance
ANDS $15, R0, ZR
BNE unaligned_to_16
aligned_to_16:
LSR $4, R1, R2
zero_by_16:
STP.P (ZR, ZR), 16(R0) // Store pair with post index.
SUBS $1, R2, R2
BNE zero_by_16
ANDS $15, R1, R1
BEQ end
// Zero buffer with size=R1 < 16
tail_zero:
TBZ $3, R1, tail_zero_4
MOVD.P ZR, 8(R0)
tail_zero_4:
TBZ $2, R1, tail_zero_2
MOVW.P ZR, 4(R0)
tail_zero_2:
TBZ $1, R1, tail_zero_1
MOVH.P ZR, 2(R0)
tail_zero_1:
TBZ $0, R1, end
MOVB ZR, (R0)
end:
RET
unaligned_to_16:
MOVD R0, R2
head_loop:
MOVBU.P ZR, 1(R0)
ANDS $15, R0, ZR
BNE head_loop
// Adjust length for what remains
SUB R2, R0, R3
SUB R3, R1
// If size is less than 16 bytes, use tail_zero to zero what remains
CMP $16, R1
BLT tail_zero
B aligned_to_16
// func addrOfMemclr() uintptr
TEXT ·addrOfMemclr(SB), $0-8
MOVD $·memclr(SB), R0
MOVD R0, ret+0(FP)
RET

225
pkg/safecopy/memcpy_amd64.s Normal file
View file

@ -0,0 +1,225 @@
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
// Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved.
// Portions Copyright 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "textflag.h"
// handleMemcpyFault returns (the value stored in AX, the value stored in DI).
// Control is transferred to it when memcpy below receives SIGSEGV or SIGBUS,
// with the faulting address stored in AX and the signal number stored in DI.
//
// It must have the same frame configuration as memcpy so that it can undo any
// potential call frame set up by the assembler.
TEXT handleMemcpyFault(SB), NOSPLIT|NOFRAME, $0-36
MOVQ AX, addr+24(FP)
MOVL DI, sig+32(FP)
RET
// memcpy copies data from src to dst. If a SIGSEGV or SIGBUS signal is received
// during the copy, it returns the address that caused the fault and the number
// of the signal that was received. Otherwise, it returns an unspecified address
// and a signal number of 0.
//
// Data is copied in order, such that if a fault happens at address p, it is
// safe to assume that all data before p-maxRegisterSize has already been
// successfully copied.
//
// The code is derived from the forward copying part of runtime.memmove.
//
// func memcpy(dst, src unsafe.Pointer, n uintptr) (fault unsafe.Pointer, sig int32)
TEXT ·memcpy(SB), NOSPLIT|NOFRAME, $0-36
// Store 0 as the returned signal number. If we run to completion,
// this is the value the caller will see; if a signal is received,
// handleMemcpyFault will store a different value in this address.
MOVL $0, sig+32(FP)
MOVQ dst+0(FP), DI
MOVQ src+8(FP), SI
MOVQ n+16(FP), BX
tail:
// BSR+branch table make almost all memmove/memclr benchmarks worse. Not
// worth doing.
TESTQ BX, BX
JEQ move_0
CMPQ BX, $2
JBE move_1or2
CMPQ BX, $4
JBE move_3or4
CMPQ BX, $8
JB move_5through7
JE move_8
CMPQ BX, $16
JBE move_9through16
CMPQ BX, $32
JBE move_17through32
CMPQ BX, $64
JBE move_33through64
CMPQ BX, $128
JBE move_65through128
CMPQ BX, $256
JBE move_129through256
move_257plus:
SUBQ $256, BX
MOVOU (SI), X0
MOVOU X0, (DI)
MOVOU 16(SI), X1
MOVOU X1, 16(DI)
MOVOU 32(SI), X2
MOVOU X2, 32(DI)
MOVOU 48(SI), X3
MOVOU X3, 48(DI)
MOVOU 64(SI), X4
MOVOU X4, 64(DI)
MOVOU 80(SI), X5
MOVOU X5, 80(DI)
MOVOU 96(SI), X6
MOVOU X6, 96(DI)
MOVOU 112(SI), X7
MOVOU X7, 112(DI)
MOVOU 128(SI), X8
MOVOU X8, 128(DI)
MOVOU 144(SI), X9
MOVOU X9, 144(DI)
MOVOU 160(SI), X10
MOVOU X10, 160(DI)
MOVOU 176(SI), X11
MOVOU X11, 176(DI)
MOVOU 192(SI), X12
MOVOU X12, 192(DI)
MOVOU 208(SI), X13
MOVOU X13, 208(DI)
MOVOU 224(SI), X14
MOVOU X14, 224(DI)
MOVOU 240(SI), X15
MOVOU X15, 240(DI)
CMPQ BX, $256
LEAQ 256(SI), SI
LEAQ 256(DI), DI
JGE move_257plus
JMP tail
move_1or2:
MOVB (SI), AX
MOVB AX, (DI)
MOVB -1(SI)(BX*1), CX
MOVB CX, -1(DI)(BX*1)
RET
move_0:
RET
move_3or4:
MOVW (SI), AX
MOVW AX, (DI)
MOVW -2(SI)(BX*1), CX
MOVW CX, -2(DI)(BX*1)
RET
move_5through7:
MOVL (SI), AX
MOVL AX, (DI)
MOVL -4(SI)(BX*1), CX
MOVL CX, -4(DI)(BX*1)
RET
move_8:
// We need a separate case for 8 to make sure we write pointers atomically.
MOVQ (SI), AX
MOVQ AX, (DI)
RET
move_9through16:
MOVQ (SI), AX
MOVQ AX, (DI)
MOVQ -8(SI)(BX*1), CX
MOVQ CX, -8(DI)(BX*1)
RET
move_17through32:
MOVOU (SI), X0
MOVOU X0, (DI)
MOVOU -16(SI)(BX*1), X1
MOVOU X1, -16(DI)(BX*1)
RET
move_33through64:
MOVOU (SI), X0
MOVOU X0, (DI)
MOVOU 16(SI), X1
MOVOU X1, 16(DI)
MOVOU -32(SI)(BX*1), X2
MOVOU X2, -32(DI)(BX*1)
MOVOU -16(SI)(BX*1), X3
MOVOU X3, -16(DI)(BX*1)
RET
move_65through128:
MOVOU (SI), X0
MOVOU X0, (DI)
MOVOU 16(SI), X1
MOVOU X1, 16(DI)
MOVOU 32(SI), X2
MOVOU X2, 32(DI)
MOVOU 48(SI), X3
MOVOU X3, 48(DI)
MOVOU -64(SI)(BX*1), X4
MOVOU X4, -64(DI)(BX*1)
MOVOU -48(SI)(BX*1), X5
MOVOU X5, -48(DI)(BX*1)
MOVOU -32(SI)(BX*1), X6
MOVOU X6, -32(DI)(BX*1)
MOVOU -16(SI)(BX*1), X7
MOVOU X7, -16(DI)(BX*1)
RET
move_129through256:
MOVOU (SI), X0
MOVOU X0, (DI)
MOVOU 16(SI), X1
MOVOU X1, 16(DI)
MOVOU 32(SI), X2
MOVOU X2, 32(DI)
MOVOU 48(SI), X3
MOVOU X3, 48(DI)
MOVOU 64(SI), X4
MOVOU X4, 64(DI)
MOVOU 80(SI), X5
MOVOU X5, 80(DI)
MOVOU 96(SI), X6
MOVOU X6, 96(DI)
MOVOU 112(SI), X7
MOVOU X7, 112(DI)
MOVOU -128(SI)(BX*1), X8
MOVOU X8, -128(DI)(BX*1)
MOVOU -112(SI)(BX*1), X9
MOVOU X9, -112(DI)(BX*1)
MOVOU -96(SI)(BX*1), X10
MOVOU X10, -96(DI)(BX*1)
MOVOU -80(SI)(BX*1), X11
MOVOU X11, -80(DI)(BX*1)
MOVOU -64(SI)(BX*1), X12
MOVOU X12, -64(DI)(BX*1)
MOVOU -48(SI)(BX*1), X13
MOVOU X13, -48(DI)(BX*1)
MOVOU -32(SI)(BX*1), X14
MOVOU X14, -32(DI)(BX*1)
MOVOU -16(SI)(BX*1), X15
MOVOU X15, -16(DI)(BX*1)
RET
// func addrOfMemcpy() uintptr
TEXT ·addrOfMemcpy(SB), $0-8
MOVQ $·memcpy(SB), AX
MOVQ AX, ret+0(FP)
RET

View file

@ -0,0 +1,86 @@
// Copyright 2014 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 or at
// https://developers.google.com/open-source/licenses/bsd.
#include "textflag.h"
// handleMemcpyFault returns (the value stored in R0, the value stored in R1).
// Control is transferred to it when memcpy below receives SIGSEGV or SIGBUS,
// with the faulting address stored in R0 and the signal number stored in R1.
//
// It must have the same frame configuration as memcpy so that it can undo any
// potential call frame set up by the assembler.
TEXT handleMemcpyFault(SB), NOSPLIT, $0-36
MOVD R0, addr+24(FP)
MOVW R1, sig+32(FP)
RET
// memcpy copies data from src to dst. If a SIGSEGV or SIGBUS signal is received
// during the copy, it returns the address that caused the fault and the number
// of the signal that was received. Otherwise, it returns an unspecified address
// and a signal number of 0.
//
// Data is copied in order, such that if a fault happens at address p, it is
// safe to assume that all data before p-maxRegisterSize has already been
// successfully copied.
//
// The code is derived from the Go source runtime.memmove.
//
// func memcpy(dst, src unsafe.Pointer, n uintptr) (fault unsafe.Pointer, sig int32)
TEXT ·memcpy(SB), NOSPLIT, $-8-36
// Store 0 as the returned signal number. If we run to completion,
// this is the value the caller will see; if a signal is received,
// handleMemcpyFault will store a different value in this address.
MOVW $0, sig+32(FP)
MOVD dst+0(FP), R3
MOVD src+8(FP), R4
MOVD n+16(FP), R5
CMP $0, R5
BNE check
RET
check:
AND $~7, R5, R7 // R7 is N&~7.
SUB R7, R5, R6 // R6 is N&7.
// Copying forward proceeds by copying R7/8 words then copying R6 bytes.
// R3 and R4 are advanced as we copy.
// (There may be implementations of armv8 where copying by bytes until
// at least one of source or dest is word aligned is a worthwhile
// optimization, but the on the one tested so far (xgene) it did not
// make a significance difference.)
CMP $0, R7 // Do we need to do any word-by-word copying?
BEQ noforwardlarge
ADD R3, R7, R9 // R9 points just past where we copy by word.
forwardlargeloop:
MOVD.P 8(R4), R8 // R8 is just a scratch register.
MOVD.P R8, 8(R3)
CMP R3, R9
BNE forwardlargeloop
noforwardlarge:
CMP $0, R6 // Do we need to do any byte-by-byte copying?
BNE forwardtail
RET
forwardtail:
ADD R3, R6, R9 // R9 points just past the destination memory.
forwardtailloop:
MOVBU.P 1(R4), R8
MOVBU.P R8, 1(R3)
CMP R3, R9
BNE forwardtailloop
RET
// func addrOfMemcpy() uintptr
TEXT ·addrOfMemcpy(SB), $0-8
MOVD $·memcpy(SB), R0
MOVD R0, ret+0(FP)
RET

151
pkg/safecopy/safecopy.go Normal file
View file

@ -0,0 +1,151 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package safecopy provides an efficient implementation of functions to access
// memory that may result in SIGSEGV or SIGBUS being sent to the accessor.
package safecopy
import (
"fmt"
"runtime"
"github.com/sagernet/gvisor/pkg/errors"
"github.com/sagernet/gvisor/pkg/errors/linuxerr"
"github.com/sagernet/gvisor/pkg/sighandling"
"golang.org/x/sys/unix"
)
// SegvError is returned when a safecopy function receives SIGSEGV.
type SegvError struct {
// Addr is the address at which the SIGSEGV occurred.
Addr uintptr
}
// Error implements error.Error.
func (e SegvError) Error() string {
return fmt.Sprintf("SIGSEGV at %#x", e.Addr)
}
// BusError is returned when a safecopy function receives SIGBUS.
type BusError struct {
// Addr is the address at which the SIGBUS occurred.
Addr uintptr
}
// Error implements error.Error.
func (e BusError) Error() string {
return fmt.Sprintf("SIGBUS at %#x", e.Addr)
}
// AlignmentError is returned when a safecopy function is passed an address
// that does not meet alignment requirements.
type AlignmentError struct {
// Addr is the invalid address.
Addr uintptr
// Alignment is the required alignment.
Alignment uintptr
}
// Error implements error.Error.
func (e AlignmentError) Error() string {
return fmt.Sprintf("address %#x is not aligned to a %d-byte boundary", e.Addr, e.Alignment)
}
var (
// The begin and end addresses below are for the functions that are
// checked by the signal handler.
memcpyBegin uintptr
memcpyEnd uintptr
memclrBegin uintptr
memclrEnd uintptr
swapUint32Begin uintptr
swapUint32End uintptr
swapUint64Begin uintptr
swapUint64End uintptr
compareAndSwapUint32Begin uintptr
compareAndSwapUint32End uintptr
loadUint32Begin uintptr
loadUint32End uintptr
// savedSigSegVHandler is a pointer to the SIGSEGV handler that was
// configured before we replaced it with our own. We still call into it
// when we get a SIGSEGV that is not interesting to us.
savedSigSegVHandler uintptr
// Same as above, but for SIGBUS signals.
savedSigBusHandler uintptr
)
// signalHandler is our replacement signal handler for SIGSEGV and SIGBUS
// signals.
func signalHandler()
// addrOfSignalHandler returns the start address of signalHandler.
//
// See comment on addrOfMemcpy for more details.
func addrOfSignalHandler() uintptr
// FindEndAddress returns the end address (one byte beyond the last) of the
// function that contains the specified address (begin).
func FindEndAddress(begin uintptr) uintptr {
f := runtime.FuncForPC(begin)
if f != nil {
for p := begin; ; p++ {
g := runtime.FuncForPC(p)
if f != g {
return p
}
}
}
return begin
}
// initializeAddresses initializes the addresses used by the signal handler.
func initializeAddresses() {
// The following functions are written in assembly language, so they won't
// be inlined by the existing compiler/linker. Tests will fail if this
// assumption is violated.
memcpyBegin = addrOfMemcpy()
memcpyEnd = FindEndAddress(memcpyBegin)
memclrBegin = addrOfMemclr()
memclrEnd = FindEndAddress(memclrBegin)
swapUint32Begin = addrOfSwapUint32()
swapUint32End = FindEndAddress(swapUint32Begin)
swapUint64Begin = addrOfSwapUint64()
swapUint64End = FindEndAddress(swapUint64Begin)
compareAndSwapUint32Begin = addrOfCompareAndSwapUint32()
compareAndSwapUint32End = FindEndAddress(compareAndSwapUint32Begin)
loadUint32Begin = addrOfLoadUint32()
loadUint32End = FindEndAddress(loadUint32Begin)
initializeArchAddresses()
}
func init() {
initializeAddresses()
if err := sighandling.ReplaceSignalHandler(unix.SIGSEGV, addrOfSignalHandler(), &savedSigSegVHandler); err != nil {
panic(fmt.Sprintf("Unable to set handler for SIGSEGV: %v", err))
}
if err := sighandling.ReplaceSignalHandler(unix.SIGBUS, addrOfSignalHandler(), &savedSigBusHandler); err != nil {
panic(fmt.Sprintf("Unable to set handler for SIGBUS: %v", err))
}
linuxerr.AddErrorUnwrapper(func(e error) (*errors.Error, bool) {
switch e.(type) {
case SegvError, BusError, AlignmentError:
return linuxerr.EFAULT, true
default:
return nil, false
}
})
}

View file

@ -0,0 +1,42 @@
// Copyright 2023 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build amd64 || i386
// +build amd64 i386
package safecopy
import (
"unsafe"
)
var (
checkXstateBegin uintptr
checkXstateEnd uintptr
)
func initializeArchAddresses() {
checkXstateBegin = addrOfCheckXstate()
checkXstateEnd = FindEndAddress(checkXstateBegin)
}
//go:noescape
func checkXstate(addr uintptr) (fault uintptr, sig int32, mxcsr uint32, cw uint16)
func addrOfCheckXstate() uintptr
// CheckXstate verifies that xstate can be restored by the xrstor instruction.
func CheckXstate(state *byte) error {
_, sig, _, _ := checkXstate(uintptr(unsafe.Pointer(state)))
return errorFromFaultSignal(uintptr(unsafe.Pointer(state)), sig)
}

View file

@ -0,0 +1,6 @@
// automatically generated by stateify.
//go:build amd64 || i386
// +build amd64 i386
package safecopy

View file

@ -0,0 +1,21 @@
// Copyright 2023 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build arm64
// +build arm64
package safecopy
func initializeArchAddresses() {
}

View file

@ -0,0 +1,6 @@
// automatically generated by stateify.
//go:build arm64
// +build arm64
package safecopy

View file

@ -0,0 +1,3 @@
// automatically generated by stateify.
package safecopy

View file

@ -0,0 +1,333 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package safecopy
import (
"fmt"
"runtime"
"unsafe"
"golang.org/x/sys/unix"
)
// maxRegisterSize is the maximum register size used in memcpy and memclr. It
// is used to decide by how much to rewind the copy (for memcpy) or zeroing
// (for memclr) before proceeding.
const maxRegisterSize = 16
// memcpy copies data from src to dst. If a SIGSEGV or SIGBUS signal is received
// during the copy, it returns the address that caused the fault and the number
// of the signal that was received. Otherwise, it returns an unspecified address
// and a signal number of 0.
//
// Data is copied in order, such that if a fault happens at address p, it is
// safe to assume that all data before p-maxRegisterSize has already been
// successfully copied.
//
//go:noescape
func memcpy(dst, src uintptr, n uintptr) (fault uintptr, sig int32)
// memclr sets the n bytes following ptr to zeroes. If a SIGSEGV or SIGBUS
// signal is received during the write, it returns the address that caused the
// fault and the number of the signal that was received. Otherwise, it returns
// an unspecified address and a signal number of 0.
//
// Data is written in order, such that if a fault happens at address p, it is
// safe to assume that all data before p-maxRegisterSize has already been
// successfully written.
//
//go:noescape
func memclr(ptr uintptr, n uintptr) (fault uintptr, sig int32)
// swapUint32 atomically stores new into *ptr and returns (the previous *ptr
// value, 0). If a SIGSEGV or SIGBUS signal is received during the swap, the
// value of old is unspecified, and sig is the number of the signal that was
// received.
//
// Preconditions: ptr must be aligned to a 4-byte boundary.
//
//go:noescape
func swapUint32(ptr unsafe.Pointer, new uint32) (old uint32, sig int32)
// swapUint64 atomically stores new into *ptr and returns (the previous *ptr
// value, 0). If a SIGSEGV or SIGBUS signal is received during the swap, the
// value of old is unspecified, and sig is the number of the signal that was
// received.
//
// Preconditions: ptr must be aligned to a 8-byte boundary.
//
//go:noescape
func swapUint64(ptr unsafe.Pointer, new uint64) (old uint64, sig int32)
// compareAndSwapUint32 is like sync/atomic.CompareAndSwapUint32, but returns
// (the value previously stored at ptr, 0). If a SIGSEGV or SIGBUS signal is
// received during the operation, the value of prev is unspecified, and sig is
// the number of the signal that was received.
//
// Preconditions: ptr must be aligned to a 4-byte boundary.
//
//go:noescape
func compareAndSwapUint32(ptr unsafe.Pointer, old, new uint32) (prev uint32, sig int32)
// LoadUint32 is like sync/atomic.LoadUint32, but operates with user memory. It
// may fail with SIGSEGV or SIGBUS if it is received while reading from ptr.
//
// Preconditions: ptr must be aligned to a 4-byte boundary.
//
//go:noescape
func loadUint32(ptr unsafe.Pointer) (val uint32, sig int32)
// Return the start address of the functions above.
//
// In Go 1.17+, Go references to assembly functions resolve to an ABIInternal
// wrapper function rather than the function itself. We must reference from
// assembly to get the ABI0 (i.e., primary) address.
func addrOfMemcpy() uintptr
func addrOfMemclr() uintptr
func addrOfSwapUint32() uintptr
func addrOfSwapUint64() uintptr
func addrOfCompareAndSwapUint32() uintptr
func addrOfLoadUint32() uintptr
// CopyIn copies len(dst) bytes from src to dst. It returns the number of bytes
// copied and an error if SIGSEGV or SIGBUS is received while reading from src.
func CopyIn(dst []byte, src unsafe.Pointer) (int, error) {
n, err := copyIn(dst, uintptr(src))
runtime.KeepAlive(src)
return n, err
}
// copyIn is the underlying definition for CopyIn.
func copyIn(dst []byte, src uintptr) (int, error) {
toCopy := uintptr(len(dst))
if len(dst) == 0 {
return 0, nil
}
fault, sig := memcpy(uintptr(unsafe.Pointer(&dst[0])), src, toCopy)
if sig == 0 {
return len(dst), nil
}
if fault < src || fault >= src+toCopy {
panic(fmt.Sprintf("CopyIn raised signal %d at %#x, which is outside source [%#x, %#x)", sig, fault, src, src+toCopy))
}
// memcpy might have ended the copy up to maxRegisterSize bytes before
// fault, if an instruction caused a memory access that straddled two
// pages, and the second one faulted. Try to copy up to the fault.
var done int
if fault-src > maxRegisterSize {
done = int(fault - src - maxRegisterSize)
}
n, err := copyIn(dst[done:int(fault-src)], src+uintptr(done))
done += n
if err != nil {
return done, err
}
return done, errorFromFaultSignal(fault, sig)
}
// CopyOut copies len(src) bytes from src to dst. If returns the number of
// bytes done and an error if SIGSEGV or SIGBUS is received while writing to
// dst.
func CopyOut(dst unsafe.Pointer, src []byte) (int, error) {
n, err := copyOut(uintptr(dst), src)
runtime.KeepAlive(dst)
return n, err
}
// copyOut is the underlying definition for CopyOut.
func copyOut(dst uintptr, src []byte) (int, error) {
toCopy := uintptr(len(src))
if toCopy == 0 {
return 0, nil
}
fault, sig := memcpy(dst, uintptr(unsafe.Pointer(&src[0])), toCopy)
if sig == 0 {
return len(src), nil
}
if fault < dst || fault >= dst+toCopy {
panic(fmt.Sprintf("CopyOut raised signal %d at %#x, which is outside destination [%#x, %#x)", sig, fault, dst, dst+toCopy))
}
// memcpy might have ended the copy up to maxRegisterSize bytes before
// fault, if an instruction caused a memory access that straddled two
// pages, and the second one faulted. Try to copy up to the fault.
var done int
if fault-dst > maxRegisterSize {
done = int(fault - dst - maxRegisterSize)
}
n, err := copyOut(dst+uintptr(done), src[done:int(fault-dst)])
done += n
if err != nil {
return done, err
}
return done, errorFromFaultSignal(fault, sig)
}
// Copy copies toCopy bytes from src to dst. It returns the number of bytes
// copied and an error if SIGSEGV or SIGBUS is received while reading from src
// or writing to dst.
//
// Data is copied in order; if [src, src+toCopy) and [dst, dst+toCopy) overlap,
// the resulting contents of dst are unspecified.
func Copy(dst, src unsafe.Pointer, toCopy uintptr) (uintptr, error) {
n, err := copyN(uintptr(dst), uintptr(src), toCopy)
runtime.KeepAlive(dst)
runtime.KeepAlive(src)
return n, err
}
// copyN is the underlying definition for Copy.
func copyN(dst, src uintptr, toCopy uintptr) (uintptr, error) {
if toCopy == 0 {
return 0, nil
}
fault, sig := memcpy(dst, src, toCopy)
if sig == 0 {
return toCopy, nil
}
// Did the fault occur while reading from src or writing to dst?
faultAfterSrc := ^uintptr(0)
if fault >= src {
faultAfterSrc = fault - src
}
faultAfterDst := ^uintptr(0)
if fault >= dst {
faultAfterDst = fault - dst
}
if faultAfterSrc >= toCopy && faultAfterDst >= toCopy {
panic(fmt.Sprintf("Copy raised signal %d at %#x, which is outside source [%#x, %#x) and destination [%#x, %#x)", sig, fault, src, src+toCopy, dst, dst+toCopy))
}
faultedAfter := faultAfterSrc
if faultedAfter > faultAfterDst {
faultedAfter = faultAfterDst
}
// memcpy might have ended the copy up to maxRegisterSize bytes before
// fault, if an instruction caused a memory access that straddled two
// pages, and the second one faulted. Try to copy up to the fault.
var done uintptr
if faultedAfter > maxRegisterSize {
done = faultedAfter - maxRegisterSize
}
n, err := copyN(dst+done, src+done, faultedAfter-done)
done += n
if err != nil {
return done, err
}
return done, errorFromFaultSignal(fault, sig)
}
// ZeroOut writes toZero zero bytes to dst. It returns the number of bytes
// written and an error if SIGSEGV or SIGBUS is received while writing to dst.
func ZeroOut(dst unsafe.Pointer, toZero uintptr) (uintptr, error) {
n, err := zeroOut(uintptr(dst), toZero)
runtime.KeepAlive(dst)
return n, err
}
// zeroOut is the underlying definition for ZeroOut.
func zeroOut(dst uintptr, toZero uintptr) (uintptr, error) {
if toZero == 0 {
return 0, nil
}
fault, sig := memclr(dst, toZero)
if sig == 0 {
return toZero, nil
}
if fault < dst || fault >= dst+toZero {
panic(fmt.Sprintf("ZeroOut raised signal %d at %#x, which is outside destination [%#x, %#x)", sig, fault, dst, dst+toZero))
}
// memclr might have ended the write up to maxRegisterSize bytes before
// fault, if an instruction caused a memory access that straddled two
// pages, and the second one faulted. Try to write up to the fault.
var done uintptr
if fault-dst > maxRegisterSize {
done = fault - dst - maxRegisterSize
}
n, err := zeroOut(dst+done, fault-dst-done)
done += n
if err != nil {
return done, err
}
return done, errorFromFaultSignal(fault, sig)
}
// SwapUint32 is equivalent to sync/atomic.SwapUint32, except that it returns
// an error if SIGSEGV or SIGBUS is received while accessing ptr, or if ptr is
// not aligned to a 4-byte boundary.
func SwapUint32(ptr unsafe.Pointer, new uint32) (uint32, error) {
if addr := uintptr(ptr); addr&3 != 0 {
return 0, AlignmentError{addr, 4}
}
old, sig := swapUint32(ptr, new)
return old, errorFromFaultSignal(uintptr(ptr), sig)
}
// SwapUint64 is equivalent to sync/atomic.SwapUint64, except that it returns
// an error if SIGSEGV or SIGBUS is received while accessing ptr, or if ptr is
// not aligned to an 8-byte boundary.
func SwapUint64(ptr unsafe.Pointer, new uint64) (uint64, error) {
if addr := uintptr(ptr); addr&7 != 0 {
return 0, AlignmentError{addr, 8}
}
old, sig := swapUint64(ptr, new)
return old, errorFromFaultSignal(uintptr(ptr), sig)
}
// CompareAndSwapUint32 is equivalent to atomicbitops.CompareAndSwapUint32,
// except that it returns an error if SIGSEGV or SIGBUS is received while
// accessing ptr, or if ptr is not aligned to a 4-byte boundary.
func CompareAndSwapUint32(ptr unsafe.Pointer, old, new uint32) (uint32, error) {
if addr := uintptr(ptr); addr&3 != 0 {
return 0, AlignmentError{addr, 4}
}
prev, sig := compareAndSwapUint32(ptr, old, new)
return prev, errorFromFaultSignal(uintptr(ptr), sig)
}
// LoadUint32 is like sync/atomic.LoadUint32, but operates with user memory. It
// may fail with SIGSEGV or SIGBUS if it is received while reading from ptr.
//
// Preconditions: ptr must be aligned to a 4-byte boundary.
func LoadUint32(ptr unsafe.Pointer) (uint32, error) {
if addr := uintptr(ptr); addr&3 != 0 {
return 0, AlignmentError{addr, 4}
}
val, sig := loadUint32(ptr)
return val, errorFromFaultSignal(uintptr(ptr), sig)
}
func errorFromFaultSignal(addr uintptr, sig int32) error {
switch sig {
case 0:
return nil
case int32(unix.SIGSEGV):
return SegvError{addr}
case int32(unix.SIGBUS):
return BusError{addr}
default:
panic(fmt.Sprintf("safecopy got unexpected signal %d at address %#x", sig, addr))
}
}

View file

@ -0,0 +1,3 @@
// automatically generated by stateify.
package safecopy

View file

@ -0,0 +1,147 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "textflag.h"
// The signals handled by sigHandler.
#define SIGBUS 7
#define SIGSEGV 11
// Offsets to the registers in context->uc_mcontext.gregs[].
#define REG_RDI 0x68
#define REG_RAX 0x90
#define REG_IP 0xa8
// Offset to the si_addr field of siginfo.
#define SI_CODE 0x08
#define SI_ADDR 0x10
// signalHandler is the signal handler for SIGSEGV and SIGBUS signals. It must
// not be set up as a handler to any other signals.
//
// If the instruction causing the signal is within a safecopy-protected
// function, the signal is handled such that execution resumes in the
// appropriate fault handling stub with AX containing the faulting address and
// DI containing the signal number. Otherwise control is transferred to the
// previously configured signal handler (savedSigSegvHandler or
// savedSigBusHandler).
//
// This function cannot be written in go because it runs whenever a signal is
// received by the thread (preempting whatever was running), which includes when
// garbage collector has stopped or isn't expecting any interactions (like
// barriers).
//
// The arguments are the following:
// DI - The signal number.
// SI - Pointer to siginfo_t structure.
// DX - Pointer to ucontext structure.
TEXT ·signalHandler(SB),NOSPLIT|NOFRAME,$0
// Check if the signal is from the kernel.
MOVQ $0x0, CX
CMPL CX, SI_CODE(SI)
JGE original_handler
// Check if RIP is within the area we care about.
MOVQ REG_IP(DX), CX
CMPQ CX, ·memcpyBegin(SB)
JB not_memcpy
CMPQ CX, ·memcpyEnd(SB)
JAE not_memcpy
// Modify the context such that execution will resume in the fault
// handler.
LEAQ handleMemcpyFault(SB), CX
JMP handle_fault
not_memcpy:
CMPQ CX, ·memclrBegin(SB)
JB not_memclr
CMPQ CX, ·memclrEnd(SB)
JAE not_memclr
LEAQ handleMemclrFault(SB), CX
JMP handle_fault
not_memclr:
CMPQ CX, ·swapUint32Begin(SB)
JB not_swapuint32
CMPQ CX, ·swapUint32End(SB)
JAE not_swapuint32
LEAQ handleSwapUint32Fault(SB), CX
JMP handle_fault
not_swapuint32:
CMPQ CX, ·swapUint64Begin(SB)
JB not_swapuint64
CMPQ CX, ·swapUint64End(SB)
JAE not_swapuint64
LEAQ handleSwapUint64Fault(SB), CX
JMP handle_fault
not_swapuint64:
CMPQ CX, ·compareAndSwapUint32Begin(SB)
JB not_casuint32
CMPQ CX, ·compareAndSwapUint32End(SB)
JAE not_casuint32
LEAQ handleCompareAndSwapUint32Fault(SB), CX
JMP handle_fault
not_casuint32:
CMPQ CX, ·loadUint32Begin(SB)
JB not_loaduint32
CMPQ CX, ·loadUint32End(SB)
JAE not_loaduint32
LEAQ handleLoadUint32Fault(SB), CX
JMP handle_fault
not_loaduint32:
CMPQ CX, ·checkXstateBegin(SB)
JB not_checkXstate
CMPQ CX, ·checkXstateEnd(SB)
JAE not_checkXstate
LEAQ handleCheckXstateFault(SB), CX
JMP handle_fault
not_checkXstate:
original_handler:
// Jump to the previous signal handler, which is likely the golang one.
XORQ CX, CX
MOVQ ·savedSigBusHandler(SB), AX
CMPL DI, $SIGSEGV
CMOVQEQ ·savedSigSegVHandler(SB), AX
JMP AX
handle_fault:
// Entered with the address of the fault handler in RCX; store it in
// RIP.
MOVQ CX, REG_IP(DX)
// Store the faulting address in RAX.
MOVQ SI_ADDR(SI), CX
MOVQ CX, REG_RAX(DX)
// Store the signal number in EDI.
MOVL DI, REG_RDI(DX)
RET
// func addrOfSignalHandler() uintptr
TEXT ·addrOfSignalHandler(SB), $0-8
MOVQ $·signalHandler(SB), AX
MOVQ AX, ret+0(FP)
RET

View file

@ -0,0 +1,149 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "textflag.h"
// The signals handled by sigHandler.
#define SIGBUS 7
#define SIGSEGV 11
// Offsets to the registers in context->uc_mcontext.gregs[].
#define REG_R0 0xB8
#define REG_R1 0xC0
#define REG_PC 0x1B8
// Offset to the si_addr field of siginfo.
#define SI_CODE 0x08
#define SI_ADDR 0x10
// signalHandler is the signal handler for SIGSEGV and SIGBUS signals. It must
// not be set up as a handler to any other signals.
//
// If the instruction causing the signal is within a safecopy-protected
// function, the signal is handled such that execution resumes in the
// appropriate fault handling stub with R0 containing the faulting address and
// R1 containing the signal number. Otherwise control is transferred to the
// previously configured signal handler (savedSigSegvHandler or
// savedSigBusHandler).
//
// This function cannot be written in go because it runs whenever a signal is
// received by the thread (preempting whatever was running), which includes when
// garbage collector has stopped or isn't expecting any interactions (like
// barriers).
//
// The arguments are the following:
// R0 - The signal number.
// R1 - Pointer to siginfo_t structure.
// R2 - Pointer to ucontext structure.
TEXT ·signalHandler(SB),NOSPLIT,$0
// Check if the signal is from the kernel, si_code > 0 means a kernel signal.
MOVD SI_CODE(R1), R7
CMPW $0x0, R7
BLE original_handler
// Check if PC is within the area we care about.
MOVD REG_PC(R2), R7
MOVD ·memcpyBegin(SB), R8
CMP R8, R7
BLO not_memcpy
MOVD ·memcpyEnd(SB), R8
CMP R8, R7
BHS not_memcpy
// Modify the context such that execution will resume in the fault handler.
MOVD $handleMemcpyFault(SB), R7
B handle_fault
not_memcpy:
MOVD ·memclrBegin(SB), R8
CMP R8, R7
BLO not_memclr
MOVD ·memclrEnd(SB), R8
CMP R8, R7
BHS not_memclr
MOVD $handleMemclrFault(SB), R7
B handle_fault
not_memclr:
MOVD ·swapUint32Begin(SB), R8
CMP R8, R7
BLO not_swapuint32
MOVD ·swapUint32End(SB), R8
CMP R8, R7
BHS not_swapuint32
MOVD $handleSwapUint32Fault(SB), R7
B handle_fault
not_swapuint32:
MOVD ·swapUint64Begin(SB), R8
CMP R8, R7
BLO not_swapuint64
MOVD ·swapUint64End(SB), R8
CMP R8, R7
BHS not_swapuint64
MOVD $handleSwapUint64Fault(SB), R7
B handle_fault
not_swapuint64:
MOVD ·compareAndSwapUint32Begin(SB), R8
CMP R8, R7
BLO not_casuint32
MOVD ·compareAndSwapUint32End(SB), R8
CMP R8, R7
BHS not_casuint32
MOVD $handleCompareAndSwapUint32Fault(SB), R7
B handle_fault
not_casuint32:
MOVD ·loadUint32Begin(SB), R8
CMP R8, R7
BLO not_loaduint32
MOVD ·loadUint32End(SB), R8
CMP R8, R7
BHS not_loaduint32
MOVD $handleLoadUint32Fault(SB), R7
B handle_fault
not_loaduint32:
original_handler:
// Jump to the previous signal handler, which is likely the golang one.
MOVD ·savedSigBusHandler(SB), R7
MOVD ·savedSigSegVHandler(SB), R8
CMPW $SIGSEGV, R0
CSEL EQ, R8, R7, R7
B (R7)
handle_fault:
// Entered with the address of the fault handler in R7; store it in PC.
MOVD R7, REG_PC(R2)
// Store the faulting address in R0.
MOVD SI_ADDR(R1), R7
MOVD R7, REG_R0(R2)
// Store the signal number in R1.
MOVW R0, REG_R1(R2)
RET
// func addrOfSignalHandler() uintptr
TEXT ·addrOfSignalHandler(SB), $0-8
MOVD $·signalHandler(SB), R0
MOVD R0, ret+0(FP)
RET

View file

@ -0,0 +1,59 @@
// Copyright 2023 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "textflag.h"
// handleCheckXstateFault returns (the value stored in AX, the value stored in DI).
// Control is transferred to it when checkXstate below receives SIGSEGV or SIGBUS,
// with the faulting address stored in AX and the signal number stored in DI.
//
// It must have the same frame configuration as memcpy so that it can undo any
// potential call frame set up by the assembler.
TEXT handleCheckXstateFault(SB), NOSPLIT|NOFRAME, $0-26
MOVQ AX, addr+8(FP)
MOVL DI, sig+16(FP)
LDMXCSR mxcsr+20(FP)
BYTE $0xDB; BYTE $0xE2; // FNCLEX
FLDCW cw+24(FP)
RET
// ·checkXstate verifies that the specified floating point state can be loaded.
TEXT ·checkXstate(SB),NOSPLIT|NOFRAME,$0-26
// Store 0 as the returned signal number. If we run to completion,
// this is the value the caller will see; if a signal is received,
// handleMemcpyFault will store a different value in this address.
MOVL $0, sig+16(FP)
// MXCSR and the x87 control word are the only floating point state
// that is callee-save and thus we must save.
STMXCSR mxcsr+20(FP)
FSTCW cw+24(FP)
MOVQ addr+0(FP), DI
MOVL $0xffffffff, AX
MOVL $0xffffffff, DX
XRSTOR64 (DI)
// Restore MXCSR and the x87 control word.
LDMXCSR mxcsr+20(FP)
BYTE $0xDB; BYTE $0xE2; // FNCLEX
FLDCW cw+24(FP)
RET
// func addrOfCheckXstate() uintptr
TEXT ·addrOfCheckXstate(SB), $0-8
MOVQ $·checkXstate(SB), AX
MOVQ AX, ret+0(FP)
RET