tests: more qrexec tests, this time for deadlocks

Tests for recently discovered deadlocks on write(2).

QubesOS/qubes-issues#1347
This commit is contained in:
Marek Marczykowski-Górecki 2015-10-25 15:23:12 +01:00
parent 75168c8aef
commit e475e291a9
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724

View File

@ -318,6 +318,138 @@ class TC_00_AppVMMixin(qubes.tests.SystemTestsMixin):
(stdout, stderr) = p.communicate()
self.assertEqual(stdout, "3\n")
def test_070_qrexec_vm_simultaneous_write(self):
"""Test for simultaneous write in VM(src)->VM(dst) connection
This is regression test for #1347
Check for deadlock when initially both sides writes a lot of data
(and not read anything). When one side starts reading, it should
get the data and the remote side should be possible to write then more.
There was a bug where remote side was waiting on write(2) and not
handling anything else.
"""
result = multiprocessing.Value('i', -1)
def run(self):
p = self.testvm1.run(
"/usr/lib/qubes/qrexec-client-vm %s test.write "
"/bin/sh -c '"
# first write a lot of data to fill all the buffers
"dd if=/dev/zero bs=993 count=10000 iflag=fullblock & "
# then after some time start reading
"sleep 1; "
"dd of=/dev/null bs=993 count=10000 iflag=fullblock; "
"wait"
"'" % self.testvm2.name, passio_popen=True)
p.communicate()
result.value = p.returncode
self.testvm1.start()
self.testvm2.start()
p = self.testvm2.run("cat > /etc/qubes-rpc/test.write", user="root",
passio_popen=True)
# first write a lot of data
p.stdin.write("dd if=/dev/zero bs=993 count=10000 iflag=fullblock\n")
# and only then read something
p.stdin.write("dd of=/dev/null bs=993 count=10000 iflag=fullblock\n")
p.stdin.close()
p.wait()
policy = open("/etc/qubes-rpc/policy/test.write", "w")
policy.write("%s %s allow" % (self.testvm1.name, self.testvm2.name))
policy.close()
self.addCleanup(os.unlink, "/etc/qubes-rpc/policy/test.write")
t = multiprocessing.Process(target=run, args=(self,))
t.start()
t.join(timeout=10)
if t.is_alive():
t.terminate()
self.fail("Timeout, probably deadlock")
self.assertEqual(result.value, 0, "Service call failed")
def test_071_qrexec_dom0_simultaneous_write(self):
"""Test for simultaneous write in dom0(src)->VM(dst) connection
Similar to test_070_qrexec_vm_simultaneous_write, but with dom0
as a source.
"""
result = multiprocessing.Value('i', -1)
def run(self):
result.value = self.testvm2.run_service(
"test.write", localcmd="/bin/sh -c '"
# first write a lot of data to fill all the buffers
"dd if=/dev/zero bs=993 count=10000 iflag=fullblock & "
# then after some time start reading
"sleep 1; "
"dd of=/dev/null bs=993 count=10000 iflag=fullblock; "
"wait"
"'")
self.testvm2.start()
p = self.testvm2.run("cat > /etc/qubes-rpc/test.write", user="root",
passio_popen=True)
# first write a lot of data
p.stdin.write("dd if=/dev/zero bs=993 count=10000 iflag=fullblock\n")
# and only then read something
p.stdin.write("dd of=/dev/null bs=993 count=10000 iflag=fullblock\n")
p.stdin.close()
p.wait()
policy = open("/etc/qubes-rpc/policy/test.write", "w")
policy.write("%s %s allow" % (self.testvm1.name, self.testvm2.name))
policy.close()
self.addCleanup(os.unlink, "/etc/qubes-rpc/policy/test.write")
t = multiprocessing.Process(target=run, args=(self,))
t.start()
t.join(timeout=10)
if t.is_alive():
t.terminate()
self.fail("Timeout, probably deadlock")
self.assertEqual(result.value, 0, "Service call failed")
def test_072_qrexec_to_dom0_simultaneous_write(self):
"""Test for simultaneous write in dom0(src)<-VM(dst) connection
Similar to test_071_qrexec_dom0_simultaneous_write, but with dom0
as a "hanging" side.
"""
result = multiprocessing.Value('i', -1)
def run(self):
result.value = self.testvm2.run_service(
"test.write", localcmd="/bin/sh -c '"
# first write a lot of data to fill all the buffers
"dd if=/dev/zero bs=993 count=10000 iflag=fullblock "
# then, only when all written, read something
"dd of=/dev/null bs=993 count=10000 iflag=fullblock; "
"'")
self.testvm2.start()
p = self.testvm2.run("cat > /etc/qubes-rpc/test.write", user="root",
passio_popen=True)
# first write a lot of data
p.stdin.write("dd if=/dev/zero bs=993 count=10000 iflag=fullblock &\n")
# and only then read something
p.stdin.write("dd of=/dev/null bs=993 count=10000 iflag=fullblock\n")
p.stdin.write("sleep 1; \n")
p.stdin.write("wait\n")
p.stdin.close()
p.wait()
policy = open("/etc/qubes-rpc/policy/test.write", "w")
policy.write("%s %s allow" % (self.testvm1.name, self.testvm2.name))
policy.close()
self.addCleanup(os.unlink, "/etc/qubes-rpc/policy/test.write")
t = multiprocessing.Process(target=run, args=(self,))
t.start()
t.join(timeout=10)
if t.is_alive():
t.terminate()
self.fail("Timeout, probably deadlock")
self.assertEqual(result.value, 0, "Service call failed")
@unittest.skipUnless(spawn.find_executable('xdotool'),
"xdotool not installed")
def test_100_qrexec_filecopy(self):