From 4d1d8e20315a1ac3e814742d79e4ced4257d1256 Mon Sep 17 00:00:00 2001 From: Ondrej Holy Date: Tue, 30 Mar 2021 15:36:45 +0200 Subject: client: Prevent socket leaks if socket dir is inaccessible GVfs fallbacks to session bus if it is not possible to establish peer-to-peer connection (e.g. inside Flatpak sandbox). However, the DBus server is not terminated and the socket is leaked. The named sockets are counted as open files, so it can easily lead to "Too many open files" errors. Let's fallback to the session bus immediately if the socket dir is not accessible to prevent the leaks. This should fix the most common case, when the sockets are leaked. Fixes: https://gitlab.gnome.org/GNOME/gvfs/-/issues/542 --- client/gvfsdaemondbus.c | 88 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 13 deletions(-) diff --git a/client/gvfsdaemondbus.c b/client/gvfsdaemondbus.c index f8deb097..f7b383a5 100644 --- a/client/gvfsdaemondbus.c +++ b/client/gvfsdaemondbus.c @@ -31,12 +31,14 @@ #include #include +#include #include #include "gvfsdaemondbus.h" #include #include #include +#include /* Extra vfs-specific data for GDBusConnections */ typedef struct { @@ -156,6 +158,7 @@ set_connection_for_async (GDBusConnection *connection, const char *dbus_id) typedef struct { char *dbus_id; + GVfsDBusDaemon *proxy; GDBusConnection *connection; GCancellable *cancellable; @@ -174,6 +177,7 @@ async_call_finish (AsyncDBusCall *async_call) async_call->io_error, async_call->callback_data); + g_clear_object (&async_call->proxy); g_clear_object (&async_call->connection); g_clear_object (&async_call->cancellable); g_clear_error (&async_call->io_error); @@ -260,32 +264,67 @@ async_get_connection_response (GVfsDBusDaemon *proxy, g_free (address1); } +static void +socket_dir_query_info_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + AsyncDBusCall *async_call = user_data; + g_autoptr (GFileInfo) socket_dir_info = NULL; + + socket_dir_info = g_file_query_info_finish (G_FILE (source_object), + res, + &async_call->io_error); + if (socket_dir_info == NULL || + !g_file_info_get_attribute_boolean (socket_dir_info, + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) + { + if (!async_call->io_error) + async_call->io_error = g_error_new_literal (G_IO_ERROR, + G_IO_ERROR_PERMISSION_DENIED, + _("Permission denied")); + + async_call_finish (async_call); + return; + } + + g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (async_call->proxy), G_VFS_DBUS_TIMEOUT_MSECS); + + gvfs_dbus_daemon_call_get_connection (async_call->proxy, + async_call->cancellable, + (GAsyncReadyCallback) async_get_connection_response, + async_call); +} + static void open_connection_async_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { - GVfsDBusDaemon *proxy; AsyncDBusCall *async_call = user_data; GError *error = NULL; - - proxy = gvfs_dbus_daemon_proxy_new_finish (res, &error); - if (proxy == NULL) + g_autofree gchar *socket_dir_path = NULL; + g_autoptr (GFile) socket_dir = NULL; + + async_call->proxy = gvfs_dbus_daemon_proxy_new_finish (res, &error); + if (async_call->proxy == NULL) { async_call->io_error = g_error_copy (error); g_error_free (error); async_call_finish (async_call); return; } - - g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (proxy), G_VFS_DBUS_TIMEOUT_MSECS); - - gvfs_dbus_daemon_call_get_connection (proxy, - async_call->cancellable, - (GAsyncReadyCallback) async_get_connection_response, - async_call); - - g_object_unref (proxy); + + /* This is needed to prevent socket leaks. */ + socket_dir_path = gvfs_get_socket_dir (); + socket_dir = g_file_new_for_path (socket_dir_path); + g_file_query_info_async (socket_dir, + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, + G_FILE_QUERY_INFO_NONE, + G_PRIORITY_DEFAULT, + async_call->cancellable, + socket_dir_query_info_cb, + user_data); } static void @@ -522,6 +561,9 @@ _g_dbus_connection_get_sync (const char *dbus_id, gchar *address1; GVfsDBusDaemon *daemon_proxy; gboolean res; + g_autofree gchar *socket_dir_path = NULL; + g_autoptr (GFile) socket_dir = NULL; + g_autoptr (GFileInfo) socket_dir_info = NULL; if (g_cancellable_set_error_if_cancelled (cancellable, error)) return NULL; @@ -591,6 +633,26 @@ _g_dbus_connection_get_sync (const char *dbus_id, if (daemon_proxy == NULL) return NULL; + /* This is needed to prevent socket leaks. */ + socket_dir_path = gvfs_get_socket_dir (); + socket_dir = g_file_new_for_path (socket_dir_path); + socket_dir_info = g_file_query_info (socket_dir, + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, + G_FILE_QUERY_INFO_NONE, + cancellable, + error); + if (socket_dir_info == NULL || + !g_file_info_get_attribute_boolean (socket_dir_info, + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) + { + if (error && !*error) + *error = g_error_new_literal (G_IO_ERROR, + G_IO_ERROR_PERMISSION_DENIED, + _("Permission denied")); + + return NULL; + } + address1 = NULL; res = gvfs_dbus_daemon_call_get_connection_sync (daemon_proxy, &address1, -- cgit v1.2.1