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
215
pkg/fdnotifier/fdnotifier.go
Normal file
215
pkg/fdnotifier/fdnotifier.go
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
// 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.
|
||||
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
// Package fdnotifier contains an adapter that translates IO events (e.g., a
|
||||
// file became readable/writable) from native FDs to the notifications in the
|
||||
// waiter package. It uses epoll in edge-triggered mode to receive notifications
|
||||
// for registered FDs.
|
||||
package fdnotifier
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type fdInfo struct {
|
||||
queue *waiter.Queue
|
||||
waiting bool
|
||||
}
|
||||
|
||||
// notifier holds all the state necessary to issue notifications when IO events
|
||||
// occur in the observed FDs.
|
||||
type notifier struct {
|
||||
// epFD is the epoll file descriptor used to register for io
|
||||
// notifications.
|
||||
epFD int
|
||||
|
||||
// mu protects fdMap.
|
||||
mu sync.Mutex
|
||||
|
||||
// fdMap maps file descriptors to their notification queues and waiting
|
||||
// status.
|
||||
fdMap map[int32]*fdInfo
|
||||
}
|
||||
|
||||
// newNotifier creates a new notifier object.
|
||||
func newNotifier() (*notifier, error) {
|
||||
epfd, err := unix.EpollCreate1(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w := ¬ifier{
|
||||
epFD: epfd,
|
||||
fdMap: make(map[int32]*fdInfo),
|
||||
}
|
||||
|
||||
go w.waitAndNotify() // S/R-SAFE: no waiter exists during save / load.
|
||||
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// waitFD waits on mask for fd. The fdMap mutex must be hold.
|
||||
func (n *notifier) waitFD(fd int32, fi *fdInfo, mask waiter.EventMask) error {
|
||||
if !fi.waiting && mask == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
e := unix.EpollEvent{
|
||||
Events: mask.ToLinux() | unix.EPOLLET,
|
||||
Fd: fd,
|
||||
}
|
||||
|
||||
switch {
|
||||
case !fi.waiting && mask != 0:
|
||||
if err := unix.EpollCtl(n.epFD, unix.EPOLL_CTL_ADD, int(fd), &e); err != nil {
|
||||
return err
|
||||
}
|
||||
fi.waiting = true
|
||||
case fi.waiting && mask == 0:
|
||||
unix.EpollCtl(n.epFD, unix.EPOLL_CTL_DEL, int(fd), nil)
|
||||
fi.waiting = false
|
||||
case fi.waiting && mask != 0:
|
||||
if err := unix.EpollCtl(n.epFD, unix.EPOLL_CTL_MOD, int(fd), &e); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addFD adds an FD to the list of FDs observed by n.
|
||||
func (n *notifier) addFD(fd int32, queue *waiter.Queue) error {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
// Panic if we're already notifying on this FD.
|
||||
if _, ok := n.fdMap[fd]; ok {
|
||||
panic(fmt.Sprintf("File descriptor %v added twice", fd))
|
||||
}
|
||||
|
||||
info := &fdInfo{queue: queue}
|
||||
// We might already have something in queue to wait for.
|
||||
if err := n.waitFD(fd, info, queue.Events()); err != nil {
|
||||
return err
|
||||
}
|
||||
// Add it to the map.
|
||||
n.fdMap[fd] = info
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateFD updates the set of events the fd needs to be notified on.
|
||||
func (n *notifier) updateFD(fd int32) error {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
if fi, ok := n.fdMap[fd]; ok {
|
||||
return n.waitFD(fd, fi, fi.queue.Events())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveFD removes an FD from the list of FDs observed by n.
|
||||
func (n *notifier) removeFD(fd int32) {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
// Remove from map, then from epoll object.
|
||||
n.waitFD(fd, n.fdMap[fd], 0)
|
||||
delete(n.fdMap, fd)
|
||||
}
|
||||
|
||||
// hasFD returns true if the fd is in the list of observed FDs.
|
||||
func (n *notifier) hasFD(fd int32) bool {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
_, ok := n.fdMap[fd]
|
||||
return ok
|
||||
}
|
||||
|
||||
// waitAndNotify run is its own goroutine and loops waiting for io event
|
||||
// notifications from the epoll object. Once notifications arrive, they are
|
||||
// dispatched to the registered queue.
|
||||
func (n *notifier) waitAndNotify() error {
|
||||
e := make([]unix.EpollEvent, 100)
|
||||
for {
|
||||
v, err := epollWait(n.epFD, e, -1)
|
||||
if err == unix.EINTR {
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notified := false
|
||||
n.mu.Lock()
|
||||
for i := 0; i < v; i++ {
|
||||
if fi, ok := n.fdMap[e[i].Fd]; ok {
|
||||
fi.queue.Notify(waiter.EventMaskFromLinux(e[i].Events))
|
||||
notified = true
|
||||
}
|
||||
}
|
||||
n.mu.Unlock()
|
||||
if notified {
|
||||
// Let goroutines woken by Notify get a chance to run before we
|
||||
// epoll_wait again.
|
||||
sync.Goyield()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var shared struct {
|
||||
notifier *notifier
|
||||
once sync.Once
|
||||
initErr error
|
||||
}
|
||||
|
||||
// AddFD adds an FD to the list of observed FDs.
|
||||
func AddFD(fd int32, queue *waiter.Queue) error {
|
||||
shared.once.Do(func() {
|
||||
shared.notifier, shared.initErr = newNotifier()
|
||||
})
|
||||
|
||||
if shared.initErr != nil {
|
||||
return shared.initErr
|
||||
}
|
||||
|
||||
return shared.notifier.addFD(fd, queue)
|
||||
}
|
||||
|
||||
// UpdateFD updates the set of events the fd needs to be notified on.
|
||||
func UpdateFD(fd int32) error {
|
||||
return shared.notifier.updateFD(fd)
|
||||
}
|
||||
|
||||
// RemoveFD removes an FD from the list of observed FDs.
|
||||
func RemoveFD(fd int32) {
|
||||
shared.notifier.removeFD(fd)
|
||||
}
|
||||
|
||||
// HasFD returns true if the FD is in the list of observed FDs.
|
||||
//
|
||||
// This should only be used by tests to assert that FDs are correctly registered.
|
||||
func HasFD(fd int32) bool {
|
||||
return shared.notifier.hasFD(fd)
|
||||
}
|
||||
6
pkg/fdnotifier/fdnotifier_state_autogen.go
Normal file
6
pkg/fdnotifier/fdnotifier_state_autogen.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package fdnotifier
|
||||
6
pkg/fdnotifier/fdnotifier_unsafe_state_autogen.go
Normal file
6
pkg/fdnotifier/fdnotifier_unsafe_state_autogen.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package fdnotifier
|
||||
82
pkg/fdnotifier/poll_unsafe.go
Normal file
82
pkg/fdnotifier/poll_unsafe.go
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
// 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.
|
||||
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package fdnotifier
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// NonBlockingPoll polls the given FD in non-blocking fashion. It is used just
|
||||
// to query the FD's current state.
|
||||
func NonBlockingPoll(fd int32, mask waiter.EventMask) waiter.EventMask {
|
||||
e := struct {
|
||||
fd int32
|
||||
events int16
|
||||
revents int16
|
||||
}{
|
||||
fd: fd,
|
||||
events: int16(mask.ToLinux()),
|
||||
}
|
||||
|
||||
ts := unix.Timespec{
|
||||
Sec: 0,
|
||||
Nsec: 0,
|
||||
}
|
||||
|
||||
for {
|
||||
n, _, err := unix.RawSyscall6(unix.SYS_PPOLL, uintptr(unsafe.Pointer(&e)), 1,
|
||||
uintptr(unsafe.Pointer(&ts)), 0, 0, 0)
|
||||
// Interrupted by signal, try again.
|
||||
if err == unix.EINTR {
|
||||
continue
|
||||
}
|
||||
// If an error occur we'll conservatively say the FD is ready for
|
||||
// whatever is being checked.
|
||||
if err != 0 {
|
||||
return mask
|
||||
}
|
||||
|
||||
// If no FDs were returned, it wasn't ready for anything.
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Otherwise we got the ready events in the revents field.
|
||||
return waiter.EventMaskFromLinux(uint32(e.revents))
|
||||
}
|
||||
}
|
||||
|
||||
// epollWait performs a blocking wait on epfd.
|
||||
//
|
||||
// Preconditions: len(events) > 0
|
||||
func epollWait(epfd int, events []unix.EpollEvent, msec int) (int, error) {
|
||||
if len(events) == 0 {
|
||||
panic("Empty events passed to EpollWait")
|
||||
}
|
||||
|
||||
// We actually use epoll_pwait with NULL sigmask instead of epoll_wait
|
||||
// since that is what the Go >= 1.11 runtime prefers.
|
||||
r, _, e := unix.Syscall6(unix.SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(unsafe.Pointer(&events[0])), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
if e != 0 {
|
||||
return 0, e
|
||||
}
|
||||
return int(r), nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue