From 0f1011081e3110feea564042fa9ab8c792c0830d Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 14 Jul 2021 23:55:31 +0200 Subject: [PATCH] Socket: Fix cascading crash when Socket.run() crashes. When a driver's run() method crashes, supybot.drivers.run() marks it as dead and sets its 'irc' attribute to None. This would be fine for "normal" independent drivers (like Socket used to be), because this driver would never be called again. But now that we use select(), some other thread may hold a reference to this driver in a select() call frame, and call the dead driver's '_read()' method when there is data to be read from the socket. There is already a safeguard in '_read()' in the case the socket could be read from, but this safeguard was missing from _handleSocketError. This caused the "live" driver's select() to crash, which propagagated to its run(), which caused the driver to be marked as dead, etc. Eventually, all drivers could die, and we end up with the dreadful "Schedule is the only remaining driver, why do we continue to live?" in an infinite loop. --- src/drivers/Socket.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/drivers/Socket.py b/src/drivers/Socket.py index 33e15dd68..a88cb46a0 100644 --- a/src/drivers/Socket.py +++ b/src/drivers/Socket.py @@ -114,6 +114,13 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin): except: pass self.connected = False + if self.irc is None: + # This driver is dead already, but we're still running because + # of select() running in an other driver's thread that started + # before this one died and stil holding a reference to this + # instance. + # Just return, and we should never be called again. + return self.scheduleReconnect() else: log.debug('Got EAGAIN, current count: %s.', self.eagains) @@ -208,6 +215,8 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin): msg = drivers.parseMsg(line) if msg is not None and self.irc is not None: + # self.irc may be None if this driver is already dead, + # see comment in _handleSocketError self.irc.feedMsg(msg) except socket.timeout: pass