mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-11-04 14:49:53 +08:00
add ability to run at memfd
This commit is contained in:
parent
ae552994b2
commit
3247555505
@ -24,6 +24,7 @@ New Features:
|
||||
4. Allow pipes as input / output files
|
||||
5. Use Linux Namespace to isolate file access (elimilate ptrace)
|
||||
6. Use Linux Control Groups to limit & acct CPU & memory (elimilate wait4.rusage)
|
||||
7. Container tech with execveat memfd, sethostname, setdomainname
|
||||
|
||||
Default file access action:
|
||||
|
||||
@ -71,4 +72,3 @@ It seems unshare net or ipc takes time, maybe limits action by seccomp instead.
|
||||
TODO:
|
||||
|
||||
1. Add ability to pre-fork container deamons
|
||||
2. Add fork-exec with execve memfd (execveat), sethostname, setdomainname, prepare devices (mknod)
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/criyle/go-judger/cgroup"
|
||||
"github.com/criyle/go-judger/memfd"
|
||||
"github.com/criyle/go-judger/runconfig"
|
||||
"github.com/criyle/go-judger/runprogram"
|
||||
"github.com/criyle/go-judger/rununshared"
|
||||
@ -33,13 +34,15 @@ func printUsage() {
|
||||
func main() {
|
||||
var (
|
||||
addReadable, addWritable, addRawReadable, addRawWritable arrayFlags
|
||||
allowProc, unsafe, showDetails, namespace, useCGroup bool
|
||||
allowProc, unsafe, showDetails, namespace, useCGroup, memfile bool
|
||||
pType, result string
|
||||
timeLimit, realTimeLimit, memoryLimit, outputLimit, stackLimit uint64
|
||||
inputFileName, outputFileName, errorFileName, workPath string
|
||||
runner Runner
|
||||
cg *cgroup.CGroup
|
||||
err error
|
||||
|
||||
runner Runner
|
||||
cg *cgroup.CGroup
|
||||
err error
|
||||
execFile uintptr
|
||||
)
|
||||
|
||||
flag.Usage = printUsage
|
||||
@ -63,6 +66,7 @@ func main() {
|
||||
flag.Var(&addRawWritable, "add-writable-raw", "Add a writable file (don't transform to its real path)")
|
||||
flag.BoolVar(&namespace, "ns", false, "Use namespace to restrict file accesses")
|
||||
flag.BoolVar(&useCGroup, "cgroup", false, "Use cgroup to colloct resource usage")
|
||||
flag.BoolVar(&memfile, "memfd", false, "Use memfd as exec file")
|
||||
flag.Parse()
|
||||
|
||||
args := flag.Args()
|
||||
@ -110,6 +114,21 @@ func main() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if memfile {
|
||||
fin, err := os.Open(args[0])
|
||||
if err != nil {
|
||||
panic(fmt.Sprintln("filed to open args[0]", err))
|
||||
}
|
||||
execf, err := memfd.DupToMemfd("run_program", fin)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintln("dup to memfd failed", err))
|
||||
}
|
||||
fin.Close()
|
||||
defer execf.Close()
|
||||
execFile = execf.Fd()
|
||||
println("memfd: ", execFile)
|
||||
}
|
||||
|
||||
// open input / output / err files
|
||||
files, err := prepareFiles(inputFileName, outputFileName, errorFileName)
|
||||
if err != nil {
|
||||
@ -142,11 +161,12 @@ func main() {
|
||||
panic("cannot make temp root for new namespace")
|
||||
}
|
||||
runner = &rununshared.RunUnshared{
|
||||
Args: h.Args,
|
||||
Env: []string{pathEnv},
|
||||
WorkDir: "/w",
|
||||
Files: fds,
|
||||
RLimits: rlims,
|
||||
Args: h.Args,
|
||||
Env: []string{pathEnv},
|
||||
ExecFile: execFile,
|
||||
WorkDir: "/w",
|
||||
Files: fds,
|
||||
RLimits: rlims,
|
||||
ResLimits: specs.ResLimit{
|
||||
TimeLimit: timeLimit * 1e3,
|
||||
RealTimeLimit: realTimeLimit * 1e3,
|
||||
@ -162,13 +182,16 @@ func main() {
|
||||
}),
|
||||
ShowDetails: showDetails,
|
||||
SyncFunc: syncFunc,
|
||||
HostName: "run_program",
|
||||
DomainName: "run_program",
|
||||
}
|
||||
} else {
|
||||
runner = &runprogram.RunProgram{
|
||||
Args: h.Args,
|
||||
Env: []string{pathEnv},
|
||||
WorkDir: workPath,
|
||||
RLimits: rlims,
|
||||
Args: h.Args,
|
||||
Env: []string{pathEnv},
|
||||
ExecFile: execFile,
|
||||
WorkDir: workPath,
|
||||
RLimits: rlims,
|
||||
TraceLimit: specs.ResLimit{
|
||||
TimeLimit: timeLimit * 1e3,
|
||||
RealTimeLimit: realTimeLimit * 1e3,
|
||||
|
||||
@ -41,6 +41,18 @@ func (r *Runner) Start() (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// prepare hostname
|
||||
hostname, err := syscallStringFromString(r.HostName)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// prepare domainname
|
||||
domainname, err := syscallStringFromString(r.DomainName)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// prepare pivot_root param
|
||||
pivotRoot, oldRoot, err := preparePivotRoot(r.PivotRoot)
|
||||
if err != nil {
|
||||
@ -172,6 +184,14 @@ func (r *Runner) Start() (int, error) {
|
||||
pipe = nextfd
|
||||
nextfd++
|
||||
}
|
||||
if r.ExecFile > 0 && int(r.ExecFile) < nextfd {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_DUP3, r.ExecFile, uintptr(nextfd), syscall.FD_CLOEXEC)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
r.ExecFile = uintptr(nextfd)
|
||||
nextfd++
|
||||
}
|
||||
for i := 0; i < len(fd); i++ {
|
||||
if fd[i] >= 0 && fd[i] < int(i) {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_DUP3, uintptr(fd[i]), uintptr(nextfd), syscall.FD_CLOEXEC)
|
||||
@ -300,6 +320,18 @@ func (r *Runner) Start() (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetHostName
|
||||
if hostname != nil {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_SETHOSTNAME,
|
||||
uintptr(unsafe.Pointer(hostname)), uintptr(len(r.HostName)), 0)
|
||||
}
|
||||
|
||||
// SetDomainName
|
||||
if domainname != nil {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_SETDOMAINNAME,
|
||||
uintptr(unsafe.Pointer(domainname)), uintptr(len(r.DomainName)), 0)
|
||||
}
|
||||
|
||||
// chdir for child
|
||||
if workdir != nil {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_CHDIR, uintptr(unsafe.Pointer(workdir)), 0, 0)
|
||||
@ -402,10 +434,18 @@ func (r *Runner) Start() (int, error) {
|
||||
// at this point, runner is successfully attached for seccomp trap filter
|
||||
// or execve traped without seccomp filter
|
||||
// time to exec
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_EXECVE,
|
||||
uintptr(unsafe.Pointer(argv0)),
|
||||
uintptr(unsafe.Pointer(&argv[0])),
|
||||
uintptr(unsafe.Pointer(&envv[0])))
|
||||
// if execfile fd is specified, call fexecve
|
||||
if r.ExecFile > 0 {
|
||||
_, _, err1 = syscall.RawSyscall6(unix.SYS_EXECVEAT, r.ExecFile,
|
||||
uintptr(unsafe.Pointer(&empty[0])),
|
||||
uintptr(unsafe.Pointer(&argv[0])),
|
||||
uintptr(unsafe.Pointer(&envv[0])), unix.AT_EMPTY_PATH, 0)
|
||||
} else {
|
||||
_, _, err1 = syscall.RawSyscall6(unix.SYS_EXECVEAT, uintptr(_AT_FDCWD),
|
||||
uintptr(unsafe.Pointer(argv0)),
|
||||
uintptr(unsafe.Pointer(&argv[0])),
|
||||
uintptr(unsafe.Pointer(&envv[0])), 0, 0)
|
||||
}
|
||||
|
||||
childerror:
|
||||
syscall.RawSyscall(syscall.SYS_EXIT, uintptr(err1), 0, 0)
|
||||
|
||||
@ -17,6 +17,9 @@ type Runner struct {
|
||||
Args []string
|
||||
Env []string
|
||||
|
||||
// if exec_fd is defined, then at the end, fd_execve is called
|
||||
ExecFile uintptr
|
||||
|
||||
// POSIX Resource limit set by set rlimit
|
||||
RLimits []rlimit.RLimit
|
||||
|
||||
@ -50,11 +53,14 @@ type Runner struct {
|
||||
UnshareFlags uintptr
|
||||
|
||||
// mounts defines the mount syscalls after unshare mount namespace
|
||||
// need CAP_ADMIN inside the namespace (e.g. unshare user namespace)
|
||||
// need CAP_SYS_ADMIN inside the namespace (e.g. unshare user namespace)
|
||||
// if pivot root is provided, relative target is better for chdir-mount meta
|
||||
// and pivot root will mount as tmpfs before any mount
|
||||
Mounts []*mount.Mount
|
||||
|
||||
// HostName and DomainName to be set after unshare UTS & user (CAP_SYS_ADMIN)
|
||||
HostName, DomainName string
|
||||
|
||||
// pivot_root defines a readonly new root after unshare mount namespace
|
||||
// it should be a directory in absolute path
|
||||
// Before mounts, tt will call:
|
||||
|
||||
@ -78,7 +78,7 @@ var (
|
||||
// default syscalls to trace
|
||||
defaultSyscallTraces = []string{
|
||||
// should be traced
|
||||
"execve",
|
||||
"execveat",
|
||||
|
||||
// file open
|
||||
"open",
|
||||
|
||||
@ -15,6 +15,9 @@ type RunProgram struct {
|
||||
Env []string
|
||||
WorkDir string
|
||||
|
||||
// fexecve
|
||||
ExecFile uintptr
|
||||
|
||||
// file disriptors for new process, from 0 to len - 1
|
||||
Files []uintptr
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@ func (r *RunProgram) Start() (rt specs.TraceResult, err error) {
|
||||
ch := &forkexec.Runner{
|
||||
Args: r.Args,
|
||||
Env: r.Env,
|
||||
ExecFile: r.ExecFile,
|
||||
RLimits: r.RLimits.PrepareRLimit(),
|
||||
Files: r.Files,
|
||||
WorkDir: r.WorkDir,
|
||||
|
||||
@ -36,6 +36,7 @@ func (r *RunUnshared) Start() (rt specs.TraceResult, err error) {
|
||||
ch := &forkexec.Runner{
|
||||
Args: r.Args,
|
||||
Env: r.Env,
|
||||
ExecFile: r.ExecFile,
|
||||
RLimits: r.RLimits.PrepareRLimit(),
|
||||
Files: r.Files,
|
||||
WorkDir: r.WorkDir,
|
||||
@ -44,6 +45,8 @@ func (r *RunUnshared) Start() (rt specs.TraceResult, err error) {
|
||||
StopBeforeSeccomp: false,
|
||||
UnshareFlags: UnshareFlags,
|
||||
Mounts: r.Mounts,
|
||||
HostName: r.HostName,
|
||||
DomainName: r.DomainName,
|
||||
PivotRoot: r.Root,
|
||||
DropCaps: true,
|
||||
SyncFunc: r.SyncFunc,
|
||||
|
||||
@ -12,6 +12,9 @@ type RunUnshared struct {
|
||||
Args []string
|
||||
Env []string
|
||||
|
||||
// fexecve param
|
||||
ExecFile uintptr
|
||||
|
||||
// workdir is the current dir after unshare mount namespaces
|
||||
WorkDir string
|
||||
|
||||
@ -33,6 +36,9 @@ type RunUnshared struct {
|
||||
// Mount syscalls
|
||||
Mounts []*mount.Mount
|
||||
|
||||
// hostname & domainname
|
||||
HostName, DomainName string
|
||||
|
||||
// Show Details
|
||||
ShowDetails bool
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user