Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r79702:890cecbe0c7d
Date: 2015-09-19 09:56 +0200
http://bitbucket.org/pypy/pypy/changeset/890cecbe0c7d/

Log:    Fix sandboxing on Windows, at least as far as running the tests

diff --git a/rpython/translator/c/src/entrypoint.c 
b/rpython/translator/c/src/entrypoint.c
--- a/rpython/translator/c/src/entrypoint.c
+++ b/rpython/translator/c/src/entrypoint.c
@@ -27,6 +27,13 @@
 #  include "forwarddecl.h"
 # endif
 
+#if defined(MS_WINDOWS) && defined(RPY_SANDBOXED)
+#  include <stdio.h>
+#  include <fcntl.h>
+#  include <io.h>
+#endif
+
+
 RPY_EXTERN
 int pypy_main_function(int argc, char *argv[])
 {
@@ -34,6 +41,11 @@
     int i, exitcode;
     RPyListOfString *list;
 
+#if defined(MS_WINDOWS) && defined(RPY_SANDBOXED)
+    _setmode(0, _O_BINARY);
+    _setmode(1, _O_BINARY);
+#endif
+
 #ifdef PYPY_USE_ASMGCC
     
pypy_g_rpython_rtyper_lltypesystem_rffi_StackCounter.sc_inst_stacks_counter++;
 #endif
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
@@ -3,10 +3,6 @@
 trampolines that marshal their input arguments, dump them to STDOUT,
 and wait for an answer on STDIN.  Enable with 'translate.py --sandbox'.
 """
-import sys
-if sys.platform == 'win32':
-    raise TypeError("sandbox not supported on windows")
-
 import py
 
 from rpython.rlib import rmarshal, types
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
@@ -10,6 +10,9 @@
 from rpython.translator.sandbox.vfs import UID, GID
 import py
 
+WIN32 = os.name == "nt"
+
+
 def create_log():
     """Make and return a log for the sandbox to use, if needed."""
     # These imports are local to avoid importing pypy if we don't need to.
@@ -40,14 +43,7 @@
 RESULTTYPE_STATRESULT = object()
 RESULTTYPE_LONGLONG = object()
 
-def read_message(f, timeout=None):
-    # warning: 'timeout' is not really reliable and should only be used
-    # for testing.  Also, it doesn't work if the file f does any buffering.
-    if timeout is not None:
-        import select
-        iwtd, owtd, ewtd = select.select([f], [], [], timeout)
-        if not iwtd:
-            raise EOFError("timed out waiting for data")
+def read_message(f):
     return marshal.load(f)
 
 def write_message(g, msg, resulttype=None):
@@ -140,7 +136,7 @@
                                       bufsize=-1,
                                       stdin=subprocess.PIPE,
                                       stdout=subprocess.PIPE,
-                                      close_fds=True,
+                                      close_fds=False if WIN32 else True,
                                       env={})
         self.popenlock = None
         self.currenttimeout = None
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
@@ -2,43 +2,51 @@
 import sys, os, time
 import struct
 import subprocess
+import signal
 
 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
 
+if hasattr(signal, 'alarm'):
+    _orig_read_message = read_message
+
+    def _timed_out(*args):
+        raise EOFError("timed out waiting for data")
+
+    def read_message(f):
+        signal.signal(signal.SIGALRM, _timed_out)
+        signal.alarm(20)
+        try:
+            return _orig_read_message(f)
+        finally:
+            signal.alarm(0)
+            signal.signal(signal.SIGALRM, signal.SIG_DFL)
+
 def expect(f, g, fnname, args, result, resulttype=None):
-    msg = read_message(f, timeout=10.0)
+    msg = read_message(f)
     assert msg == fnname
-    msg = read_message(f, timeout=10.0)
+    msg = read_message(f)
     assert msg == args
     if isinstance(result, Exception):
         write_exception(g, result)
     else:
         write_message(g, 0)
         write_message(g, result, resulttype)
-        g.flush()
+    g.flush()
 
 def compile(f, gc='ref'):
     t = Translation(f, backend='c', sandbox=True, gc=gc,
                     check_str_without_nul=True)
     return str(t.compile())
 
-unsupported_platform = ('False', '')
-if sys.platform == 'win32':
-    unsupported_platform = ('True', 'sandbox not supported on this platform')
-    def test_unavailable():
-        def entry_point(argv):
-            fd = os.open("/tmp/foobar", os.O_RDONLY, 0777)
-            os.close(fd)
-            return 0
-        exc = py.test.raises(TypeError, compile, entry_point)
-        assert str(exc).find('not supported') >= 0
+def run_in_subprocess(exe):
+    popen = subprocess.Popen(exe, stdin=subprocess.PIPE,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
+    return popen.stdin, popen.stdout
 
-supported = py.test.mark.skipif(unsupported_platform[0], 
reason=unsupported_platform[1])
-
-@supported
 def test_open_dup():
     def entry_point(argv):
         fd = os.open("/tmp/foobar", os.O_RDONLY, 0777)
@@ -48,7 +56,7 @@
         return 0
 
     exe = compile(entry_point)
-    g, f = os.popen2(exe, "t", 0)
+    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,), 78)
     g.close()
@@ -56,7 +64,6 @@
     f.close()
     assert tail == ""
 
-@supported
 def test_read_write():
     def entry_point(argv):
         fd = os.open("/tmp/foobar", os.O_RDONLY, 0777)
@@ -69,7 +76,7 @@
         return 0
 
     exe = compile(entry_point)
-    g, f = os.popen2(exe, "t", 0)
+    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_read",  (77, 123), "he\x00llo")
     expect(f, g, "ll_os.ll_os_write", (77, "world\x00!\x00"), 42)
@@ -79,7 +86,6 @@
     f.close()
     assert tail == ""
 
-@supported
 def test_dup2_access():
     def entry_point(argv):
         os.dup2(34, 56)
@@ -87,7 +93,7 @@
         return 1 - y
 
     exe = compile(entry_point)
-    g, f = os.popen2(exe, "t", 0)
+    g, f = run_in_subprocess(exe)
     expect(f, g, "ll_os.ll_os_dup2",   (34, 56), None)
     expect(f, g, "ll_os.ll_os_access", ("spam", 77), True)
     g.close()
@@ -95,19 +101,21 @@
     f.close()
     assert tail == ""
 
-@supported
 def test_stat_ftruncate():
     from rpython.translator.sandbox.sandlib import RESULTTYPE_STATRESULT
     from rpython.rlib.rarithmetic import r_longlong
     r0x12380000007 = r_longlong(0x12380000007)
 
+    if not hasattr(os, 'ftruncate'):
+        py.test.skip("posix only")
+
     def entry_point(argv):
         st = os.stat("somewhere")
         os.ftruncate(st.st_mode, st.st_size)  # nonsense, just to see outside
         return 0
 
     exe = compile(entry_point)
-    g, f = os.popen2(exe, "t", 0)
+    g, f = run_in_subprocess(exe)
     st = os.stat_result((55, 0, 0, 0, 0, 0, 0x12380000007, 0, 0, 0))
     expect(f, g, "ll_os.ll_os_stat", ("somewhere",), st,
            resulttype = RESULTTYPE_STATRESULT)
@@ -117,7 +125,6 @@
     f.close()
     assert tail == ""
 
-@supported
 def test_time():
     def entry_point(argv):
         t = time.time()
@@ -125,7 +132,7 @@
         return 0
 
     exe = compile(entry_point)
-    g, f = os.popen2(exe, "t", 0)
+    g, f = run_in_subprocess(exe)
     expect(f, g, "ll_time.ll_time_time", (), 3.141592)
     expect(f, g, "ll_os.ll_os_dup", (3141,), 3)
     g.close()
@@ -133,7 +140,6 @@
     f.close()
     assert tail == ""
 
-@supported
 def test_getcwd():
     def entry_point(argv):
         t = os.getcwd()
@@ -141,7 +147,7 @@
         return 0
 
     exe = compile(entry_point)
-    g, f = os.popen2(exe, "t", 0)
+    g, f = run_in_subprocess(exe)
     expect(f, g, "ll_os.ll_os_getcwd", (), "/tmp/foo/bar")
     expect(f, g, "ll_os.ll_os_dup", (len("/tmp/foo/bar"),), 3)
     g.close()
@@ -149,7 +155,6 @@
     f.close()
     assert tail == ""
 
-@supported
 def test_oserror():
     def entry_point(argv):
         try:
@@ -159,7 +164,7 @@
         return 0
 
     exe = compile(entry_point)
-    g, f = os.popen2(exe, "t", 0)
+    g, f = run_in_subprocess(exe)
     expect(f, g, "ll_os.ll_os_stat", ("somewhere",), OSError(6321, "egg"))
     expect(f, g, "ll_os.ll_os_close", (6321,), None)
     g.close()
@@ -167,7 +172,6 @@
     f.close()
     assert tail == ""
 
-@supported
 def test_hybrid_gc():
     def entry_point(argv):
         l = []
@@ -192,7 +196,6 @@
     rescode = pipe.wait()
     assert rescode == 0
 
-@supported
 def test_segfault_1():
     class A:
         def __init__(self, m):
@@ -206,16 +209,12 @@
         return int(x.m)
 
     exe = compile(entry_point)
-    g, f, e = os.popen3(exe, "t", 0)
+    g, f = run_in_subprocess(exe)
     g.close()
     tail = f.read()
     f.close()
-    assert tail == ""
-    errors = e.read()
-    e.close()
-    assert 'Invalid RPython operation' in errors
+    assert 'Invalid RPython operation' in tail
 
-@supported
 def test_segfault_2():
     py.test.skip("hum, this is one example, but we need to be very careful")
     class Base:
@@ -248,7 +247,6 @@
     e.close()
     assert '...think what kind of errors to get...' in errors
 
-@supported
 def test_safe_alloc():
     from rpython.rlib.rmmap import alloc, free
 
@@ -269,7 +267,6 @@
     rescode = pipe.wait()
     assert rescode == 0
 
-@supported
 def test_unsafe_mmap():
     py.test.skip("Since this stuff is unimplemented, it won't work anyway "
                  "however, the day it starts working, it should pass test")
@@ -295,7 +292,6 @@
     rescode = pipe.wait()
     assert rescode == 0
 
-@supported
 class TestPrintedResults:
 
     def run(self, entry_point, args, expected):
diff --git a/rpython/translator/sandbox/test/test_sandlib.py 
b/rpython/translator/sandbox/test/test_sandlib.py
--- a/rpython/translator/sandbox/test/test_sandlib.py
+++ b/rpython/translator/sandbox/test/test_sandlib.py
@@ -6,10 +6,10 @@
 from rpython.translator.sandbox.sandlib import SimpleIOSandboxedProc
 from rpython.translator.sandbox.sandlib import VirtualizedSandboxedProc
 from rpython.translator.sandbox.sandlib import VirtualizedSocketProc
-from rpython.translator.sandbox.test.test_sandbox import compile, supported
+from rpython.translator.sandbox.test.test_sandbox import compile
 from rpython.translator.sandbox.vfs import Dir, File, RealDir, RealFile
 
-@supported
+
 class MockSandboxedProc(SandboxedProc):
     """A sandbox process wrapper that replays expected syscalls."""
 
@@ -35,7 +35,7 @@
     do_ll_os__ll_os_write = _make_method("write")
     do_ll_os__ll_os_close = _make_method("close")
 
-@supported
+
 def test_lib():
     def entry_point(argv):
         fd = os.open("/tmp/foobar", os.O_RDONLY, 0777)
@@ -63,7 +63,6 @@
     proc.handle_forever()
     assert proc.seen == len(proc.expected)
 
-@supported
 def test_foobar():
     py.test.skip("to be updated")
     foobar = rffi.llexternal("foobar", [rffi.CCHARP], rffi.LONG)
@@ -80,7 +79,6 @@
     proc.handle_forever()
     assert proc.seen == len(proc.expected)
 
-@supported
 def test_simpleio():
     def entry_point(argv):
         print "Please enter a number:"
@@ -102,7 +100,6 @@
     assert output == "Please enter a number:\nThe double is: 42\n"
     assert error == ""
 
-@supported
 def test_socketio():
     class SocketProc(VirtualizedSocketProc, SimpleIOSandboxedProc):
         def build_virtual_root(self):
@@ -119,7 +116,6 @@
     output, error = proc.communicate("")
     assert output.startswith('HTTP/1.1 301 Moved Permanently')
 
-@supported
 def test_oserror():
     def entry_point(argv):
         try:
@@ -137,7 +133,6 @@
     assert proc.seen == len(proc.expected)
 
 
-@supported
 class SandboxedProcWithFiles(VirtualizedSandboxedProc, SimpleIOSandboxedProc):
     """A sandboxed process with a simple virtualized filesystem.
 
@@ -150,7 +145,6 @@
             'this.pyc': RealFile(__file__),
              })
 
-@supported
 def test_too_many_opens():
     def entry_point(argv):
         try:
@@ -192,7 +186,6 @@
     assert output == "All ok!\n"
     assert error == ""
 
-@supported
 def test_fstat():
     def compare(a, b, i):
         if a != b:
@@ -226,7 +219,6 @@
     assert output == "All ok!\n"
     assert error == ""
 
-@supported
 def test_lseek():
     def char_should_be(c, should):
         if c != should:
@@ -256,8 +248,10 @@
     assert output == "All ok!\n"
     assert error == ""
 
-@supported
 def test_getuid():
+    if not hasattr(os, 'getuid'):
+        py.test.skip("posix only")
+
     def entry_point(argv):
         import os
         print "uid is %s" % os.getuid()
diff --git a/rpython/translator/sandbox/test/test_vfs.py 
b/rpython/translator/sandbox/test/test_vfs.py
--- a/rpython/translator/sandbox/test/test_vfs.py
+++ b/rpython/translator/sandbox/test/test_vfs.py
@@ -2,13 +2,10 @@
 import sys, stat, os
 from rpython.translator.sandbox.vfs import *
 from rpython.tool.udir import udir
-from rpython.translator.sandbox.test.test_sandbox import unsupported_platform
 
 HASLINK = hasattr(os, 'symlink')
 
 def setup_module(mod):
-    if unsupported_platform[0] == 'True':
-        py.test.skip(unsupported_platform[1])
     d = udir.ensure('test_vfs', dir=1)
     d.join('file1').write('somedata1')
     d.join('file2').write('somelongerdata2')
@@ -70,11 +67,12 @@
 
             f = v_test_vfs.join('file2')
             assert f.getsize() == len('somelongerdata2')
-            py.test.raises(OSError, f.open)
+            if os.name != 'nt':     # can't have unreadable files there?
+                py.test.raises(OSError, f.open)
 
             py.test.raises(OSError, v_test_vfs.join, 'does_not_exist')
             py.test.raises(OSError, v_test_vfs.join, 'symlink3')
-            if follow_links:
+            if follow_links and HASLINK:
                 d = v_test_vfs.join('symlink1')
                 assert stat.S_ISDIR(d.stat().st_mode)
                 assert d.keys() == ['subfile1']
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to