diff --git a/SOURCES/pipewire-1.4.11-CVE-2026-14324.patch b/SOURCES/pipewire-1.4.11-CVE-2026-14324.patch new file mode 100644 index 0000000..27b6391 --- /dev/null +++ b/SOURCES/pipewire-1.4.11-CVE-2026-14324.patch @@ -0,0 +1,51 @@ +From 9e0fb4cdd619c4ea007855a49733df684087db88 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +Date: Mon, 27 Apr 2026 13:05:12 +0200 +Subject: [PATCH] security: limit RTSP content-length and check allocation in + RAOP client + +Input Validation / Memory Safety: Medium + +The RTSP client used for RAOP/AirPlay communication accepted arbitrarily +large Content-Length values from the remote server without any upper +bound. A malicious or compromised AirPlay server could specify a very +large Content-Length, causing the client to allocate unbounded memory +and potentially exhaust system resources (denial of service). + +Additionally, the return value of pw_array_add() was not checked. If +the allocation failed, the subsequent memcpy would dereference a NULL +pointer, causing a crash. + +Add a 64KB limit on Content-Length (more than sufficient for RTSP +control messages) and check the pw_array_add return value. + +Co-Authored-By: Claude Opus 4.6 +--- + src/modules/module-raop/rtsp-client.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/modules/module-raop/rtsp-client.c b/src/modules/module-raop/rtsp-client.c +index fae71977c..097787144 100644 +--- a/src/modules/module-raop/rtsp-client.c ++++ b/src/modules/module-raop/rtsp-client.c +@@ -322,6 +322,11 @@ static int process_header(struct pw_rtsp_client *client, char *buf) + pw_log_info(" %s: %s", it->key, it->value); + + client->content_length = pw_properties_get_uint32(client->headers, "Content-Length", 0); ++ if (client->content_length > 64 * 1024) { ++ pw_log_error("Content-Length %zu exceeds maximum", ++ client->content_length); ++ return -EOVERFLOW; ++ } + if (client->content_length > 0) + client->recv_state = CLIENT_RECV_CONTENT; + else +@@ -351,6 +356,8 @@ static int process_content(struct pw_rtsp_client *client) + } + + void *p = pw_array_add(&client->content, res); ++ if (p == NULL) ++ return -ENOMEM; + memcpy(p, buf, res); + + spa_assert((size_t) res <= client->content_length); diff --git a/SOURCES/pipewire-1.4.11-CVE-2026-14330.patch b/SOURCES/pipewire-1.4.11-CVE-2026-14330.patch new file mode 100644 index 0000000..9736ea6 --- /dev/null +++ b/SOURCES/pipewire-1.4.11-CVE-2026-14330.patch @@ -0,0 +1,140 @@ +From 4782950f99984e093b419a7309005c0119326d84 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +Date: Thu, 23 Apr 2026 18:48:13 +0200 +Subject: [PATCH 1/2] security: fix stack exhaustion via unbounded alloca in + pulse-server + +Memory Safety: Medium + +Several functions in the PulseAudio protocol implementation use alloca() +to allocate arrays of port_info, profile_info, or dict_item structs +based on counts derived from card parameters or client property lists. +These counts have no upper bounds, so a card object with a very large +number of parameters or a client sending many properties can cause +alloca() to exhaust the stack, resulting in a stack overflow crash. + +Add a MAX_ALLOCA_SIZE (64KB) limit and check element counts before each +alloca() call. If the requested allocation exceeds the limit, the +function returns -ENOMEM instead of crashing. + +Co-Authored-By: Claude Opus 4.6 +--- + src/modules/module-protocol-pulse/pulse-server.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c +index 20a9cf490..220907292 100644 +--- a/src/modules/module-protocol-pulse/pulse-server.c ++++ b/src/modules/module-protocol-pulse/pulse-server.c +@@ -71,6 +71,7 @@ + /* The max amount of data we send in one block when capturing. In PulseAudio this + * size is derived from the mempool PA_MEMPOOL_SLOT_SIZE */ + #define MAX_BLOCK (64*1024) ++#define MAX_ALLOCA_SIZE (64*1024) + + #define TEMPORARY_MOVE_TIMEOUT (SPA_NSEC_PER_SEC) + +@@ -3133,6 +3134,8 @@ static int do_set_port_latency_offset(struct client *client, uint32_t command, u + return -ENOENT; + + collect_card_info(card, &card_info); ++ if (card_info.n_ports > MAX_ALLOCA_SIZE / sizeof(*port_info)) ++ return -ENOMEM; + port_info = alloca(card_info.n_ports * sizeof(*port_info)); + card_info.active_profile = SPA_ID_INVALID; + n_ports = collect_port_info(card, &card_info, NULL, port_info); +@@ -3272,6 +3275,8 @@ static int do_remove_proplist(struct client *client, uint32_t command, uint32_t + } + + dict.n_items = props->dict.n_items; ++ if (dict.n_items > MAX_ALLOCA_SIZE / sizeof(struct spa_dict_item)) ++ return -ENOMEM; + dict.items = items = alloca(sizeof(struct spa_dict_item) * dict.n_items); + for (i = 0; i < dict.n_items; i++) { + items[i].key = props->dict.items[i].key; +@@ -3555,6 +3560,8 @@ static int fill_card_info(struct client *client, struct message *m, + TAG_U32, card_info.n_profiles, /* n_profiles */ + TAG_INVALID); + ++ if (card_info.n_profiles > MAX_ALLOCA_SIZE / sizeof(*profile_info)) ++ return -ENOMEM; + profile_info = alloca(card_info.n_profiles * sizeof(*profile_info)); + n_profiles = collect_profile_info(o, &card_info, profile_info); + +@@ -3584,6 +3591,8 @@ static int fill_card_info(struct client *client, struct message *m, + uint32_t n_ports; + struct port_info *port_info, *pi; + ++ if (card_info.n_ports > MAX_ALLOCA_SIZE / sizeof(*port_info)) ++ return -ENOMEM; + port_info = alloca(card_info.n_ports * sizeof(*port_info)); + card_info.active_profile = SPA_ID_INVALID; + n_ports = collect_port_info(o, &card_info, NULL, port_info); +@@ -3788,6 +3797,8 @@ static int fill_sink_info(struct client *client, struct message *m, + uint32_t n_ports, n; + struct port_info *port_info, *pi; + ++ if (card_info.n_ports > MAX_ALLOCA_SIZE / sizeof(*port_info)) ++ return -ENOMEM; + port_info = alloca(card_info.n_ports * sizeof(*port_info)); + n_ports = collect_port_info(card, &card_info, &dev_info, port_info); + +@@ -3984,6 +3995,8 @@ static int fill_source_info(struct client *client, struct message *m, + uint32_t n_ports, n; + struct port_info *port_info, *pi; + ++ if (card_info.n_ports > MAX_ALLOCA_SIZE / sizeof(*port_info)) ++ return -ENOMEM; + port_info = alloca(card_info.n_ports * sizeof(*port_info)); + n_ports = collect_port_info(card, &card_info, &dev_info, port_info); + + +From 4ab2b386e8f5c6360addcda79b9bc177f1f20a30 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +Date: Fri, 24 Apr 2026 14:12:50 +0200 +Subject: [PATCH 2/2] security: fix unchecked alloca in pulse-server property + list handling + +Memory Safety: Medium + +Two alloca() calls in the PulseAudio protocol server were missed by the +previous alloca bounds-checking fix (commit 0d2877c0d): + +1. fill_node_info_proplist() adds n_items counts from node properties + and client properties without checking the total before alloca(). + A client with a very large number of properties can exhaust the stack. + +2. fill_card_info() uses pi->n_props from port info for an alloca() + without bounds checking. A card object with many port properties can + similarly exhaust the stack. + +Add MAX_ALLOCA_SIZE checks consistent with the existing pattern to +prevent stack overflow from large property counts. + +Co-Authored-By: Claude Opus 4.6 +--- + src/modules/module-protocol-pulse/pulse-server.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c +index 220907292..89f589e03 100644 +--- a/src/modules/module-protocol-pulse/pulse-server.c ++++ b/src/modules/module-protocol-pulse/pulse-server.c +@@ -3608,7 +3608,8 @@ static int fill_card_info(struct client *client, struct message *m, + + pi = &port_info[n]; + +- if (pi->info && pi->n_props > 0) { ++ if (pi->info && pi->n_props > 0 && ++ pi->n_props <= MAX_ALLOCA_SIZE / sizeof(*items)) { + items = alloca(pi->n_props * sizeof(*items)); + dict.items = items; + pdict = collect_props(pi->info, &dict); +@@ -4047,6 +4048,7 @@ static const char *get_media_name(struct pw_node_info *info) + return media_name; + } + ++ + static int fill_sink_input_info(struct client *client, struct message *m, + struct pw_manager_object *o) + { diff --git a/SPECS/pipewire.spec b/SPECS/pipewire.spec index f49a113..d7022da 100644 --- a/SPECS/pipewire.spec +++ b/SPECS/pipewire.spec @@ -69,7 +69,7 @@ Name: pipewire Summary: Media Sharing Server Version: %{majorversion}.%{minorversion}.%{microversion} -Release: %{baserelease}%{?snapdate:.%{snapdate}git%{shortcommit}}%{?dist} +Release: %{baserelease}%{?snapdate:.%{snapdate}git%{shortcommit}}%{?dist}.2 License: MIT URL: https://pipewire.org/ %if 0%{?snapdate} @@ -86,9 +86,16 @@ Patch0003: 0003-dlopen-support-search-path-ending-in.patch Patch0004: 0004-filter-graph-error-when-there-are-no-valid-nodes.patch Patch0005: 0005-filter-graph-relax-LADSPA-plugin-loading.patch +# CVE-2026-14330 +# https://gitlab.freedesktop.org/pipewire/pipewire/-/commit/00413a3263a65ccaba082a533078da7cd8ac3315 +# https://gitlab.freedesktop.org/pipewire/pipewire/-/commit/a6155387da106b60b63eb5ee37d138d0fd89a63e +Patch0006: pipewire-1.4.11-CVE-2026-14330.patch + ## upstreamable patches ## RHEL patches +# https://gitlab.freedesktop.org/pipewire/pipewire/-/commit/87ee525b0124755ccab99d873ddf85d9d4969f73 +Patch0007: pipewire-1.4.11-CVE-2026-14324.patch BuildRequires: gettext BuildRequires: meson >= 0.59.0 @@ -918,10 +925,19 @@ systemctl --no-reload preset --global pipewire.socket >/dev/null 2>&1 || : %{_datadir}/pipewire/pipewire.conf.d/50-raop.conf %changelog +* Thu Aug 27 2026 RHEL Packaging Agent - 1.4.11-1.2 +- Fix CVE-2026-14324: limit RTSP Content-Length in RAOP client + Resolves: RHEL-249297 + +* Thu Aug 27 2026 RHEL Packaging Agent - 1.4.11-1.1 +- Fix stack exhaustion via unbounded alloca in pulse-server + (CVE-2026-14330) + Resolves: RHEL-249317 + * Fri May 15 2026 Wim Taymans - 1.4.11-1 - Rebase to 1.4.11 - Add fixes for CVE-2026-5674 - Resolves: RHEL-164830 + Resolves: RHEL-164828 * Fri Nov 21 2025 Wim Taymans - 1.4.9-1 - Rebase to 1.4.9