def test_stdout(): import sys config = createSandboxConfig(disable_debug=True) with capture_stdout() as stdout: def print_denied(): print "Hello Sandbox 1" try: Sandbox(config).call(print_denied) except SandboxError: pass else: assert False def print_allowed(): print "Hello Sandbox 2" config2 = createSandboxConfig('stdout') Sandbox(config2).call(print_allowed) print "Hello Sandbox 3" sys.stdout.flush() stdout.seek(0) output = stdout.read() assert output == "Hello Sandbox 2\nHello Sandbox 3\n"
def test_run_command_exec_memory(self): factor_input = "1234567890123456789012345678901" run_result = Runner.run_command(sandbox=Sandbox(), command="factor {}".format(factor_input), timeout=1.0) factor_output = "{}: 7742394596501 159455563099482401".format(factor_input) self.assertEqual(run_result.output.decode().strip(), factor_output) self.assertGreater(run_result.exec_memory, 1 << 20) # More than 1MB self.assertLess(run_result.exec_memory, 1 << 23) # And less than 8MB sandbox = Sandbox() sandbox.put_file(os.path.join(self.PATH_FIXTURES, "..", "sandbox/mem_allocator.cpp")) run_result = Runner.run_command(sandbox=sandbox, timeout=10.0, privileged=True, command="g++ -O2 -std=c++17 -o mem_allocator mem_allocator.cpp") self.assertEqual(run_result.exit_code, 0) self.assertTrue(sandbox.has_file("mem_allocator")) run_result = Runner.run_command(sandbox=sandbox, command="./mem_allocator heap 50000000", timeout=1.0) self.assertEqual(run_result.exit_code, 0) self.assertGreater(run_result.exec_memory, 50000000) self.assertLess(run_result.exec_memory, 55000000) # Allowing up to 5MB overhead run_result = Runner.run_command(sandbox=sandbox, command="./mem_allocator heap 250000000", timeout=1.0) self.assertEqual(run_result.exit_code, 0) self.assertGreater(run_result.exec_memory, 250000000) self.assertLess(run_result.exec_memory, 255000000) # Allowing up to 5MB overhead run_result = Runner.run_command(sandbox=sandbox, command="./mem_allocator stack 10000000", timeout=1.0) self.assertEqual(run_result.exit_code, 0) self.assertGreater(run_result.exec_memory, 10000000) self.assertLess(run_result.exec_memory, 15000000) # Allowing up to 5MB overhead run_result = Runner.run_command(sandbox=sandbox, command="./mem_allocator stack 50000000", timeout=1.0) self.assertEqual(run_result.exit_code, 0) self.assertGreater(run_result.exec_memory, 50000000) self.assertLess(run_result.exec_memory, 55000000) # Allowing up to 5MB overhead
def test_no_localhost_access(self): stdout, stderr = self.sandbox_helper(sandbox=Sandbox(), command="ping localhost") self.assertIn("Operation not permitted", stderr) stdout, stderr = self.sandbox_helper(sandbox=Sandbox(), command="ping 127.0.0.1") self.assertIn("Operation not permitted", stderr)
def test_cannot_rm_rf(self): stdout, stderr = self.sandbox_helper(sandbox=Sandbox(), command="rm -rf /") self.assertNotEqual("", stderr) stdout, stderr = self.sandbox_helper(sandbox=Sandbox(), command="sudo rm -rf /") self.assertNotEqual("", stderr)
def test_run_program_exec_time_sleeping(self): path_source = os.path.join(self.PATH_FIXTURES, "sleeper.cpp") path_executable = os.path.join(config.PATH_SANDBOX, "sleeper.o") status = Compiler.compile(config.LANGUAGE_CPP, path_source, path_executable) self.assertEqual(status, "") # Sleeping programs don't waste CPU, thus have negligible exec_time (although high clock-time) start_time = perf_counter() run_result = Runner.run_program(sandbox=Sandbox(), executable_path=path_executable, memory_limit=32000000, timeout=0.5, input_bytes=None) self.assertEqual(run_result.exit_code, 0) self.assertLess(run_result.exec_time, 0.1) self.assertGreaterEqual(perf_counter() - start_time, 0.4) self.assertLess(perf_counter() - start_time, 0.6) self.assertEqual(run_result.output.decode().strip(), "2075") # ... except if they don't exceed the time limit, in which case their clock time is recorded start_time = perf_counter() run_result = Runner.run_program(sandbox=Sandbox(), executable_path=path_executable, memory_limit=32000000, timeout=0.3, input_bytes=None) self.assertEqual(run_result.exit_code, 9) self.assertGreaterEqual(run_result.exec_time, 0.29) self.assertLess(run_result.exec_time, 0.4) self.assertGreaterEqual(perf_counter() - start_time, 0.3) self.assertLess(perf_counter() - start_time, 0.5) self.assertEqual(run_result.output.decode().strip(), "")
def test_cannot_chroot_second_time(self): stdout, stderr = self.sandbox_helper(sandbox=Sandbox(), command="chroot ..") self.assertIn("Operation not permitted", stderr) stdout, stderr = self.sandbox_helper(sandbox=Sandbox(), command="sudo chroot ..") self.assertIn("is not allowed to execute '/usr/sbin/chroot ..'", stderr)
def test_run_command_stderr_handling(self): run_result = Runner.run_command(sandbox=Sandbox(), command="g++ -O2 -o foo foo.cpp", timeout=1.0) self.assertNotEqual(run_result.exit_code, 0) self.assertEqual(run_result.output.decode(), "") run_result = Runner.run_command(sandbox=Sandbox(), command="g++ -O2 -o foo foo.cpp", timeout=1.0, print_stderr=True) self.assertNotEqual(run_result.exit_code, 0) self.assertNotEqual(run_result.output.decode(), "") self.assertIn("fatal error", run_result.output.decode())
def test_run_command_exit_code(self): run_result = Runner.run_command(sandbox=Sandbox(), command="exit 0", timeout=1.0) self.assertEqual(run_result.exit_code, 0) run_result = Runner.run_command(sandbox=Sandbox(), command="exit 42", timeout=1.0) self.assertEqual(run_result.exit_code, 42) run_result = Runner.run_command(sandbox=Sandbox(), command="factor {}".format("1234567890" * 2), timeout=1.0) self.assertEqual(run_result.exit_code, 0) run_result = Runner.run_command(sandbox=Sandbox(), command="factor {}".format("1234567890" * 5), timeout=1.0) self.assertEqual(run_result.exit_code, 1)
def test_sys_structure_is_mounted(self): # There are files in the mounted directories stdout, stderr = self.sandbox_helper(sandbox=Sandbox(), command="cat /proc/uptime") self.assertEqual("", stderr) self.assertNotEqual("", stdout) # Sanity check that an error is printed on a missing file stdout, stderr = self.sandbox_helper(sandbox=Sandbox(), command="cat /proc/foobarbaz") self.assertNotEqual("", stderr) self.assertEqual("", stdout)
def execute_standard(submit_id, test: TestInfo, run_config: RunConfig) -> RunResult: # Prepare input data (provided to the program through stdin) with open(test.inpPath, mode="rb") as inp: input_bytes = inp.read() # Run the solution inside a sandbox and delete the sandbox to free up the worker sandbox = Sandbox() run_result = Runner.run_program(sandbox=sandbox, executable_path=run_config.executable_path, memory_limit=run_config.memory_limit, timeout=run_config.timeout, input_bytes=input_bytes, print_stderr=False) del sandbox # If there is a checker, run it as well if run_config.checker_path is not None: # Create a temporary file and write the output there out_file = NamedTemporaryFile(mode="w+b", delete=True) with open(out_file.name, "wb") as out: out.write(run_result.output) out_file.seek(0) # Create execution config for the checker and run it sandbox = Sandbox() sandbox.put_file(test.inpPath, target_name="input.txt") sandbox.put_file(out_file.name, target_name="output.txt") sandbox.put_file(test.solPath, target_name="solution.txt") checker_result = Runner.run_program( sandbox=sandbox, executable_path=run_config.checker_path, memory_limit=config.MAX_EXECUTION_MEMORY, timeout=config.CHECKER_TIMEOUT, print_stderr=True, args=["input.txt", "output.txt", "solution.txt"]) del sandbox # Close and delete temporary file with user's output out_file.close() if checker_result.exit_code != 0: message = "Checker returned non-zero exit code. Checker's output: '{}'".format( checker_result.output) logger.error("[Submission {id}] Internal Error: {error}".format( id=submit_id, error=message)) return RunResult(status=TestStatus.INTERNAL_ERROR, error=message) run_result.output = checker_result.output return run_result
def test_timeout_command_available(self): stdout, stderr = self.sandbox_helper( sandbox=Sandbox(), command="ls -la /usr/bin | grep -w timeout") self.assertEqual("", stderr) self.assertTrue( stdout.startswith("-rwx") and stdout.endswith("timeout")) start_time = perf_counter() command = "/usr/bin/timeout 0.3s /bin/bash -c 'sleep 1.0; echo foo'" stdout, stderr = self.sandbox_helper(sandbox=Sandbox(), command=command) self.assertEqual(stdout, "") # No output, killed before that self.assertLess(perf_counter() - start_time, 0.5) # Killed shortly after the timeout
def create_game(args): ''' Create all the semi-permanent game structures (i.e. sockets and dockers and stuff ''' # Load the Game state info game = server.Game(logging_level=logging.ERROR, game_map=args['map'], time_pool=args['time_pool'], time_additional=args['time_additional']) # Find a good filename to use as socket file for index in range(10000): sock_file = "/tmp/battlecode-" + str(index) if not os.path.exists(sock_file): break # Assign the docker instances client ids dockers = {} Sandbox.initialize() for index in range(len(game.players)): key = [player['id'] for player in game.players][index] dockers[key] = Sandbox(sock_file, player_key=key, local_dir=args['dir_p1' if index % 2 == 0 else 'dir_p2']) return (game, dockers, sock_file)
def test_del_builtin(): code = unindent(''' def del_builtin_import(): import_func = __builtins__['__import__'] dict.__delitem__(__builtins__, '__import__') try: try: import sys except NameError, err: assert str(err) == "type object 'dict' has no attribute '__setitem__'" finally: __builtins__['__import__'] = import_func ''') unsafe_code = code + unindent(''' try: del_builtin_import() except AttributeError, err: assert str(err) == "type object 'dict' has no attribute '__delitem__'" except SandboxError, err: assert str(err) == "Read only object" else: assert False ''') config = createSandboxConfig() config.allowModule('sys') Sandbox(config).execute(unsafe_code)
def execute_child(): input_filename = sys.argv[1] output_filename = sys.argv[2] output = open(output_filename, "wb") base_exception = BaseException try: with open(input_filename, 'rb') as input_file: input_data = pickle.load(input_file) code = input_data['code'] config = input_data['config'] locals = input_data['locals'] globals = input_data['globals'] set_process_limits(config) sandbox = Sandbox(config) result = sandbox._execute(code, globals, locals) output_data = {'result': result} if input_data['globals'] is not None: del globals['__builtins__'] output_data['globals'] = globals if 'locals' in input_data: output_data['locals'] = locals except base_exception, err: output_data = {'error': err}
def test_replay(self): root = os.environ.get("RECORD_SANDBOX_BUFFERS_DIR") if not root: self.skipTest("RECORD_SANDBOX_BUFFERS_DIR not set") for dirpath, dirnames, filenames in os.walk(root): if "input" not in filenames: continue print("Checking " + dirpath) input_path = os.path.join(dirpath, "input") output_path = os.path.join(dirpath, "output") new_output_path = os.path.join(dirpath, "new_output") with open(input_path, "rb") as external_input: with open(new_output_path, "wb") as external_output: sandbox = Sandbox(external_input, external_output) run(sandbox) original_output = marshal_load_all(output_path) # _send_to_js does two layers of marshalling, # and NSandbox._onSandboxData parses one of those layers before writing, # hence original_output is 'more parsed' than marshal_load_all(new_output_path) new_output = [ marshal.loads(b) for b in marshal_load_all(new_output_path) ] # It's usually not worth asserting a match, see comments at the top of the file print("Match:", original_output == new_output)
def run(self, inp=None): name = self.get_obj_file_name() sandbox = Sandbox() cmd = self.get_run_command(name, sandbox) start = timer() stdout = b'' stderr = b'' env = os.environ.copy() r = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=4*1024, cwd=MEDIA_ROOT, preexec_fn=os.setsid,env=env) try: if inp is not None: stdout, stderr = r.communicate(timeout=timeout, input=inp.encode()) else: stdout, stderr = r.communicate(timeout=timeout) print('STDOUT : ' + str(stdout, "utf-8")) print('STDERR : ' + str(stderr, "utf-8")) except subprocess.TimeoutExpired as e: print("Timeout expired") os.killpg(r.pid, signal.SIGINT) r.returncode = 124 print('Return Code : ' + str(r.returncode)) if self.lang != 'python': os.remove(MEDIA_ROOT+'/'+name) print('Elapsed seconds: {:.2f}'.format(timer() - start)) sandbox.delete_sandbox() return Result(timer() - start, r.returncode, stdout)
def test_open_whitelist(): config = createSandboxConfig() if config.cpython_restricted: # the CPython restricted mode denies to open any file raise SkipTest("require _sandbox") config.allowPath(READ_FILENAME) Sandbox(config).call(read_first_line, open)
def create_container( self, submission_id: str, **ks, # pass to sandbox ): if submission_id not in self.result: raise SubmissionIdNotFoundError(f'{submission_id} not found!') self.container_count += 1 res = Sandbox( src_dir=str(self.get_host_path(submission_id).absolute()), ignores=[ '__pycache__', ] + [f.name for f in self.get_path(submission_id).iterdir()], **ks, ).run() self.container_count -= 1 self.logger.info(f'finish task {submission_id}') # truncate long stdout/stderr _res = res.copy() for k in ('stdout', 'stderr'): _res[k] = textwrap.shorten(_res.get(k, ''), 37, placeholder='...') # extract filename if 'files' in _res: _res['files'] = [f.name for f in _res['files']] self.logger.debug(f'runner result: {_res}') # completion if self.testing: self.logger.info( 'current in testing' f'skip submission [{submission_id}] completion', ) return True # post data self.on_complete(submission_id, res) # remove this submission self.result.remove(submission_id)
def test_clone(self): s = Sandbox(self.task[2]) s.policy = MinimalPolicy() s.run() self.assertEqual(s.status, Sandbox.S_STATUS_FIN) self.assertEqual(s.result, Sandbox.S_RESULT_RF) pass
def test(*lines, **kw): code = "; ".join(lines) config = createSandboxConfig() if HAVE_PYPY: # FIXME: is it really needed? config._builtins_whitelist.add('compile') Sandbox(config).execute(code, **kw)
def main(): filecpp = sys.argv[1] filetxt = sys.argv[2] filext = sys.argv[3] cmd = "./bash1.sh" + " " + filecpp + " " + filetxt + " " + filext resource.setrlimit(resource.RLIMIT_CPU, (1, 3)) #The maximum amount of processor time (in seconds) that a process can use. soft, hard = 10**10, 10**10 resource.setrlimit(resource.RLIMIT_AS, (soft, hard)) #The maximum area (in bytes) of address space which may be taken by the process. # we can provide more restriction by using these.. #resource.setrlimit(resource.RLIMIT_DATA,(s,h)) #The maximum size (in bytes) of the process s heap. #resource.setrlimit(resource.RLIMIT_STACK(s,h)) #The maximum size (in bytes) of the call stack for the current process. #resource.setrlimit(resource.RLIMIT_NPROC,(4,4)) #The maximum number of processes the current process may create. sandbox = Sandbox() sandbox.call(perform, cmd) #executing the code in sandbox environment signal.signal(signal.SIGXCPU, time_exceeded) soft, hard = resource.getrlimit(resource.RLIMIT_CPU)
def compile_code(language, code, stdin): temp_folder = os.path.join('/tmp', str(uuid.uuid4())) timeout_value = 20 path = os.getcwd() sandbox = Sandbox(timeout_value=timeout_value, path=path, temp_folder=temp_folder, compiler_name=compiler_dict[language][0], file_name=compiler_dict[language][1], code=code, output_command=compiler_dict[language][2], language_name=compiler_dict[language][3], e_arguments=compiler_dict[language][4], stdin_data=stdin) (data, exec_time, err) = sandbox.run() return { "output": data, "langid": language, "code": code, "errors": err, "time": exec_time }
def run_user_code(language, code, stdin): error, error_msg, output = False, None, None sandbox = None try: if language not in LANG_CONFIG: raise UnsupportedLanguage(f'{language} is not supported') sandbox = Sandbox() sandbox.run(language, code, stdin) except Exception as e: error = True error_msg = f'[{e.__class__.__name__}] {e}' try: if not error: with open(sandbox.output_file_path, 'r') as f: output = f.read() else: output = '' except Exception as e: output = '' rv = { 'error': error, 'error_msg': error_msg, 'output': output, 'exec_time': sandbox.execution_time if sandbox else -1, } return rv
def test_sandbox_wait_kills_sleepers(self): stdout, stderr = TemporaryFile(mode="w+"), TemporaryFile(mode="w+") self.sandbox = Sandbox() self.sandbox.execute(command=":(){ :|:& };:", stdin_fd=None, stdout_fd=stdout, stderr_fd=stderr, blocking=False) # While the program is within its time limit it is at max processes sleep(0.2) self.assertTrue(self.sandbox.is_running()) ps_info = os.popen("ps -U {}".format(self.sandbox._worker.name)).read() self.assertEqual(len(ps_info.splitlines()) - 1, config.MAX_PROCESSES) # What's worse, even after that they are still alive # (as they don't use much CPU, so are not affected by MAX_EXECUTION_TIME) sleep(0.2) self.assertTrue(self.sandbox.is_running()) ps_info = os.popen("ps -U {}".format(self.sandbox._worker.name)).read() self.assertEqual(len(ps_info.splitlines()) - 1, config.MAX_PROCESSES) # However, wait() should terminate everything self.sandbox.wait(0.1) self.assertFalse(self.sandbox.is_running()) ps_info = os.popen("ps -U {}".format(self.sandbox._worker.name)).read() self.assertEqual(len(ps_info.splitlines()) - 1, 0)
def test_output_limit(self): self.sandbox = Sandbox() file_size = 1000000 # 1MB output = NamedTemporaryFile(mode="w+", delete=True) for i in range(file_size // 10): output.write("test test\n") output.flush() self.sandbox.put_file(output.name, "foo.txt") target_size = 0 while target_size + file_size <= config.MAX_EXECUTION_OUTPUT: target_size += file_size stdout, stderr = self.sandbox_helper( sandbox=self.sandbox, command="for i in {{1..{}}}; do cat foo.txt; done;".format( target_size // file_size)) self.assertEqual("", stderr) self.assertEqual(len(stdout), target_size - 1) target_size += file_size stdout, stderr = self.sandbox_helper( sandbox=self.sandbox, command="for i in {{1..{}}}; do cat foo.txt; done;".format( target_size // file_size)) self.assertIn("File size limit exceeded", stderr) self.assertEqual(len(stdout), config.MAX_EXECUTION_OUTPUT)
def test_del_file(self): self.sandbox = Sandbox() self.assertFalse(self.sandbox.has_file("foo.txt")) self.sandbox.put_file("install_steps.txt", "foo.txt") self.assertTrue(self.sandbox.has_file("foo.txt")) self.sandbox.del_file("foo.txt") self.assertFalse(self.sandbox.has_file("foo.txt"))
def test_open_whitelist(): if not HAVE_CSANDBOX: # restricted python denies access to all files raise SkipTest("require _sandbox") config = createSandboxConfig() config.allowPath(READ_FILENAME) Sandbox(config).call(read_first_line, open)
def test_languages_are_available(self): stdout, stderr = self.sandbox_helper(sandbox=Sandbox(), command="g++") self.assertIn("g++: fatal error: no input files", stderr) stdout, stderr = self.sandbox_helper(sandbox=Sandbox(), command="java") self.assertIn("Usage: java [options]", stderr) stdout, stderr = self.sandbox_helper(sandbox=Sandbox(), command="javac") self.assertIn("Usage: javac <options> <source files>", stdout) stdout, stderr = self.sandbox_helper(sandbox=Sandbox(), command="jar") self.assertIn("Usage: jar [OPTION...]", stderr) stdout, stderr = self.sandbox_helper(sandbox=Sandbox(), command="pypy3 --version") self.assertIn("Python 3.", stdout)
def test_exec_echo3(self): sandbox = Sandbox(54321) test_str = "a1234567999918" test_cmd = "echo -n " + test_str result = sandbox.exec(test_cmd) self.assertEqual(test_str, str(result.get('Output'), 'utf-8')) self.assertEqual(0, result.get('ExitCode'))
def test_exec_echo4(self): sandbox = Sandbox(54321) test_str = "A-Judge Sandbox method test" test_cmd = "echo -n " + test_str result = sandbox.exec(test_cmd) self.assertEqual(test_str, str(result.get('Output'), 'utf-8')) self.assertEqual(0, result.get('ExitCode'))