diff options
author | Alexander Hesse <webmaster@aquanasoft.de> | 2013-01-06 21:30:35 +0100 |
---|---|---|
committer | Alexander Hesse <webmaster@aquanasoft.de> | 2013-01-06 21:30:35 +0100 |
commit | 84c8c653bf5fc39a68b2441780539ab961cd7521 (patch) | |
tree | cae049db98ef1cc4cfa97e17814b739aa288ba15 /pypy | |
parent | goal/test -> goal/test2 (diff) | |
download | pypy-84c8c653bf5fc39a68b2441780539ab961cd7521.tar.gz pypy-84c8c653bf5fc39a68b2441780539ab961cd7521.tar.bz2 pypy-84c8c653bf5fc39a68b2441780539ab961cd7521.zip |
Renamed pypy_interact to pypy.sandbox.pypysandbox. Removed pypydir import from cbuild
Diffstat (limited to 'pypy')
-rw-r--r-- | pypy/bin/pypysandbox | 5 | ||||
-rw-r--r-- | pypy/sandbox/__init__.py | 1 | ||||
-rwxr-xr-x | pypy/sandbox/pypysandbox.py | 129 | ||||
-rw-r--r-- | pypy/sandbox/test/test_pypysandbox.py | 81 |
4 files changed, 216 insertions, 0 deletions
diff --git a/pypy/bin/pypysandbox b/pypy/bin/pypysandbox new file mode 100644 index 0000000000..f236f296ef --- /dev/null +++ b/pypy/bin/pypysandbox @@ -0,0 +1,5 @@ +#!/usr/bin/env pypy + +from pypy.sandbox import pypysandbox + +pypysandbox.main()
\ No newline at end of file diff --git a/pypy/sandbox/__init__.py b/pypy/sandbox/__init__.py new file mode 100644 index 0000000000..bc63beba4a --- /dev/null +++ b/pypy/sandbox/__init__.py @@ -0,0 +1 @@ +# empty
\ No newline at end of file diff --git a/pypy/sandbox/pypysandbox.py b/pypy/sandbox/pypysandbox.py new file mode 100755 index 0000000000..b9bd4605e3 --- /dev/null +++ b/pypy/sandbox/pypysandbox.py @@ -0,0 +1,129 @@ +#! /usr/bin/env python + +"""Interacts with a PyPy subprocess translated with --sandbox. + +Usage: + pypy_interact.py [options] <executable> <args...> + +Options: + --tmp=DIR the real directory that corresponds to the virtual /tmp, + which is the virtual current dir (always read-only for now) + --heapsize=N limit memory usage to N bytes, or kilo- mega- giga-bytes + with the 'k', 'm' or 'g' suffix respectively. + --timeout=N limit execution time to N (real-time) seconds. + --log=FILE log all user input into the FILE. + --verbose log all proxied system calls. + +Note that you can get readline-like behavior with a tool like 'ledit', +provided you use enough -u options: + + ledit python -u pypy_interact.py pypy-c-sandbox -u +""" + +import sys, os +sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))) +from rpython.translator.sandbox.sandlib import SimpleIOSandboxedProc +from rpython.translator.sandbox.sandlib import VirtualizedSandboxedProc +from rpython.translator.sandbox.vfs import Dir, RealDir, RealFile +import pypy +LIB_ROOT = os.path.dirname(os.path.dirname(pypy.__file__)) + +class PyPySandboxedProc(VirtualizedSandboxedProc, SimpleIOSandboxedProc): + argv0 = '/bin/pypy-c' + virtual_cwd = '/tmp' + virtual_env = {} + virtual_console_isatty = True + + def __init__(self, executable, arguments, tmpdir=None, debug=True): + self.executable = executable = os.path.abspath(executable) + self.tmpdir = tmpdir + self.debug = debug + super(PyPySandboxedProc, self).__init__([self.argv0] + arguments, + executable=executable) + + def build_virtual_root(self): + # build a virtual file system: + # * can access its own executable + # * can access the pure Python libraries + # * can access the temporary usession directory as /tmp + exclude = ['.pyc', '.pyo'] + if self.tmpdir is None: + tmpdirnode = Dir({}) + else: + tmpdirnode = RealDir(self.tmpdir, exclude=exclude) + libroot = str(LIB_ROOT) + + return Dir({ + 'bin': Dir({ + 'pypy-c': RealFile(self.executable), + 'lib-python': RealDir(os.path.join(libroot, 'lib-python'), + exclude=exclude), + 'lib_pypy': RealDir(os.path.join(libroot, 'lib_pypy'), + exclude=exclude), + }), + 'tmp': tmpdirnode, + }) + +def main(): + from getopt import getopt # and not gnu_getopt! + options, arguments = getopt(sys.argv[1:], 't:hv', + ['tmp=', 'heapsize=', 'timeout=', 'log=', + 'verbose', 'help']) + tmpdir = None + timeout = None + logfile = None + debug = False + extraoptions = [] + + def help(): + print >> sys.stderr, __doc__ + sys.exit(2) + + for option, value in options: + if option in ['-t', '--tmp']: + value = os.path.abspath(value) + if not os.path.isdir(value): + raise OSError("%r is not a directory" % (value,)) + tmpdir = value + elif option == '--heapsize': + value = value.lower() + if value.endswith('k'): + bytes = int(value[:-1]) * 1024 + elif value.endswith('m'): + bytes = int(value[:-1]) * 1024 * 1024 + elif value.endswith('g'): + bytes = int(value[:-1]) * 1024 * 1024 * 1024 + else: + bytes = int(value) + if bytes <= 0: + raise ValueError + if bytes > sys.maxint: + raise OverflowError("--heapsize maximum is %d" % sys.maxint) + extraoptions[:0] = ['--heapsize', str(bytes)] + elif option == '--timeout': + timeout = int(value) + elif option == '--log': + logfile = value + elif option in ['-v', '--verbose']: + debug = True + elif option in ['-h', '--help']: + help() + else: + raise ValueError(option) + + if len(arguments) < 1: + help() + + sandproc = PyPySandboxedProc(arguments[0], extraoptions + arguments[1:], + tmpdir=tmpdir, debug=debug) + if timeout is not None: + sandproc.settimeout(timeout, interrupt_main=True) + if logfile is not None: + sandproc.setlogfile(logfile) + try: + sandproc.interact() + finally: + sandproc.kill() + +if __name__ == '__main__': + main()
\ No newline at end of file diff --git a/pypy/sandbox/test/test_pypysandbox.py b/pypy/sandbox/test/test_pypysandbox.py new file mode 100644 index 0000000000..771655d614 --- /dev/null +++ b/pypy/sandbox/test/test_pypysandbox.py @@ -0,0 +1,81 @@ +import os, sys, stat, errno +from pypy.sandbox.pypysandbox import PyPySandboxedProc +from rpython.translator.interactive import Translation + +from pypy.module.sys.version import CPYTHON_VERSION +from pypy.tool.lib_pypy import LIB_PYTHON + +VERSION = '%d.%d' % CPYTHON_VERSION[:2] +SITE_PY_CONTENT = LIB_PYTHON.join('site.py').read() +ERROR_TEXT = os.strerror(errno.ENOENT) + +def assert_(cond, text): + if not cond: + print "assert failed:", text + raise AssertionError + +def mini_pypy_like_entry_point(argv): + """An RPython standalone executable that does the same kind of I/O as + PyPy when it starts up. + """ + assert_(len(argv) == 3, "expected len(argv) == 3") + assert_(argv[1] == 'foo', "bad argv[1]") + assert_(argv[2] == 'bar', "bad argv[2]") + env = os.environ.items() + assert_(len(env) == 0, "empty environment expected") + assert_(argv[0] == '/bin/pypy-c', "bad argv[0]") + st = os.lstat('/bin/pypy-c') + assert_(stat.S_ISREG(st.st_mode), "bad st_mode for /bin/pypy-c") + for dirname in ['/bin/lib-python/' + VERSION, '/bin/lib_pypy']: + st = os.stat(dirname) + assert_(stat.S_ISDIR(st.st_mode), "bad st_mode for " + dirname) + assert_(os.environ.get('PYTHONPATH') is None, "unexpected $PYTHONPATH") + try: + os.stat('site') + except OSError: + pass + else: + assert_(False, "os.stat('site') should have failed") + + try: + os.stat('/bin/lib-python/%s/site.pyc' % VERSION) + except OSError: + pass + else: + assert_(False, "os.stat('....pyc') should have failed") + fd = os.open('/bin/lib-python/%s/site.py' % VERSION, + os.O_RDONLY, 0666) + length = 8192 + ofs = 0 + while True: + data = os.read(fd, length) + if not data: break + end = ofs+length + if end > len(SITE_PY_CONTENT): + end = len(SITE_PY_CONTENT) + assert_(data == SITE_PY_CONTENT[ofs:end], "bad data from site.py") + ofs = end + os.close(fd) + assert_(ofs == len(SITE_PY_CONTENT), "not enough data from site.py") + assert_(os.getcwd() == '/tmp', "bad cwd") + assert_(os.strerror(errno.ENOENT) == ERROR_TEXT, "bad strerror(ENOENT)") + assert_(os.isatty(0), "isatty(0) returned False") + # an obvious 'attack' + try: + os.open('/spam', os.O_RDWR | os.O_CREAT, 0666) + except OSError: + pass + else: + assert_(False, "os.open('/spam') should have failed") + return 0 + + +def setup_module(mod): + t = Translation(mini_pypy_like_entry_point, backend='c', sandbox=True) + mod.executable = str(t.compile()) + + +def test_run(): + sandproc = PyPySandboxedProc(executable, ['foo', 'bar']) + returncode = sandproc.interact() + assert returncode == 0 |