sandbox/

directory
v0.0.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 19, 2021 License: GPL-3.0

README

沙箱工具

这是一个基于Go语言自带的syscall库和os库里搬运出来进程调度工具,未来它的目标是实现一个更加复杂的沙箱。

本工具基于最新的Go 1.16源代码提取并编译,经测试在1.14版本及以上可以编译通过,其他低版本没有测试。

本工具仅在Linux(amd64)和MacOS(amd64,arm64)操作系统下测试编译并通过,请注意平台兼容性。

官方文档

改动

  • syscall包里的 ProcAttrSysProcAttr抽出到forkexec包,原os包里的ProcAttr抽出到process
  • forkexec包里的SysProcAttr增加了ExecRLimit定义。注意,除栈限制外,只要是值为0就表示不做限制。
type ExecRLimit struct {
    TimeLimit     int           // 时间限制 (ms)
    RealTimeLimit int           // 真实时间限制 (ms, 触发SIGALRM)
    MemoryLimit   int           // 内存限制 (KB)
    FileSizeLimit int           // 文件读写限制 (B)
    StackLimit    int           // 栈大小限制 (KB,0表示用内存限制的值,-1表示不限制)
}
  • process包的ProcAttr修改了Files的类型为interface{}
type ProcAttr struct {
    Dir string
    Env []string
    Files []interface{}         // 支持传入*os.File或者是uintptr(即使用syscall.Pipe()获取的管道文件描述符号)
    Sys *forkexec.SysProcAttr   // 换成修改过的SysProcAttr,支持setrlimit操作
}

使用

  • 简单的运行一个程序并把子程序的内容输出到stdout
func run() {
    p, err := process.StartProcess("./test", nil, &process.ProcAttr{
        Env:   os.Environ(),
        Files: []interface{}{os.Stdin,  os.Stdout,  os.Stderr},
        Sys: &forkexec.SysProcAttr {
            Rlimit: forkexec.ExecRLimit {
                TimeLimit: 1000,
                RealTimeLimit: 2000,
                MemoryLimit: 128000,
                StackLimit: 128000,
                FileSizeLimit: 1024 * 1024 * 50,
            },
        },
    })
    if err != nil {
        panic(err)
    }
    ps, err := p.Wait()
    if err != nil {
        panic(err)
    }
    ws := ps.Sys().(syscall.WaitStatus)
    fmt.Printf("\n\nExitCode: %d, Signal: %d\n", ps.ExitCode(), ws.Signal())
}
  • 运行一个交互程序,此时它们的输入输出使用管道进行连接。
func program(stdin, stdout uintptr) {
    p, err := process.StartProcess("./target", nil, &process.ProcAttr{
        Env:   os.Environ(),
        Files: []interface{}{stdin,  stdout,  os.Stderr},
        Sys: &forkexec.SysProcAttr {
            Rlimit: forkexec.ExecRLimit {
                TimeLimit: 1000,
                RealTimeLimit: 2000,
                MemoryLimit: 128000,
                StackLimit: 65500,
                FileSizeLimit: 1024 * 1024 * 50,
            },
        },
    })
    if err != nil {
        panic(err)
    }
    ps, err := p.Wait()
    if err != nil {
        panic(err)
    }
    ws := ps.Sys().(syscall.WaitStatus)
    fmt.Printf("\n\nExitCode: %d, Signal: %d\n", ps.ExitCode(), ws.Signal())
}

func judgement(stdin, stdout uintptr) {
    p, err := process.StartProcess("./judgement", []string {
        "./judgement",
        "./0.in",
        "./0.out",
        "./0.err",
        "./0.log",
    }, &process.ProcAttr{
        Env:   os.Environ(),
        Files: []interface{}{stdin,  stdout,  os.Stderr},
        Sys: &forkexec.SysProcAttr {
            Rlimit: forkexec.ExecRLimit {
                TimeLimit: 1000,
                RealTimeLimit: 2000,
                MemoryLimit: 128000,
                StackLimit: 65500,
                FileSizeLimit: 1024 * 1024 * 50,
            },
        },
    })
    if err != nil {
        panic(err)
    }
    ps, err := p.Wait()
    if err != nil {
        panic(err)
    }
    ws := ps.Sys().(syscall.WaitStatus)
    fmt.Printf("\n\nExitCode: %d, Signal: %d\n", ps.ExitCode(), ws.Signal())
}

func run() {
    fdjudge, err := forkexec.GetPipe()
    if err != nil {
        panic(err)
    }
    
    fdtarget, err := forkexec.GetPipe()
    if err != nil {
        panic(err)
    }

    go program(fdtarget[0], fdjudge[1])
    go judgement(fdjudge[0], fdtarget[1])

    // 这里演示,只是简单的睡一下,一般会用管道去监听协程,并注意进行超时处理。
    time.Sleep(10 * time.Second)
}

未来计划

  • ptrace + seccomp
  • windows支持

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL