Commit e7ef8dbe by Ned Batchelder

Real-time limit now works.

parent 5b274e89
...@@ -65,6 +65,7 @@ Other details here that depend on your configuration: ...@@ -65,6 +65,7 @@ Other details here that depend on your configuration:
$ visudo -f /etc/sudoers.d/01-sandbox $ visudo -f /etc/sudoers.d/01-sandbox
<WWWUSER> ALL=(sandbox) NOPASSWD:<SANDENV>/bin/python <WWWUSER> ALL=(sandbox) NOPASSWD:<SANDENV>/bin/python
<WWWUSER> ALL=(ALL) NOPASSWD:/usr/bin/pkill
5. Edit an AppArmor profile. This is a text file specifying the limits on the 5. Edit an AppArmor profile. This is a text file specifying the limits on the
sandboxed Python executable. The file must be in `/etc/apparmor.d` and must sandboxed Python executable. The file must be in `/etc/apparmor.d` and must
......
...@@ -167,10 +167,9 @@ def jail_code(command, code=None, files=None, argv=None, stdin=None): ...@@ -167,10 +167,9 @@ def jail_code(command, code=None, files=None, argv=None, stdin=None):
stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
) )
# TODO: time limiting. The ProcessKillerThread doesn't work yet, so # Start the time killer thread.
# don't launch it. killer = ProcessKillerThread(subproc, limit=1.0)
# killer = ProcessKillerThread(subproc) killer.start()
# killer.start()
result = JailResult() result = JailResult()
result.stdout, result.stderr = subproc.communicate(stdin) result.stdout, result.stderr = subproc.communicate(stdin)
...@@ -183,7 +182,11 @@ def set_process_limits(): # pragma: no cover ...@@ -183,7 +182,11 @@ def set_process_limits(): # pragma: no cover
""" """
Set limits on this processs, to be used first in a child process. Set limits on this processs, to be used first in a child process.
""" """
# No subprocesses or files # Set a new session id so that this process and all its children will be
# in a new process group, so we can kill them all later if we need to.
os.setsid()
# No subprocesses or files.
resource.setrlimit(resource.RLIMIT_NPROC, (0, 0)) resource.setrlimit(resource.RLIMIT_NPROC, (0, 0))
resource.setrlimit(resource.RLIMIT_FSIZE, (0, 0)) resource.setrlimit(resource.RLIMIT_FSIZE, (0, 0))
...@@ -202,7 +205,7 @@ class ProcessKillerThread(threading.Thread): ...@@ -202,7 +205,7 @@ class ProcessKillerThread(threading.Thread):
""" """
A thread to kill a process after a given time limit. A thread to kill a process after a given time limit.
""" """
def __init__(self, subproc, limit=1): def __init__(self, subproc, limit):
super(ProcessKillerThread, self).__init__() super(ProcessKillerThread, self).__init__()
self.subproc = subproc self.subproc = subproc
self.limit = limit self.limit = limit
...@@ -210,16 +213,20 @@ class ProcessKillerThread(threading.Thread): ...@@ -210,16 +213,20 @@ class ProcessKillerThread(threading.Thread):
def run(self): def run(self):
start = time.time() start = time.time()
while (time.time() - start) < self.limit: while (time.time() - start) < self.limit:
time.sleep(.1) time.sleep(.25)
if self.subproc.poll() is not None: if self.subproc.poll() is not None:
# Process ended, no need for us any more. # Process ended, no need for us any more.
return return
if self.subproc.poll() is None: if self.subproc.poll() is None:
# Can't use subproc.kill because we launched the subproc with sudo. # Can't use subproc.kill because we launched the subproc with sudo.
killargs = ["sudo", "kill", "-9", str(self.subproc.pid)] pgid = os.getpgid(self.subproc.pid)
log.warning(
"Killing process %r (group %r), ran too long: %.1fs",
self.subproc.pid, pgid, time.time()-start
)
killargs = ["sudo", "pkill", "-9", "-g", str(pgid)]
kill = subprocess.Popen( kill = subprocess.Popen(
killargs, stdout=subprocess.PIPE, stderr=subprocess.PIPE killargs, stdout=subprocess.PIPE, stderr=subprocess.PIPE
) )
out, err = kill.communicate() out, err = kill.communicate()
# TODO: This doesn't actually kill the process.... :(
...@@ -115,7 +115,6 @@ class TestLimits(JailCodeHelpers, unittest.TestCase): ...@@ -115,7 +115,6 @@ class TestLimits(JailCodeHelpers, unittest.TestCase):
self.assertNotEqual(res.status, 0) self.assertNotEqual(res.status, 0)
def test_cant_use_too_much_time(self): def test_cant_use_too_much_time(self):
raise SkipTest # TODO: test this once we can kill sleeping processes.
res = jailpy(code=dedent("""\ res = jailpy(code=dedent("""\
import time import time
time.sleep(5) time.sleep(5)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment