remove duplicate sync pipe

This commit is contained in:
criyle 2019-08-14 22:18:04 -07:00
parent 4452ebd803
commit 45315e5ab2
2 changed files with 28 additions and 39 deletions

View File

@ -1,6 +1,6 @@
# go-judger # go-judger
Original goal is to reimplement [uoj-judger/run_program](https://github.com/vfleaking/uoj) in GO language using [libseccomp](https://github.com/seccomp/libseccomp-golang). As technology grows, implements new technologies such as Linux namespace & cgroup. Original goal is to reimplement [uoj-judger/run_program](https://github.com/vfleaking/uoj) in GO language using [libseccomp](https://github.com/seccomp/libseccomp-golang). As technology grows, it also implements new technologies such as Linux namespace & cgroup.
## Install ## Install

View File

@ -20,13 +20,12 @@ func afterForkInChild()
// Return pid and potential error // Return pid and potential error
// Reference to src/syscall/exec_linux.go // Reference to src/syscall/exec_linux.go
// The runtime OS thread must be locked before calling this function // The runtime OS thread must be locked before calling this function
//go:noinline // if ptrace is set to true
//go:norace //go:norace
func (r *Runner) Start() (int, error) { func (r *Runner) Start() (int, error) {
var ( var (
err1, err2 syscall.Errno err1, err2 syscall.Errno
r1 uintptr r1 uintptr
p [2]int
unshareUser = r.UnshareFlags&unix.CLONE_NEWUSER == unix.CLONE_NEWUSER unshareUser = r.UnshareFlags&unix.CLONE_NEWUSER == unix.CLONE_NEWUSER
) )
@ -65,27 +64,17 @@ func (r *Runner) Start() (int, error) {
return 0, nil return 0, nil
} }
// pipe p used to notify child the uid / gid mapping have been setup // socketpair p used to notify child the uid / gid mapping have been setup
if unshareUser { // socketpair p is also used to sync with parent before final execve
err = unix.Pipe2(p[:], unix.O_CLOEXEC) // p[0] is used by parent and p[1] is used by child
p, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
if err != nil { if err != nil {
return 0, err return 0, err
} }
}
// socketpair p2 used to sync with parent before final execve
p2, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
if err != nil {
if p[0] > 0 { // avoid fd leak
unix.Close(p[0])
unix.Close(p[1])
}
return 0, err
}
// similar to exec_linux, avoid side effect by shuffling around // similar to exec_linux, avoid side effect by shuffling around
fd, nextfd := prepareFds(r.Files) fd, nextfd := prepareFds(r.Files)
pipe := p2[1] pipe := p[1]
// Acquire the fork lock so that no other threads // Acquire the fork lock so that no other threads
// create new fds that are not yet close-on-exec // create new fds that are not yet close-on-exec
@ -103,28 +92,27 @@ func (r *Runner) Start() (int, error) {
afterFork() afterFork()
syscall.ForkLock.Unlock() syscall.ForkLock.Unlock()
// sync with child
unix.Close(p[1])
// clone syscall failed // clone syscall failed
if err1 != 0 { if err1 != 0 {
unix.Close(p[0])
return int(pid), syscall.Errno(err1) return int(pid), syscall.Errno(err1)
} }
// synchronize with child for uid / gid map // synchronize with child for uid / gid map
if unshareUser { if unshareUser {
unix.Close(p[0])
if err = writeIDMaps(int(pid)); err != nil { if err = writeIDMaps(int(pid)); err != nil {
err2 = err.(syscall.Errno) err2 = err.(syscall.Errno)
} }
syscall.RawSyscall(syscall.SYS_WRITE, uintptr(p[1]), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2))) syscall.RawSyscall(syscall.SYS_WRITE, uintptr(p[0]), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2)))
unix.Close(p[1])
} }
// sync with child r1, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(p[0]), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2)))
unix.Close(p2[1])
r1, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(p2[0]), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2)))
// child returned error code // child returned error code
if r1 != unsafe.Sizeof(err2) || err2 != 0 || err1 != 0 { if r1 != unsafe.Sizeof(err2) || err2 != 0 || err1 != 0 {
unix.Close(p2[0]) unix.Close(p[0])
if r1 == unsafe.Sizeof(err2) { if r1 == unsafe.Sizeof(err2) {
err = syscall.Errno(err2) err = syscall.Errno(err2)
} }
@ -138,19 +126,23 @@ func (r *Runner) Start() (int, error) {
if r.SyncFunc != nil { if r.SyncFunc != nil {
err = r.SyncFunc(int(pid)) err = r.SyncFunc(int(pid))
} }
// if syncfunc return error, then fail child immediately // if syncfunc return error, then fail child immediately. Otherwise, ack child (err1 == 0)
if err == nil { if err == nil {
err1 = 0 syscall.RawSyscall(syscall.SYS_WRITE, uintptr(p[0]), uintptr(unsafe.Pointer(&err1)), uintptr(unsafe.Sizeof((err1))))
syscall.RawSyscall(syscall.SYS_WRITE, uintptr(p2[0]), uintptr(unsafe.Pointer(&err1)), uintptr(unsafe.Sizeof((err1))))
} else { } else {
unix.Close(p2[0]) unix.Close(p[0])
handleChildFailed(pid) handleChildFailed(pid)
return 0, err return 0, err
} }
// if stopped before execve, then do not wait until execve
if r.Ptrace && r.Seccomp != nil || r.StopBeforeSeccomp {
return int(pid), nil
}
// if read anything mean child failed after sync (close_on_exec so it should not block) // if read anything mean child failed after sync (close_on_exec so it should not block)
r1, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(p2[0]), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2))) r1, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(p[0]), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2)))
unix.Close(p2[0]) unix.Close(p[0])
if r1 != 0 || err1 != 0 { if r1 != 0 || err1 != 0 {
if r1 == unsafe.Sizeof(err2) { if r1 == unsafe.Sizeof(err2) {
err = syscall.Errno(err2) err = syscall.Errno(err2)
@ -173,11 +165,11 @@ func (r *Runner) Start() (int, error) {
// We need parent to setup uid_map / gid_map for us since we do not have capabilities // We need parent to setup uid_map / gid_map for us since we do not have capabilities
// in the original namespace // in the original namespace
// At the same time, socket pair / pipe sychronization is required as well // At the same time, socket pair / pipe sychronization is required as well
if unshareUser { if _, _, err1 = syscall.RawSyscall(syscall.SYS_CLOSE, uintptr(p[0]), 0, 0); err1 != 0 {
if _, _, err1 = syscall.RawSyscall(syscall.SYS_CLOSE, uintptr(p[1]), 0, 0); err1 != 0 {
goto childerror goto childerror
} }
r1, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(p[0]), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2)) if unshareUser {
r1, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(pipe), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2))
if err1 != 0 { if err1 != 0 {
goto childerror goto childerror
} }
@ -189,9 +181,6 @@ func (r *Runner) Start() (int, error) {
err1 = err2 err1 = err2
goto childerror goto childerror
} }
if _, _, err1 = syscall.RawSyscall(syscall.SYS_CLOSE, uintptr(p[0]), 0, 0); err1 != 0 {
goto childerror
}
} }
// Get pid of child // Get pid of child