snapshot: sagernet/gvisor v0.0.0-20260727.0-sing-box-mod.1 + SPEC 048 guard
Обновление снапшота с v0.0.0-20250811.0 на пин, которого требует sing-box после мержа 235 коммитов (upstream d620bbbf2 "Update gvisor to 20260727.0"). Прежний снапшот был взят 2026-08-04 ровно с той версии, на которой тогда стоял апстрим; разрыв возник 2026-08-05 вместе с его бампом. За год апстрим-gvisor изменил ~14 000 строк в 292 файлах. Значимое для нас — сетевой стек: tcp/connect.go (PMTU-discovery + исправление начального RTT/RTO: раньше задержка ACK внутри стека завышала стартовый таймаут на несколько RTT), tcp/snd.go, tcp/rcv.go, stack/conntrack.go, stack/packet_buffer.go. Всего 30 файлов в TCP и 37 в stack. Баг SPEC 048 апстрим НЕ исправил — проверено по коду новой версии: handleConnecting по-прежнему проверяет состояние endpoint'а, но не ep.h, а performHandshake так же зануляет h и отпускает мьютекс до Close(). Поэтому guard перенесён (12 строк) вместе со своим тестом (45 строк). Red/green проверен на новой базе: без guard'а тест падает с той же nil-паникой, что в полевом крашдампе; с ним зелёный.
This commit is contained in:
parent
ffebe42860
commit
117243aa02
293 changed files with 16413 additions and 2842 deletions
165
sandboxexec/sandbox/oci.go
Normal file
165
sandboxexec/sandbox/oci.go
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
// Copyright 2026 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
|
||||
//
|
||||
// https://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 sandbox
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// BundleConfig holds configuration for creating an OCI bundle.
|
||||
type BundleConfig struct {
|
||||
ID string
|
||||
RuntimeDir string
|
||||
EnableNetworking bool
|
||||
Mounts []Mount
|
||||
Env []string
|
||||
Annotations map[string]string
|
||||
WorkingDir string
|
||||
Hostname string
|
||||
}
|
||||
|
||||
// NewBundle creates a temporary OCI bundle on the fly with the given configuration.
|
||||
func NewBundle(cfg BundleConfig) (string, error) {
|
||||
// Create a bundle directory for the sandbox.
|
||||
bundleDir := filepath.Join(cfg.RuntimeDir, cfg.ID)
|
||||
rootfsDir := filepath.Join(bundleDir, "rootfs")
|
||||
|
||||
if err := os.MkdirAll(rootfsDir, 0o755); err != nil {
|
||||
return "", fmt.Errorf("failed to create bundle directories: %w", err)
|
||||
}
|
||||
|
||||
// Define the OCI Specification programmatically.
|
||||
namespaces := []specs.LinuxNamespace{
|
||||
{Type: specs.PIDNamespace},
|
||||
{Type: specs.MountNamespace},
|
||||
{Type: specs.UTSNamespace},
|
||||
{Type: specs.IPCNamespace},
|
||||
}
|
||||
|
||||
if os.Geteuid() != 0 {
|
||||
namespaces = append(namespaces, specs.LinuxNamespace{Type: specs.UserNamespace})
|
||||
}
|
||||
if cfg.EnableNetworking {
|
||||
namespaces = append(namespaces, specs.LinuxNamespace{Type: specs.NetworkNamespace})
|
||||
}
|
||||
|
||||
spec := &specs.Spec{
|
||||
Version: "1.0.0",
|
||||
Annotations: cfg.Annotations,
|
||||
Root: &specs.Root{
|
||||
Path: "rootfs",
|
||||
Readonly: false,
|
||||
},
|
||||
Process: &specs.Process{
|
||||
Terminal: false,
|
||||
User: specs.User{UID: 0, GID: 0},
|
||||
// Keeps the sandbox alive in the background.
|
||||
Args: []string{"sleep", "infinity"},
|
||||
Cwd: cfg.WorkingDir,
|
||||
},
|
||||
Mounts: []specs.Mount{
|
||||
// Mandatory Linux API Filesystems
|
||||
{Destination: "/proc", Type: "proc", Source: "proc"},
|
||||
{Destination: "/dev", Type: "tmpfs", Source: "tmpfs"},
|
||||
},
|
||||
// enable basic namespaces for gVisor.
|
||||
Linux: &specs.Linux{
|
||||
Namespaces: namespaces,
|
||||
},
|
||||
Hostname: cfg.Hostname,
|
||||
}
|
||||
|
||||
baseEnv := []string{"PATH=/bin:/usr/bin:/usr/local/bin"}
|
||||
spec.Process.Env = append(baseEnv, cfg.Env...)
|
||||
|
||||
if os.Geteuid() != 0 {
|
||||
spec.Linux.UIDMappings = []specs.LinuxIDMapping{
|
||||
{ContainerID: 0, HostID: uint32(os.Geteuid()), Size: 1},
|
||||
}
|
||||
spec.Linux.GIDMappings = []specs.LinuxIDMapping{
|
||||
{ContainerID: 0, HostID: uint32(os.Getegid()), Size: 1},
|
||||
}
|
||||
}
|
||||
|
||||
// Map host binaries & libraries as readonly. The binaries will be
|
||||
// executed in gVisor sandbox, not on the host.
|
||||
for _, p := range []string{"/bin", "/usr", "/lib", "/lib64", "/etc/alternatives"} {
|
||||
if _, err := os.Stat(p); err == nil {
|
||||
opts := []string{"rbind", "ro", "nosuid", "nodev"}
|
||||
if p == "/etc/alternatives" {
|
||||
opts = []string{"rbind", "ro"}
|
||||
}
|
||||
spec.Mounts = append(spec.Mounts, specs.Mount{
|
||||
Destination: p,
|
||||
Type: "bind",
|
||||
Source: p,
|
||||
Options: opts,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Add custom mounts. Custom mounts overriding default host mounts create duplicate OCI
|
||||
// entries. The later entry overrides the earlier one, as expected by OCI specs.
|
||||
for _, m := range cfg.Mounts {
|
||||
switch m.Type {
|
||||
case MountTypeBind:
|
||||
opts := []string{"rbind"}
|
||||
if m.ReadOnly {
|
||||
opts = append(opts, "ro")
|
||||
} else {
|
||||
opts = append(opts, "rw")
|
||||
}
|
||||
spec.Mounts = append(spec.Mounts, specs.Mount{
|
||||
Destination: filepath.Clean(m.Destination),
|
||||
Source: filepath.Clean(m.Source),
|
||||
Type: "bind",
|
||||
Options: opts,
|
||||
})
|
||||
case MountTypeTmpfs:
|
||||
spec.Mounts = append(spec.Mounts, specs.Mount{
|
||||
Destination: filepath.Clean(m.Destination),
|
||||
Source: "tmpfs",
|
||||
Type: "tmpfs",
|
||||
})
|
||||
case MountTypeProc:
|
||||
spec.Mounts = append(spec.Mounts, specs.Mount{
|
||||
Destination: filepath.Clean(m.Destination),
|
||||
Source: "proc",
|
||||
Type: "proc",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Write the spec to config.json
|
||||
configPath := filepath.Join(bundleDir, "config.json")
|
||||
configFile, err := os.Create(configPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create config.json: %w", err)
|
||||
}
|
||||
defer configFile.Close()
|
||||
|
||||
encoder := json.NewEncoder(configFile)
|
||||
encoder.SetIndent("", " ")
|
||||
if err := encoder.Encode(spec); err != nil {
|
||||
return "", fmt.Errorf("failed to encode config.json: %w", err)
|
||||
}
|
||||
|
||||
return bundleDir, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue