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

View File

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

View File

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

View File

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

View File

@ -78,7 +78,7 @@ var (
// default syscalls to trace
defaultSyscallTraces = []string{
// should be traced
"execve",
"execveat",
// file open
"open",

View File

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

View File

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

View File

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

View File

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