From 807b93fb05701bbd88be2387fae654e7d1aed15c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 29 Apr 2026 17:19:08 +0200 Subject: [PATCH] security: add per-client pending sample limit in PulseAudio protocol There was no limit on concurrent PLAY_SAMPLE operations per client. Each creates a PipeWire stream, allowing a client to exhaust server resources. Add a MAX_PENDING_SAMPLES (64) limit per client. Co-Authored-By: Claude Opus 4.7 --- src/modules/module-protocol-pulse/client.h | 1 + src/modules/module-protocol-pulse/defs.h | 1 + src/modules/module-protocol-pulse/pending-sample.c | 10 +++++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/modules/module-protocol-pulse/client.h b/src/modules/module-protocol-pulse/client.h index 39a0fe685..1231f3f35 100644 --- a/src/modules/module-protocol-pulse/client.h +++ b/src/modules/module-protocol-pulse/client.h @@ -83,6 +83,7 @@ struct client { uint32_t n_operations; struct spa_list pending_samples; + uint32_t n_pending_samples; unsigned int disconnect:1; unsigned int new_msg_since_last_flush:1; diff --git a/src/modules/module-protocol-pulse/defs.h b/src/modules/module-protocol-pulse/defs.h index 663cc8be8..1cd2093a1 100644 --- a/src/modules/module-protocol-pulse/defs.h +++ b/src/modules/module-protocol-pulse/defs.h @@ -43,6 +43,7 @@ #define MAX_CLIENTS 64u #define MAX_STREAMS 64u #define MAX_OPERATIONS 64u +#define MAX_PENDING_SAMPLES 64u #define MODULE_INDEX_MASK 0xfffffffu #define MODULE_FLAG (1u << 29) diff --git a/src/modules/module-protocol-pulse/pending-sample.c b/src/modules/module-protocol-pulse/pending-sample.c index 065b460ca..98adce575 100644 --- a/src/modules/module-protocol-pulse/pending-sample.c +++ b/src/modules/module-protocol-pulse/pending-sample.c @@ -9,6 +9,7 @@ #include "client.h" #include "collect.h" #include "commands.h" +#include "defs.h" #include "internal.h" #include "log.h" #include "message.h" @@ -106,7 +107,12 @@ static const struct client_events client_events = { int pending_sample_new(struct client *client, struct sample *sample, struct pw_properties *props, uint32_t tag) { struct pending_sample *ps; - struct sample_play *p = sample_play_new(client->core, sample, props, sizeof(*ps)); + struct sample_play *p; + + if (client->n_pending_samples >= MAX_PENDING_SAMPLES) + return -ENOSPC; + + p = sample_play_new(client->core, sample, props, sizeof(*ps)); if (!p) return -errno; @@ -117,6 +123,7 @@ int pending_sample_new(struct client *client, struct sample *sample, struct pw_p sample_play_add_listener(p, &ps->listener, &sample_play_events, ps); client_add_listener(client, &ps->client_listener, &client_events, ps); spa_list_append(&client->pending_samples, &ps->link); + client->n_pending_samples++; client->ref++; return 0; @@ -127,6 +134,7 @@ void pending_sample_free(struct pending_sample *ps) struct client * const client = ps->client; struct impl * const impl = client->impl; + client->n_pending_samples--; spa_list_remove(&ps->link); spa_hook_remove(&ps->listener); spa_hook_remove(&ps->client_listener);