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:
commit
2c4ae3b0a4
712 changed files with 185689 additions and 0 deletions
160
pkg/safecopy/atomic_amd64.s
Normal file
160
pkg/safecopy/atomic_amd64.s
Normal 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
152
pkg/safecopy/atomic_arm64.s
Normal 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
155
pkg/safecopy/memclr_amd64.s
Normal 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
|
||||
82
pkg/safecopy/memclr_arm64.s
Normal file
82
pkg/safecopy/memclr_arm64.s
Normal 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
225
pkg/safecopy/memcpy_amd64.s
Normal 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
|
||||
86
pkg/safecopy/memcpy_arm64.s
Normal file
86
pkg/safecopy/memcpy_arm64.s
Normal 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
151
pkg/safecopy/safecopy.go
Normal 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
|
||||
}
|
||||
})
|
||||
}
|
||||
42
pkg/safecopy/safecopy_amd64_unsafe.go
Normal file
42
pkg/safecopy/safecopy_amd64_unsafe.go
Normal 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)
|
||||
}
|
||||
6
pkg/safecopy/safecopy_amd64_unsafe_state_autogen.go
Normal file
6
pkg/safecopy/safecopy_amd64_unsafe_state_autogen.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
//go:build amd64 || i386
|
||||
// +build amd64 i386
|
||||
|
||||
package safecopy
|
||||
21
pkg/safecopy/safecopy_arm64.go
Normal file
21
pkg/safecopy/safecopy_arm64.go
Normal 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() {
|
||||
}
|
||||
6
pkg/safecopy/safecopy_arm64_state_autogen.go
Normal file
6
pkg/safecopy/safecopy_arm64_state_autogen.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
//go:build arm64
|
||||
// +build arm64
|
||||
|
||||
package safecopy
|
||||
3
pkg/safecopy/safecopy_state_autogen.go
Normal file
3
pkg/safecopy/safecopy_state_autogen.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package safecopy
|
||||
333
pkg/safecopy/safecopy_unsafe.go
Normal file
333
pkg/safecopy/safecopy_unsafe.go
Normal 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))
|
||||
}
|
||||
}
|
||||
3
pkg/safecopy/safecopy_unsafe_state_autogen.go
Normal file
3
pkg/safecopy/safecopy_unsafe_state_autogen.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package safecopy
|
||||
147
pkg/safecopy/sighandler_amd64.s
Normal file
147
pkg/safecopy/sighandler_amd64.s
Normal 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
|
||||
149
pkg/safecopy/sighandler_arm64.s
Normal file
149
pkg/safecopy/sighandler_arm64.s
Normal 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
|
||||
59
pkg/safecopy/xrstor_amd64.s
Normal file
59
pkg/safecopy/xrstor_amd64.s
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue