github.com/LanceLRQ/deer-common@v0.0.9-0.20210319081233-e8222ac018a8/sandbox/README.md (about) 1 # 沙箱工具 2 3 这是一个基于Go语言自带的syscall库和os库里搬运出来进程调度工具,未来它的目标是实现一个更加复杂的沙箱。 4 5 本工具基于最新的Go 1.16源代码提取并编译,经测试在1.14版本及以上可以编译通过,其他低版本没有测试。 6 7 本工具**仅在**Linux(amd64)和MacOS(amd64,arm64)操作系统下测试编译并通过,请注意平台兼容性。 8 9 ## 官方文档 10 - `forkexec` 对应 `syscall` [https://pkg.go.dev/syscall](https://pkg.go.dev/syscall) 11 - `process` 对应 `os` [https://pkg.go.dev/os](https://pkg.go.dev/os) 12 13 ## 改动 14 - 原`syscall`包里的 `ProcAttr` 和 `SysProcAttr`抽出到`forkexec`包,原`os`包里的`ProcAttr`抽出到`process`包 15 - `forkexec`包里的`SysProcAttr`增加了`ExecRLimit`定义。注意,除栈限制外,只要是值为0就表示不做限制。 16 ```golang 17 type ExecRLimit struct { 18 TimeLimit int // 时间限制 (ms) 19 RealTimeLimit int // 真实时间限制 (ms, 触发SIGALRM) 20 MemoryLimit int // 内存限制 (KB) 21 FileSizeLimit int // 文件读写限制 (B) 22 StackLimit int // 栈大小限制 (KB,0表示用内存限制的值,-1表示不限制) 23 } 24 ``` 25 26 - `process`包的`ProcAttr`修改了`Files`的类型为`interface{}` 27 ```golang 28 type ProcAttr struct { 29 Dir string 30 Env []string 31 Files []interface{} // 支持传入*os.File或者是uintptr(即使用syscall.Pipe()获取的管道文件描述符号) 32 Sys *forkexec.SysProcAttr // 换成修改过的SysProcAttr,支持setrlimit操作 33 } 34 ``` 35 36 ## 使用 37 - 简单的运行一个程序并把子程序的内容输出到stdout 38 ```golang 39 func run() { 40 p, err := process.StartProcess("./test", nil, &process.ProcAttr{ 41 Env: os.Environ(), 42 Files: []interface{}{os.Stdin, os.Stdout, os.Stderr}, 43 Sys: &forkexec.SysProcAttr { 44 Rlimit: forkexec.ExecRLimit { 45 TimeLimit: 1000, 46 RealTimeLimit: 2000, 47 MemoryLimit: 128000, 48 StackLimit: 128000, 49 FileSizeLimit: 1024 * 1024 * 50, 50 }, 51 }, 52 }) 53 if err != nil { 54 panic(err) 55 } 56 ps, err := p.Wait() 57 if err != nil { 58 panic(err) 59 } 60 ws := ps.Sys().(syscall.WaitStatus) 61 fmt.Printf("\n\nExitCode: %d, Signal: %d\n", ps.ExitCode(), ws.Signal()) 62 } 63 ``` 64 - 运行一个交互程序,此时它们的输入输出使用管道进行连接。 65 ```golang 66 func program(stdin, stdout uintptr) { 67 p, err := process.StartProcess("./target", nil, &process.ProcAttr{ 68 Env: os.Environ(), 69 Files: []interface{}{stdin, stdout, os.Stderr}, 70 Sys: &forkexec.SysProcAttr { 71 Rlimit: forkexec.ExecRLimit { 72 TimeLimit: 1000, 73 RealTimeLimit: 2000, 74 MemoryLimit: 128000, 75 StackLimit: 65500, 76 FileSizeLimit: 1024 * 1024 * 50, 77 }, 78 }, 79 }) 80 if err != nil { 81 panic(err) 82 } 83 ps, err := p.Wait() 84 if err != nil { 85 panic(err) 86 } 87 ws := ps.Sys().(syscall.WaitStatus) 88 fmt.Printf("\n\nExitCode: %d, Signal: %d\n", ps.ExitCode(), ws.Signal()) 89 } 90 91 func judgement(stdin, stdout uintptr) { 92 p, err := process.StartProcess("./judgement", []string { 93 "./judgement", 94 "./0.in", 95 "./0.out", 96 "./0.err", 97 "./0.log", 98 }, &process.ProcAttr{ 99 Env: os.Environ(), 100 Files: []interface{}{stdin, stdout, os.Stderr}, 101 Sys: &forkexec.SysProcAttr { 102 Rlimit: forkexec.ExecRLimit { 103 TimeLimit: 1000, 104 RealTimeLimit: 2000, 105 MemoryLimit: 128000, 106 StackLimit: 65500, 107 FileSizeLimit: 1024 * 1024 * 50, 108 }, 109 }, 110 }) 111 if err != nil { 112 panic(err) 113 } 114 ps, err := p.Wait() 115 if err != nil { 116 panic(err) 117 } 118 ws := ps.Sys().(syscall.WaitStatus) 119 fmt.Printf("\n\nExitCode: %d, Signal: %d\n", ps.ExitCode(), ws.Signal()) 120 } 121 122 func run() { 123 fdjudge, err := forkexec.GetPipe() 124 if err != nil { 125 panic(err) 126 } 127 128 fdtarget, err := forkexec.GetPipe() 129 if err != nil { 130 panic(err) 131 } 132 133 go program(fdtarget[0], fdjudge[1]) 134 go judgement(fdjudge[0], fdtarget[1]) 135 136 // 这里演示,只是简单的睡一下,一般会用管道去监听协程,并注意进行超时处理。 137 time.Sleep(10 * time.Second) 138 } 139 ``` 140 141 ## 未来计划 142 143 - ptrace + seccomp 144 - windows支持