plugins/admin: make AdminPolicy state per-adapter
PATCH 1/7 plugins/admin: make AdminPolicy state per-adapter
Fix AdminPolicy D-Bus updates being emitted on the wrong adapter path by removing the single global policy context and moving to per-adapter policy objects.
Changes include:
- track policy contexts in a policy queue keyed by adapter pointer
- keep per-adapter device lists inside each policy context
- emit ServiceAllowList changes using the callback's adapter context
- scope device affected updates to the current adapter only
- clean up probe/remove lifecycle so adapters are registered and torn down independently
- remove remaining global policy_data/devices coupling
--- plugins/admin.c | 130 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 99 insertions(+), 31 deletions(-)
plugins/admin.c +99 -31
@@ -39,21 +39,45 @@#define BTD_DEVICE_INTERFACE "org.bluez.Device1"static DBusConnection *dbus_conn; -static struct queue *devices;/* List of struct device_data objects */ +static struct queue *policies;/* List of struct btd_admin_policy objects */ -/* |policy_data| has the same life cycle as btd_adapter */ -static struct btd_admin_policy { +/* One policy context per adapter */ +struct btd_admin_policy {struct btd_adapter *adapter;uint16_t adapter_id;struct queue *service_allowlist; -} *policy_data =NULL ; +struct queue *devices; +};struct device_data {struct btd_device *device;char *path;bool affected; +struct btd_admin_policy *policy; }; +static void free_device_data (void *data); +static void unregister_device_data (void *data,void *user_data); + +static bool policy_match_adapter (const void *data,const void *match_data) +{ +const struct btd_admin_policy *policy = data; +const struct btd_adapter *adapter = match_data; + +if (!policy) +return false; + +return policy->adapter == adapter; +} + +static struct btd_admin_policy *admin_policy_find (struct btd_adapter *adapter) +{ +if (!policies) +return NULL ; + +return queue_find (policies, policy_match_adapter, adapter); +} +static struct btd_admin_policy *admin_policy_new (struct btd_adapter *adapter) {struct btd_admin_policy *admin_policy =NULL ;
@@ -68,6 +92,16 @@ static struct btd_admin_policy *admin_policy_new(struct btd_adapter *adapter) admin_policy->adapter = adapter; admin_policy->adapter_id =btd_adapter_get_index (adapter); admin_policy->service_allowlist =queue_new (); + admin_policy->devices =queue_new (); + +if (!admin_policy->service_allowlist || !admin_policy->devices) { +queue_destroy (admin_policy->service_allowlist, free); +queue_destroy (admin_policy->devices,NULL ); +g_free (admin_policy); +btd_error (btd_adapter_get_index (adapter), +"Failed to allocate queues for admin_policy" ); +return NULL ; + }return admin_policy; }
@@ -82,6 +116,7 @@ static void admin_policy_free(void *data)struct btd_admin_policy *admin_policy = data;free_service_allowlist (admin_policy->service_allowlist); +queue_destroy (admin_policy->devices, free_device_data);g_free (admin_policy); }
@@ -89,6 +124,8 @@ static void admin_policy_destroy(struct btd_admin_policy *admin_policy) {const char *path =adapter_get_path (admin_policy->adapter); +queue_foreach (admin_policy->devices, unregister_device_data,NULL ); +g_dbus_unregister_interface (dbus_conn, path,ADMIN_POLICY_SET_INTERFACE ); g_dbus_unregister_interface(dbus_conn, path,
@@ -346,7 +383,7 @@ static void load_policy_settings(struct btd_admin_policy *admin_policy) btd_adapter_get_storage_dir(admin_policy->adapter));if (stat (filename, &st) <0 ) -store_policy_settings (policy_data); +store_policy_settings (admin_policy); key_file =g_key_file_new ();
@@ -387,11 +424,11 @@ static DBusMessage *set_service_allowlist(DBusConnection *conn, }g_dbus_emit_property_changed (dbus_conn, -adapter_get_path (policy_data->adapter), +adapter_get_path (admin_policy->adapter),ADMIN_POLICY_STATUS_INTERFACE ,"ServiceAllowList" ); -queue_foreach (devices, update_device_affected,NULL ); +queue_foreach (admin_policy->devices, update_device_affected,NULL );return dbus_message_new_method_return (msg); }
@@ -488,66 +525,86 @@ static void remove_device_data(void *data)DBG ("device_data for %s removing" , device_data->path); -queue_remove (devices, device_data); +if (device_data->policy) +queue_remove (device_data->policy->devices, device_data); +free_device_data (device_data); }static int admin_policy_adapter_probe (struct btd_adapter *adapter) { +struct btd_admin_policy *policy;const char *adapter_path; -if (!devices) - devices =queue_new (); +if (!policies) + policies =queue_new (); -if (policy_data) { -btd_warn (policy_data->adapter_id, +if (!policies) +return -ENOMEM ; + +if (admin_policy_find (adapter)) { +btd_warn (btd_adapter_get_index (adapter),"Policy data already exists" ); - policy_data =NULL ; +return -EALREADY ; } - policy_data =admin_policy_new (adapter); -if (!policy_data) + policy =admin_policy_new (adapter); +if (!policy)return -ENOMEM ; -load_policy_settings (policy_data); +load_policy_settings (policy); adapter_path =adapter_get_path (adapter);if (!g_dbus_register_interface (dbus_conn, adapter_path,ADMIN_POLICY_SET_INTERFACE , admin_policy_adapter_methods,NULL , -NULL , policy_data,NULL )) { -btd_error (policy_data->adapter_id, +NULL , policy,NULL )) { +btd_error (policy->adapter_id,"Admin Policy Set interface init failed on path %s" , adapter_path); +admin_policy_free (policy);return -EINVAL ; } -btd_info (policy_data->adapter_id, +btd_info (policy->adapter_id,"Admin Policy Set interface registered" );if (!g_dbus_register_interface (dbus_conn, adapter_path,ADMIN_POLICY_STATUS_INTERFACE ,NULL ,NULL , admin_policy_adapter_properties, - policy_data,NULL )) { -btd_error (policy_data->adapter_id, + policy,NULL )) { +btd_error (policy->adapter_id,"Admin Policy Status interface init failed on path %s" , adapter_path); +g_dbus_unregister_interface (dbus_conn, adapter_path, +ADMIN_POLICY_SET_INTERFACE ); +admin_policy_free (policy);return -EINVAL ; } -btd_info (policy_data->adapter_id, +btd_info (policy->adapter_id,"Admin Policy Status interface registered" ); +queue_push_tail (policies, policy); +return 0 ; }static void admin_policy_device_added (struct btd_adapter *adapter,struct btd_device *device) { +struct btd_admin_policy *policy;struct device_data *data; -if (queue_find (devices, device_data_match, device)) + policy =admin_policy_find (adapter); +if (!policy) { +btd_warn (btd_adapter_get_index (adapter), +"Policy data not found for adapter" ); +return ; + } + +if (queue_find (policy->devices, device_data_match, device))return ; data = g_new0(struct device_data, 1);
@@ -560,6 +617,7 @@ static void admin_policy_device_added(struct btd_adapter *adapter, data->device = device; data->path =g_strdup (device_get_path (device)); data->affected = !btd_device_all_services_allowed (data->device); + data->policy = policy; if (!g_dbus_register_interface(dbus_conn, data->path, ADMIN_POLICY_STATUS_INTERFACE,
@@ -573,7 +631,7 @@ static void admin_policy_device_added(struct btd_adapter *adapter,return ; } -queue_push_tail (devices, data); +queue_push_tail (policy->devices, data);DBG ("device_data for %s added" , data->path); }
@@ -589,9 +647,14 @@ static void unregister_device_data(void *data, void *user_data)static void admin_policy_device_removed (struct btd_adapter *adapter,struct btd_device *device) { +struct btd_admin_policy *policy;struct device_data *data; - data =queue_find (devices, device_data_match, device); + policy =admin_policy_find (adapter); +if (!policy) +return ; + + data =queue_find (policy->devices, device_data_match, device);if (data)unregister_device_data (data,NULL );
@@ -599,15 +662,20 @@ static void admin_policy_device_removed(struct btd_adapter *adapter,static void admin_policy_remove (struct btd_adapter *adapter) { +struct btd_admin_policy *policy; +DBG ("" ); -queue_foreach (devices, unregister_device_data,NULL ); -queue_destroy (devices, g_free); - devices =NULL ; + policy =admin_policy_find (adapter); +if (!policy) +return ; + +queue_remove (policies, policy); +admin_policy_destroy (policy); -if (policy_data) { -admin_policy_destroy (policy_data); - policy_data =NULL ; +if (!queue_length (policies)) { +queue_destroy (policies,NULL ); + policies =NULL ; } }
Signature · 1 line
2.43.0
PATCH 2/7 client/bluetoothctl: make admin.allow controller-aware
Teach admin.allow to target the selected default controller when no controller is provided, and to accept an explicit [ctrl] argument.
Replace single cached AdminPolicy proxies with per-controller proxy lookup keyed by controller object path, so controller selection changes are respected.
Export controller lookup/default helpers and shared controller completion from main.c for reuse by admin.c.
--- client/admin.c | 114 ++++++++++++++++++++++++++++++++++++++----------- client/admin.h | 4 ++ client/main.c | 25 +++++++++-- 3 files changed, 116 insertions(+), 27 deletions(-)
client/admin.c +90 -24
@@ -16,6 +16,7 @@#include <string.h> #include <stdlib.h> +#include "bluetooth/bluetooth.h" #include "gdbus/gdbus.h" #include "src/shared/shell.h"
@@ -23,27 +24,79 @@#define _GNU_SOURCEstatic DBusConnection *dbus_conn; -static GList *admin_proxies; -static GDBusProxy *set_proxy; -static GDBusProxy *status_proxy; +static GList *set_proxies; +static GList *status_proxies;static void admin_menu_pre_run (const struct bt_shell_menu *menu); -static void admin_policy_set_set_proxy (GDBusProxy *proxy) +static GDBusProxy *admin_policy_find_proxy (GList *proxies, +const char *path) { - set_proxy = proxy; +GList *list; + +for (list =g_list_first (proxies); list; list =g_list_next (list)) { +GDBusProxy *proxy = list->data; + +if (!strcmp (g_dbus_proxy_get_path (proxy), path)) +return proxy; + } + +return NULL ; +} + +static GDBusProxy *admin_policy_get_status_proxy (const char *controller_path) +{ +if (!controller_path) +return NULL ; + +return admin_policy_find_proxy (status_proxies, controller_path); +} + +static GDBusProxy *admin_policy_get_set_proxy (const char *controller_path) +{ +if (!controller_path) +return NULL ; + +return admin_policy_find_proxy (set_proxies, controller_path); } -static void admin_policy_set_status_proxy (GDBusProxy *proxy) +static GDBusProxy *admin_policy_get_controller (int argc,char *argv[], +int *arg_index) { - status_proxy = proxy; +GDBusProxy *controller; + + *arg_index =1 ; + +if (argc >1 &&strlen (argv[1 ])) { + controller =bluetoothctl_find_controller (argv[1 ]); +if (controller) { + *arg_index =2 ; +return controller; + } + +if (bachk (argv[1 ]) ==0 ) { +bt_shell_printf ("Controller %s not available\n" , + argv[1 ]); +return NULL ; + } + } + + controller =bluetoothctl_get_default_controller (); +if (controller) +return controller; + +bt_shell_printf ("No default controller available\n" ); +return NULL ; } -static void admin_policy_read_service_allowlist (DBusConnection *dbus_conn) +static void admin_policy_read_service_allowlist (GDBusProxy *controller) {DBusMessageIter iter, subiter; +GDBusProxy *status_proxy;char *uuid =NULL ; +const char *controller_path =g_dbus_proxy_get_path (controller); + status_proxy =admin_policy_get_status_proxy (controller_path);if (!status_proxy || !g_dbus_proxy_get_property (status_proxy,"ServiceAllowList" , &iter)) {bt_shell_printf ("Failed to get property\n" );
@@ -106,10 +159,14 @@ static void set_service_reply(DBusMessage *message, void *user_data)return bt_shell_noninteractive_quit (EXIT_FAILURE ); } -static void admin_policy_set_service_allowlist (int argc,char *argv[]) +static void admin_policy_set_service_allowlist (GDBusProxy *controller, +int argc,char *argv[]) {struct uuid_list_data data; +GDBusProxy *set_proxy; +const char *controller_path =g_dbus_proxy_get_path (controller); + set_proxy =admin_policy_get_set_proxy (controller_path);if (!set_proxy) {bt_shell_printf ("Set proxy not ready\n" );return bt_shell_noninteractive_quit (EXIT_FAILURE );
@@ -128,15 +185,23 @@ static void admin_policy_set_service_allowlist(int argc, char *argv[])static void cmd_admin_allow (int argc,char *argv[]) { -if (argc <=1 ) { -admin_policy_read_service_allowlist (dbus_conn); +GDBusProxy *controller; +int arg_index; + + controller =admin_policy_get_controller (argc, argv, &arg_index); +if (!controller) +return bt_shell_noninteractive_quit (EXIT_FAILURE ); + +if (argc <= arg_index) { +admin_policy_read_service_allowlist (controller);return ; } -if (strcmp (argv[1 ],"clear" ) ==0 ) - argc--; +if (strcmp (argv[arg_index],"clear" ) ==0 ) + arg_index++; -admin_policy_set_service_allowlist (argc -1 , argv +1 ); +admin_policy_set_service_allowlist (controller, argc - arg_index, + argv + arg_index); }static const struct bt_shell_menu admin_menu = {
@@ -144,15 +209,15 @@ static const struct bt_shell_menu admin_menu = { .desc = "Admin Policy Submenu", .pre_run = admin_menu_pre_run, .entries = { - { "allow", "[clear/uuid1 uuid2 ...]", cmd_admin_allow, - "Allow service UUIDs and block rest of them"}, + { "allow", "[ctrl] [clear/uuid1 uuid2 ...]", cmd_admin_allow, + "Allow service UUIDs and block rest of them", + bluetoothctl_controller_generator}, {} }, }; static void admin_policy_status_added(GDBusProxy *proxy) { - admin_proxies = g_list_append(admin_proxies, proxy); - admin_policy_set_status_proxy(proxy); + status_proxies = g_list_append(status_proxies, proxy); } static void proxy_added(GDBusProxy *proxy, void *user_data)
@@ -162,15 +227,14 @@ static void proxy_added(GDBusProxy *proxy, void *user_data) interface =g_dbus_proxy_get_interface (proxy);if (!strcmp (interface,"org.bluez.AdminPolicySet1" )) -admin_policy_set_set_proxy (proxy); + set_proxies =g_list_append (set_proxies, proxy);else if (!strcmp (interface,"org.bluez.AdminPolicyStatus1" ))admin_policy_status_added (proxy); }static void admin_policy_status_removed (GDBusProxy *proxy) { - admin_proxies =g_list_remove (admin_proxies, proxy); -admin_policy_set_status_proxy (NULL ); + status_proxies =g_list_remove (status_proxies, proxy); }static void proxy_removed (GDBusProxy *proxy,void *user_data)
@@ -180,7 +244,7 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data) interface =g_dbus_proxy_get_interface (proxy);if (!strcmp (interface,"org.bluez.AdminPolicySet1" )) -admin_policy_set_set_proxy (NULL ); + set_proxies =g_list_remove (set_proxies, proxy);else if (!strcmp (interface,"org.bluez.AdminPolicyStatus1" ))admin_policy_status_removed (proxy); }
@@ -189,8 +253,10 @@ static GDBusClient *client;static void disconnect_handler (DBusConnection *connection,void *user_data) { -g_list_free_full (admin_proxies,NULL ); - admin_proxies =NULL ; +g_list_free_full (set_proxies,NULL ); + set_proxies =NULL ; +g_list_free_full (status_proxies,NULL ); + status_proxies =NULL ; }void admin_add_submenu (void )
client/admin.h +4 -0
@@ -10,3 +10,7 @@void admin_add_submenu (void );void admin_remove_submenu (void ); + +GDBusProxy *bluetoothctl_get_default_controller (void ); +GDBusProxy *bluetoothctl_find_controller (const char *address); +char *bluetoothctl_controller_generator (const char *text,int state);
client/main.c +22 -3
@@ -881,6 +881,25 @@ static struct adapter *find_ctrl_by_address(GList *source, const char *address)return NULL ; } +GDBusProxy *bluetoothctl_get_default_controller (void ) +{ +if (!default_ctrl) +return NULL ; + +return default_ctrl->proxy; +} + +GDBusProxy *bluetoothctl_find_controller (const char *address) +{ +struct adapter *adapter; + + adapter =find_ctrl_by_address (ctrl_list, address); +if (!adapter) +return NULL ; + +return adapter->proxy; +} +static GDBusProxy *find_proxies_by_iface (GList *source,const char *path,const char *iface) {
@@ -2788,7 +2807,7 @@ static char *generic_generator(const char *text, int state,return NULL ; } -static char *ctrl_generator (const char *text,int state) +char *bluetoothctl_controller_generator (const char *text,int state) {static int index =0 ;static int len =0 ;
@@ -3876,9 +3895,9 @@ static const struct bt_shell_menu main_menu = { .entries = { { "list", NULL, cmd_list, "List available controllers" }, { "show", "[ctrl]", cmd_show, "Controller information", - ctrl_generator }, + bluetoothctl_controller_generator }, { "select", "<ctrl>", cmd_select, "Select default controller", - ctrl_generator }, + bluetoothctl_controller_generator }, { "devices", "[Paired/Bonded/Trusted/Connected]", cmd_devices,"List available devices, with an " "optional property as the filter" },
Signature · 1 line
2.43.0
PATCH 3/7 doc: document admin.allow optional controller argument
In reply to Frédéric DanisUpdate bluetoothctl usage to 'admin.allow [ctrl] [clear/uuid1 uuid2 ...]' and add explicit controller examples.
Refresh AdminPolicySet examples to include the controller-qualified admin.allow form.
--- doc/bluetoothctl-admin.rst | 13 +++++++++++-- doc/org.bluez.AdminPolicySet.rst | 1 + 2 files changed, 12 insertions(+), 2 deletions(-)
doc/bluetoothctl-admin.rst +11 -2
@@ -9,7 +9,7 @@ Admin Policy Submenu :Version: BlueZ :Copyright: Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL). -:Date: November 2022 +:Date: June 2026 :Manual section: 1 :Manual group: Linux System Administration
@@ -30,15 +30,22 @@ allow Allow service UUIDs and block rest of them. -:Usage: **> allow [clear/uuid1 uuid2 ...]** +:Usage: **> allow [ctrl] [clear/uuid1 uuid2 ...]** +:[ctrl]: Bluetooth controller address (optional; defaults to the selected controller) :Uses: **org.bluez.AdminPolicySet(5)** property **ServiceAllowList** :[clear/uuid1 uuid2 ...]: List of service UUIDs to allow, or "clear" to remove all restrictions :Example Get current allowed UUIDs list: | **> allow** +:Example Get current allowed UUIDs list for a specific controller: + | **> allow 00:11:22:33:44:55** :Example Allow Serial Port Profile only: | **> allow 0x1101** +:Example Allow Serial Port Profile on a specific controller: + | **> allow 00:11:22:33:44:55 0x1101** :Example Allow Serial Port Profile and LAN Access Profile: | **> allow 0x1101 0x1102** +:Example Allow Serial Port Profile and LAN Access Profile on a specific controller: + | **> allow 00:11:22:33:44:55 0x1101 0x1102** :Example Allow Serial Port Profile, LAN Access Profile, and Dialup Networking Profile: | **> allow 0x1101 0x1102 0x1103** :Example Allow Advanced Audio Distribution Profile only:
@@ -51,6 +58,8 @@ Allow service UUIDs and block rest of them. | **> allow 00001101-0000-1000-8000-00805f9b34fb 00001102-0000-1000-8000-00805f9b34fb** :Example Remove all UUID restrictions: | **> allow clear** +:Example Remove all UUID restrictions on a specific controller: + | **> allow 00:11:22:33:44:55 clear** :Example Allow SPP, LAP, and DUN using mixed UUID formats: | **> allow 0x1101 00001102-0000-1000-8000-00805f9b34fb 0x1103**
doc/org.bluez.AdminPolicySet.rst +1 -0
@@ -55,3 +55,4 @@ Possible errors: Examples: :bluetoothctl: > admin.allow [clear/uuid1 uuid2 ...] +:bluetoothctl: > admin.allow [ctrl] [clear/uuid1 uuid2 ...]
Signature · 1 line
2.43.0
PATCH 4/7 src/adapter: enforce allowlist for local services
In reply to Frédéric DanisApply admin allowlist to adapter/server service startup and registration, and reapply policy dynamically when allowlist changes.
- Gate adapter profile probe by allowlist-derived UUID policy
- Reapply active adapter profiles on allowlist updates (stop disallowed, start newly allowed)
- Block SDP service registration when UUID is not allowed
- Reapply existing local SDP registrations at runtime by removing services that become disallowed
--- src/adapter.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/adapter.h | 1 + 2 files changed, 140 insertions(+)
src/adapter.c +139 -0
@@ -1346,6 +1346,7 @@ done:int adapter_service_add (struct btd_adapter *adapter,sdp_record_t *rec) { +char *svc_uuid;int ret;/*
@@ -1357,6 +1358,18 @@ int adapter_service_add(struct btd_adapter *adapter, sdp_record_t *rec)DBG ("%s" , adapter->path); + svc_uuid =bt_uuid2string (&rec->svclass); +if (!svc_uuid) +return -ENOMEM ; + +if (!btd_adapter_is_uuid_allowed (adapter, svc_uuid)) { +DBG ("Service %s blocked by admin allowlist" , svc_uuid); +free (svc_uuid); +return -EPERM ; + } + +free (svc_uuid); + ret =add_record_to_server (&adapter->bdaddr, rec);if (ret <0 )return ret;
@@ -5201,6 +5214,70 @@ static void load_drivers(struct btd_adapter *adapter)probe_driver (adapter, l->data); } +struct profile_allowlist_map { +const char *name; +const char *uuid; +bool use_remote_uuid; +}; + +/* +* Adapter server policy UUID defaults to local_uuid when available. +* Profiles listed below are exceptions where adapter-side server behavior +* needs a different UUID source (e.g. A2DP source/sink role inversion) or +* where server policy is derived from the profile remote_uuid. +*/ +static const struct profile_allowlist_map profile_allowlist_map[] = { + {"a2dp-source" ,A2DP_SINK_UUID , false }, + {"a2dp-sink" ,A2DP_SOURCE_UUID , false }, + {"audio-avrcp-target" ,NULL , true }, + {"avrcp-controller" ,NULL , true }, + {"vcp" ,NULL , true }, + {"micp" ,NULL , true }, + {"ccp" ,NULL , true }, + {"gmap" ,NULL , true }, + {"tmap" ,NULL , true }, + {"bass" ,NULL , true }, + {"bap" ,NULL , true }, + {"mcp-gmcs" ,NULL , true }, +}; + +static const char *profile_allowlist_uuid (const struct btd_profile *profile) +{ +size_t i; + +if (profile->local_uuid) +return profile->local_uuid; + +if (!profile->name) +return NULL ; + +for (i =0 ; i <ARRAY_SIZE (profile_allowlist_map); i++) { +const struct profile_allowlist_map *entry = + &profile_allowlist_map[i]; + +if (strcmp (profile->name, entry->name)) +continue ; + +if (entry->use_remote_uuid) +return profile->remote_uuid; + +return entry->uuid; + } + +return NULL ; +} + +static bool adapter_profile_is_allowed (struct btd_adapter *adapter, +const struct btd_profile *profile) +{ +const char *uuid =profile_allowlist_uuid (profile); + +if (!uuid) +return true; + +return btd_adapter_is_uuid_allowed (adapter, uuid); +} +static void probe_profile (struct btd_profile *profile,void *data) {struct btd_adapter *adapter = data;
@@ -5209,6 +5286,11 @@ static void probe_profile(struct btd_profile *profile, void *data)if (profile->adapter_probe ==NULL )return ; +if (!adapter_profile_is_allowed (adapter, profile)) { +DBG ("%s blocked by admin allowlist" , profile->name); +return ; + } + err = profile->adapter_probe (profile, adapter);if (err <0 ) { btd_error(adapter->dev_id, "%s: %s (%d)", profile->name,
@@ -5219,6 +5301,63 @@ static void probe_profile(struct btd_profile *profile, void *data) adapter->profiles =g_slist_prepend (adapter->profiles, profile); } +static void reapply_profile (struct btd_profile *profile,void *data) +{ +struct btd_adapter *adapter = data; +bool active; + +if (profile->adapter_probe ==NULL ) +return ; + + active =g_slist_find (adapter->profiles, profile) !=NULL ; + +if (adapter_profile_is_allowed (adapter, profile)) { +if (!active) +probe_profile (profile, adapter); +return ; + } + +if (!active) +return ; + + adapter->profiles =g_slist_remove (adapter->profiles, profile); + +if (profile->adapter_remove) + profile->adapter_remove (profile, adapter); +} + +static void reapply_adapter_services (struct btd_adapter *adapter) +{ +sdp_list_t *l; + +for (l = adapter->services; l !=NULL ;) { +sdp_list_t *next = l->next; +sdp_record_t *rec = l->data; +char *svc_uuid; + + svc_uuid =bt_uuid2string (&rec->svclass); +if (!svc_uuid) { + l = next; +continue ; + } + +if (!btd_adapter_is_uuid_allowed (adapter, svc_uuid)) +adapter_service_remove (adapter, rec->handle); + +free (svc_uuid); + l = next; + } +} + +void btd_adapter_reapply_allowed_uuids (struct btd_adapter *adapter) +{ +if (!adapter || !adapter->initialized) +return ; + +btd_profile_foreach (reapply_profile, adapter); +reapply_adapter_services (adapter); +} +void adapter_add_profile (struct btd_adapter *adapter,gpointer p) {struct btd_profile *profile = p;
src/adapter.h +1 -0
@@ -295,6 +295,7 @@ bool btd_adapter_set_allowed_uuids(struct btd_adapter *adapter, struct queue *uuids);bool btd_adapter_is_uuid_allowed (struct btd_adapter *adapter,const char *uuid_str); +void btd_adapter_reapply_allowed_uuids (struct btd_adapter *adapter); void btd_adapter_load_conn_param(struct btd_adapter *adapter, const bdaddr_t *peer, uint8_t bdaddr_type,
Signature · 1 line
2.43.0
PATCH 5/7 plugins/admin: reapply allowlist on policy updates
In reply to Frédéric DanisInvoke adapter allowlist reapply after SetServiceAllowList updates so runtime state follows policy changes immediately.
--- plugins/admin.c | 1 + 1 file changed, 1 insertion(+)
plugins/admin.c +1 -0
@@ -203,6 +203,7 @@ static bool service_allowlist_set(struct btd_admin_policy *admin_policy,free_service_allowlist (admin_policy->service_allowlist); admin_policy->service_allowlist = uuid_list; +btd_adapter_reapply_allowed_uuids (adapter);return true; }
Signature · 1 line
2.43.0
PATCH 6/7 doc: describe admin allowlist runtime enforcement
In reply to Frédéric DanisDocument that ServiceAllowList now also governs local adapter/server startup and registration, and that allowlist updates are applied immediately on initialized adapters.
Clarify ServiceAllowList status semantics for both remote profile connection policy and local server policy.
--- doc/org.bluez.AdminPolicySet.rst | 11 +++++++++++ doc/org.bluez.AdminPolicyStatus.rst | 5 +++++ 2 files changed, 16 insertions(+)
doc/org.bluez.AdminPolicySet.rst +11 -0
@@ -41,6 +41,17 @@ Sets the service allowlist by specifying service UUIDs. When called, **bluetoothd(8)** will block incoming and outgoing connections to the service not in UUIDs for all of the clients. +The allowlist also applies to local adapter/server services. When an allowlist +exists, only adapter/server services whose policy UUID is in UUIDs are started +or registered. + +Updating the allowlist is applied immediately on initialized adapters: + +- services that become disallowed are stopped/removed +- services that become allowed are started/registered + +This does not require restarting **bluetoothd(8)** or power-cycling adapters. + Any subsequent calls to this method will supersede any previously set allowlist values. Calling this method with an empty array will allow any service UUIDs to be used.
doc/org.bluez.AdminPolicyStatus.rst +5 -0
@@ -43,6 +43,11 @@ array{string} ServiceAllowList [readonly, adapter-only] Current value of service allow list. +When non-empty, this list controls both: + +- remote service connection policy for device profiles +- local adapter/server service startup and registration policy + bool IsAffectedByPolicy [readonly, device-only] ```````````````````````````````````````````````
Signature · 1 line
2.43.0
PATCH 7/7 profiles/audio: fix UAF on external media service teardown
In reply to Frédéric DanisKeep media_app endpoint/player queues in sync with object lifetime to avoid stale pointers during proxy removal.
When admin allowlist reapply removes audio services, endpoint/player objects may be destroyed through non-proxy paths first. Later proxy_removed_cb calls queue_remove_if() and matching by path can dereference freed endpoint/player memory.
Fix by:
- adding media_app back-references in media_endpoint/local_player
- unlinking from app queues inside media_endpoint_remove or local_player_remove
- setting ownership when app-registering endpoint/player objects
This prevents heap-use-after-free in match_endpoint_by_path or match_player_by_path during service disconnect.
--- profiles/audio/media.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
profiles/audio/media.c +16 -0
@@ -136,12 +136,14 @@ struct media_endpoint {guint watch;GSList *requests;struct media_adapter *adapter; +struct media_app *app;GSList *transports;struct endpoint_features features; };struct local_player {struct media_adapter *adapter; +struct media_app *app;char *sender;/* Player DBus bus id */ char *path;/* Player object path */ GHashTable *settings;/* Player settings */
@@ -305,6 +307,11 @@ static void media_endpoint_remove(void *data) }#endif +if (endpoint->app) { +queue_remove (endpoint->app->endpoints, endpoint); + endpoint->app =NULL ; + } +info ("Endpoint unregistered: sender=%s path=%s" , endpoint->sender, endpoint->path);
@@ -2076,6 +2083,11 @@ static void local_player_remove(void *data) {struct local_player *mp = data; +if (mp->app) { +queue_remove (mp->app->players, mp); + mp->app =NULL ; + } +info ("Player unregistered: sender=%s path=%s" , mp->sender, mp->path);local_player_destroy (mp);
@@ -3168,6 +3180,8 @@ static void app_register_endpoint(void *data, void *user_data)return ; } + endpoint->app = app; +queue_push_tail (app->endpoints, endpoint);return ;
@@ -3196,6 +3210,8 @@ static void app_register_player(void *data, void *user_data)if (!player)return ; + player->app = app; +if (g_dbus_proxy_get_property (proxy,"PlaybackStatus" , &iter)) {if (!set_status (player, &iter)) goto fail;
Signature · 1 line
2.43.0
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list. This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1120331
---Test result---
Test Summary:
CheckPatch FAIL 2.63 seconds GitLint PASS 1.64 seconds BuildEll PASS 20.98 seconds BluezMake PASS 546.48 seconds MakeCheck PASS 19.10 seconds MakeDistcheck PASS 160.21 seconds CheckValgrind PASS 229.85 seconds CheckSmatch PASS 315.68 seconds bluezmakeextell PASS 102.92 seconds IncrementalBuild PASS 585.69 seconds ScanBuild PASS 973.73 seconds
Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[BlueZ,1/7] plugins/admin: make AdminPolicy state per-adapter WARNING:BAD_SIGN_OFF: Non-standard signature: Assisted-by:
#80:
ERROR:BAD_SIGN_OFF: Unrecognized email address: 'GPT:GPT-5.3-Codex' #80:
/github/workspace/src/patch/14658608.patch total: 1 errors, 1 warnings, 259 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/github/workspace/src/patch/14658608.patch has style problems, please review.
NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
[BlueZ,2/7] client/bluetoothctl: make admin.allow controller-aware WARNING:BAD_SIGN_OFF: Non-standard signature: Assisted-by:
#81:
ERROR:BAD_SIGN_OFF: Unrecognized email address: 'GPT:GPT-5.3-Codex' #81:
/github/workspace/src/patch/14658607.patch total: 1 errors, 1 warnings, 244 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/github/workspace/src/patch/14658607.patch has style problems, please review.
NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
[BlueZ,3/7] doc: document admin.allow optional controller argument WARNING:BAD_SIGN_OFF: Non-standard signature: Assisted-by:
#77:
ERROR:BAD_SIGN_OFF: Unrecognized email address: 'GPT:GPT-5.3-Codex' #77:
/github/workspace/src/patch/14658606.patch total: 1 errors, 1 warnings, 43 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/github/workspace/src/patch/14658606.patch has style problems, please review.
NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
[BlueZ,4/7] src/adapter: enforce allowlist for local services WARNING:BAD_SIGN_OFF: Non-standard signature: Assisted-by:
#80:
ERROR:BAD_SIGN_OFF: Unrecognized email address: 'GPT:GPT-5.3-Codex' #80:
/github/workspace/src/patch/14658613.patch total: 1 errors, 1 warnings, 176 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/github/workspace/src/patch/14658613.patch has style problems, please review.
NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
[BlueZ,5/7] plugins/admin: reapply allowlist on policy updates WARNING:BAD_SIGN_OFF: Non-standard signature: Assisted-by:
#73:
ERROR:BAD_SIGN_OFF: Unrecognized email address: 'GPT:GPT-5.3-Codex' #73:
/github/workspace/src/patch/14658605.patch total: 1 errors, 1 warnings, 7 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/github/workspace/src/patch/14658605.patch has style problems, please review.
NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
[BlueZ,6/7] doc: describe admin allowlist runtime enforcement WARNING:BAD_SIGN_OFF: Non-standard signature: Assisted-by:
#77:
ERROR:BAD_SIGN_OFF: Unrecognized email address: 'GPT:GPT-5.3-Codex' #77:
/github/workspace/src/patch/14658609.patch total: 1 errors, 1 warnings, 28 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/github/workspace/src/patch/14658609.patch has style problems, please review.
NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
[BlueZ,7/7] profiles/audio: fix UAF on external media service teardown WARNING:BAD_SIGN_OFF: Non-standard signature: Assisted-by:
#88:
ERROR:BAD_SIGN_OFF: Unrecognized email address: 'GPT:GPT-5.3-Codex' #88:
/github/workspace/src/patch/14658610.patch total: 1 errors, 1 warnings, 52 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/github/workspace/src/patch/14658610.patch has style problems, please review.
NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
https://github.com/bluez/bluez/pull/2262
---
Regards,
Linux Bluetooth
This series was applied to bluetooth/bluez.git (master) by Luiz Augusto von Dentz <[email protected]>:
Hello:
This series was applied to bluetooth/bluez.git (master) by Luiz Augusto von Dentz <[email protected]>:
Quoted 13 lines · On Thu, 2 Jul 2026 10:36:35 +0200 you wrote:
Fix AdminPolicy D-Bus updates being emitted on the wrong adapter path by removing the single global policy context and moving to per-adapter policy objects.
Changes include:
- track policy contexts in a policy queue keyed by adapter pointer
- keep per-adapter device lists inside each policy context
- emit ServiceAllowList changes using the callback's adapter context
- scope device affected updates to the current adapter only
- clean up probe/remove lifecycle so adapters are registered and torn down independently
- remove remaining global policy_data/devices coupling
Quoted 1 line
[...]
Here is the summary with links:
- [BlueZ,1/7] plugins/admin: make AdminPolicy state per-adapter (no matching commit)
- [BlueZ,2/7] client/bluetoothctl: make admin.allow controller-aware (no matching commit)
- [BlueZ,3/7] doc: document admin.allow optional controller argument (no matching commit)
- [BlueZ,4/7] src/adapter: enforce allowlist for local services (no matching commit)
- [BlueZ,5/7] plugins/admin: reapply allowlist on policy updates (no matching commit)
- [BlueZ,6/7] doc: describe admin allowlist runtime enforcement (no matching commit)
- [BlueZ,7/7] profiles/audio: fix UAF on external media service teardown
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=7482aceece70
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html