Remix.run Logo
high_byte 7 months ago

I didn't use RestrictPython. I did manage to patch the __subclasses__() escape with a hack. if only I can patch the exceptions traceback too I think it will be good enough :)

edit: here are my silly little patches: https://github.com/hananbeer/cpython-toy-sandbox/commit/fa3f...

this is of course assuming exec(globals={..}) without certain builtins and is, again, not expected to use system apis like files or sockets or anything.

eesmith 7 months ago | parent [-]

As a reminder, in case you didn't consider it, some code in your exec string might be run after the exec has finished, due to garbage collection.

    d = {"__builtins__": {"print": print}}

    exec("""

    def delay_until_gc():
      try:
        try:
          yield 1
        finally:
          print((1).__class__.__bases__[0].__subclasses__()[:3])
      except:
        raise

    it = delay_until_gc()
    it.__next__()

    """, d, d)
    del d
    print("Finished exec.")
The output for this on my system is

  Finished exec.
  [<class 'type'>, <class 'async_generator'>, <class 'bytearray_iterator'>]
which means you'll need to ensure those dictionaries are cleared and garbage collected before you can clear your toybox state, something like:

  import gc
  toybox(1)
  exec(..., d, d)
  del d
  gc.collect()
  toybox(0)
The "del" is not good enough due to the cyclical reference as the iterator function's globals contain the active iterator.

If you allow any mutable object into the globals or locals dictionary, such that the exec'ed code can attach something to it, then you can't even use gc.collect() to ensure the exec'ed code can no longer be executed.