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 <noreply@anthropic.com>
This commit is contained in:
Wim Taymans 2026-04-29 17:19:08 +02:00
parent 138e30df38
commit 807b93fb05
3 changed files with 11 additions and 1 deletions

View file

@ -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;

View file

@ -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)

View file

@ -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);