From 246773ed076e77bafe7106db5e13ea7a37fb6d72 Mon Sep 17 00:00:00 2001 From: Alban Crequy Date: Thu, 7 Aug 2014 15:39:56 +0100 Subject: [PATCH 2/3] enforce new limit max_connections_per_process https://bugs.freedesktop.org/show_bug.cgi?id=82346 --- bus/connection.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 118 insertions(+), 5 deletions(-) diff --git a/bus/connection.c b/bus/connection.c index ea2d155..4c19c8c 100644 --- a/bus/connection.c +++ b/bus/connection.c @@ -59,6 +59,7 @@ struct BusConnections int n_incomplete; /**< Length of incomplete list */ BusContext *context; DBusHashTable *completed_by_user; /**< Number of completed connections for each UID */ + DBusHashTable *completed_by_process; /**< Number of completed connections for each pid */ DBusTimeout *expire_timeout; /**< Timeout for expiring incomplete connections. */ int stamp; /**< Incrementing number */ BusExpireList *pending_replies; /**< List of pending replies */ @@ -90,6 +91,7 @@ typedef struct DBusMessage *oom_message; DBusPreallocatedSend *oom_preallocated; BusClientPolicy *policy; + unsigned long pid; char *cached_loginfo_string; BusSELinuxID *selinux_id; @@ -143,6 +145,23 @@ get_connections_for_uid (BusConnections *connections, return current_count; } +static int +get_connections_for_pid (BusConnections *connections, + dbus_pid_t pid) +{ + void *val; + int current_count; + + /* val is NULL is 0 when it isn't in the hash yet */ + + val = _dbus_hash_table_lookup_uintptr (connections->completed_by_process, + pid); + + current_count = _DBUS_POINTER_TO_INT (val); + + return current_count; +} + static dbus_bool_t adjust_connections_for_uid (BusConnections *connections, dbus_uid_t uid, @@ -184,6 +203,47 @@ adjust_connections_for_uid (BusConnections *connections, } } +static dbus_bool_t +adjust_connections_for_pid (BusConnections *connections, + dbus_pid_t pid, + int adjustment) +{ + int current_count; + + current_count = get_connections_for_pid (connections, pid); + + _dbus_verbose ("Adjusting connection count for PID " DBUS_PID_FORMAT + ": was %d adjustment %d making %d\n", + pid, current_count, adjustment, current_count + adjustment); + + _dbus_assert (current_count >= 0); + + current_count += adjustment; + + _dbus_assert (current_count >= 0); + + if (current_count == 0) + { + _dbus_hash_table_remove_uintptr (connections->completed_by_process, pid); + return TRUE; + } + else + { + dbus_bool_t retval; + + retval = _dbus_hash_table_insert_uintptr (connections->completed_by_process, + pid, _DBUS_INT_TO_POINTER (current_count)); + + /* only positive adjustment can fail as otherwise + * a hash entry should already exist + */ + _dbus_assert (adjustment > 0 || + (adjustment <= 0 && retval)); + + return retval; + } +} + void bus_connection_disconnected (DBusConnection *connection) { @@ -287,6 +347,12 @@ bus_connection_disconnected (DBusConnection *connection) uid, -1)) _dbus_assert_not_reached ("adjusting downward should never fail"); } + if (d->pid > 0) + { + if (!adjust_connections_for_pid (d->connections, + d->pid, -1)) + _dbus_assert_not_reached ("adjusting downward should never fail"); + } } else { @@ -429,11 +495,16 @@ bus_connections_new (BusContext *context) if (connections->completed_by_user == NULL) goto failed_2; + connections->completed_by_process = _dbus_hash_table_new (DBUS_HASH_UINTPTR, + NULL, NULL); + if (connections->completed_by_process == NULL) + goto failed_3; + connections->expire_timeout = _dbus_timeout_new (100, /* irrelevant */ expire_incomplete_timeout, connections, NULL); if (connections->expire_timeout == NULL) - goto failed_3; + goto failed_4; _dbus_timeout_set_enabled (connections->expire_timeout, FALSE); @@ -442,21 +513,23 @@ bus_connections_new (BusContext *context) bus_pending_reply_expired, connections); if (connections->pending_replies == NULL) - goto failed_4; + goto failed_5; if (!_dbus_loop_add_timeout (bus_context_get_loop (context), connections->expire_timeout)) - goto failed_5; + goto failed_6; connections->refcount = 1; connections->context = context; return connections; - failed_5: + failed_6: bus_expire_list_free (connections->pending_replies); - failed_4: + failed_5: _dbus_timeout_unref (connections->expire_timeout); + failed_4: + _dbus_hash_table_unref (connections->completed_by_process); failed_3: _dbus_hash_table_unref (connections->completed_by_user); failed_2: @@ -521,6 +594,7 @@ bus_connections_unref (BusConnections *connections) _dbus_timeout_unref (connections->expire_timeout); _dbus_hash_table_unref (connections->completed_by_user); + _dbus_hash_table_unref (connections->completed_by_process); dbus_free (connections); @@ -1358,6 +1432,8 @@ bus_connection_complete (DBusConnection *connection, { BusConnectionData *d; unsigned long uid; + dbus_bool_t rollback_uid = FALSE; + dbus_bool_t rollback_pid = FALSE; d = BUS_CONNECTION_DATA (connection); _dbus_assert (d != NULL); @@ -1400,6 +1476,14 @@ bus_connection_complete (DBusConnection *connection, if (!adjust_connections_for_uid (d->connections, uid, 1)) goto fail; + rollback_uid = TRUE; + } + + if (d->pid > 0) + { + if (!adjust_connections_for_pid (d->connections, d->pid, 1)) + goto fail; + rollback_pid = TRUE; } /* Create and cache a string which holds information about the @@ -1427,6 +1511,10 @@ bus_connection_complete (DBusConnection *connection, return TRUE; fail: BUS_SET_OOM (error); + if (rollback_uid) + adjust_connections_for_uid (d->connections, uid, -1); + if (rollback_pid) + adjust_connections_for_pid (d->connections, d->pid, -1); dbus_free (d->name); d->name = NULL; if (d->policy) @@ -1492,6 +1580,11 @@ bus_connections_check_limits (BusConnections *connections, DBusError *error) { unsigned long uid; + unsigned long pid; + BusConnectionData *d; + + d = BUS_CONNECTION_DATA (requesting_completion); + _dbus_assert (d != NULL); if (connections->n_completed >= bus_context_get_max_completed_connections (connections->context)) @@ -1513,6 +1606,26 @@ bus_connections_check_limits (BusConnections *connections, } } + /* bus_connections_check_limits() could be called several times on the same + * connection. Make sure to be idempotent. */ + if (d->pid == 0 && + dbus_connection_get_unix_process_id (requesting_completion, &pid)) + { + d->pid = pid; + } + + if (d->pid > 0) + { + if (get_connections_for_pid (connections, d->pid) >= + bus_context_get_max_connections_per_process (connections->context)) + { + dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED, + "The maximum number of active connections for PID %lu has been reached", + d->pid); + return FALSE; + } + } + return TRUE; } -- 1.8.5.3