railiance-forge/tools/receive-secret-scan/volatile.go
tegwick ea65606c12
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Batch small receive inputs within the unchanged scan budget
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a14bfc-9643-7872-aa12-98326dcf25e7
2026-10-18 13:25:08 +02:00

268 lines
6.8 KiB
Go

// Linux volatile input custody; no raw input is written to ordinary disk.
package main
import (
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"syscall"
"time"
)
type storageIdentity struct {
dev uint64
ino uint64
uid uint32
}
func volatileIdentity(path string) (storageIdentity, error) {
var id storageIdentity
if filepath.Dir(path) != "/dev/shm" || !strings.HasPrefix(filepath.Base(path), "forge-receive-scan-") {
return id, errors.New("volatile-storage-refused")
}
fd, err := syscall.Open(path, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW|syscall.O_CLOEXEC, 0)
if err != nil {
return id, errors.New("volatile-storage-unavailable")
}
defer syscall.Close(fd)
var st syscall.Stat_t
var fs syscall.Statfs_t
if syscall.Fstat(fd, &st) != nil || syscall.Fstatfs(fd, &fs) != nil || fs.Type != 0x01021994 || st.Mode&07777 != 0700 || st.Uid != uint32(os.Getuid()) {
return id, errors.New("volatile-storage-refused")
}
return storageIdentity{uint64(st.Dev), st.Ino, st.Uid}, nil
}
func removeVolatile(path string, expected storageIdentity) error {
if _, err := os.Lstat(path); os.IsNotExist(err) {
return nil
}
id, err := volatileIdentity(path)
if err != nil || id != expected {
return errors.New("volatile-storage-identity-changed")
}
if os.RemoveAll(path) != nil {
return errors.New("volatile-storage-cleanup-failed")
}
return nil
}
// Pin the exact parent using Linux pidfd, not a reusable numeric process ID.
func parentDescriptor(parent int) (*os.File, error) {
if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" {
return nil, errors.New("cleanup-platform-unsupported")
}
if parent <= 1 || os.Getppid() != parent {
return nil, errors.New("cleanup-parent-refused")
}
fd, _, errno := syscall.Syscall(434, uintptr(parent), 0, 0) // pidfd_open, Linux 5.3+
if errno != 0 {
return nil, errors.New("cleanup-parent-unavailable")
}
f := os.NewFile(fd, "parent-pidfd")
if os.Getppid() != parent || fd >= 1024 {
f.Close()
return nil, errors.New("cleanup-parent-refused")
}
return f, nil
}
func waitParent(f *os.File, duration time.Duration) bool {
fd := int(f.Fd())
var deadline time.Time
if duration >= 0 {
deadline = time.Now().Add(duration)
}
for {
// select may mutate both arguments, including when interrupted.
var set syscall.FdSet
set.Bits[fd/64] |= 1 << uint(fd%64)
var timeout *syscall.Timeval
if duration >= 0 {
remaining := time.Until(deadline)
if remaining < 0 {
remaining = 0
}
value := syscall.NsecToTimeval(remaining.Nanoseconds())
timeout = &value
}
n, err := syscall.Select(fd+1, &set, nil, nil, timeout)
if err == syscall.EINTR {
continue
}
return err == nil && n > 0
}
}
func signalParent(f *os.File, signal syscall.Signal) {
_, _, _ = syscall.Syscall6(424, f.Fd(), uintptr(signal), 0, 0, 0, 0) // pidfd_send_signal
}
func cleanupProcess(args []string) int {
if len(args) != 5 {
return 4
}
dev, e1 := strconv.ParseUint(args[1], 10, 64)
ino, e2 := strconv.ParseUint(args[2], 10, 64)
uid, e3 := strconv.ParseUint(args[3], 10, 32)
parent, e4 := strconv.Atoi(args[4])
if e1 != nil || e2 != nil || e3 != nil || e4 != nil {
return 4
}
expected := storageIdentity{dev, ino, uint32(uid)}
id, err := volatileIdentity(args[0])
if err != nil || id != expected {
return 3
}
process, err := parentDescriptor(parent)
if err != nil {
return 4
}
defer process.Close()
if _, err = os.Stdout.Write([]byte("R")); err != nil {
return 4
}
eof := make(chan bool, 1)
go func() { var b [1]byte; n, e := os.Stdin.Read(b[:]); eof <- n == 0 && e == io.EOF }()
select {
case ok := <-eof:
if !ok {
return 4
}
case <-time.After(180 * time.Second):
signalParent(process, syscall.SIGTERM)
if !waitParent(process, 5*time.Second) {
signalParent(process, syscall.SIGKILL)
if !waitParent(process, -1) {
return 4
}
}
}
if removeVolatile(args[0], expected) != nil {
return 3
}
return 0
}
type volatileStorage struct {
path string
id storageIdentity
pipe io.WriteCloser
command *exec.Cmd
done chan error
}
func newVolatileStorage() (*volatileStorage, error) {
root, err := syscall.Open("/dev/shm", syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW|syscall.O_CLOEXEC, 0)
if err != nil {
return nil, errors.New("volatile-storage-unavailable")
}
var fs syscall.Statfs_t
err = syscall.Fstatfs(root, &fs)
syscall.Close(root)
if err != nil || fs.Type != 0x01021994 {
return nil, errors.New("volatile-storage-unavailable")
}
path, err := os.MkdirTemp("/dev/shm", "forge-receive-scan-")
if err != nil {
return nil, errors.New("volatile-storage-unavailable")
}
id, err := volatileIdentity(path)
if err != nil {
os.RemoveAll(path)
return nil, err
}
cmd := exec.Command("/proc/self/exe", "--cleanup-storage", path, fmt.Sprint(id.dev), fmt.Sprint(id.ino), fmt.Sprint(id.uid), strconv.Itoa(os.Getpid()))
cmd.Env = []string{"PATH=/usr/bin:/bin", "LANG=C"}
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
writer, err := cmd.StdinPipe()
if err != nil {
removeVolatile(path, id)
return nil, errors.New("cleanup-watchdog-unavailable")
}
reader, err := cmd.StdoutPipe()
if err != nil {
writer.Close()
removeVolatile(path, id)
return nil, errors.New("cleanup-watchdog-unavailable")
}
if err = cmd.Start(); err != nil {
writer.Close()
reader.Close()
removeVolatile(path, id)
return nil, errors.New("cleanup-watchdog-unavailable")
}
v := &volatileStorage{path, id, writer, cmd, make(chan error, 1)}
go func() { v.done <- cmd.Wait() }()
ready := make(chan bool, 1)
go func() {
var b [1]byte
_, e := io.ReadFull(reader, b[:])
reader.Close()
ready <- e == nil && b[0] == 'R'
}()
select {
case ok := <-ready:
if ok && v.live() {
return v, nil
}
case <-time.After(5 * time.Second):
}
v.close()
return nil, errors.New("cleanup-watchdog-unavailable")
}
func (v *volatileStorage) live() bool {
select {
case err := <-v.done:
v.done <- err
return false
default:
return true
}
}
func (v *volatileStorage) close() error {
err := removeVolatile(v.path, v.id)
v.pipe.Close()
select {
case outcome := <-v.done:
if outcome != nil && err == nil {
err = errors.New("cleanup-watchdog-failed")
}
case <-time.After(5 * time.Second):
v.command.Process.Kill()
<-v.done
if err == nil {
err = errors.New("cleanup-watchdog-failed")
}
}
return err
}
// Source-reading/writing children cannot survive loss of their exact parent.
func runChild(cmd *exec.Cmd) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
cmd.SysProcAttr = &syscall.SysProcAttr{Pdeathsig: syscall.SIGKILL}
return cmd.Run()
}
func internalProcess(args []string) (int, bool) {
if len(args) == 0 {
return 0, false
}
switch args[0] {
case "--cleanup-storage":
return cleanupProcess(args[1:]), true
case "--batch-scanner":
return batchScannerProcess(args[1:]), true
}
return 0, false
}