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
|
4. Allow pipes as input / output files
|
||||||
5. Use Linux Namespace to isolate file access (elimilate ptrace)
|
5. Use Linux Namespace to isolate file access (elimilate ptrace)
|
||||||
6. Use Linux Control Groups to limit & acct CPU & memory (elimilate wait4.rusage)
|
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:
|
Default file access action:
|
||||||
|
|
||||||
@ -71,4 +72,3 @@ It seems unshare net or ipc takes time, maybe limits action by seccomp instead.
|
|||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
1. Add ability to pre-fork container deamons
|
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"
|
"time"
|
||||||
|
|
||||||
"github.com/criyle/go-judger/cgroup"
|
"github.com/criyle/go-judger/cgroup"
|
||||||
|
"github.com/criyle/go-judger/memfd"
|
||||||
"github.com/criyle/go-judger/runconfig"
|
"github.com/criyle/go-judger/runconfig"
|
||||||
"github.com/criyle/go-judger/runprogram"
|
"github.com/criyle/go-judger/runprogram"
|
||||||
"github.com/criyle/go-judger/rununshared"
|
"github.com/criyle/go-judger/rununshared"
|
||||||
@ -33,13 +34,15 @@ func printUsage() {
|
|||||||
func main() {
|
func main() {
|
||||||
var (
|
var (
|
||||||
addReadable, addWritable, addRawReadable, addRawWritable arrayFlags
|
addReadable, addWritable, addRawReadable, addRawWritable arrayFlags
|
||||||
allowProc, unsafe, showDetails, namespace, useCGroup bool
|
allowProc, unsafe, showDetails, namespace, useCGroup, memfile bool
|
||||||
pType, result string
|
pType, result string
|
||||||
timeLimit, realTimeLimit, memoryLimit, outputLimit, stackLimit uint64
|
timeLimit, realTimeLimit, memoryLimit, outputLimit, stackLimit uint64
|
||||||
inputFileName, outputFileName, errorFileName, workPath string
|
inputFileName, outputFileName, errorFileName, workPath string
|
||||||
|
|
||||||
runner Runner
|
runner Runner
|
||||||
cg *cgroup.CGroup
|
cg *cgroup.CGroup
|
||||||
err error
|
err error
|
||||||
|
execFile uintptr
|
||||||
)
|
)
|
||||||
|
|
||||||
flag.Usage = printUsage
|
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.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(&namespace, "ns", false, "Use namespace to restrict file accesses")
|
||||||
flag.BoolVar(&useCGroup, "cgroup", false, "Use cgroup to colloct resource usage")
|
flag.BoolVar(&useCGroup, "cgroup", false, "Use cgroup to colloct resource usage")
|
||||||
|
flag.BoolVar(&memfile, "memfd", false, "Use memfd as exec file")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
args := flag.Args()
|
args := flag.Args()
|
||||||
@ -110,6 +114,21 @@ func main() {
|
|||||||
return nil
|
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
|
// open input / output / err files
|
||||||
files, err := prepareFiles(inputFileName, outputFileName, errorFileName)
|
files, err := prepareFiles(inputFileName, outputFileName, errorFileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -144,6 +163,7 @@ func main() {
|
|||||||
runner = &rununshared.RunUnshared{
|
runner = &rununshared.RunUnshared{
|
||||||
Args: h.Args,
|
Args: h.Args,
|
||||||
Env: []string{pathEnv},
|
Env: []string{pathEnv},
|
||||||
|
ExecFile: execFile,
|
||||||
WorkDir: "/w",
|
WorkDir: "/w",
|
||||||
Files: fds,
|
Files: fds,
|
||||||
RLimits: rlims,
|
RLimits: rlims,
|
||||||
@ -162,11 +182,14 @@ func main() {
|
|||||||
}),
|
}),
|
||||||
ShowDetails: showDetails,
|
ShowDetails: showDetails,
|
||||||
SyncFunc: syncFunc,
|
SyncFunc: syncFunc,
|
||||||
|
HostName: "run_program",
|
||||||
|
DomainName: "run_program",
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
runner = &runprogram.RunProgram{
|
runner = &runprogram.RunProgram{
|
||||||
Args: h.Args,
|
Args: h.Args,
|
||||||
Env: []string{pathEnv},
|
Env: []string{pathEnv},
|
||||||
|
ExecFile: execFile,
|
||||||
WorkDir: workPath,
|
WorkDir: workPath,
|
||||||
RLimits: rlims,
|
RLimits: rlims,
|
||||||
TraceLimit: specs.ResLimit{
|
TraceLimit: specs.ResLimit{
|
||||||
|
|||||||
@ -41,6 +41,18 @@ func (r *Runner) Start() (int, error) {
|
|||||||
return 0, err
|
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
|
// prepare pivot_root param
|
||||||
pivotRoot, oldRoot, err := preparePivotRoot(r.PivotRoot)
|
pivotRoot, oldRoot, err := preparePivotRoot(r.PivotRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -172,6 +184,14 @@ func (r *Runner) Start() (int, error) {
|
|||||||
pipe = nextfd
|
pipe = nextfd
|
||||||
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++ {
|
for i := 0; i < len(fd); i++ {
|
||||||
if fd[i] >= 0 && fd[i] < int(i) {
|
if fd[i] >= 0 && fd[i] < int(i) {
|
||||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_DUP3, uintptr(fd[i]), uintptr(nextfd), syscall.FD_CLOEXEC)
|
_, _, 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
|
// chdir for child
|
||||||
if workdir != nil {
|
if workdir != nil {
|
||||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_CHDIR, uintptr(unsafe.Pointer(workdir)), 0, 0)
|
_, _, 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
|
// at this point, runner is successfully attached for seccomp trap filter
|
||||||
// or execve traped without seccomp filter
|
// or execve traped without seccomp filter
|
||||||
// time to exec
|
// time to exec
|
||||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_EXECVE,
|
// 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(argv0)),
|
||||||
uintptr(unsafe.Pointer(&argv[0])),
|
uintptr(unsafe.Pointer(&argv[0])),
|
||||||
uintptr(unsafe.Pointer(&envv[0])))
|
uintptr(unsafe.Pointer(&envv[0])), 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
childerror:
|
childerror:
|
||||||
syscall.RawSyscall(syscall.SYS_EXIT, uintptr(err1), 0, 0)
|
syscall.RawSyscall(syscall.SYS_EXIT, uintptr(err1), 0, 0)
|
||||||
|
|||||||
@ -17,6 +17,9 @@ type Runner struct {
|
|||||||
Args []string
|
Args []string
|
||||||
Env []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
|
// POSIX Resource limit set by set rlimit
|
||||||
RLimits []rlimit.RLimit
|
RLimits []rlimit.RLimit
|
||||||
|
|
||||||
@ -50,11 +53,14 @@ type Runner struct {
|
|||||||
UnshareFlags uintptr
|
UnshareFlags uintptr
|
||||||
|
|
||||||
// mounts defines the mount syscalls after unshare mount namespace
|
// 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
|
// if pivot root is provided, relative target is better for chdir-mount meta
|
||||||
// and pivot root will mount as tmpfs before any mount
|
// and pivot root will mount as tmpfs before any mount
|
||||||
Mounts []*mount.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
|
// pivot_root defines a readonly new root after unshare mount namespace
|
||||||
// it should be a directory in absolute path
|
// it should be a directory in absolute path
|
||||||
// Before mounts, tt will call:
|
// Before mounts, tt will call:
|
||||||
|
|||||||
@ -78,7 +78,7 @@ var (
|
|||||||
// default syscalls to trace
|
// default syscalls to trace
|
||||||
defaultSyscallTraces = []string{
|
defaultSyscallTraces = []string{
|
||||||
// should be traced
|
// should be traced
|
||||||
"execve",
|
"execveat",
|
||||||
|
|
||||||
// file open
|
// file open
|
||||||
"open",
|
"open",
|
||||||
|
|||||||
@ -15,6 +15,9 @@ type RunProgram struct {
|
|||||||
Env []string
|
Env []string
|
||||||
WorkDir string
|
WorkDir string
|
||||||
|
|
||||||
|
// fexecve
|
||||||
|
ExecFile uintptr
|
||||||
|
|
||||||
// file disriptors for new process, from 0 to len - 1
|
// file disriptors for new process, from 0 to len - 1
|
||||||
Files []uintptr
|
Files []uintptr
|
||||||
|
|
||||||
|
|||||||
@ -28,6 +28,7 @@ func (r *RunProgram) Start() (rt specs.TraceResult, err error) {
|
|||||||
ch := &forkexec.Runner{
|
ch := &forkexec.Runner{
|
||||||
Args: r.Args,
|
Args: r.Args,
|
||||||
Env: r.Env,
|
Env: r.Env,
|
||||||
|
ExecFile: r.ExecFile,
|
||||||
RLimits: r.RLimits.PrepareRLimit(),
|
RLimits: r.RLimits.PrepareRLimit(),
|
||||||
Files: r.Files,
|
Files: r.Files,
|
||||||
WorkDir: r.WorkDir,
|
WorkDir: r.WorkDir,
|
||||||
|
|||||||
@ -36,6 +36,7 @@ func (r *RunUnshared) Start() (rt specs.TraceResult, err error) {
|
|||||||
ch := &forkexec.Runner{
|
ch := &forkexec.Runner{
|
||||||
Args: r.Args,
|
Args: r.Args,
|
||||||
Env: r.Env,
|
Env: r.Env,
|
||||||
|
ExecFile: r.ExecFile,
|
||||||
RLimits: r.RLimits.PrepareRLimit(),
|
RLimits: r.RLimits.PrepareRLimit(),
|
||||||
Files: r.Files,
|
Files: r.Files,
|
||||||
WorkDir: r.WorkDir,
|
WorkDir: r.WorkDir,
|
||||||
@ -44,6 +45,8 @@ func (r *RunUnshared) Start() (rt specs.TraceResult, err error) {
|
|||||||
StopBeforeSeccomp: false,
|
StopBeforeSeccomp: false,
|
||||||
UnshareFlags: UnshareFlags,
|
UnshareFlags: UnshareFlags,
|
||||||
Mounts: r.Mounts,
|
Mounts: r.Mounts,
|
||||||
|
HostName: r.HostName,
|
||||||
|
DomainName: r.DomainName,
|
||||||
PivotRoot: r.Root,
|
PivotRoot: r.Root,
|
||||||
DropCaps: true,
|
DropCaps: true,
|
||||||
SyncFunc: r.SyncFunc,
|
SyncFunc: r.SyncFunc,
|
||||||
|
|||||||
@ -12,6 +12,9 @@ type RunUnshared struct {
|
|||||||
Args []string
|
Args []string
|
||||||
Env []string
|
Env []string
|
||||||
|
|
||||||
|
// fexecve param
|
||||||
|
ExecFile uintptr
|
||||||
|
|
||||||
// workdir is the current dir after unshare mount namespaces
|
// workdir is the current dir after unshare mount namespaces
|
||||||
WorkDir string
|
WorkDir string
|
||||||
|
|
||||||
@ -33,6 +36,9 @@ type RunUnshared struct {
|
|||||||
// Mount syscalls
|
// Mount syscalls
|
||||||
Mounts []*mount.Mount
|
Mounts []*mount.Mount
|
||||||
|
|
||||||
|
// hostname & domainname
|
||||||
|
HostName, DomainName string
|
||||||
|
|
||||||
// Show Details
|
// Show Details
|
||||||
ShowDetails bool
|
ShowDetails bool
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user