summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Tomlins <alex@tomlins.org.uk>2018-12-05 12:58:05 +0000
committerBrian May <brian@linuxpenguins.xyz>2018-12-09 18:03:54 +1100
commitd43db80decebce2d3e072f3b0a6acb9d0f71d523 (patch)
tree5f5b89af66a1c346c82a571ec281b83c13408350
parent0b1a260436eec93005a224a51baa5b6f6de02819 (diff)
Fix deadlock with iptables with large ruleset
When running sshuttle with a large list of routes it's failing to clean them up at exit. It returns the following: $ sshuttle -r user@host.example.com -s /tmp/aws-cidrs.txt user@host.example.com's password: client: Connected. ^CAnother app is currently holding the xtables lock; still -9s 0us time ahead to have a chance to grab the lock... Another app is currently holding the xtables lock; still -19s 0us time ahead to have a chance to grab the lock... Another app is currently holding the xtables lock; still -29s 0us time ahead to have a chance to grab the lock... This continues indefinitely. Looking in ps reveals that there are 2 iptables processes running. Killing -9 the first one, allows sshuttle to continue and clean up successfully. The problem lies with the use of Popen here. The function currently returns as soon as it finds a match without consuming everything from stdout. This means that if there's more output from iptables than will fit in the buffer it doesn't exit, and therefore doesn't release the kernel xtables lock.
-rw-r--r--sshuttle/linux.py11
1 files changed, 5 insertions, 6 deletions
diff --git a/sshuttle/linux.py b/sshuttle/linux.py
index c0bf28b..5251ab3 100644
--- a/sshuttle/linux.py
+++ b/sshuttle/linux.py
@@ -24,13 +24,12 @@ def ipt_chain_exists(family, table, name):
'PATH': os.environ['PATH'],
'LC_ALL': "C",
}
- p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, env=env)
- for line in p.stdout:
- if line.startswith(b'Chain %s ' % name.encode("ASCII")):
+ completed = ssubprocess.run(argv, stdout=ssubprocess.PIPE, env=env, encoding='ASCII')
+ if completed.returncode:
+ raise Fatal('%r returned %d' % (argv, completed.returncode))
+ for line in completed.stdout.split('\n'):
+ if line.startswith('Chain %s ' % name):
return True
- rv = p.wait()
- if rv:
- raise Fatal('%r returned %d' % (argv, rv))
def ipt(family, table, *args):