add ability to run at memfd

This commit is contained in:
criyle 2019-08-05 15:55:36 -07:00
parent ae552994b2
commit 3247555505
9 changed files with 102 additions and 20 deletions

View File

@ -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)

View File

@ -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{

View File

@ -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)

View File

@ -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:

View File

@ -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",

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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