From 7cdf07d0a00ac7322971f9a96a7d5b9945cd50f6 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Thu, 16 Feb 2023 11:17:26 -0500 Subject: [PATCH] user-accounts: Don't access user before it's loaded libaccountsservice documents that it is undefined to access an ActUser object's properties before the 'is-loaded' property is set to try. Control-center unfortunately sometimes accesses objects that aren't fully loaded, leading to log spew and memory corruption. This commit fixes the problem by setting up a signal handler to listen for the is-loaded notification when necessary. --- panels/user-accounts/cc-user-panel.c | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/panels/user-accounts/cc-user-panel.c b/panels/user-accounts/cc-user-panel.c index 612bd21fa..e5b620d2c 100644 --- a/panels/user-accounts/cc-user-panel.c +++ b/panels/user-accounts/cc-user-panel.c @@ -72,60 +72,61 @@ struct _CcUserPanel { GtkSwitch *account_type_switch; GtkWidget *add_user_button; GtkListBoxRow *autologin_row; GtkSwitch *autologin_switch; GtkButton *back_button; CcListRow *fingerprint_row; GtkStack *full_name_stack; GtkLabel *full_name_label; GtkToggleButton *full_name_edit_button; GtkEntry *full_name_entry; CcListRow *language_row; CcListRow *last_login_row; GtkWidget *no_users_box; GtkRevealer *notification_revealer; AdwPreferencesGroup *other_users; GtkListBox *other_users_listbox; GtkLabel *password_button_label; #ifdef HAVE_MALCONTENT GtkLabel *parental_controls_button_label; GtkListBoxRow *parental_controls_row; #endif CcListRow *password_row; CcPermissionInfobar *permission_infobar; GtkButton *remove_user_button; GtkStack *stack; AdwAvatar *user_avatar; GtkMenuButton *user_avatar_edit_button; GtkOverlay *users_overlay; ActUser *selected_user; + ActUser *pending_show_user; GPermission *permission; CcLanguageChooser *language_chooser; GListStore *other_users_model; GtkFlattenListModel *other_users_flat_model; CcAvatarChooser *avatar_chooser; CcFingerprintManager *fingerprint_manager; }; CC_PANEL_REGISTER (CcUserPanel, cc_user_panel) static void show_restart_notification (CcUserPanel *self, const gchar *locale); typedef struct { CcUserPanel *self; GCancellable *cancellable; gchar *login; } AsyncDeleteData; static void async_delete_data_free (AsyncDeleteData *data) { g_clear_object (&data->self); g_clear_object (&data->cancellable); g_clear_pointer (&data->login, g_free); g_slice_free (AsyncDeleteData, data); } G_DEFINE_AUTOPTR_CLEANUP_FUNC (AsyncDeleteData, async_delete_data_free) @@ -806,71 +807,100 @@ update_fingerprint_row_state (CcUserPanel *self, GParamSpec *spec, CcFingerprint { CcFingerprintState state = cc_fingerprint_manager_get_state (fingerprint_manager); if (state != CC_FINGERPRINT_STATE_UPDATING) { gtk_widget_set_visible (GTK_WIDGET (self->fingerprint_row), state != CC_FINGERPRINT_STATE_NONE); } gtk_widget_set_sensitive (GTK_WIDGET (self->fingerprint_row), state != CC_FINGERPRINT_STATE_UPDATING); if (state == CC_FINGERPRINT_STATE_ENABLED) cc_list_row_set_secondary_label (self->fingerprint_row, _("Enabled")); else if (state == CC_FINGERPRINT_STATE_DISABLED) cc_list_row_set_secondary_label (self->fingerprint_row, _("Disabled")); } static void show_or_hide_back_button (CcUserPanel *self) { gboolean show; gboolean folded; g_object_get(self, "folded", &folded, NULL); show = folded || act_user_get_uid (self->selected_user) != getuid(); gtk_widget_set_visible (GTK_WIDGET (self->back_button), show); } +static void +on_pending_show_user_is_loaded (ActUser *user, + GParamSpec *param, + CcUserPanel *self) +{ + if (!act_user_is_loaded (user)) { + return; + } + + show_user (user, self); +} + static void show_user (ActUser *user, CcUserPanel *self) { g_autofree gchar *lang = NULL; g_autofree gchar *name = NULL; gboolean show, enable; ActUser *current; #ifdef HAVE_MALCONTENT g_autofree gchar *malcontent_control_path = NULL; #endif + if (self->pending_show_user != NULL) { + g_signal_handlers_disconnect_by_func (G_OBJECT (self->pending_show_user), + on_pending_show_user_is_loaded, + self); + g_clear_object (&self->pending_show_user); + } + + if (!act_user_is_loaded (user)) { + g_set_object (&self->pending_show_user, user); + g_signal_connect_object (G_OBJECT (self->pending_show_user), + "notify::is-loaded", + G_CALLBACK (on_pending_show_user_is_loaded), + self, + 0); + return; + } + g_set_object (&self->selected_user, user); setup_avatar_for_user (self->user_avatar, user); cc_avatar_chooser_set_user (self->avatar_chooser, user); gtk_label_set_label (self->full_name_label, get_real_or_user_name (user)); gtk_editable_set_text (GTK_EDITABLE (self->full_name_entry), gtk_label_get_label (self->full_name_label)); gtk_widget_set_tooltip_text (GTK_WIDGET (self->full_name_label), get_real_or_user_name (user)); g_signal_handlers_block_by_func (self->full_name_edit_button, full_name_edit_button_toggled, self); gtk_stack_set_visible_child (self->full_name_stack, GTK_WIDGET (self->full_name_label)); gtk_toggle_button_set_active (self->full_name_edit_button, FALSE); g_signal_handlers_unblock_by_func (self->full_name_edit_button, full_name_edit_button_toggled, self); enable = (act_user_get_account_type (user) == ACT_USER_ACCOUNT_TYPE_ADMINISTRATOR); gtk_switch_set_active (self->account_type_switch, enable); gtk_label_set_label (self->password_button_label, get_password_mode_text (user)); enable = act_user_is_local_account (user); gtk_widget_set_sensitive (GTK_WIDGET (self->password_button_label), enable); g_signal_handlers_block_by_func (self->autologin_switch, autologin_changed, self); gtk_switch_set_active (self->autologin_switch, act_user_get_automatic_login (user)); g_signal_handlers_unblock_by_func (self->autologin_switch, autologin_changed, self); gtk_widget_set_sensitive (GTK_WIDGET (self->autologin_switch), get_autologin_possible (user)); lang = g_strdup (act_user_get_language (user)); if (lang && *lang != '\0') { name = gnome_get_language_from_locale (lang, NULL); } else { @@ -1508,60 +1538,61 @@ cc_user_panel_init (CcUserPanel *self) /* register types that the builder might need */ type = cc_permission_infobar_get_type (); gtk_widget_init_template (GTK_WIDGET (self)); self->um = act_user_manager_get_default (); provider = gtk_css_provider_new (); gtk_css_provider_load_from_resource (provider, "/org/gnome/control-center/user-accounts/user-accounts-dialog.css"); gtk_style_context_add_provider_for_display (gdk_display_get_default (), GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); self->login_screen_settings = settings_or_null ("org.gnome.login-screen"); setup_main_window (self); g_signal_connect_swapped (self, "notify::folded", G_CALLBACK (show_or_hide_back_button), self); } static void cc_user_panel_dispose (GObject *object) { CcUserPanel *self = CC_USER_PANEL (object); g_clear_object (&self->selected_user); + g_clear_object (&self->pending_show_user); g_clear_object (&self->login_screen_settings); g_clear_pointer ((GtkWindow **)&self->language_chooser, gtk_window_destroy); g_clear_object (&self->permission); G_OBJECT_CLASS (cc_user_panel_parent_class)->dispose (object); } static const char * cc_user_panel_get_help_uri (CcPanel *panel) { return "help:gnome-help/user-accounts"; } static void cc_user_panel_class_init (CcUserPanelClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); CcPanelClass *panel_class = CC_PANEL_CLASS (klass); object_class->dispose = cc_user_panel_dispose; object_class->constructed = cc_user_panel_constructed; panel_class->get_help_uri = cc_user_panel_get_help_uri; gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/user-accounts/cc-user-panel.ui"); gtk_widget_class_bind_template_child (widget_class, CcUserPanel, account_settings_box); gtk_widget_class_bind_template_child (widget_class, CcUserPanel, account_type_row); gtk_widget_class_bind_template_child (widget_class, CcUserPanel, account_type_switch); -- 2.39.2