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

Reply via email to