From: Vibhav Pant vibhavp@gmail.com
Disconnecting and removing remote devices hold device_list_cs before blocking on the DBus event loop, which may cause a deadlock if we receive a message from BlueZ whose handler also attempts to enter device_list_cs (Like BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED). To fix this, the device/radio handle is duplicated once found, device_list_cs is released, and the DBus method call is made using the duplicate handle(s), which are then freed normally. --- dlls/winebth.sys/unixlib.c | 16 ++++++++++++++ dlls/winebth.sys/unixlib.h | 12 ++++++++++ dlls/winebth.sys/winebluetooth.c | 18 +++++++++++++++ dlls/winebth.sys/winebth.c | 38 ++++++++++++++++++++++---------- dlls/winebth.sys/winebth_priv.h | 2 ++ 5 files changed, 74 insertions(+), 12 deletions(-)
diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 763f2f32346..8db70b8f8a5 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -177,6 +177,13 @@ static NTSTATUS bluetooth_adapter_free( void *args ) return STATUS_SUCCESS; }
+static NTSTATUS bluetooth_adapter_dup( void *args ) +{ + struct bluetooth_adapter_dup_params *params = args; + unix_name_dup( params->adapter ); + return STATUS_SUCCESS; +} + static NTSTATUS bluetooth_adapter_set_prop( void *arg ) { struct bluetooth_adapter_set_prop_params *params = arg; @@ -192,6 +199,13 @@ static NTSTATUS bluetooth_device_free( void *args ) return STATUS_SUCCESS; }
+static NTSTATUS bluetooth_device_dup( void *args ) +{ + struct bluetooth_device_dup_params *params = args; + unix_name_dup( params->device ); + return STATUS_SUCCESS; +} + static NTSTATUS bluetooth_adapter_start_discovery( void *args ) { struct bluetooth_adapter_start_discovery_params *params = args; @@ -282,8 +296,10 @@ const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_adapter_stop_discovery, bluetooth_adapter_remove_device, bluetooth_adapter_free, + bluetooth_adapter_dup,
bluetooth_device_free, + bluetooth_device_dup, bluetooth_device_disconnect, bluetooth_device_start_pairing,
diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index 4629a7191d4..765003a5fc3 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -49,11 +49,21 @@ struct bluetooth_adapter_free_params unix_name_t adapter; };
+struct bluetooth_adapter_dup_params +{ + unix_name_t adapter; +}; + struct bluetooth_device_free_params { unix_name_t device; };
+struct bluetooth_device_dup_params +{ + unix_name_t device; +}; + struct bluetooth_gatt_service_free_params { unix_name_t service; @@ -132,8 +142,10 @@ enum bluetoothapis_funcs unix_bluetooth_adapter_stop_discovery, unix_bluetooth_adapter_remove_device, unix_bluetooth_adapter_free, + unix_bluetooth_adapter_dup,
unix_bluetooth_device_free, + unix_bluetooth_device_dup, unix_bluetooth_device_disconnect, unix_bluetooth_device_start_pairing,
diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index 7762b0f3fd1..ec81a64ec3b 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -111,6 +111,15 @@ void winebluetooth_radio_free( winebluetooth_radio_t radio ) UNIX_BLUETOOTH_CALL( bluetooth_adapter_free, &args ); }
+void winebluetooth_radio_dup( winebluetooth_radio_t radio ) +{ + struct bluetooth_adapter_dup_params args = {0}; + TRACE( "(%p)\n", (void *)radio.handle ); + + args.adapter = radio.handle; + UNIX_BLUETOOTH_CALL( bluetooth_adapter_dup, &args ); +} + void winebluetooth_device_free( winebluetooth_device_t device ) { struct bluetooth_device_free_params args = {0}; @@ -120,6 +129,15 @@ void winebluetooth_device_free( winebluetooth_device_t device ) UNIX_BLUETOOTH_CALL( bluetooth_device_free, &args ); }
+void winebluetooth_device_dup( winebluetooth_device_t device ) +{ + struct bluetooth_device_dup_params args = {0}; + TRACE( "(%p)\n", (void *)device.handle ); + + args.device = device.handle; + UNIX_BLUETOOTH_CALL( bluetooth_device_dup, &args ); +} + NTSTATUS winebluetooth_device_disconnect( winebluetooth_device_t device ) { struct bluetooth_device_disconnect_params args = {0}; diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 60e6d98dcaf..7ecb52d11cc 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -406,8 +406,10 @@ static NTSTATUS bluetooth_radio_dispatch( DEVICE_OBJECT *device, struct bluetoot case IOCTL_BTH_DISCONNECT_DEVICE: { const BTH_ADDR *param = irp->AssociatedIrp.SystemBuffer; - BTH_ADDR device_addr; struct bluetooth_remote_device *device; + winebluetooth_device_t device_handle; + BTH_ADDR device_addr; + BOOL found = FALSE;
if (!param || insize < sizeof( *param )) { @@ -421,19 +423,22 @@ static NTSTATUS bluetooth_radio_dispatch( DEVICE_OBJECT *device, struct bluetoot EnterCriticalSection( &device_list_cs ); LIST_FOR_EACH_ENTRY( device, &ext->remote_devices, struct bluetooth_remote_device, entry ) { - BOOL matches; - EnterCriticalSection( &device->props_cs ); - matches = device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS && - device_addr == device->props.address.ullLong; + found = device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS && + device_addr == device->props.address.ullLong; LeaveCriticalSection( &device->props_cs ); - if (matches) + if (found) { - status = winebluetooth_device_disconnect( device->device ); + winebluetooth_device_dup(( device_handle = device->device )); break; } } LeaveCriticalSection( &device_list_cs ); + if (found) + { + status = winebluetooth_device_disconnect( device_handle ); + winebluetooth_device_free( device_handle ); + } break; } case IOCTL_WINEBTH_RADIO_SET_FLAG: @@ -550,6 +555,9 @@ static NTSTATUS bluetooth_radio_dispatch( DEVICE_OBJECT *device, struct bluetoot { const BTH_ADDR *param = irp->AssociatedIrp.SystemBuffer; struct bluetooth_remote_device *device; + winebluetooth_device_t device_handle; + winebluetooth_radio_t radio_handle; + BOOL found = FALSE;
if (!param) { @@ -566,19 +574,25 @@ static NTSTATUS bluetooth_radio_dispatch( DEVICE_OBJECT *device, struct bluetoot EnterCriticalSection( &device_list_cs ); LIST_FOR_EACH_ENTRY( device, &ext->remote_devices, struct bluetooth_remote_device, entry ) { - BOOL matches; EnterCriticalSection( &device->props_cs ); - matches = device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS && - device->props.address.ullLong == *param && device->props.paired; + found = device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS && + device->props.address.ullLong == *param && device->props.paired; LeaveCriticalSection( &device->props_cs );
- if (matches) + if (found) { - status = winebluetooth_radio_remove_device( ext->radio, device->device ); + winebluetooth_device_dup(( device_handle = device->device )); + winebluetooth_radio_dup(( radio_handle = ext->radio )); break; } } LeaveCriticalSection( &device_list_cs ); + if (found) + { + status = winebluetooth_radio_remove_device( radio_handle, device_handle ); + winebluetooth_device_free( device_handle ); + winebluetooth_radio_free( radio_handle ); + } break; } default: diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index 34582ed3759..765fe1d55f7 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -221,6 +221,7 @@ typedef struct NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char *name, SIZE_T *size ); void winebluetooth_radio_free( winebluetooth_radio_t radio ); +void winebluetooth_radio_dup( winebluetooth_radio_t radio ); static inline BOOL winebluetooth_radio_equal( winebluetooth_radio_t r1, winebluetooth_radio_t r2 ) { return r1.handle == r2.handle; @@ -234,6 +235,7 @@ NTSTATUS winebluetooth_radio_remove_device( winebluetooth_radio_t radio, wineblu NTSTATUS winebluetooth_auth_agent_enable_incoming( void );
void winebluetooth_device_free( winebluetooth_device_t device ); +void winebluetooth_device_dup( winebluetooth_device_t device ); static inline BOOL winebluetooth_device_equal( winebluetooth_device_t d1, winebluetooth_device_t d2 ) { return d1.handle == d2.handle;