├── test ├── resources │ ├── c │ │ ├── compiler_bomb_1.c │ │ ├── include_leaks.c │ │ ├── plain_text.c │ │ ├── compiler_bomb_0.c │ │ ├── core_dump_1.c │ │ ├── infinite_loop.c │ │ ├── core_dump_0.c │ │ ├── run_command_line_0.c │ │ ├── run_command_line_1.c │ │ ├── syscall_0.c │ │ ├── fork_bomb_0.c │ │ ├── core_dump_2.c │ │ ├── compiler_bomb_3.c │ │ ├── compiler_bomb_2.c │ │ ├── ac.c │ │ ├── fork_bomb_1.c │ │ ├── memory_allocation.c │ │ ├── get_host_by_name.c │ │ └── tcp_client.c │ └── cpp │ │ ├── include_leaks.cpp │ │ ├── compiler_bomb_1.cpp │ │ ├── core_dump_0.cpp │ │ ├── infinite_loop.cpp │ │ ├── run_command_line_0.cpp │ │ ├── fork_bomb.cpp │ │ ├── compiler_bomb_2.cpp │ │ ├── plain_text.cpp │ │ ├── memory_allocation.cpp │ │ ├── compiler_bomb_4.cpp │ │ ├── thread.cpp │ │ ├── compiler_bomb_3.cpp │ │ └── ac.cpp ├── cpp_test.go └── c_test.go ├── Makefile ├── scripts ├── clean_cpu_cgroup.sh ├── clean_pids_cgroup.sh └── clean_memory_cgroup.sh ├── .idea ├── deployment.xml ├── thriftCompiler.xml ├── vcs.xml ├── modules.xml └── sandbox.iml ├── README.md ├── .gitignore ├── go.mod ├── LICENSE ├── model └── result.go ├── compiler.go ├── go.sum ├── sandbox ├── namespace.go └── cgroup.go └── container.go /test/resources/c/compiler_bomb_1.c: -------------------------------------------------------------------------------- 1 | #include -------------------------------------------------------------------------------- /test/resources/c/include_leaks.c: -------------------------------------------------------------------------------- 1 | #include -------------------------------------------------------------------------------- /test/resources/c/plain_text.c: -------------------------------------------------------------------------------- 1 | compile error test for gcc -------------------------------------------------------------------------------- /test/resources/cpp/include_leaks.cpp: -------------------------------------------------------------------------------- 1 | #include -------------------------------------------------------------------------------- /test/resources/c/compiler_bomb_0.c: -------------------------------------------------------------------------------- 1 | int main[0x10000000]={1}; -------------------------------------------------------------------------------- /test/resources/cpp/compiler_bomb_1.cpp: -------------------------------------------------------------------------------- 1 | #include -------------------------------------------------------------------------------- /test/resources/cpp/core_dump_0.cpp: -------------------------------------------------------------------------------- 1 | int main() { 2 | throw "throw"; 3 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | 3 | test: 4 | go test -v -race -count=1 ./test/... 5 | -------------------------------------------------------------------------------- /scripts/clean_cpu_cgroup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rmdir /sys/fs/cgroup/cpu/$1 -------------------------------------------------------------------------------- /scripts/clean_pids_cgroup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rmdir /sys/fs/cgroup/pids/$1 -------------------------------------------------------------------------------- /scripts/clean_memory_cgroup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rmdir /sys/fs/cgroup/memory/$1 -------------------------------------------------------------------------------- /test/resources/c/core_dump_1.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 3 / 0; 3 | return 0; 4 | } -------------------------------------------------------------------------------- /test/resources/c/infinite_loop.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int i = 0; 3 | for(;;) i++; 4 | return 0; 5 | } -------------------------------------------------------------------------------- /test/resources/cpp/infinite_loop.cpp: -------------------------------------------------------------------------------- 1 | int main() { 2 | int i = 0; 3 | for(;;) i++; 4 | return 0; 5 | } -------------------------------------------------------------------------------- /test/resources/c/core_dump_0.c: -------------------------------------------------------------------------------- 1 | int foo() { 2 | return foo(); 3 | } 4 | 5 | int main() { 6 | foo(); 7 | return 0; 8 | } -------------------------------------------------------------------------------- /test/resources/cpp/run_command_line_0.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | system("reboot"); 5 | return 0; 6 | } -------------------------------------------------------------------------------- /test/resources/c/run_command_line_0.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | system("rm /etc/hosts"); 5 | return 0; 6 | } -------------------------------------------------------------------------------- /test/resources/c/run_command_line_1.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | system("shutdown -h now"); 5 | return 0; 6 | } -------------------------------------------------------------------------------- /test/resources/c/syscall_0.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | kill(1, SIGSEGV); 6 | return 0; 7 | } -------------------------------------------------------------------------------- /test/resources/cpp/fork_bomb.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char **argv){ 4 | while (1) system(argv[0]); 5 | return 0; 6 | } -------------------------------------------------------------------------------- /test/resources/c/fork_bomb_0.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | while(1) 6 | fork(); 7 | return 0; 8 | } -------------------------------------------------------------------------------- /test/resources/cpp/compiler_bomb_2.cpp: -------------------------------------------------------------------------------- 1 | int main() { 2 | struct x struct zv 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/thriftCompiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/resources/cpp/memory_allocation.cpp: -------------------------------------------------------------------------------- 1 | int main() { 2 | for (;;) { 3 | int *x = new int[100000000]; 4 | x[0] = 0; 5 | x[100000000 - 1] = 123; 6 | } 7 | } -------------------------------------------------------------------------------- /test/resources/c/compiler_bomb_3.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | char magic[1024 * 1024 * 1024] = {'\n'}; 4 | 5 | int main() { 6 | magic[0] = 'a'; 7 | printf("test"); 8 | return 0; 9 | } -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/resources/cpp/compiler_bomb_4.cpp: -------------------------------------------------------------------------------- 1 | #define Z struct X 2 | #define T templateZ;struct Y{static int f(){return 0;}};T>Z:Y{};T>Z<0,N>:Y{};T,int M>Z{static int f(){static int x[99999]={X::f()+X::f()};}};int x=X<80>::f(); -------------------------------------------------------------------------------- /test/resources/cpp/thread.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | void task() { 8 | cout << "invoke in threads.\n"; 9 | } 10 | 11 | int main() { 12 | thread t(task); 13 | t.join(); 14 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Justice Online Judge 2 | 3 | This repo is the sandbox of [Justice](https://www.justice.plus) online judge. 4 | 5 | For more details, please refer to [Justice Deployment Guide](https://github.com/justice-oj/documents) or [My Blogs](https://tech.liuchao.me/tag/justice-oj/). -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/resources/c/compiler_bomb_2.c: -------------------------------------------------------------------------------- 1 | #define a 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 2 | #define b a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a 3 | #define c b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b 4 | #define d c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c 5 | #define e d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d 6 | #define f e,e,e,e,e,e,e,e,e,e,e,e,e,e,e,e 7 | __int128 x[]={f,f,f,f,f,f,f,f}; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/**/workspace.xml 2 | .idea/**/tasks.xml 3 | .idea/**/usage.statistics.xml 4 | .idea/**/dictionaries 5 | .idea/**/shelf 6 | .idea/**/dataSources/ 7 | .idea/**/dataSources.ids 8 | .idea/**/dataSources.local.xml 9 | .idea/**/sqlDataSources.xml 10 | .idea/**/dynamic.xml 11 | .idea/**/uiDesigner.xml 12 | .idea/**/dbnavigator.xml 13 | 14 | bin/ 15 | -------------------------------------------------------------------------------- /test/resources/c/ac.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | int hh, mm, ss; 6 | char tt[3] = ""; 7 | 8 | scanf("%d:%d:%d %[^\n]%*c", &hh, &mm, &ss, tt); 9 | hh = (strcmp(tt, "PM") == 0 && hh != 12) ? hh + 12 : hh; 10 | if (strcmp(tt, "AM") == 0 && hh == 12) hh = 0; 11 | printf("%02d:%02d:%02d", hh, mm, ss); 12 | 13 | return 0; 14 | } -------------------------------------------------------------------------------- /test/resources/c/fork_bomb_1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | if (!fork()) { 7 | while (1) { 8 | fork(); 9 | } 10 | } 11 | puts("hello, world"); 12 | int *a = malloc(1024 * 1024 * 10); 13 | a[0] = 456; 14 | a[1024 * 1024 * 100 / sizeof(int) - 1] = 123; 15 | return 233; 16 | } -------------------------------------------------------------------------------- /test/resources/c/memory_allocation.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | int chunk_size = 1024 * 1024; 8 | void *p = NULL; 9 | 10 | while (1) { 11 | if ((p = (int *) malloc((size_t) chunk_size)) == NULL) { 12 | break; 13 | } 14 | memset(p, 1, (size_t) chunk_size); 15 | } 16 | return 0; 17 | } -------------------------------------------------------------------------------- /test/resources/cpp/compiler_bomb_3.cpp: -------------------------------------------------------------------------------- 1 | template 2 | 3 | struct a { 4 | static const int n; 5 | }; 6 | 7 | template const int a::n = a::n>::n; 8 | 9 | template 10 | 11 | struct a { 12 | static const int n = a::n; 13 | }; 14 | 15 | template 16 | 17 | struct a<0, B> { 18 | static const int n = B + 1; 19 | }; 20 | 21 | int h = a<4, 2>::n; -------------------------------------------------------------------------------- /.idea/sandbox.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/resources/cpp/ac.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | int main(){ 8 | int h, m, s; 9 | char ch, aorp; 10 | 11 | cin >> h >> ch >> m >> ch >> s >> aorp >> ch; 12 | h = (aorp == 'A') ? (h==12 ? 0 : h) : (h==12 ? 12 : h+12); 13 | 14 | cout << setw(2) << setfill('0') << h << ":" 15 | << setw(2) << setfill('0') << m << ":" 16 | << setw(2) << setfill('0') << s << endl; 17 | 18 | return 0; 19 | } -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/justice-oj/sandbox 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/docker/docker v1.13.1 7 | github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c // indirect 8 | github.com/jtolds/gls v4.20.0+incompatible // indirect 9 | github.com/kr/pretty v0.1.0 // indirect 10 | github.com/satori/go.uuid v1.2.0 11 | github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 // indirect 12 | github.com/smartystreets/goconvey v0.0.0-20170602164621-9e8dc3f972df 13 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /test/resources/c/get_host_by_name.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | char *hostname = "www.amazon.com"; 8 | char ip[100]; 9 | struct hostent *he; 10 | struct in_addr **addr_list; 11 | 12 | if ((he = gethostbyname(hostname)) == NULL) { 13 | printf("gethostbyname error"); 14 | return 1; 15 | } 16 | 17 | addr_list = (struct in_addr **) he->h_addr_list; 18 | 19 | for (int i = 0; addr_list[i] != NULL; i++) { 20 | strcpy(ip, inet_ntoa(*addr_list[i])); 21 | } 22 | 23 | printf("ip is: %s", ip); 24 | return 0; 25 | } -------------------------------------------------------------------------------- /test/resources/c/tcp_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]) { 6 | int sock; 7 | struct sockaddr_in server; 8 | char message[1000], server_reply[2000]; 9 | 10 | sock = socket(AF_INET, SOCK_STREAM, 0); 11 | if (sock == -1) { 12 | printf("create socket failed"); 13 | return 1; 14 | } 15 | 16 | server.sin_addr.s_addr = inet_addr("54.182.3.77"); 17 | server.sin_family = AF_INET; 18 | server.sin_port = htons(80); 19 | 20 | if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) { 21 | printf("connect failed"); 22 | return 1; 23 | } 24 | 25 | printf("connected"); 26 | close(sock); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Chao Liu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /model/result.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | // +build go1.15 3 | 4 | package model 5 | 6 | type Result struct { 7 | Runtime int64 `json:"runtime,omitempty"` 8 | Memory int64 `json:"memory,omitempty"` 9 | Status int32 `json:"status"` 10 | Error string `json:"error,omitempty"` 11 | Input string `json:"input,omitempty"` 12 | Output string `json:"output,omitempty"` 13 | Expected string `json:"expected,omitempty"` 14 | } 15 | 16 | const ( 17 | StatusAc = iota 18 | _ 19 | StatusRe 20 | StatusTle 21 | _ 22 | StatusWa 23 | ) 24 | 25 | func (r *Result) GetAcceptedTaskResult(runtime, memory int64) *Result { 26 | r.Status = StatusAc 27 | r.Runtime = runtime 28 | r.Memory = memory 29 | return r 30 | } 31 | 32 | func (r *Result) GetRuntimeErrorTaskResult() *Result { 33 | r.Status = StatusRe 34 | r.Error = "Runtime Error" 35 | return r 36 | } 37 | 38 | func (r *Result) GetTimeLimitExceededErrorTaskResult() *Result { 39 | r.Status = StatusTle 40 | r.Error = "Runtime Error" 41 | return r 42 | } 43 | 44 | func (r *Result) GetWrongAnswerTaskResult(input, output, expected string) *Result { 45 | r.Status = StatusWa 46 | r.Input = input 47 | r.Output = output 48 | r.Expected = expected 49 | return r 50 | } 51 | -------------------------------------------------------------------------------- /compiler.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | // +build go1.15 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "flag" 9 | "fmt" 10 | "os" 11 | "os/exec" 12 | "syscall" 13 | "time" 14 | ) 15 | 16 | // compiler wrapper with timeout limitation 17 | // os.Stderr will not be empty if any error occurred 18 | func main() { 19 | compiler := flag.String("compiler", "/usr/bin/gcc", "C/CPP compiler with abs path") 20 | basedir := flag.String("basedir", "/tmp", "basedir of tmp C/CPP code snippet") 21 | filename := flag.String("filename", "Main.c", "name of file to be compiled") 22 | timeout := flag.Int("timeout", 5000, "compile timeout in milliseconds") 23 | std := flag.String("std", "gnu11", "language standards supported by gcc") 24 | flag.Parse() 25 | 26 | var stdout, stderr bytes.Buffer 27 | cmd := exec.Command(*compiler, *filename, "-save-temps", "-std="+*std, "-fmax-errors=10", "-static", "-o", "Main") 28 | cmd.SysProcAttr = &syscall.SysProcAttr{ 29 | Setpgid: true, 30 | } 31 | cmd.Stdout = &stdout 32 | cmd.Stderr = &stderr 33 | cmd.Dir = *basedir 34 | 35 | time.AfterFunc(time.Duration(*timeout)*time.Millisecond, func() { 36 | _ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) 37 | }) 38 | 39 | if err := cmd.Run(); err != nil { 40 | // err.Error() == "signal: killed" means compiler is killed by our timer. 41 | _, _ = os.Stderr.WriteString(fmt.Sprintf("stderr: %s, err: %s\n", stderr.String(), err.Error())) 42 | return 43 | } 44 | 45 | _, _ = os.Stdout.WriteString("Compile OK\n") 46 | } 47 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo= 2 | github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 3 | github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= 4 | github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 5 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 6 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 7 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 8 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 9 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 10 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 11 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 12 | github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= 13 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 14 | github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 h1:hBSHahWMEgzwRyS6dRpxY0XyjZsHyQ61s084wo5PJe0= 15 | github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 16 | github.com/smartystreets/goconvey v0.0.0-20170602164621-9e8dc3f972df h1:AawEzDdiSpy07QO9efSOHQ/BRincGLxilju4pOq3k8s= 17 | github.com/smartystreets/goconvey v0.0.0-20170602164621-9e8dc3f972df/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= 18 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 19 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 20 | -------------------------------------------------------------------------------- /sandbox/namespace.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | // +build go1.15 3 | 4 | package sandbox 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | "path/filepath" 10 | "syscall" 11 | ) 12 | 13 | //noinspection GoUnusedExportedFunction 14 | func InitNamespace(newRoot string) error { 15 | _, _ = os.Stderr.WriteString(fmt.Sprintf("InitNamespace(%s) starting...\n", newRoot)) 16 | 17 | if err := pivotRoot(newRoot); err != nil { 18 | _, _ = os.Stderr.WriteString(fmt.Sprintf("pivotRoot(%s) failed, err: %s\n", newRoot, err.Error())) 19 | return err 20 | } 21 | 22 | if err := syscall.Sethostname([]byte("justice")); err != nil { 23 | _, _ = os.Stderr.WriteString(fmt.Sprintf("syscall.Sethostname failed, err: %s\n", err.Error())) 24 | return err 25 | } 26 | 27 | _, _ = os.Stderr.WriteString(fmt.Sprintf("InitNamespace(%s) done\n", newRoot)) 28 | return nil 29 | } 30 | 31 | func pivotRoot(newRoot string) error { 32 | putOld := filepath.Join(newRoot, "/.pivot_root") 33 | 34 | // bind mount new_root to itself - this is a slight hack needed to satisfy requirement (2) 35 | // 36 | // The following restrictions apply to new_root and put_old: 37 | // 1. They must be directories. 38 | // 2. new_root and put_old must not be on the same filesystem as the current root. 39 | // 3. put_old must be underneath new_root, that is, adding a nonzero 40 | // number of /.. to the string pointed to by put_old must yield the same directory as new_root. 41 | // 4. No other filesystem may be mounted on put_old. 42 | if err := syscall.Mount(newRoot, newRoot, "", syscall.MS_BIND|syscall.MS_REC, ""); err != nil { 43 | _, _ = os.Stderr.WriteString(fmt.Sprintf("syscall.Mount(%s, %s, \"\", syscall.MS_BIND|syscall.MS_REC, \"\") failed\n", newRoot, newRoot)) 44 | return err 45 | } 46 | 47 | // create put_old directory 48 | if err := os.MkdirAll(putOld, 0700); err != nil { 49 | _, _ = os.Stderr.WriteString(fmt.Sprintf("os.MkdirAll(%s, 0700) failed\n", putOld)) 50 | return err 51 | } 52 | 53 | // call pivotRoot 54 | if err := syscall.PivotRoot(newRoot, putOld); err != nil { 55 | _, _ = os.Stderr.WriteString(fmt.Sprintf("syscall.PivotRoot(%s, %s) failed\n", newRoot, putOld)) 56 | return err 57 | } 58 | 59 | // Note that this also applies to the calling process: pivotRoot() may 60 | // or may not affect its current working directory. It is therefore 61 | // recommended to call chdir("/") immediately after pivotRoot(). 62 | if err := os.Chdir("/"); err != nil { 63 | _, _ = os.Stderr.WriteString(fmt.Sprintf("os.Chdir(\"/\") failed\n")) 64 | return err 65 | } 66 | 67 | // umount put_old, which now lives at /.pivot_root 68 | putOld = "/.pivot_root" 69 | if err := syscall.Unmount(putOld, syscall.MNT_DETACH); err != nil { 70 | _, _ = os.Stderr.WriteString(fmt.Sprintf("syscall.Unmount(%s, syscall.MNT_DETACH) failed\n", putOld)) 71 | return err 72 | } 73 | 74 | // remove put_old 75 | if err := os.RemoveAll(putOld); err != nil { 76 | _, _ = os.Stderr.WriteString(fmt.Sprintf("os.RemoveAll(%s) failed\n", putOld)) 77 | return err 78 | } 79 | 80 | return nil 81 | } 82 | -------------------------------------------------------------------------------- /sandbox/cgroup.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | // +build go1.15 3 | 4 | package sandbox 5 | 6 | import ( 7 | "fmt" 8 | "io/ioutil" 9 | "os" 10 | "path/filepath" 11 | ) 12 | 13 | const ( 14 | cgCPUPathPrefix = "/sys/fs/cgroup/cpu/" 15 | cgPidPathPrefix = "/sys/fs/cgroup/pids/" 16 | cgMemoryPathPrefix = "/sys/fs/cgroup/memory/" 17 | ) 18 | 19 | //noinspection GoUnusedExportedFunction 20 | func InitCGroup(pid, containerID, memory string) error { 21 | _, _ = os.Stderr.WriteString(fmt.Sprintf("InitCGroup(%s, %s, %s) starting...\n", pid, containerID, memory)) 22 | 23 | dirs := []string{ 24 | filepath.Join(cgCPUPathPrefix, containerID), 25 | filepath.Join(cgPidPathPrefix, containerID), 26 | filepath.Join(cgMemoryPathPrefix, containerID), 27 | } 28 | 29 | for _, dir := range dirs { 30 | if err := os.MkdirAll(dir, os.ModePerm); err != nil { 31 | _, _ = os.Stderr.WriteString(fmt.Sprintf("os.MkdirAll(%s, os.ModePerm) failed, err: %s\n", dir, err.Error())) 32 | return err 33 | } 34 | } 35 | 36 | if err := cpuCGroup(pid, containerID); err != nil { 37 | _, _ = os.Stderr.WriteString(fmt.Sprintf("cpuCGroup(%s, %s) failed, err: %s\n", pid, containerID, err.Error())) 38 | return err 39 | } 40 | 41 | if err := pidCGroup(pid, containerID); err != nil { 42 | _, _ = os.Stderr.WriteString(fmt.Sprintf("pidCGroup(%s, %s) failed, err: %s\n", pid, containerID, err.Error())) 43 | return err 44 | } 45 | 46 | if err := memoryCGroup(pid, containerID, memory); err != nil { 47 | _, _ = os.Stderr.WriteString(fmt.Sprintf("memoryCGroup(%s, %s) failed, err: %s\n", pid, containerID, err.Error())) 48 | return err 49 | } 50 | 51 | _, _ = os.Stderr.WriteString(fmt.Sprintf("InitCGroup(%s, %s, %s) done\n", pid, containerID, memory)) 52 | return nil 53 | } 54 | 55 | // https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt 56 | func cpuCGroup(pid, containerID string) error { 57 | cgCPUPath := filepath.Join(cgCPUPathPrefix, containerID) 58 | mapping := map[string]string{ 59 | "tasks": pid, 60 | "cpu.cfs_quota_us": "10000", 61 | } 62 | 63 | for key, value := range mapping { 64 | path := filepath.Join(cgCPUPath, key) 65 | if err := ioutil.WriteFile(path, []byte(value), 0644); err != nil { 66 | _, _ = os.Stderr.WriteString(fmt.Sprintf("Writing [%s] to file: %s failed\n", value, path)) 67 | return err 68 | } 69 | c, _ := ioutil.ReadFile(path) 70 | _, _ = os.Stderr.WriteString(fmt.Sprintf("Content of %s is: %s", path, c)) 71 | } 72 | return nil 73 | } 74 | 75 | // https://www.kernel.org/doc/Documentation/cgroup-v1/pids.txt 76 | func pidCGroup(pid, containerID string) error { 77 | cgPidPath := filepath.Join(cgPidPathPrefix, containerID) 78 | mapping := map[string]string{ 79 | "cgroup.procs": pid, 80 | "pids.max": "64", 81 | } 82 | 83 | for key, value := range mapping { 84 | path := filepath.Join(cgPidPath, key) 85 | if err := ioutil.WriteFile(path, []byte(value), 0644); err != nil { 86 | _, _ = os.Stderr.WriteString(fmt.Sprintf("Writing [%s] to file: %s failed\n", value, path)) 87 | return err 88 | } 89 | c, _ := ioutil.ReadFile(path) 90 | _, _ = os.Stderr.WriteString(fmt.Sprintf("Content of %s is: %s", path, c)) 91 | } 92 | return nil 93 | } 94 | 95 | // https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt 96 | func memoryCGroup(pid, containerID, memory string) error { 97 | cgMemoryPath := filepath.Join(cgMemoryPathPrefix, containerID) 98 | mapping := map[string]string{ 99 | "memory.kmem.limit_in_bytes": "64m", 100 | "tasks": pid, 101 | "memory.limit_in_bytes": fmt.Sprintf("%sm", memory), 102 | } 103 | 104 | for key, value := range mapping { 105 | path := filepath.Join(cgMemoryPath, key) 106 | if err := ioutil.WriteFile(path, []byte(value), 0644); err != nil { 107 | _, _ = os.Stderr.WriteString(fmt.Sprintf("Writing [%s] to file: %s failed\n", value, path)) 108 | return err 109 | } 110 | c, _ := ioutil.ReadFile(path) 111 | _, _ = os.Stderr.WriteString(fmt.Sprintf("Content of %s is: %s", path, c)) 112 | } 113 | return nil 114 | } 115 | -------------------------------------------------------------------------------- /container.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | // +build go1.15 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "encoding/json" 9 | "flag" 10 | "fmt" 11 | "os" 12 | "os/exec" 13 | "strconv" 14 | "strings" 15 | "syscall" 16 | "time" 17 | 18 | "github.com/docker/docker/pkg/reexec" 19 | "github.com/justice-oj/sandbox/model" 20 | "github.com/justice-oj/sandbox/sandbox" 21 | "github.com/satori/go.uuid" 22 | ) 23 | 24 | func init() { 25 | // register "justiceInit" => justiceInit() every time 26 | reexec.Register("justiceInit", justiceInit) 27 | 28 | /** 29 | * 0. `init()` adds key "justiceInit" in `map`; 30 | * 1. reexec.Init() seeks if key `os.Args[0]` exists in `registeredInitializers`; 31 | * 2. for the first time this binary is invoked, the key is os.Args[0], AKA "/path/to/clike_container", 32 | which `registeredInitializers` will return `false`; 33 | * 3. `main()` calls binary itself by reexec.Command("justiceInit", args...); 34 | * 4. for the second time this binary is invoked, the key is os.Args[0], AKA "justiceInit", 35 | * which exists in `registeredInitializers`; 36 | * 5. the value `justiceInit()` is invoked, any hooks(like set hostname) before fork() can be placed here. 37 | */ 38 | if reexec.Init() { 39 | os.Exit(0) 40 | } 41 | } 42 | 43 | func justiceInit() { 44 | basedir := os.Args[1] 45 | input := os.Args[2] 46 | expected := os.Args[3] 47 | timeout, _ := strconv.ParseInt(os.Args[4], 10, 32) 48 | 49 | r := new(model.Result) 50 | if err := sandbox.InitNamespace(basedir); err != nil { 51 | result, _ := json.Marshal(r.GetRuntimeErrorTaskResult()) 52 | _, _ = os.Stdout.Write(result) 53 | os.Exit(0) 54 | } 55 | 56 | var o, e bytes.Buffer 57 | cmd := exec.Command("/Main") 58 | cmd.Stdin = strings.NewReader(input) 59 | cmd.Stdout = &o 60 | cmd.Stderr = &e 61 | cmd.SysProcAttr = &syscall.SysProcAttr{ 62 | Setpgid: true, 63 | } 64 | cmd.Env = []string{"PS1=[justice] # "} 65 | 66 | time.AfterFunc(time.Duration(timeout)*time.Millisecond, func() { 67 | _ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) 68 | }) 69 | 70 | startTime := time.Now().UnixNano() / 1e6 71 | if err := cmd.Run(); err != nil { 72 | result, _ := json.Marshal(r.GetRuntimeErrorTaskResult()) 73 | _, _ = os.Stdout.Write(result) 74 | _, _ = os.Stderr.WriteString(fmt.Sprintf("err: %s\n", err.Error())) 75 | return 76 | } 77 | endTime := time.Now().UnixNano() / 1e6 78 | 79 | if e.Len() > 0 { 80 | result, _ := json.Marshal(r.GetRuntimeErrorTaskResult()) 81 | _, _ = os.Stdout.Write(result) 82 | _, _ = os.Stderr.WriteString(fmt.Sprintf("stderr: %s\n", e.String())) 83 | return 84 | } 85 | 86 | output := strings.TrimSpace(o.String()) 87 | if output == expected { 88 | // ms, MB 89 | timeCost, memoryCost := endTime-startTime, cmd.ProcessState.SysUsage().(*syscall.Rusage).Maxrss/1024 90 | // timeCost value 0 will be omitted 91 | if timeCost == 0 { 92 | timeCost = 1 93 | } 94 | 95 | result, _ := json.Marshal(r.GetAcceptedTaskResult(timeCost, memoryCost)) 96 | _, _ = os.Stdout.Write(result) 97 | } else { 98 | result, _ := json.Marshal(r.GetWrongAnswerTaskResult(input, output, expected)) 99 | _, _ = os.Stdout.Write(result) 100 | } 101 | 102 | _, _ = os.Stderr.WriteString(fmt.Sprintf("output: %s | expected: %s\n", output, expected)) 103 | } 104 | 105 | // logs will be printed to os.Stderr 106 | func main() { 107 | basedir := flag.String("basedir", "/tmp", "basedir of tmp C binary") 108 | input := flag.String("input", "", "test case input") 109 | expected := flag.String("expected", "", "test case expected") 110 | timeout := flag.String("timeout", "2000", "timeout in milliseconds") 111 | memory := flag.String("memory", "256", "memory limitation in MB") 112 | flag.Parse() 113 | 114 | result, u := new(model.Result), uuid.NewV4() 115 | if err := sandbox.InitCGroup(strconv.Itoa(os.Getpid()), u.String(), *memory); err != nil { 116 | result, _ := json.Marshal(result.GetRuntimeErrorTaskResult()) 117 | _, _ = os.Stdout.Write(result) 118 | os.Exit(0) 119 | } 120 | 121 | cmd := reexec.Command("justiceInit", *basedir, *input, *expected, *timeout, *memory) 122 | cmd.Stdin = os.Stdin 123 | cmd.Stdout = os.Stdout 124 | cmd.Stderr = os.Stderr 125 | cmd.SysProcAttr = &syscall.SysProcAttr{ 126 | Cloneflags: syscall.CLONE_NEWNS | 127 | syscall.CLONE_NEWUTS | 128 | syscall.CLONE_NEWIPC | 129 | syscall.CLONE_NEWPID | 130 | syscall.CLONE_NEWNET | 131 | syscall.CLONE_NEWUSER, 132 | UidMappings: []syscall.SysProcIDMap{ 133 | { 134 | ContainerID: 0, 135 | HostID: os.Getuid(), 136 | Size: 1, 137 | }, 138 | }, 139 | GidMappings: []syscall.SysProcIDMap{ 140 | { 141 | ContainerID: 0, 142 | HostID: os.Getgid(), 143 | Size: 1, 144 | }, 145 | }, 146 | } 147 | 148 | if err := cmd.Run(); err != nil { 149 | result, _ := json.Marshal(result.GetRuntimeErrorTaskResult()) 150 | _, _ = os.Stderr.WriteString(fmt.Sprintf("%s\n", err.Error())) 151 | _, _ = os.Stdout.Write(result) 152 | } 153 | 154 | os.Exit(0) 155 | } 156 | -------------------------------------------------------------------------------- /test/cpp_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | "strings" 9 | "testing" 10 | 11 | . "github.com/smartystreets/goconvey/convey" 12 | ) 13 | 14 | var ( 15 | CPPBaseDir string 16 | CPPProjectDir string 17 | ) 18 | 19 | // copy test source file `*.cpp` to tmp dir 20 | func copyCPPSourceFile(name string, t *testing.T) { 21 | t.Logf("Copying file %s ...", name) 22 | if err := os.MkdirAll(CPPBaseDir, os.ModePerm); err != nil { 23 | t.Errorf("Invoke mkdir(%s) err: %v", CPPBaseDir, err.Error()) 24 | } 25 | 26 | args := []string{ 27 | CPPProjectDir + "/resources/cpp/" + name, 28 | CPPBaseDir + "/Main.cpp", 29 | } 30 | cmd := exec.Command("cp", args...) 31 | if err := cmd.Run(); err != nil { 32 | t.Errorf("Invoke `cp %s` err: %v", strings.Join(args, " "), err) 33 | } 34 | } 35 | 36 | // compile CPP source file 37 | func compileCPP(name, baseDir string, t *testing.T) string { 38 | t.Logf("Compiling file %s ...", name) 39 | 40 | var stderr bytes.Buffer 41 | args := []string{ 42 | "-compiler=/usr/bin/g++", 43 | "-basedir=" + baseDir, 44 | "-filename=Main.cpp", 45 | "-timeout=3000", 46 | "-std=gnu++14", 47 | } 48 | cmd := exec.Command("/opt/justice-sandbox/bin/clike_compiler", args...) 49 | cmd.Stderr = &stderr 50 | if err := cmd.Run(); err != nil { 51 | t.Errorf("Invoke `/opt/justice-sandbox/bin/clike_compiler %s` err: %v", strings.Join(args, " "), err) 52 | } 53 | 54 | return stderr.String() 55 | } 56 | 57 | // run binary in our container 58 | func runCPP(baseDir, memory, timeout string, t *testing.T) string { 59 | t.Log("Running file /Main ...") 60 | 61 | var stdout, stderr bytes.Buffer 62 | args := []string{ 63 | "-basedir=" + baseDir, 64 | "-input=10:10:23AM", 65 | "-expected=10:10:23", 66 | "-memory=" + memory, 67 | "-timeout=" + timeout, 68 | } 69 | cmd := exec.Command("/opt/justice-sandbox/bin/clike_container", args...) 70 | cmd.Stdout = &stdout 71 | cmd.Stderr = &stderr 72 | if err := cmd.Run(); err != nil { 73 | t.Errorf("Invoke `/opt/justice-sandbox/bin/clike_container %s` err: %v", strings.Join(args, " "), err) 74 | } 75 | 76 | t.Logf("stderr of runCPP: %s", stderr.String()) 77 | return stdout.String() 78 | } 79 | 80 | func TestCPP0000Fixture(t *testing.T) { 81 | CPPProjectDir, _ = os.Getwd() 82 | CPPBaseDir = t.TempDir() 83 | } 84 | 85 | func TestCPP0001AC(t *testing.T) { 86 | name := "ac.cpp" 87 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 88 | copyCPPSourceFile(name, t) 89 | 90 | So(compileCPP(name, CPPBaseDir, t), ShouldBeEmpty) 91 | So(runCPP(CPPBaseDir, "16", "1000", t), ShouldContainSubstring, `"status":0`) 92 | }) 93 | } 94 | 95 | func TestCPP0002CompilerBomb1(t *testing.T) { 96 | name := "compiler_bomb_1.cpp" 97 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 98 | copyCPPSourceFile(name, t) 99 | 100 | So(compileCPP(name, CPPBaseDir, t), ShouldContainSubstring, "signal: killed") 101 | }) 102 | } 103 | 104 | func TestCPP0003CompilerBomb2(t *testing.T) { 105 | name := "compiler_bomb_2.cpp" 106 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 107 | copyCPPSourceFile(name, t) 108 | 109 | So(compileCPP(name, CPPBaseDir, t), ShouldContainSubstring, "compilation terminated due to -fmax-errors=") 110 | }) 111 | } 112 | 113 | func TestCPP0004CompilerBomb3(t *testing.T) { 114 | name := "compiler_bomb_3.cpp" 115 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 116 | copyCPPSourceFile(name, t) 117 | 118 | So(compileCPP(name, CPPBaseDir, t), ShouldContainSubstring, "template instantiation depth exceeds maximum of") 119 | }) 120 | } 121 | 122 | func TestCPP0005CompilerBomb4(t *testing.T) { 123 | name := "compiler_bomb_4.cpp" 124 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 125 | copyCPPSourceFile(name, t) 126 | 127 | So(compileCPP(name, CPPBaseDir, t), ShouldContainSubstring, "signal: killed") 128 | }) 129 | } 130 | 131 | func TestCPP0006CoreDump0(t *testing.T) { 132 | name := "core_dump_0.cpp" 133 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 134 | copyCPPSourceFile(name, t) 135 | 136 | So(compileCPP(name, CPPBaseDir, t), ShouldBeEmpty) 137 | // terminate called after throwing an instance of 'char const*' 138 | So(runCPP(CPPBaseDir, "64", "1000", t), ShouldContainSubstring, "Runtime Error") 139 | }) 140 | } 141 | 142 | func TestCPP0007ForkBomb(t *testing.T) { 143 | name := "fork_bomb.cpp" 144 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 145 | copyCPPSourceFile(name, t) 146 | 147 | So(compileCPP(name, CPPBaseDir, t), ShouldBeEmpty) 148 | So(runCPP(CPPBaseDir, "64", "1000", t), ShouldContainSubstring, "Runtime Error") 149 | }) 150 | } 151 | 152 | func TestCPP0008IncludeLeaks(t *testing.T) { 153 | name := "include_leaks.cpp" 154 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 155 | copyCPPSourceFile(name, t) 156 | 157 | So(compileCPP(name, CPPBaseDir, t), ShouldContainSubstring, "/etc/shadow") 158 | }) 159 | } 160 | 161 | func TestCPP0009InfiniteLoop(t *testing.T) { 162 | name := "infinite_loop.cpp" 163 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 164 | copyCPPSourceFile(name, t) 165 | 166 | So(compileCPP(name, CPPBaseDir, t), ShouldBeEmpty) 167 | So(runCPP(CPPBaseDir, "64", "1000", t), ShouldContainSubstring, "Runtime Error") 168 | }) 169 | } 170 | 171 | func TestCPP0010MemoryAllocation(t *testing.T) { 172 | name := "memory_allocation.cpp" 173 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 174 | copyCPPSourceFile(name, t) 175 | 176 | So(compileCPP(name, CPPBaseDir, t), ShouldBeEmpty) 177 | So(runCPP(CPPBaseDir, "64", "1000", t), ShouldContainSubstring, "Runtime Error") 178 | }) 179 | } 180 | 181 | func TestCPP0011PlainText(t *testing.T) { 182 | name := "plain_text.cpp" 183 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 184 | copyCPPSourceFile(name, t) 185 | 186 | So(compileCPP(name, CPPBaseDir, t), ShouldContainSubstring, "error") 187 | }) 188 | } 189 | 190 | func TestCPP0012RunCommandLine0(t *testing.T) { 191 | name := "run_command_line_0.cpp" 192 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 193 | copyCPPSourceFile(name, t) 194 | 195 | So(compileCPP(name, CPPBaseDir, t), ShouldBeEmpty) 196 | So(runCPP(CPPBaseDir, "16", "1000", t), ShouldContainSubstring, `"status":5`) 197 | }) 198 | } 199 | -------------------------------------------------------------------------------- /test/c_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | "strings" 9 | "testing" 10 | 11 | . "github.com/smartystreets/goconvey/convey" 12 | ) 13 | 14 | var ( 15 | CBaseDir string 16 | CProjectDir string 17 | ) 18 | 19 | // copy test source file `*.c` to tmp dir 20 | func copyCSourceFile(name string, t *testing.T) { 21 | t.Logf("Copying file %s ...", name) 22 | if err := os.MkdirAll(CBaseDir, os.ModePerm); err != nil { 23 | t.Errorf("Invoke mkdir(%s) err: %v", CBaseDir, err.Error()) 24 | } 25 | 26 | args := []string{ 27 | CProjectDir + "/resources/c/" + name, 28 | CBaseDir + "/Main.c", 29 | } 30 | cmd := exec.Command("cp", args...) 31 | if err := cmd.Run(); err != nil { 32 | t.Errorf("Invoke `cp %s` err: %v", strings.Join(args, " "), err) 33 | } 34 | } 35 | 36 | // compile C source file 37 | func compileC(name, baseDir string, t *testing.T) string { 38 | t.Logf("Compiling file %s ...", name) 39 | 40 | var stderr bytes.Buffer 41 | args := []string{ 42 | "-compiler=/usr/bin/gcc", 43 | "-basedir=" + baseDir, 44 | "-filename=Main.c", 45 | "-timeout=3000", 46 | "-std=gnu11", 47 | } 48 | cmd := exec.Command("/opt/justice-sandbox/bin/clike_compiler", args...) 49 | cmd.Stderr = &stderr 50 | if err := cmd.Run(); err != nil { 51 | t.Errorf("Invoke `/opt/justice-sandbox/bin/clike_compiler %s` err: %v", strings.Join(args, " "), err) 52 | } 53 | 54 | return stderr.String() 55 | } 56 | 57 | // run binary in our container 58 | func runC(baseDir, memory, timeout string, t *testing.T) string { 59 | t.Log("Running binary /Main ...") 60 | 61 | var stdout, stderr bytes.Buffer 62 | args := []string{ 63 | "-basedir=" + baseDir, 64 | "-input=10:10:23PM", 65 | "-expected=22:10:23", 66 | "-memory=" + memory, 67 | "-timeout=" + timeout, 68 | } 69 | cmd := exec.Command("/opt/justice-sandbox/bin/clike_container", args...) 70 | cmd.Stdout = &stdout 71 | cmd.Stderr = &stderr 72 | if err := cmd.Run(); err != nil { 73 | t.Errorf("Invoke `/opt/justice-sandbox/bin/clike_container %s` err: %v", strings.Join(args, " "), err) 74 | } 75 | 76 | t.Logf("stderr of runC: %s", stderr.String()) 77 | return stdout.String() 78 | } 79 | 80 | func TestC0000Fixture(t *testing.T) { 81 | CProjectDir, _ = os.Getwd() 82 | CBaseDir = t.TempDir() 83 | } 84 | 85 | func TestC0001AC(t *testing.T) { 86 | name := "ac.c" 87 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 88 | copyCSourceFile(name, t) 89 | 90 | So(compileC(name, CBaseDir, t), ShouldBeEmpty) 91 | So(runC(CBaseDir, "64", "1000", t), ShouldContainSubstring, `"status":0`) 92 | }) 93 | } 94 | 95 | func TestC0002CompilerBomb0(t *testing.T) { 96 | name := "compiler_bomb_0.c" 97 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 98 | copyCSourceFile(name, t) 99 | 100 | So(compileC(name, CBaseDir, t), ShouldContainSubstring, "signal: killed") 101 | }) 102 | } 103 | 104 | func TestC0003CompilerBomb1(t *testing.T) { 105 | name := "compiler_bomb_1.c" 106 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 107 | copyCSourceFile(name, t) 108 | 109 | So(compileC(name, CBaseDir, t), ShouldContainSubstring, "signal: killed") 110 | }) 111 | } 112 | 113 | func TestC0004CompilerBomb2(t *testing.T) { 114 | name := "compiler_bomb_2.c" 115 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 116 | copyCSourceFile(name, t) 117 | 118 | So(compileC(name, CBaseDir, t), ShouldContainSubstring, "signal: killed") 119 | }) 120 | } 121 | 122 | func TestC0005CompilerBomb3(t *testing.T) { 123 | name := "compiler_bomb_3.c" 124 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 125 | copyCSourceFile(name, t) 126 | 127 | So(compileC(name, CBaseDir, t), ShouldContainSubstring, "signal: killed") 128 | }) 129 | } 130 | 131 | func TestC0006CoreDump0(t *testing.T) { 132 | name := "core_dump_0.c" 133 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 134 | copyCSourceFile(name, t) 135 | 136 | So(compileC(name, CBaseDir, t), ShouldBeEmpty) 137 | So(runC(CBaseDir, "64", "1000", t), ShouldContainSubstring, "Runtime Error") 138 | }) 139 | } 140 | 141 | func TestC0007CoreDump1(t *testing.T) { 142 | name := "core_dump_1.c" 143 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 144 | copyCSourceFile(name, t) 145 | 146 | // warning: division by zero [-Wdiv-by-zero] 147 | So(compileC(name, CBaseDir, t), ShouldBeEmpty) 148 | So(runC(CBaseDir, "64", "1000", t), ShouldContainSubstring, "Runtime Error") 149 | }) 150 | } 151 | 152 | func TestC0008CoreDump2(t *testing.T) { 153 | name := "core_dump_2.c" 154 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 155 | copyCSourceFile(name, t) 156 | 157 | So(compileC(name, CBaseDir, t), ShouldBeEmpty) 158 | // *** stack smashing detected ***: terminated 159 | So(runC(CBaseDir, "64", "1000", t), ShouldContainSubstring, "Runtime Error") 160 | }) 161 | } 162 | 163 | func TestC0009ForkBomb0(t *testing.T) { 164 | name := "fork_bomb_0.c" 165 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 166 | copyCSourceFile(name, t) 167 | 168 | So(compileC(name, CBaseDir, t), ShouldBeEmpty) 169 | // got `signal: killed` 170 | So(runC(CBaseDir, "64", "1000", t), ShouldContainSubstring, "Runtime Error") 171 | }) 172 | } 173 | 174 | func TestC0010ForkBomb1(t *testing.T) { 175 | name := "fork_bomb_1.c" 176 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 177 | copyCSourceFile(name, t) 178 | 179 | So(compileC(name, CBaseDir, t), ShouldBeEmpty) 180 | // got `signal: killed` 181 | So(runC(CBaseDir, "64", "1000", t), ShouldContainSubstring, "Runtime Error") 182 | }) 183 | } 184 | 185 | func TestC0011GetHostByName(t *testing.T) { 186 | name := "get_host_by_name.c" 187 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 188 | copyCSourceFile(name, t) 189 | 190 | So(compileC(name, CBaseDir, t), ShouldBeEmpty) 191 | // Main.c:(.text+0x28): warning: Using 'gethostbyname' in statically linked applications 192 | // requires at runtime the shared libraries from the glibc version used for linking 193 | // got `exit status 1` 194 | So(runC(CBaseDir, "64", "1000", t), ShouldContainSubstring, `"status":2`) 195 | }) 196 | } 197 | 198 | func TestC0012IncludeLeaks(t *testing.T) { 199 | name := "include_leaks.c" 200 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 201 | copyCSourceFile(name, t) 202 | 203 | So(compileC(name, CBaseDir, t), ShouldContainSubstring, "/etc/shadow") 204 | }) 205 | } 206 | 207 | func TestC0013InfiniteLoop(t *testing.T) { 208 | name := "infinite_loop.c" 209 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 210 | copyCSourceFile(name, t) 211 | 212 | So(compileC(name, CBaseDir, t), ShouldBeEmpty) 213 | // got `signal: killed` 214 | So(runC(CBaseDir, "64", "1000", t), ShouldContainSubstring, "Runtime Error") 215 | }) 216 | } 217 | 218 | func TestC0014MemoryAllocation(t *testing.T) { 219 | name := "memory_allocation.c" 220 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 221 | copyCSourceFile(name, t) 222 | 223 | So(compileC(name, CBaseDir, t), ShouldBeEmpty) 224 | So(runC(CBaseDir, "8", "5000", t), ShouldContainSubstring, "Runtime Error") 225 | }) 226 | } 227 | 228 | func TestC0015PlainText(t *testing.T) { 229 | name := "plain_text.c" 230 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 231 | copyCSourceFile(name, t) 232 | 233 | So(compileC(name, CBaseDir, t), ShouldContainSubstring, "error") 234 | }) 235 | } 236 | 237 | func TestC0016RunCommandLine0(t *testing.T) { 238 | name := "run_command_line_0.c" 239 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 240 | copyCSourceFile(name, t) 241 | 242 | So(compileC(name, CBaseDir, t), ShouldBeEmpty) 243 | So(runC(CBaseDir, "64", "1000", t), ShouldContainSubstring, `"status":5`) 244 | }) 245 | } 246 | 247 | func TestC0017RunCommandLine1(t *testing.T) { 248 | name := "run_command_line_1.c" 249 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 250 | copyCSourceFile(name, t) 251 | 252 | So(compileC(name, CBaseDir, t), ShouldBeEmpty) 253 | So(runC(CBaseDir, "64", "1000", t), ShouldContainSubstring, `"status":5`) 254 | }) 255 | } 256 | 257 | func fixmeTestC0018Syscall0(t *testing.T) { 258 | name := "syscall_0.c" 259 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 260 | copyCSourceFile(name, t) 261 | 262 | So(compileC(name, CBaseDir, t), ShouldBeEmpty) 263 | So(runC(CBaseDir, "16", "1000", t), ShouldContainSubstring, `"status":5`) 264 | }) 265 | } 266 | 267 | func TestC0019TCPClient(t *testing.T) { 268 | name := "tcp_client.c" 269 | Convey(fmt.Sprintf("Testing [%s]...", name), t, func() { 270 | copyCSourceFile(name, t) 271 | 272 | So(compileC(name, CBaseDir, t), ShouldBeEmpty) 273 | So(runC(CBaseDir, "16", "5000", t), ShouldContainSubstring, "Runtime Error") 274 | }) 275 | } 276 | --------------------------------------------------------------------------------