[pypy-svn] r45202 - in pypy/dist/pypy: annotation rpython/module translator/c translator/c/test
arigo at codespeak.net
arigo at codespeak.net
Thu Jul 19 13:56:44 CEST 2007
Author: arigo
Date: Thu Jul 19 13:56:43 2007
New Revision: 45202
Modified:
pypy/dist/pypy/annotation/builtin.py
pypy/dist/pypy/rpython/module/ll_os.py
pypy/dist/pypy/translator/c/sandbox.py
pypy/dist/pypy/translator/c/sandboxmsg.py
pypy/dist/pypy/translator/c/test/test_sandbox.py
Log:
Add hooks for ll external functions to specify how to marshal/unmarshal
their arguments and return value for the purpose of sandboxing.
Fight with annotation and rtyping ordering issues by throwing piles of
hack at it :-(
Modified: pypy/dist/pypy/annotation/builtin.py
==============================================================================
--- pypy/dist/pypy/annotation/builtin.py (original)
+++ pypy/dist/pypy/annotation/builtin.py Thu Jul 19 13:56:43 2007
@@ -65,8 +65,11 @@
if step == 0:
raise Exception, "range() with step zero"
if s_start.is_constant() and s_stop.is_constant():
- if len(xrange(s_start.const, s_stop.const, step)) == 0:
- empty = True
+ try:
+ if len(xrange(s_start.const, s_stop.const, step)) == 0:
+ empty = True
+ except TypeError: # if one of the .const is a Symbolic
+ pass
if empty:
s_item = s_ImpossibleValue
else:
Modified: pypy/dist/pypy/rpython/module/ll_os.py
==============================================================================
--- pypy/dist/pypy/rpython/module/ll_os.py (original)
+++ pypy/dist/pypy/rpython/module/ll_os.py Thu Jul 19 13:56:43 2007
@@ -167,6 +167,20 @@
register_external(os.read, [int, int], str, "ll_os.ll_os_read",
llimpl=os_read_lltypeimpl, oofakeimpl=os_read_oofakeimpl)
+# '--sandbox' support
+def os_read_marshal_input(msg, fd, buf, size):
+ msg.packnum(rffi.cast(lltype.Signed, fd))
+ msg.packsize_t(size)
+def os_read_unmarshal_output(msg, fd, buf, size):
+ data = msg.nextstring()
+ if len(data) > rffi.cast(lltype.Signed, size):
+ raise OverflowError
+ for i in range(len(data)):
+ buf[i] = data[i]
+ return rffi.cast(rffi.SIZE_T, len(data))
+os_read._obj._marshal_input = os_read_marshal_input
+os_read._obj._unmarshal_output = os_read_unmarshal_output
+
# ------------------------------- os.write ------------------------------
os_write = rffi.llexternal('write', [rffi.INT, rffi.VOIDP, rffi.SIZE_T],
@@ -193,6 +207,12 @@
register_external(os.write, [int, str], int, "ll_os.ll_os_write",
llimpl=os_write_lltypeimpl, oofakeimpl=os_write_oofakeimpl)
+# '--sandbox' support
+def os_write_marshal_input(msg, fd, buf, size):
+ msg.packnum(rffi.cast(lltype.Signed, fd))
+ msg.packbuf(buf, 0, rffi.cast(lltype.Signed, size))
+os_write._obj._marshal_input = os_write_marshal_input
+
# ------------------------------- os.close ------------------------------
os_close = rffi.llexternal('close', [rffi.INT], rffi.INT)
Modified: pypy/dist/pypy/translator/c/sandbox.py
==============================================================================
--- pypy/dist/pypy/translator/c/sandbox.py (original)
+++ pypy/dist/pypy/translator/c/sandbox.py Thu Jul 19 13:56:43 2007
@@ -13,6 +13,7 @@
from pypy.rpython.lltypesystem import lltype, rffi
from pypy.annotation import model as annmodel
from pypy.rlib.unroll import unrolling_iterable
+from pypy.rlib.objectmodel import CDefinedIntSymbolic
from pypy.translator.c import funcgen
from pypy.tool.sourcetools import func_with_new_name
from pypy.rpython.annlowlevel import MixLevelHelperAnnotator
@@ -70,68 +71,113 @@
c0 -= 0x100
return (c0 << 24) | (c1 << 16) | (c2 << 8) | c3
-
-def get_external_function_sandbox_graph(fnobj, db):
- """Build the graph of a helper trampoline function to be used
- in place of real calls to the external function 'fnobj'. The
- trampoline marshals its input arguments, dumps them to STDOUT,
- and waits for an answer on STDIN.
- """
- # XXX for now, only supports function with int and string arguments
- # and returning an int.
- FUNCTYPE = lltype.typeOf(fnobj)
+def build_default_marshal_input(FUNCTYPE, namehint, cache={}):
+ # return a default 'marshal_input' function
+ try:
+ return cache[FUNCTYPE]
+ except KeyError:
+ pass
unroll_args = []
for i, ARG in enumerate(FUNCTYPE.ARGS):
if ARG == rffi.INT: # 'int' argument
methodname = "packnum"
+ elif ARG == rffi.SIZE_T: # 'size_t' argument
+ methodname = "packsize_t"
elif ARG == rffi.CCHARP: # 'char*' argument, assumed zero-terminated
methodname = "packccharp"
else:
raise NotImplementedError("external function %r argument type %s" %
- (fnobj, ARG))
+ (namehint, ARG))
unroll_args.append((i, methodname))
- if FUNCTYPE.RESULT != rffi.INT:
- raise NotImplementedError("exernal function %r return type %s" % (
- fnobj, FUNCTYPE.RESULT))
unroll_args = unrolling_iterable(unroll_args)
- fnname = fnobj._name
- def execute(*args):
- STDIN = 0
- STDOUT = 1
+ def marshal_input(msg, *args):
assert len(args) == len(FUNCTYPE.ARGS)
- # marshal the input arguments
- msg = MessageBuilder()
- msg.packstring(fnname)
for index, methodname in unroll_args:
getattr(msg, methodname)(args[index])
- buf = msg.as_rffi_buf()
- try:
- writeall_not_sandboxed(STDOUT, buf, msg.getlength())
- finally:
- lltype.free(buf, flavor='raw')
- # wait for the answer
- buf = readall_not_sandboxed(STDIN, 4)
- try:
- length = buf2num(buf)
- finally:
- lltype.free(buf, flavor='raw')
+ cache[FUNCTYPE] = marshal_input
+ return marshal_input
- length -= 4 # the original length includes the header
- if length < 0:
- raise IOError
- buf = readall_not_sandboxed(STDIN, length)
+def unmarshal_int(msg, *args): return msg.nextnum()
+def unmarshal_size_t(msg, *args): return msg.nextsize_t()
+def unmarshal_void(msg, *args): pass
+
+def build_default_unmarshal_output(FUNCTYPE, namehint,
+ cache={rffi.INT : unmarshal_int,
+ rffi.SIZE_T: unmarshal_size_t,
+ lltype.Void: unmarshal_void}):
+ try:
+ return cache[FUNCTYPE.RESULT]
+ except KeyError:
+ raise NotImplementedError("exernal function %r return type %s" % (
+ namehint, FUNCTYPE.RESULT))
+
+CFalse = CDefinedIntSymbolic('0') # hack hack
+
+def sandboxed_io(msg):
+ STDIN = 0
+ STDOUT = 1
+ buf = msg.as_rffi_buf()
+ if CFalse: # hack hack to force a method to be properly annotated/rtyped
+ msg.packstring(chr(CFalse) + chr(CFalse))
+ msg.packsize_t(rffi.cast(rffi.SIZE_T, CFalse))
+ msg.packbuf(buf, CFalse * 5, CFalse * 6)
+ try:
+ writeall_not_sandboxed(STDOUT, buf, msg.getlength())
+ finally:
+ lltype.free(buf, flavor='raw')
+ # wait for the answer
+ buf = readall_not_sandboxed(STDIN, 4)
+ try:
+ length = buf2num(buf)
+ finally:
+ lltype.free(buf, flavor='raw')
+ length -= 4 # the original length includes the header
+ if length < 0:
+ raise IOError
+ buf = readall_not_sandboxed(STDIN, length)
+ msg = LLMessage(buf, 0, length)
+ if CFalse: # hack hack to force a method to be properly annotated/rtyped
+ msg.nextstring()
+ msg.nextsize_t()
+ return msg
+
+def get_external_function_sandbox_graph(fnobj, db):
+ """Build the graph of a helper trampoline function to be used
+ in place of real calls to the external function 'fnobj'. The
+ trampoline marshals its input arguments, dumps them to STDOUT,
+ and waits for an answer on STDIN.
+ """
+ # XXX for now, only supports function with int and string arguments
+ # and returning an int or void. Other cases need a custom
+ # _marshal_input and/or _unmarshal_output function on fnobj.
+ FUNCTYPE = lltype.typeOf(fnobj)
+ fnname = fnobj._name
+ if hasattr(fnobj, '_marshal_input'):
+ marshal_input = fnobj._marshal_input
+ else:
+ marshal_input = build_default_marshal_input(FUNCTYPE, fnname)
+ if hasattr(fnobj, '_unmarshal_output'):
+ unmarshal_output = fnobj._unmarshal_output
+ else:
+ unmarshal_output = build_default_unmarshal_output(FUNCTYPE, fnname)
+
+ def execute(*args):
+ # marshal the input arguments
+ msg = MessageBuilder()
+ msg.packstring(fnname)
+ marshal_input(msg, *args)
+ # send the buffer and wait for the answer
+ msg = sandboxed_io(msg)
try:
# decode the answer
- msg = LLMessage(buf, 0, length)
errcode = msg.nextnum()
if errcode != 0:
raise IOError
- result = msg.nextnum()
+ result = unmarshal_output(msg, *args)
finally:
- lltype.free(buf, flavor='raw')
-
+ lltype.free(msg.value, flavor='raw')
return result
execute = func_with_new_name(execute, 'sandboxed_' + fnname)
Modified: pypy/dist/pypy/translator/c/sandboxmsg.py
==============================================================================
--- pypy/dist/pypy/translator/c/sandboxmsg.py (original)
+++ pypy/dist/pypy/translator/c/sandboxmsg.py Thu Jul 19 13:56:43 2007
@@ -2,7 +2,7 @@
import struct
import select
-from pypy.annotation import policy, model as annmodel
+from pypy.rpython.lltypesystem import rffi, lltype
# ____________________________________________________________
#
@@ -14,28 +14,43 @@
self.value = ['\xFF', '\xFF', '\xFF', '\xFF']
def packstring(self, s):
- self.packnum(len(s), "s")
+ self.value.append("s")
+ self._pack4(len(s))
self.value += s
return self
- packstring._annenforceargs_ = policy.Sig(None, str)
def packccharp(self, p):
length = 0
while p[length] != '\x00':
length += 1
- self.packnum(length, "s")
+ self.value.append("s")
+ self._pack4(length)
for i in range(length):
self.value.append(p[i])
return self
- def packnum(self, n, prefix="i"):
- self.value.append(prefix)
+ def packbuf(self, buf, start, stop):
+ self.value.append("s")
+ self._pack4(stop - start)
+ for i in range(start, stop):
+ self.value.append(buf[i])
+ return self
+
+ def _pack4(self, n):
self.value.append(chr((n >> 24) & 0xFF))
self.value.append(chr((n >> 16) & 0xFF))
self.value.append(chr((n >> 8) & 0xFF))
self.value.append(chr((n ) & 0xFF))
+
+ def packnum(self, n):
+ self.value.append("i")
+ self._pack4(n)
+ return self
+
+ def packsize_t(self, n):
+ self.value.append("I")
+ self._pack4(rffi.cast(lltype.Signed, n))
return self
- packnum._annenforceargs_ = policy.Sig(None, int, annmodel.SomeChar())
def _fixlength(self):
n = len(self.value)
@@ -65,6 +80,7 @@
class LLMessage(object):
def __init__(self, value, start, stop):
self.value = value
+ assert 0 <= start <= stop
self.pos = start
self.stop = stop
@@ -76,7 +92,12 @@
return self.value[i]
def nextstring(self):
- length = self.nextnum("s")
+ t = self._char()
+ if t != "s":
+ raise ValueError
+ length = self._next4()
+ if length < 0:
+ raise ValueError
i = self.pos
self.pos = i + length
if self.pos > self.stop:
@@ -85,10 +106,19 @@
# not sliceable. See also the Message subclass.
return ''.join([self.value[index] for index in range(i, self.pos)])
- def nextnum(self, prefix="i"):
+ def nextnum(self):
+ t = self._char()
+ if t != "i":
+ raise ValueError
+ return self._next4()
+
+ def nextsize_t(self):
t = self._char()
- if t != prefix:
+ if t != "I":
raise ValueError
+ return rffi.cast(rffi.SIZE_T, self._next4_unsigned())
+
+ def _next4(self):
c0 = ord(self._char())
c1 = ord(self._char())
c2 = ord(self._char())
@@ -97,6 +127,13 @@
c0 -= 0x100
return (c0 << 24) | (c1 << 16) | (c2 << 8) | c3
+ def _next4_unsigned(self):
+ c0 = ord(self._char())
+ c1 = ord(self._char())
+ c2 = ord(self._char())
+ c3 = ord(self._char())
+ return (c0 << 24) | (c1 << 16) | (c2 << 8) | c3
+
def end(self):
return self.pos >= self.stop
@@ -109,7 +146,10 @@
LLMessage.__init__(self, buf, start=0, stop=len(buf))
def nextstring(self):
- length = self.nextnum("s")
+ t = self._char()
+ if t != "s":
+ raise ValueError
+ length = self._next4()
i = self.pos
self.pos = i + length
if self.pos > self.stop:
Modified: pypy/dist/pypy/translator/c/test/test_sandbox.py
==============================================================================
--- pypy/dist/pypy/translator/c/test/test_sandbox.py (original)
+++ pypy/dist/pypy/translator/c/test/test_sandbox.py Thu Jul 19 13:56:43 2007
@@ -65,3 +65,67 @@
tail = f.read()
f.close()
assert tail == ""
+
+def test_sandbox_2():
+ def entry_point(argv):
+ fd = os.open("/tmp/foobar", os.O_RDONLY, 0777)
+ assert fd == 77
+ res = os.read(fd, 123)
+ assert res == "he\x00llo"
+ count = os.write(fd, "world\x00!\x00")
+ assert count == 42
+ os.close(fd)
+ return 0
+
+ t = Translation(entry_point, backend='c', standalone=True, sandbox=True)
+ exe = t.compile()
+ g, f = os.popen2(exe, "t", 0)
+
+ msg = read_message(f, timeout=10.0)
+ m1 = msg.nextstring()
+ assert m1 == "open"
+ m2 = msg.nextstring()
+ assert m2 == "/tmp/foobar"
+ m3 = msg.nextnum()
+ assert m3 == os.O_RDONLY
+ m4 = msg.nextnum()
+ assert m4 == 0777
+ assert msg.end()
+
+ g.write(MessageBuilder().packnum(0).packnum(77).getvalue())
+
+ msg = read_message(f, timeout=10.0)
+ m1 = msg.nextstring()
+ assert m1 == "read"
+ m2 = msg.nextnum()
+ assert m2 == 77
+ m3 = msg.nextsize_t()
+ assert m3 == 123
+ assert msg.end()
+
+ g.write(MessageBuilder().packnum(0).packstring("he\x00llo").getvalue())
+
+ msg = read_message(f, timeout=10.0)
+ m1 = msg.nextstring()
+ assert m1 == "write"
+ m2 = msg.nextnum()
+ assert m2 == 77
+ m3 = msg.nextstring()
+ assert m3 == "world\x00!\x00"
+ assert msg.end()
+
+ g.write(MessageBuilder().packnum(0).packsize_t(42).getvalue())
+
+ msg = read_message(f, timeout=10.0)
+ m1 = msg.nextstring()
+ assert m1 == "close"
+ m2 = msg.nextnum()
+ assert m2 == 77
+ assert msg.end()
+
+ g.write(MessageBuilder().packnum(0).packnum(0).getvalue())
+
+ g.close()
+ tail = f.read()
+ f.close()
+ assert tail == ""
More information about the Pypy-commit
mailing list