Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH BlueZ] obexd: Reference count the phonebook back-end setup and teardown
@ 2026-08-17 15:48 Paul Menzel
  2026-08-17 17:12 ` [BlueZ] " bluez.test.bot
  0 siblings, 1 reply; 2+ messages in thread
From: Paul Menzel @ 2026-08-17 15:48 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Paul Menzel

Stopping obexd on Debian sid/unstable with *bluez* 5.87-1 logs a GObject
critical:

    obexd[10687]: Terminating
    systemd[1804]: Stopping obex.service - Bluetooth OBEX service...
    obexd[10687]: g_object_unref: assertion 'G_IS_OBJECT (object)' failed
    systemd[1804]: Stopped obex.service - Bluetooth OBEX service.

Two builtin plugins use the phonebook back-end: pbap (pbap_init() at
obexd/plugins/pbap.c:962, pbap_exit() at :1002) and irmc (irmc_init() at
obexd/plugins/irmc.c:446, irmc_exit() at :473). Each calls phonebook_init()
when it is loaded and phonebook_exit() when it is unloaded, but neither the
callers nor the back-end track ownership of the singleton they share.
plugin_init() therefore sets the back-end up twice and plugin_cleanup()
tears it down twice. A gdb trace of the shutdown path confirms both pairs
of calls.

The dummy back-end tolerates this by accident: phonebook_init() bails out
early when root_folder is already set, and the second phonebook_exit() only
repeats a g_free()/NULL assignment. The ebook back-end, which Debian builds
(*bluez-obexd* depends on *libebook-1.2* and *libedataserver*), does not.
Its phonebook_init() stores three GObject references in static variables
and phonebook_exit() unconditionally drops all three, so the second
teardown unrefs objects that were already finalized. The registry and the
address book are effectively singletons in evolution-data-server and merely
gain a second reference, but e_book_client_connect_sync() hands back a
fresh client on every call, so the first client leaks and the second one is
unreffed twice – hence a single critical rather than three.

Put the ownership tracking in one place instead of duplicating it in every
back-end: phonebook_init() and phonebook_exit() now live in a new shared
obexd/plugins/phonebook.c and reference count the back-end, so only the
first init and the last exit reach it. The back-end entry points are
renamed to phonebook_driver_init()/phonebook_driver_exit() so that they
cannot be called directly by mistake.

Assisted-by: Claude Code:claude-opus-5
---
 Makefile.obexd                    |  1 +
 obexd/plugins/phonebook-dummy.c   |  4 +--
 obexd/plugins/phonebook-ebook.c   |  4 +--
 obexd/plugins/phonebook-tracker.c |  4 +--
 obexd/plugins/phonebook.c         | 47 +++++++++++++++++++++++++++++++
 obexd/plugins/phonebook.h         | 10 +++++++
 6 files changed, 64 insertions(+), 6 deletions(-)
 create mode 100644 obexd/plugins/phonebook.c

diff --git a/Makefile.obexd b/Makefile.obexd
index 7ad74e1..9daa5aa 100644
--- a/Makefile.obexd
+++ b/Makefile.obexd
@@ -48,6 +48,7 @@ obexd_builtin_modules += pbap
 obexd_builtin_sources += obexd/plugins/pbap.c \
 				obexd/plugins/vcard.h obexd/plugins/vcard.c \
 				obexd/plugins/phonebook.h \
+				obexd/plugins/phonebook.c \
 				obexd/plugins/phonebook-@PLUGIN_PHONEBOOK@.c
 EXTRA_DIST += obexd/plugins/phonebook-dummy.c obexd/plugins/phonebook-ebook.c \
 				obexd/plugins/phonebook-tracker.c
diff --git a/obexd/plugins/phonebook-dummy.c b/obexd/plugins/phonebook-dummy.c
index 0dce13f..f308ea7 100644
--- a/obexd/plugins/phonebook-dummy.c
+++ b/obexd/plugins/phonebook-dummy.c
@@ -73,7 +73,7 @@ static void query_free(void *user_data)
 	g_free(query);
 }
 
-int phonebook_init(void)
+int phonebook_driver_init(void)
 {
 	if (root_folder)
 		return 0;
@@ -84,7 +84,7 @@ int phonebook_init(void)
 	return 0;
 }
 
-void phonebook_exit(void)
+void phonebook_driver_exit(void)
 {
 	g_free(root_folder);
 	root_folder = NULL;
diff --git a/obexd/plugins/phonebook-ebook.c b/obexd/plugins/phonebook-ebook.c
index 5fc0498..beb72b2 100644
--- a/obexd/plugins/phonebook-ebook.c
+++ b/obexd/plugins/phonebook-ebook.c
@@ -625,7 +625,7 @@ next:
 	return data;
 }
 
-int phonebook_init(void)
+int phonebook_driver_init(void)
 {
 	EClient *client;
 	GError *gerr = NULL;
@@ -662,7 +662,7 @@ int phonebook_init(void)
 	return 0;
 }
 
-void phonebook_exit(void)
+void phonebook_driver_exit(void)
 {
 	g_object_unref(book_client);
 	g_object_unref(address_book);
diff --git a/obexd/plugins/phonebook-tracker.c b/obexd/plugins/phonebook-tracker.c
index eb7a84f..36e73db 100644
--- a/obexd/plugins/phonebook-tracker.c
+++ b/obexd/plugins/phonebook-tracker.c
@@ -1420,14 +1420,14 @@ done:
 	 */
 }
 
-int phonebook_init(void)
+int phonebook_driver_init(void)
 {
 	g_type_init();
 
 	return 0;
 }
 
-void phonebook_exit(void)
+void phonebook_driver_exit(void)
 {
 }
 
diff --git a/obexd/plugins/phonebook.c b/obexd/plugins/phonebook.c
new file mode 100644
index 0000000..6afdb7a
--- /dev/null
+++ b/obexd/plugins/phonebook.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+
+#include <glib.h>
+
+#include "phonebook.h"
+
+static unsigned int refcount = 0;
+
+int phonebook_init(void)
+{
+	int err;
+
+	if (refcount > 0) {
+		refcount++;
+		return 0;
+	}
+
+	err = phonebook_driver_init();
+	if (err < 0)
+		return err;
+
+	refcount = 1;
+
+	return 0;
+}
+
+void phonebook_exit(void)
+{
+	if (refcount == 0 || --refcount > 0)
+		return;
+
+	phonebook_driver_exit();
+}
diff --git a/obexd/plugins/phonebook.h b/obexd/plugins/phonebook.h
index c73ae73..b878b14 100644
--- a/obexd/plugins/phonebook.h
+++ b/obexd/plugins/phonebook.h
@@ -79,9 +79,19 @@ typedef void (*phonebook_entry_cb) (const char *id, uint32_t handle,
 typedef void (*phonebook_cache_ready_cb) (void *user_data);
 
 
+/*
+ * Set up and tear down the phonebook back-end. The back-end is shared by
+ * the pbap and the irmc plugin, which are loaded and unloaded independently
+ * of each other, so the calls are reference counted: only the first
+ * phonebook_init() and the last phonebook_exit() reach the back-end.
+ */
 int phonebook_init(void);
 void phonebook_exit(void);
 
+/* Implemented by the back-end, only called through the pair above. */
+int phonebook_driver_init(void);
+void phonebook_driver_exit(void);
+
 /*
  * Changes the current folder in the phonebook back-end. The PBAP core
  * doesn't validate or restrict the possible values for the folders,
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-08-17 17:12 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-17 15:48 [PATCH BlueZ] obexd: Reference count the phonebook back-end setup and teardown Paul Menzel
2026-08-17 17:12 ` [BlueZ] " bluez.test.bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox