Author: Armin Rigo <[email protected]>
Branch: sandbox-2
Changeset: r97085:735745c7bd90
Date: 2019-08-07 17:41 +0200
http://bitbucket.org/pypy/pypy/changeset/735745c7bd90/
Log: Start to port the tests
diff --git a/rpython/translator/sandbox/rsandbox.py
b/rpython/translator/sandbox/rsandbox.py
--- a/rpython/translator/sandbox/rsandbox.py
+++ b/rpython/translator/sandbox/rsandbox.py
@@ -23,27 +23,6 @@
log = AnsiLogger("sandbox")
-def reraise_error(error, loader):
- if error == 1:
- raise OSError(load_int(loader), "external error")
- elif error == 2:
- raise IOError
- elif error == 3:
- raise OverflowError
- elif error == 4:
- raise ValueError
- elif error == 5:
- raise ZeroDivisionError
- elif error == 6:
- raise MemoryError
- elif error == 7:
- raise KeyError
- elif error == 8:
- raise IndexError
- else:
- raise RuntimeError
-
-
def getkind(TYPE, parent_function):
if TYPE is lltype.Void:
return 'v'
diff --git a/rpython/translator/sandbox/sandboxio.py
b/rpython/translator/sandbox/sandboxio.py
new file mode 100644
--- /dev/null
+++ b/rpython/translator/sandbox/sandboxio.py
@@ -0,0 +1,123 @@
+import struct
+
+
+class SandboxError(Exception):
+ """The sandboxed process misbehaved"""
+
+
+class Ptr(object):
+ def __init__(self, addr):
+ self.addr = addr
+
+ def __repr__(self):
+ return 'Ptr(%s)' % (hex(self.addr),)
+
+
+_ptr_size = struct.calcsize("P")
+_ptr_code = 'q' if _ptr_size == 8 else 'i'
+_pack_one_ptr = struct.Struct("=" + _ptr_code).pack
+_pack_one_longlong = struct.Struct("=q").pack
+_pack_one_double = struct.Struct("=d").pack
+_pack_two_ptrs = struct.Struct("=" + _ptr_code + _ptr_code).pack
+_unpack_one_ptr = struct.Struct("=" + _ptr_code).unpack
+
+
+class SandboxedIO(object):
+ _message_decoders = {}
+
+
+ def __init__(self, popen):
+ self.popen = popen
+ self.child_stdin = popen.stdin
+ self.child_stdout = popen.stdout
+
+ def close(self):
+ """Kill the subprocess and close the file descriptors to the pipe.
+ """
+ self.popen.terminate()
+ self.child_stdin.close()
+ self.child_stdout.close()
+
+ def _read(self, count):
+ result = self.child_stdout.read(count)
+ if len(result) != count:
+ raise SandboxError(
+ "connection interrupted with the sandboxed process")
+ return result
+
+ @staticmethod
+ def _make_message_decoder(data):
+ i1 = data.find('(')
+ i2 = data.find(')')
+ if not (i1 > 0 and i1 < i2 and i2 == len(data) - 2):
+ raise SandboxError(
+ "badly formatted data received from the sandboxed process")
+ pack_args = ['=']
+ for c in data[i1+1:i2]:
+ if c == 'p':
+ pack_args.append(_ptr_code)
+ elif c == 'i':
+ pack_args.append('q')
+ elif c == 'f':
+ pack_args.append('d')
+ elif c == 'v':
+ pass
+ else:
+ raise SandboxError(
+ "unsupported format string in parentheses: %r" % (data,))
+ unpacker = struct.Struct(''.join(pack_args))
+ decoder = unpacker, data[i1+1:i2]
+
+ SandboxedIO._message_decoders[data] = decoder
+ return decoder
+
+ def read_message(self):
+ """Wait for the next message and returns it. Raises EOFError if the
+ subprocess finished. Raises SandboxError if there is another kind
+ of detected misbehaviour.
+ """
+ ch = self.child_stdout.read(1)
+ if len(ch) == 0:
+ raise EOFError
+ n = ord(ch)
+ msg = self._read(n)
+ decoder = self._message_decoders.get(msg)
+ if decoder is None:
+ decoder = self._make_message_decoder(msg)
+
+ unpacker, codes = decoder
+ raw_args = iter(unpacker.unpack(self._read(unpacker.size)))
+ args = []
+ for c in codes:
+ if c == 'p':
+ args.append(Ptr(next(raw_args)))
+ elif c == 'v':
+ args.append(None)
+ else:
+ args.append(next(raw_args))
+ return msg, args
+
+ def read_buffer(self, ptr, length):
+ g = self.child_stdin
+ g.write("R" + _pack_two_ptrs(ptr.addr, length))
+ g.flush()
+ return self._read(length)
+
+ def read_charp(self, ptr, maxlen=-1):
+ g = self.child_stdin
+ g.write("Z" + _pack_two_ptrs(ptr.addr, maxlen))
+ g.flush()
+ length = _unpack_one_ptr(self._read(_ptr_size))[0]
+ return self._read(length)
+
+ def write_result(self, result):
+ g = self.child_stdin
+ if result is None:
+ g.write('v')
+ elif isinstance(result, Ptr):
+ g.write('p' + _pack_one_ptr(result.addr))
+ elif isinstance(result, float):
+ g.write('f' + _pack_one_double(result))
+ else:
+ g.write('i' + _pack_one_longlong(result))
+ g.flush()
diff --git a/rpython/translator/sandbox/sandlib.py
b/rpython/translator/sandbox/sandlib.py
--- a/rpython/translator/sandbox/sandlib.py
+++ b/rpython/translator/sandbox/sandlib.py
@@ -18,65 +18,6 @@
from rpython.tool.ansi_print import AnsiLogger
return AnsiLogger("sandlib")
-# Note: we use lib_pypy/marshal.py instead of the built-in marshal
-# for two reasons. The built-in module could be made to segfault
-# or be attackable in other ways by sending malicious input to
-# load(). Also, marshal.load(f) blocks with the GIL held when
-# f is a pipe with no data immediately avaialble, preventing the
-# _waiting_thread to run.
-from rpython.translator.sandbox import _marshal as marshal
-
-# Non-marshal result types
-RESULTTYPE_STATRESULT = object()
-RESULTTYPE_LONGLONG = object()
-
-def read_message(f):
- return marshal.load(f)
-
-def write_message(g, msg, resulttype=None):
- if resulttype is None:
- if sys.version_info < (2, 4):
- marshal.dump(msg, g)
- else:
- marshal.dump(msg, g, 0)
- elif resulttype is RESULTTYPE_STATRESULT:
- # Hand-coded marshal for stat results that mimics what rmarshal
expects.
- # marshal.dump(tuple(msg)) would have been too easy. rmarshal insists
- # on 64-bit ints at places, even when the value fits in 32 bits.
- import struct
- st = tuple(msg)
- fmt = "iIIiiiIfff"
- buf = []
- buf.append(struct.pack("<ci", '(', len(st)))
- for c, v in zip(fmt, st):
- if c == 'i':
- buf.append(struct.pack("<ci", c, v))
- elif c == 'I':
- buf.append(struct.pack("<cq", c, v))
- elif c == 'f':
- fstr = "%g" % v
- buf.append(struct.pack("<cB", c, len(fstr)))
- buf.append(fstr)
- g.write(''.join(buf))
- elif resulttype is RESULTTYPE_LONGLONG:
- import struct
- g.write(struct.pack("<cq", 'I', msg))
- else:
- raise Exception("Can't marshal: %r (%r)" % (msg, resulttype))
-
-# keep the table in sync with rsandbox.reraise_error()
-EXCEPTION_TABLE = [
- (1, OSError),
- (2, IOError),
- (3, OverflowError),
- (4, ValueError),
- (5, ZeroDivisionError),
- (6, MemoryError),
- (7, KeyError),
- (8, IndexError),
- (9, RuntimeError),
- ]
-
def write_exception(g, exception, tb=None):
for i, excclass in EXCEPTION_TABLE:
if isinstance(exception, excclass):
diff --git a/rpython/translator/sandbox/test/test_sandbox.py
b/rpython/translator/sandbox/test/test_sandbox.py
--- a/rpython/translator/sandbox/test/test_sandbox.py
+++ b/rpython/translator/sandbox/test/test_sandbox.py
@@ -6,10 +6,9 @@
from rpython.rtyper.lltypesystem import rffi
from rpython.translator.interactive import Translation
-from rpython.translator.sandbox.sandlib import read_message, write_message
-from rpython.translator.sandbox.sandlib import write_exception
+from rpython.translator.sandbox.sandboxio import SandboxedIO, Ptr
-if hasattr(signal, 'alarm'):
+if 0:#hasattr(signal, 'alarm'):
_orig_read_message = read_message
def _timed_out(*args):
@@ -24,18 +23,21 @@
signal.alarm(0)
signal.signal(signal.SIGALRM, signal.SIG_DFL)
-def expect(f, g, fnname, args, result, resulttype=None):
- msg = read_message(f)
+_NO_RESULT = object()
+
+def expect(sandio, fnname, expected_args, result=_NO_RESULT):
+ msg, args = sandio.read_message()
assert msg == fnname
- msg = read_message(f)
- assert msg == args
- assert [type(x) for x in msg] == [type(x) for x in args]
- if isinstance(result, Exception):
- write_exception(g, result)
- else:
- write_message(g, 0)
- write_message(g, result, resulttype)
- g.flush()
+ assert len(args) == len(expected_args)
+ for arg, expected_arg in zip(args, expected_args):
+ if type(expected_arg) is bytes:
+ assert type(arg) is Ptr
+ arg_str = sandio.read_charp(arg, len(expected_arg) + 100)
+ assert arg_str == expected_arg
+ else:
+ assert arg == expected_arg
+ if result is not _NO_RESULT:
+ sandio.write_result(result)
def compile(f, gc='ref', **kwds):
t = Translation(f, backend='c', sandbox=True, gc=gc,
@@ -46,7 +48,7 @@
popen = subprocess.Popen(exe, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
- return popen.stdin, popen.stdout
+ return SandboxedIO(popen)
def test_open_dup():
def entry_point(argv):
@@ -57,13 +59,12 @@
return 0
exe = compile(entry_point)
- g, f = run_in_subprocess(exe)
- expect(f, g, "ll_os.ll_os_open", ("/tmp/foobar", os.O_RDONLY, 0777), 77)
- expect(f, g, "ll_os.ll_os_dup", (77, True), 78)
- g.close()
- tail = f.read()
- f.close()
- assert tail == ""
+ sandio = run_in_subprocess(exe)
+ expect(sandio, "open(pii)i", ("/tmp/foobar", os.O_RDONLY, 0777), 77)
+ expect(sandio, "dup(i)i", (77,), 78)
+ with py.test.raises(EOFError):
+ sandio.read_message()
+ sandio.close()
def test_open_dup_rposix():
from rpython.rlib import rposix
@@ -75,13 +76,12 @@
return 0
exe = compile(entry_point)
- g, f = run_in_subprocess(exe)
- expect(f, g, "ll_os.ll_os_open", ("/tmp/foobar", os.O_RDONLY, 0777), 77)
- expect(f, g, "ll_os.ll_os_dup", (77, True), 78)
- g.close()
- tail = f.read()
- f.close()
- assert tail == ""
+ sandio = run_in_subprocess(exe)
+ expect(sandio, "open(pii)i", ("/tmp/foobar", os.O_RDONLY, 0777), 77)
+ expect(sandio, "dup(i)i", (77, True), 78)
+ with py.test.raises(EOFError):
+ sandio.read_message()
+ sandio.close()
def test_read_write():
def entry_point(argv):
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit