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