diff mbox series

[kirkstone,2.0,8/8] asyncrpc/client: Fix unix domain socket chdir race issues

Message ID 6603c3e39f1cf746669ec6c9f0be8c6e6ece426e.1664898736.git.steve@sakoman.com
State Accepted, archived
Commit 6603c3e39f1cf746669ec6c9f0be8c6e6ece426e
Headers show
Series [kirkstone,2.0,1/8] runqueue: Ensure deferred tasks are sorted by multiconfig | expand

Commit Message

Steve Sakoman Oct. 4, 2022, 3:54 p.m. UTC
From: Richard Purdie <richard.purdie@linuxfoundation.org>

The connect_unix() call had a bug where if a relative path to a socket
was passed (which the non-async client always does), and the current
working directory was changed after the initial call, it would fail to
reconnect if it became disconnected, since the socket couldn't be found
relative to the new current working directory.

To work around this, change the socket connection for UNIX domain
sockets to be synchronous and change current working before connecting.
This isn't ideal since the connection could block the entire event loop,
but in practice this shouldn't happen since the socket are local files
anyway.

Help debugging and resolving from Joshua Watt.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
(cherry picked from commit 5964bb67bb20df7f411ee0650cf189504a05cf25)
Signed-off-by: Steve Sakoman <steve@sakoman.com>
---
 lib/bb/asyncrpc/client.py | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)
diff mbox series

Patch

diff --git a/lib/bb/asyncrpc/client.py b/lib/bb/asyncrpc/client.py
index 881434d2..fa042bbe 100644
--- a/lib/bb/asyncrpc/client.py
+++ b/lib/bb/asyncrpc/client.py
@@ -31,7 +31,17 @@  class AsyncClient(object):
 
     async def connect_unix(self, path):
         async def connect_sock():
-            return await asyncio.open_unix_connection(path)
+            # AF_UNIX has path length issues so chdir here to workaround
+            cwd = os.getcwd()
+            try:
+                os.chdir(os.path.dirname(path))
+                # The socket must be opened synchronously so that CWD doesn't get
+                # changed out from underneath us so we pass as a sock into asyncio
+                sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+                sock.connect(os.path.basename(path))
+            finally:
+               os.chdir(cwd)
+            return await asyncio.open_unix_connection(sock=sock)
 
         self._connect_sock = connect_sock
 
@@ -150,14 +160,8 @@  class Client(object):
             setattr(self, m, self._get_downcall_wrapper(downcall))
 
     def connect_unix(self, path):
-        # AF_UNIX has path length issues so chdir here to workaround
-        cwd = os.getcwd()
-        try:
-            os.chdir(os.path.dirname(path))
-            self.loop.run_until_complete(self.client.connect_unix(os.path.basename(path)))
-            self.loop.run_until_complete(self.client.connect())
-        finally:
-            os.chdir(cwd)
+        self.loop.run_until_complete(self.client.connect_unix(path))
+        self.loop.run_until_complete(self.client.connect())
 
     @property
     def max_chunk(self):