mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-11-04 14:49:53 +08:00
finished deamon execve POC
This commit is contained in:
parent
25732d9455
commit
8bf0411dbb
@ -45,6 +45,7 @@ func main() {
|
|||||||
cg *cgroup.CGroup
|
cg *cgroup.CGroup
|
||||||
err error
|
err error
|
||||||
execFile uintptr
|
execFile uintptr
|
||||||
|
rt specs.TraceResult
|
||||||
)
|
)
|
||||||
|
|
||||||
deamon.ContainerInit()
|
deamon.ContainerInit()
|
||||||
@ -119,22 +120,6 @@ func main() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if useDeamon {
|
|
||||||
root, err := ioutil.TempDir("", "dm")
|
|
||||||
if err != nil {
|
|
||||||
panic("cannot make temp root for deamon namespace")
|
|
||||||
}
|
|
||||||
m, err := deamon.New(root)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintln("failed to new master", err))
|
|
||||||
}
|
|
||||||
err = m.Ping()
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintln("failed to ping deamon", err))
|
|
||||||
}
|
|
||||||
m.Destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
if memfile {
|
if memfile {
|
||||||
fin, err := os.Open(args[0])
|
fin, err := os.Open(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -175,7 +160,38 @@ func main() {
|
|||||||
Stack: stackLimit,
|
Stack: stackLimit,
|
||||||
}
|
}
|
||||||
|
|
||||||
if namespace {
|
if useDeamon {
|
||||||
|
sTime := time.Now()
|
||||||
|
root, err := ioutil.TempDir("", "dm")
|
||||||
|
if err != nil {
|
||||||
|
panic("cannot make temp root for deamon namespace")
|
||||||
|
}
|
||||||
|
m, err := deamon.New(root)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintln("failed to new master", err))
|
||||||
|
}
|
||||||
|
err = m.Ping()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintln("failed to ping deamon", err))
|
||||||
|
}
|
||||||
|
rTime := time.Now()
|
||||||
|
s, err := m.Execve(&deamon.ExecveParam{
|
||||||
|
Args: args,
|
||||||
|
Envv: []string{pathEnv},
|
||||||
|
Fds: fds,
|
||||||
|
RLimits: rlims.PrepareRLimit(),
|
||||||
|
SyncFunc: syncFunc,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintln("failed to execve", err))
|
||||||
|
}
|
||||||
|
println(<-s.Wait)
|
||||||
|
s.Kill <- 1
|
||||||
|
eTime := time.Now()
|
||||||
|
rt.SetUpTime = int64(rTime.Sub(sTime))
|
||||||
|
rt.RunningTime = int64(eTime.Sub(rTime))
|
||||||
|
m.Destroy()
|
||||||
|
} else if namespace {
|
||||||
h.SyscallAllow = append(h.SyscallAllow, h.SyscallTrace...)
|
h.SyscallAllow = append(h.SyscallAllow, h.SyscallTrace...)
|
||||||
root, err := ioutil.TempDir("", "ns")
|
root, err := ioutil.TempDir("", "ns")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -243,7 +259,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run tracer
|
// Run tracer
|
||||||
rt, err := runner.Start()
|
if runner != nil {
|
||||||
|
rt, err = runner.Start()
|
||||||
|
}
|
||||||
println("results:", rt, err)
|
println("results:", rt, err)
|
||||||
|
|
||||||
if useCGroup {
|
if useCGroup {
|
||||||
|
|||||||
@ -2,4 +2,11 @@ package deamon
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
cmdPing = "ping"
|
cmdPing = "ping"
|
||||||
|
cmdCopyIn = "copyin"
|
||||||
|
cmdOpen = "open"
|
||||||
|
cmdDelete = "delete"
|
||||||
|
cmdReset = "reset"
|
||||||
|
cmdExecve = "execve"
|
||||||
|
cmdOk = "ok"
|
||||||
|
cmdKill = "kill"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -5,7 +5,9 @@ import (
|
|||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/criyle/go-judger/forkexec"
|
||||||
"github.com/criyle/go-judger/unixsocket"
|
"github.com/criyle/go-judger/unixsocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,20 +37,12 @@ func ContainerInit() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("container_init: faile to new socket(%v)", err)
|
return fmt.Errorf("container_init: faile to new socket(%v)", err)
|
||||||
}
|
}
|
||||||
var (
|
|
||||||
buffer = make([]byte, bufferSize)
|
|
||||||
cmd Cmd
|
|
||||||
)
|
|
||||||
for {
|
for {
|
||||||
n, msg, err := soc.RecvMsg(buffer)
|
cmd, msg, err := recvCmd(soc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("loop: failed RecvMsg(%v)", err)
|
return fmt.Errorf("loop: %v", err)
|
||||||
}
|
}
|
||||||
dec := gob.NewDecoder(bytes.NewReader(buffer[:n]))
|
if err := handleCmd(soc, cmd, msg); err != nil {
|
||||||
if err := dec.Decode(&cmd); err != nil {
|
|
||||||
return fmt.Errorf("loop: failed to decode(%v)", err)
|
|
||||||
}
|
|
||||||
if err := handleCmd(soc, &cmd, msg); err != nil {
|
|
||||||
return fmt.Errorf("loop: failed to execute cmd(%v)", err)
|
return fmt.Errorf("loop: failed to execute cmd(%v)", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,21 +52,112 @@ func handleCmd(s *unixsocket.Socket, cmd *Cmd, msg *unixsocket.Msg) error {
|
|||||||
switch cmd.Cmd {
|
switch cmd.Cmd {
|
||||||
case cmdPing:
|
case cmdPing:
|
||||||
return handlePing(s)
|
return handlePing(s)
|
||||||
|
case cmdExecve:
|
||||||
|
return handleExecve(s, cmd, msg)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePing(s *unixsocket.Socket) error {
|
func handlePing(s *unixsocket.Socket) error {
|
||||||
return sendReply(s, &Reply{})
|
return sendReply(s, &Reply{}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendReply(s *unixsocket.Socket, reply *Reply) error {
|
func handleExecve(s *unixsocket.Socket, cmd *Cmd, msg *unixsocket.Msg) error {
|
||||||
|
var files []uintptr
|
||||||
|
if msg != nil {
|
||||||
|
files = intSliceToUintptr(msg.Fds)
|
||||||
|
}
|
||||||
|
|
||||||
|
syncFunc := func(pid int) error {
|
||||||
|
msg2 := unixsocket.Msg{
|
||||||
|
Cred: &syscall.Ucred{
|
||||||
|
Pid: int32(pid),
|
||||||
|
Uid: uint32(syscall.Getuid()),
|
||||||
|
Gid: uint32(syscall.Getgid()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err2 := sendReply(s, &Reply{}, &msg2); err2 != nil {
|
||||||
|
_ = err2
|
||||||
|
}
|
||||||
|
cmd2, _, err2 := recvCmd(s)
|
||||||
|
if err2 != nil {
|
||||||
|
_ = err2
|
||||||
|
}
|
||||||
|
switch cmd2.Cmd {
|
||||||
|
case cmdKill:
|
||||||
|
return fmt.Errorf("kill")
|
||||||
|
case cmdOk:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
r := forkexec.Runner{
|
||||||
|
Args: cmd.Argv,
|
||||||
|
Env: cmd.Envv,
|
||||||
|
RLimits: cmd.RLmits,
|
||||||
|
Files: files,
|
||||||
|
WorkDir: "/w",
|
||||||
|
NoNewPrivs: true,
|
||||||
|
DropCaps: true,
|
||||||
|
SyncFunc: syncFunc,
|
||||||
|
}
|
||||||
|
pid, err := r.Start()
|
||||||
|
if err != nil {
|
||||||
|
_ = err
|
||||||
|
}
|
||||||
|
// recv kill
|
||||||
|
go func() {
|
||||||
|
// msg must be kill
|
||||||
|
if _, _, err3 := recvCmd(s); err3 != nil {
|
||||||
|
_ = err3
|
||||||
|
}
|
||||||
|
// kill all
|
||||||
|
syscall.Kill(-1, syscall.SIGKILL)
|
||||||
|
}()
|
||||||
|
var wstatus syscall.WaitStatus
|
||||||
|
// wait pid
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
_, err = syscall.Wait4(pid, &wstatus, 0, nil)
|
||||||
|
if err != nil {
|
||||||
|
_ = err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case wstatus.Exited():
|
||||||
|
reply := Reply{
|
||||||
|
Status: wstatus.ExitStatus(),
|
||||||
|
}
|
||||||
|
if err = sendReply(s, &reply, nil); err != nil {
|
||||||
|
_ = err
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func recvCmd(s *unixsocket.Socket) (*Cmd, *unixsocket.Msg, error) {
|
||||||
|
var cmd Cmd
|
||||||
|
buffer := GetBuffer()
|
||||||
|
defer PutBuffer(buffer)
|
||||||
|
n, msg, err := s.RecvMsg(buffer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed RecvMsg(%v)", err)
|
||||||
|
}
|
||||||
|
dec := gob.NewDecoder(bytes.NewReader(buffer[:n]))
|
||||||
|
if err := dec.Decode(&cmd); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to decode(%v)", err)
|
||||||
|
}
|
||||||
|
return &cmd, msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendReply(s *unixsocket.Socket, reply *Reply, msg *unixsocket.Msg) error {
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
enc := gob.NewEncoder(&buffer)
|
enc := gob.NewEncoder(&buffer)
|
||||||
if err := enc.Encode(reply); err != nil {
|
if err := enc.Encode(reply); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.SendMsg(buffer.Bytes(), nil); err != nil {
|
if err := s.SendMsg(buffer.Bytes(), msg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -21,7 +21,7 @@ Protocol between client and deamon (not thread safe):
|
|||||||
send:
|
send:
|
||||||
reply: "success"
|
reply: "success"
|
||||||
- execve: (execute file inside container):
|
- execve: (execute file inside container):
|
||||||
send: argv, envv, fd map, rlimits, <fds>
|
send: argv, envv, rlimits, <fds>
|
||||||
reply:
|
reply:
|
||||||
- success: "success", <pid>
|
- success: "success", <pid>
|
||||||
- failed: "failed"
|
- failed: "failed"
|
||||||
@ -44,11 +44,11 @@ type Cmd struct {
|
|||||||
Flags int // open flags
|
Flags int // open flags
|
||||||
Argv []string // execve argv
|
Argv []string // execve argv
|
||||||
Envv []string // execve envv
|
Envv []string // execve envv
|
||||||
FdMap []int // execvc fd map
|
|
||||||
RLmits []rlimit.RLimit // execve posix rlimit
|
RLmits []rlimit.RLimit // execve posix rlimit
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reply is the reply message send back to controller
|
// Reply is the reply message send back to controller
|
||||||
type Reply struct {
|
type Reply struct {
|
||||||
Error string // empty if no error
|
Error string // empty if no error
|
||||||
|
Status int // waitpid exit status
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,38 +4,53 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/criyle/go-judger/unixsocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ping send ping message to container
|
// Ping send ping message to container
|
||||||
func (m *Master) Ping() error {
|
func (m *Master) Ping() error {
|
||||||
var (
|
|
||||||
wbuff bytes.Buffer
|
|
||||||
reply Reply
|
|
||||||
)
|
|
||||||
rbuff := GetBuffer()
|
|
||||||
defer PutBuffer(rbuff)
|
|
||||||
// send ping
|
// send ping
|
||||||
enc := gob.NewEncoder(&wbuff)
|
|
||||||
cmd := Cmd{
|
cmd := Cmd{
|
||||||
Cmd: cmdPing,
|
Cmd: cmdPing,
|
||||||
}
|
}
|
||||||
if err := enc.Encode(cmd); err != nil {
|
if err := m.sendCmd(&cmd, nil); err != nil {
|
||||||
return fmt.Errorf("ping: failed to encode(%v)", err)
|
return fmt.Errorf("ping: %v", err)
|
||||||
}
|
|
||||||
if err := m.socket.SendMsg(wbuff.Bytes(), nil); err != nil {
|
|
||||||
return fmt.Errorf("ping: failed to sendMsg(%v)", err)
|
|
||||||
}
|
}
|
||||||
// receive no error
|
// receive no error
|
||||||
n, _, err := m.socket.RecvMsg(rbuff)
|
reply, _, err := m.recvReply()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("ping: failed to recvMsg(%v)", err)
|
return fmt.Errorf("ping: %v", err)
|
||||||
}
|
|
||||||
dec := gob.NewDecoder(bytes.NewReader(rbuff[:n]))
|
|
||||||
if err := dec.Decode(&reply); err != nil {
|
|
||||||
return fmt.Errorf("ping: failed to decode(%v)", err)
|
|
||||||
}
|
}
|
||||||
if reply.Error != "" {
|
if reply.Error != "" {
|
||||||
return fmt.Errorf("ping: reply error(%v)", reply.Error)
|
return fmt.Errorf("ping: reply error(%v)", reply.Error)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Master) recvReply() (*Reply, *unixsocket.Msg, error) {
|
||||||
|
var reply Reply
|
||||||
|
buff := GetBuffer()
|
||||||
|
defer PutBuffer(buff)
|
||||||
|
n, msg, err := m.socket.RecvMsg(buff)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to RecvMsg(%v)", err)
|
||||||
|
}
|
||||||
|
dec := gob.NewDecoder(bytes.NewReader(buff[:n]))
|
||||||
|
if err := dec.Decode(&reply); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to decode(%v)", err)
|
||||||
|
}
|
||||||
|
return &reply, msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Master) sendCmd(cmd *Cmd, msg *unixsocket.Msg) error {
|
||||||
|
var buff bytes.Buffer
|
||||||
|
enc := gob.NewEncoder(&buff)
|
||||||
|
if err := enc.Encode(cmd); err != nil {
|
||||||
|
return fmt.Errorf("failed to encode(%v)", err)
|
||||||
|
}
|
||||||
|
if err := m.socket.SendMsg(buff.Bytes(), msg); err != nil {
|
||||||
|
return fmt.Errorf("failed to SendMsg(%v)", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
80
deamon/master_cmd_execve.go
Normal file
80
deamon/master_cmd_execve.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package deamon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/criyle/go-judger/types/rlimit"
|
||||||
|
"github.com/criyle/go-judger/unixsocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExecveParam is parameters to run process inside container
|
||||||
|
type ExecveParam struct {
|
||||||
|
Args []string
|
||||||
|
Envv []string
|
||||||
|
Fds []uintptr
|
||||||
|
// POSIX Resource limit set by set rlimit
|
||||||
|
RLimits []rlimit.RLimit
|
||||||
|
// SyncFunc called with pid before execve
|
||||||
|
SyncFunc func(pid int) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecveStatus is the return value for execve
|
||||||
|
// Wait channel will produce the waitpid exit status
|
||||||
|
// Kill channel will kill the process if value received
|
||||||
|
type ExecveStatus struct {
|
||||||
|
Wait <-chan int
|
||||||
|
Kill chan<- int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execve runs process inside container
|
||||||
|
func (m *Master) Execve(param *ExecveParam) (*ExecveStatus, error) {
|
||||||
|
msg := &unixsocket.Msg{
|
||||||
|
Fds: uintptrSliceToInt(param.Fds),
|
||||||
|
}
|
||||||
|
cmd := Cmd{
|
||||||
|
Cmd: cmdExecve,
|
||||||
|
Argv: param.Args,
|
||||||
|
Envv: param.Envv,
|
||||||
|
RLmits: param.RLimits,
|
||||||
|
}
|
||||||
|
if err := m.sendCmd(&cmd, msg); err != nil {
|
||||||
|
return nil, fmt.Errorf("execve: sendCmd %v", err)
|
||||||
|
}
|
||||||
|
reply, msg, err := m.recvReply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("execve: RecvReply %v", err)
|
||||||
|
}
|
||||||
|
if reply.Error != "" || msg == nil || msg.Cred == nil {
|
||||||
|
m.sendCmd(&Cmd{Cmd: cmdKill}, nil)
|
||||||
|
return nil, fmt.Errorf("execve: no pid recved or error(%v)", reply.Error)
|
||||||
|
}
|
||||||
|
if err := param.SyncFunc(int(msg.Cred.Pid)); err != nil {
|
||||||
|
m.sendCmd(&Cmd{Cmd: cmdKill}, nil)
|
||||||
|
return nil, fmt.Errorf("execve: syncfunc failed(%v)", err)
|
||||||
|
}
|
||||||
|
if err := m.sendCmd(&Cmd{Cmd: cmdOk}, nil); err != nil {
|
||||||
|
return nil, fmt.Errorf("execve: ok failed(%v)", err)
|
||||||
|
}
|
||||||
|
wait := make(chan int)
|
||||||
|
kill := make(chan int)
|
||||||
|
// Wait
|
||||||
|
go func() {
|
||||||
|
reply2, _, err2 := m.recvReply()
|
||||||
|
if err2 != nil {
|
||||||
|
_ = err2
|
||||||
|
}
|
||||||
|
wait <- reply2.Status
|
||||||
|
}()
|
||||||
|
// Kill
|
||||||
|
go func() {
|
||||||
|
<-kill
|
||||||
|
err3 := m.sendCmd(&Cmd{Cmd: cmdKill}, nil)
|
||||||
|
if err3 != nil {
|
||||||
|
_ = err3
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return &ExecveStatus{
|
||||||
|
Wait: wait,
|
||||||
|
Kill: kill,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
23
deamon/utils.go
Normal file
23
deamon/utils.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package deamon
|
||||||
|
|
||||||
|
func intSliceToUintptr(s []int) []uintptr {
|
||||||
|
var r []uintptr
|
||||||
|
if len(s) > 0 {
|
||||||
|
r = make([]uintptr, len(s))
|
||||||
|
for i, x := range s {
|
||||||
|
r[i] = uintptr(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func uintptrSliceToInt(s []uintptr) []int {
|
||||||
|
var r []int
|
||||||
|
if len(s) > 0 {
|
||||||
|
r = make([]int, len(s))
|
||||||
|
for i, x := range s {
|
||||||
|
r[i] = int(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user