diff --git a/tests/test_regr1.py b/tests/test_regr1.py index c502457e..791d24fc 100644 --- a/tests/test_regr1.py +++ b/tests/test_regr1.py @@ -2,6 +2,8 @@ import queue import multiprocessing import signal +import subprocess +import sys import threading import unittest @@ -117,3 +119,32 @@ def test_issue39_regression(self): finally: self.running = False signal.signal(signal.SIGALRM, signal.SIG_IGN) + + +class TestIssue760Regr(unittest.TestCase): + """See https://github.com/MagicStack/uvloop/issues/760 for details. + + Directly constructing uvloop.loop.Server with a loop of None isn't a + supported usage pattern, but it segfaulted instead of raising a + Python exception, since the None was only checked much later, deep + inside close(). Run the reproducer in a subprocess since a regression + here crashes the whole interpreter rather than raising. + """ + + def test_server_with_none_loop_raises_instead_of_crashing(self): + code = ( + "from uvloop.loop import Server\n" + "server = Server(None)\n" + "server.close()\n" + ) + proc = subprocess.run( + [sys.executable, '-c', code], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + self.assertNotEqual( + proc.returncode, -11, + f'process was killed by SIGSEGV; stderr:\n' + f'{proc.stderr.decode()}') + self.assertEqual(proc.returncode, 1) + self.assertIn(b'TypeError', proc.stderr) diff --git a/uvloop/server.pyx b/uvloop/server.pyx index 845bcfda..b53dc574 100644 --- a/uvloop/server.pyx +++ b/uvloop/server.pyx @@ -2,7 +2,7 @@ import asyncio cdef class Server: - def __cinit__(self, Loop loop): + def __cinit__(self, Loop loop not None): self._loop = loop self._servers = [] self._waiters = []