def call_parent(pid, rpipe): try: status = os.waitpid(pid, 0)[1] except: os.close(rpipe) raise if status != 0: if os.WIFSIGNALED(status): signum = os.WTERMSIG(status) if signum == SIGALRM: raise Timeout() text = "subprocess killed by signal %s" % signum elif os.WIFEXITED(status): exitcode = os.WEXITSTATUS(status) text = "subprocess failed with exit code %s" % exitcode else: text = "subprocess failed" raise SandboxError(text) rpipe_file = os.fdopen(rpipe, 'rb') try: data = pickle.load(rpipe_file) finally: rpipe_file.close() if 'error' in data: raise data['error'] return data['result']
def call_parent(config, pid, rpipe): import signal sigkill = signal.SIGKILL try: status = wait_child(config, pid, sigkill) except: os.close(rpipe) raise if status != 0: if os.WIFSIGNALED(status): signum = os.WTERMSIG(status) text = "subprocess killed by signal %s" % signum elif os.WIFEXITED(status): exitcode = os.WEXITSTATUS(status) text = "subprocess failed with exit code %s" % exitcode else: text = "subprocess failed" raise SandboxError(text) rpipe_file = os.fdopen(rpipe, 'rb') try: data = pickle.load(rpipe_file) finally: rpipe_file.close() if 'error' in data: raise data['error'] return data['result']
def test(): class A(object): pass class B(object): pass class X(A): pass X.__bases__ = (B, ) if not issubclass(X, B): raise SandboxError("yep")
def proxy(value): if isinstance(value, SAFE_TYPES): # Safe type, no need to create a proxy return value elif callable(value): return callback_proxy(proxy, value) elif isinstance(value, tuple): return tuple(proxy(item) for item in value) elif isinstance(value, list): return createReadOnlyList(value) elif isinstance(value, dict): return createReadOnlyDict(value) elif isinstance(value, OBJECT_TYPES): return createReadOnlyObject(value) else: raise SandboxError("Unable to proxy a value of type %s" % type(value))
def proxy(value): if isinstance(value, SAFE_TYPES): # Safe type, no need to create a proxy return value elif is_callable(value): return callback_proxy(proxy, value) elif isinstance(value, tuple): return tuple(proxy(item) for item in value) elif isinstance(value, list): return createReadOnlyList(value) elif isinstance(value, dict): return createReadOnlyDict(value) elif isinstance(value, OBJECT_TYPES): return createReadOnlyObject(value) elif str(value.__class__) == "<type 'code'>": # This is a horrible horrible hack; it should be able to compare with the actual # "code" type. However no proxy is needed as code objects are immutable; see # http://docs.python.org/reference/datamodel.html return value else: raise SandboxError("Unable to proxy a value of type %s" % type(value))
def execute_subprocess(sandbox, code, globals, locals): old_umask = os.umask(0177) try: input_file = tempfile.NamedTemporaryFile() output_file = tempfile.NamedTemporaryFile() finally: os.umask(old_umask) try: input_data = { 'code': code, 'config': sandbox.config, 'locals': locals, 'globals': globals, } args = ( sys.executable, # FIXME: use '-S' '-E', '-m', 'sandbox.subprocess_child', input_file.name, output_file.name) pickle.dump(input_data, input_file) input_file.flush() if sandbox.config.max_input_size: size = input_file.tell() if size > sandbox.config.max_input_size: raise SandboxError( "Input data are too big: %s bytes (max=%s)" % (size, sandbox.config.max_input_size)) # create the subprocess process = subprocess.Popen(args, close_fds=True, shell=False) # wait data exitcode = process.wait() if exitcode: if os.name != "nt" and exitcode < 0: signum = -exitcode if signum == SIGALRM: raise Timeout() text = "subprocess killed by signal %s" % signum else: text = "subprocess failed with exit code %s" % exitcode raise SandboxError(text) if sandbox.config.max_output_size: output_file.seek(0, 2) size = output_file.tell() output_file.seek(0) if size > sandbox.config.max_output_size: raise SandboxError( "Output data are too big: %s bytes (max=%s)" % (size, sandbox.config.max_output_size)) output_data = pickle.load(output_file) finally: input_file.close() output_file.close() if 'error' in output_data: raise output_data['error'] if locals is not None: locals.clear() locals.update(output_data['locals']) if globals is not None: globals.clear() globals.update(output_data['globals']) return output_data['result']
def readOnlyError(): raise SandboxError("Read only object")
def execute_subprocess(sandbox, code, globals, locals): config = sandbox.config input_filename = tempfile.mktemp() output_filename = tempfile.mktemp() args = ( sys.executable, # FIXME: use '-S' '-E', '-m', 'sandbox.subprocess_child', input_filename, output_filename, ) input_data = { 'code': code, 'config': config, 'locals': locals, 'globals': globals, } try: # serialize input data with open(input_filename, 'wb') as input_file: pickle.dump(input_data, input_file) if config.max_input_size: size = input_file.tell() if size > config.max_input_size: raise SandboxError( "Input data are too big: %s bytes (max=%s)" % (size, config.max_input_size)) # create the subprocess process = subprocess.Popen(args, close_fds=True, shell=False) # wait process exit if config.timeout: timeout = monotonic_time() + config.timeout kill = False exitcode = process.poll() while exitcode is None: dt = timeout - monotonic_time() if dt < 0: process.terminate() exitcode = process.wait() raise Timeout() if dt > 1.0: pause = 0.5 else: pause = 0.1 # TODO: handle SIGCHLD to avoid wasting time in polling time.sleep(pause) exitcode = process.poll() else: exitcode = process.wait() os.unlink(input_filename) input_filename = None # handle child process error if exitcode: if os.name != "nt" and exitcode < 0: signum = -exitcode text = "subprocess killed by signal %s" % signum else: text = "subprocess failed with exit code %s" % exitcode raise SandboxError(text) with open(output_filename, 'rb') as output_file: if config.max_output_size: output_file.seek(0, 2) size = output_file.tell() output_file.seek(0) if size > config.max_output_size: raise SandboxError( "Output data are too big: %s bytes (max=%s)" % (size, config.max_output_size)) output_data = pickle.load(output_file) os.unlink(output_filename) output_filename = None finally: temp_filenames = [] if input_filename is not None: temp_filenames.append(input_filename) if output_filename is not None: temp_filenames.append(output_filename) for filename in temp_filenames: try: os.unlink(filename) except OSError: pass if 'error' in output_data: raise output_data['error'] if locals is not None: locals.clear() locals.update(output_data['locals']) if globals is not None: globals.clear() globals.update(output_data['globals']) return output_data['result']
def safe_exit(code=0): raise SandboxError("exit() function blocked by the sandbox")
def _blocked(name): raise SandboxError("Block access to sys.%s.%s" % (stream_name, name))