mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-04 13:30:12 -05:00
pulse-server: detach clients from server
When the server or client goes away, detach the client from the server to avoid potential use-after-free issues that might occur if the client causes the unloading of the server it is connected to. E.g.: pactl load-module module-protocol-native-tcp port=4713 pactl -s localhost:4713 unload-module module-native-protocol-tcp See #1240.
This commit is contained in:
parent
3889ea5277
commit
4c13eced55
1 changed files with 41 additions and 3 deletions
|
|
@ -5336,6 +5336,24 @@ static int client_free_stream(void *item, void *data)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* tries to detach the client from the server,
|
||||||
|
* but it does not drop the server's reference
|
||||||
|
*/
|
||||||
|
static bool client_detach(struct client *client)
|
||||||
|
{
|
||||||
|
if (client->server == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pw_log_info(NAME" %p: client %p detaching", client->server, client);
|
||||||
|
|
||||||
|
/* remove from the `server->clients` list */
|
||||||
|
spa_list_remove(&client->link);
|
||||||
|
client->server = NULL;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void client_disconnect(struct client *client)
|
static void client_disconnect(struct client *client)
|
||||||
{
|
{
|
||||||
struct impl *impl = client->impl;
|
struct impl *impl = client->impl;
|
||||||
|
|
@ -5343,8 +5361,10 @@ static void client_disconnect(struct client *client)
|
||||||
if (client->disconnect)
|
if (client->disconnect)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* the client must be detached from the server to disconnect */
|
||||||
|
spa_assert(client->server == NULL);
|
||||||
|
|
||||||
client->disconnect = true;
|
client->disconnect = true;
|
||||||
spa_list_remove(&client->link);
|
|
||||||
spa_list_append(&impl->cleanup_clients, &client->link);
|
spa_list_append(&impl->cleanup_clients, &client->link);
|
||||||
|
|
||||||
pw_map_for_each(&client->streams, client_free_stream, client);
|
pw_map_for_each(&client->streams, client_free_stream, client);
|
||||||
|
|
@ -5353,6 +5373,7 @@ static void client_disconnect(struct client *client)
|
||||||
pw_loop_destroy_source(impl->loop, client->source);
|
pw_loop_destroy_source(impl->loop, client->source);
|
||||||
if (client->manager)
|
if (client->manager)
|
||||||
pw_manager_destroy(client->manager);
|
pw_manager_destroy(client->manager);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void client_free(struct client *client)
|
static void client_free(struct client *client)
|
||||||
|
|
@ -5364,8 +5385,10 @@ static void client_free(struct client *client)
|
||||||
|
|
||||||
pw_log_info(NAME" %p: client %p free", impl, client);
|
pw_log_info(NAME" %p: client %p free", impl, client);
|
||||||
|
|
||||||
|
client_detach(client);
|
||||||
client_disconnect(client);
|
client_disconnect(client);
|
||||||
|
|
||||||
|
/* remove from the `impl->cleanup_clients` list */
|
||||||
spa_list_remove(&client->link);
|
spa_list_remove(&client->link);
|
||||||
|
|
||||||
spa_list_consume(p, &client->pending_samples, link)
|
spa_list_consume(p, &client->pending_samples, link)
|
||||||
|
|
@ -5632,6 +5655,7 @@ on_client_data(void *data, int fd, uint32_t mask)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
|
/* drop the reference that was acquired at the beginning of the function */
|
||||||
client_unref(client);
|
client_unref(client);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -5641,8 +5665,18 @@ error:
|
||||||
pw_log_info(NAME" %p: client:%p [%s] disconnected", impl, client, client->name);
|
pw_log_info(NAME" %p: client:%p [%s] disconnected", impl, client, client->name);
|
||||||
SPA_FALLTHROUGH;
|
SPA_FALLTHROUGH;
|
||||||
case -EPROTO:
|
case -EPROTO:
|
||||||
client_disconnect(client);
|
/*
|
||||||
|
* drop the server's reference to the client
|
||||||
|
* (if it hasn't been dropped already),
|
||||||
|
* it is guaranteed that this will not call `client_free()`
|
||||||
|
* since at the beginning of this function an extra reference
|
||||||
|
* has been acquired which will keep the client alive
|
||||||
|
*/
|
||||||
|
if (client_detach(client))
|
||||||
client_unref(client);
|
client_unref(client);
|
||||||
|
|
||||||
|
/* then disconnect the client */
|
||||||
|
client_disconnect(client);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pw_log_error(NAME" %p: client:%p [%s] error %d (%s)", impl,
|
pw_log_error(NAME" %p: client:%p [%s] error %d (%s)", impl,
|
||||||
|
|
@ -5856,8 +5890,12 @@ void server_free(struct server *server)
|
||||||
pw_log_debug(NAME" %p: free server %p", impl, server);
|
pw_log_debug(NAME" %p: free server %p", impl, server);
|
||||||
|
|
||||||
spa_list_remove(&server->link);
|
spa_list_remove(&server->link);
|
||||||
spa_list_for_each_safe(c, t, &server->clients, link)
|
|
||||||
|
spa_list_for_each_safe(c, t, &server->clients, link) {
|
||||||
|
spa_assert_se(client_detach(c));
|
||||||
client_unref(c);
|
client_unref(c);
|
||||||
|
}
|
||||||
|
|
||||||
if (server->source)
|
if (server->source)
|
||||||
pw_loop_destroy_source(impl->loop, server->source);
|
pw_loop_destroy_source(impl->loop, server->source);
|
||||||
if (server->addr.ss_family == AF_UNIX && !server->activated)
|
if (server->addr.ss_family == AF_UNIX && !server->activated)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue