PageRenderTime 38ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/translator/sandbox/rsandbox.py

https://bitbucket.org/dac_io/pypy
Python | 179 lines | 169 code | 4 blank | 6 comment | 3 complexity | 061f18afdc2c67a017fbca5e308afe18 MD5 | raw file
  1. """Generation of sandboxing stand-alone executable from RPython code.
  2. In place of real calls to any external function, this code builds
  3. trampolines that marshal their input arguments, dump them to STDOUT,
  4. and wait for an answer on STDIN. Enable with 'translate.py --sandbox'.
  5. """
  6. from pypy.rlib import rmarshal
  7. # ____________________________________________________________
  8. #
  9. # Sandboxing code generator for external functions
  10. #
  11. from pypy.rpython.lltypesystem import lltype, rffi
  12. from pypy.annotation import model as annmodel
  13. from pypy.rlib.unroll import unrolling_iterable
  14. from pypy.rlib.objectmodel import CDefinedIntSymbolic
  15. from pypy.tool.sourcetools import func_with_new_name
  16. from pypy.rpython.annlowlevel import MixLevelHelperAnnotator
  17. from pypy.tool.ansi_print import ansi_log
  18. import py
  19. log = py.log.Producer("sandbox")
  20. py.log.setconsumer("sandbox", ansi_log)
  21. # a version of os.read() and os.write() that are not mangled
  22. # by the sandboxing mechanism
  23. ll_read_not_sandboxed = rffi.llexternal('read',
  24. [rffi.INT, rffi.CCHARP, rffi.SIZE_T],
  25. rffi.SIZE_T,
  26. sandboxsafe = True)
  27. ll_write_not_sandboxed = rffi.llexternal('write',
  28. [rffi.INT, rffi.CCHARP, rffi.SIZE_T],
  29. rffi.SIZE_T,
  30. sandboxsafe = True)
  31. def writeall_not_sandboxed(fd, buf, length):
  32. while length > 0:
  33. size = rffi.cast(rffi.SIZE_T, length)
  34. count = rffi.cast(lltype.Signed, ll_write_not_sandboxed(fd, buf, size))
  35. if count <= 0:
  36. raise IOError
  37. length -= count
  38. buf = lltype.direct_ptradd(lltype.direct_arrayitems(buf), count)
  39. buf = rffi.cast(rffi.CCHARP, buf)
  40. writeall_not_sandboxed._annenforceargs_ = [int, rffi.CCHARP, int]
  41. ##def readall_not_sandboxed(fd, length):
  42. ## buf = lltype.malloc(rffi.CCHARP.TO, length, flavor='raw')
  43. ## p = buf
  44. ## got = 0
  45. ## while got < length:
  46. ## size1 = rffi.cast(rffi.SIZE_T, length - got)
  47. ## count = rffi.cast(lltype.Signed, ll_read_not_sandboxed(fd, p, size1))
  48. ## if count <= 0:
  49. ## raise IOError
  50. ## got += count
  51. ## p = lltype.direct_ptradd(lltype.direct_arrayitems(p), count)
  52. ## p = rffi.cast(rffi.CCHARP, p)
  53. ## return buf
  54. ##readall_not_sandboxed._annenforceargs_ = [int, int]
  55. class FdLoader(rmarshal.Loader):
  56. def __init__(self, fd):
  57. rmarshal.Loader.__init__(self, "")
  58. self.fd = fd
  59. self.buflen = 4096
  60. def need_more_data(self):
  61. buflen = self.buflen
  62. buf = lltype.malloc(rffi.CCHARP.TO, buflen, flavor='raw')
  63. buflen = rffi.cast(rffi.SIZE_T, buflen)
  64. count = ll_read_not_sandboxed(self.fd, buf, buflen)
  65. count = rffi.cast(lltype.Signed, count)
  66. if count <= 0:
  67. raise IOError
  68. self.buf += ''.join([buf[i] for i in range(count)])
  69. self.buflen *= 2
  70. ##CFalse = CDefinedIntSymbolic('0') # hack hack
  71. def sandboxed_io(buf):
  72. STDIN = 0
  73. STDOUT = 1
  74. # send the buffer with the marshalled fnname and input arguments to STDOUT
  75. p = lltype.malloc(rffi.CCHARP.TO, len(buf), flavor='raw')
  76. try:
  77. for i in range(len(buf)):
  78. p[i] = buf[i]
  79. writeall_not_sandboxed(STDOUT, p, len(buf))
  80. finally:
  81. lltype.free(p, flavor='raw')
  82. # build a Loader that will get the answer from STDIN
  83. loader = FdLoader(STDIN)
  84. # check for errors
  85. error = load_int(loader)
  86. if error != 0:
  87. reraise_error(error, loader)
  88. else:
  89. # no exception; the caller will decode the actual result
  90. return loader
  91. def reraise_error(error, loader):
  92. if error == 1: raise OSError(load_int(loader), "external error")
  93. elif error == 2: raise IOError
  94. elif error == 3: raise OverflowError
  95. elif error == 4: raise ValueError
  96. elif error == 5: raise ZeroDivisionError
  97. elif error == 6: raise MemoryError
  98. elif error == 7: raise KeyError
  99. elif error == 8: raise IndexError
  100. else: raise RuntimeError
  101. def not_implemented_stub(msg):
  102. STDERR = 2
  103. buf = rffi.str2charp(msg + '\n')
  104. writeall_not_sandboxed(STDERR, buf, len(msg) + 1)
  105. rffi.free_charp(buf)
  106. raise RuntimeError(msg) # XXX in RPython, the msg is ignored at the moment
  107. not_implemented_stub._annenforceargs_ = [str]
  108. dump_string = rmarshal.get_marshaller(str)
  109. load_int = rmarshal.get_loader(int)
  110. def get_external_function_sandbox_graph(fnobj, db, force_stub=False):
  111. """Build the graph of a helper trampoline function to be used
  112. in place of real calls to the external function 'fnobj'. The
  113. trampoline marshals its input arguments, dumps them to STDOUT,
  114. and waits for an answer on STDIN.
  115. """
  116. fnname = fnobj._name
  117. if hasattr(fnobj, 'graph'):
  118. # get the annotation of the input arguments and the result
  119. graph = fnobj.graph
  120. annotator = db.translator.annotator
  121. args_s = [annotator.binding(v) for v in graph.getargs()]
  122. s_result = annotator.binding(graph.getreturnvar())
  123. else:
  124. # pure external function - fall back to the annotations
  125. # corresponding to the ll types
  126. FUNCTYPE = lltype.typeOf(fnobj)
  127. args_s = [annmodel.lltype_to_annotation(ARG) for ARG in FUNCTYPE.ARGS]
  128. s_result = annmodel.lltype_to_annotation(FUNCTYPE.RESULT)
  129. try:
  130. if force_stub: # old case - don't try to support suggested_primitive
  131. raise NotImplementedError("sandboxing for external function '%s'"
  132. % (fnname,))
  133. dump_arguments = rmarshal.get_marshaller(tuple(args_s))
  134. load_result = rmarshal.get_loader(s_result)
  135. except (NotImplementedError,
  136. rmarshal.CannotMarshal,
  137. rmarshal.CannotUnmarshall), e:
  138. msg = 'Not Implemented: %s' % (e,)
  139. log.WARNING(msg)
  140. def execute(*args):
  141. not_implemented_stub(msg)
  142. else:
  143. def execute(*args):
  144. # marshal the function name and input arguments
  145. buf = []
  146. dump_string(buf, fnname)
  147. dump_arguments(buf, args)
  148. # send the buffer and wait for the answer
  149. loader = sandboxed_io(buf)
  150. # decode the answer
  151. result = load_result(loader)
  152. loader.check_finished()
  153. return result
  154. execute = func_with_new_name(execute, 'sandboxed_' + fnname)
  155. ann = MixLevelHelperAnnotator(db.translator.rtyper)
  156. graph = ann.getgraph(execute, args_s, s_result)
  157. ann.finish()
  158. return graph