diff --git a/spa/include/spa/utils/defs.h b/spa/include/spa/utils/defs.h index 371b1e658..440f7f7fd 100644 --- a/spa/include/spa/utils/defs.h +++ b/spa/include/spa/utils/defs.h @@ -457,6 +457,16 @@ struct spa_error_location { _strp; \ }) +#define spa_alloca(n, size, max_size) \ +({ \ + void *_res = NULL; \ + if ((size_t)n > (size_t)max_size / (size_t)size) \ + errno = ENOMEM; \ + else \ + _res = alloca((size_t)n * (size_t)size); \ + _res; \ +}) + /** * \} */ diff --git a/spa/plugins/audioconvert/audioadapter.c b/spa/plugins/audioconvert/audioadapter.c index 801b736ab..d78342366 100644 --- a/spa/plugins/audioconvert/audioadapter.c +++ b/spa/plugins/audioconvert/audioadapter.c @@ -35,6 +35,7 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audioadapter"); #define MAX_PORTS (SPA_AUDIO_MAX_CHANNELS+1) #define MAX_RETRY 64 +#define MAX_BLOCKS 4096 /** \cond */ @@ -356,6 +357,7 @@ static void emit_node_info(struct impl *this, bool full) if (this->info.props) n_items = this->info.props->n_items; + n_items = SPA_MIN(n_items, 1024u); items = alloca((n_items + 2) * sizeof(struct spa_dict_item)); for (i = 0; i < n_items; i++) items[i] = this->info.props->items[i]; @@ -513,6 +515,9 @@ static int negotiate_buffers(struct impl *this) align = SPA_MAX(align, this->max_align); + if (blocks > MAX_BLOCKS) + return -ENOMEM; + datas = alloca(sizeof(struct spa_data) * blocks); memset(datas, 0, sizeof(struct spa_data) * blocks); aligns = alloca(sizeof(uint32_t) * blocks); @@ -1942,11 +1947,12 @@ static int load_converter(struct impl *this, const struct spa_dict *info, struct spa_dict_item *items; struct spa_dict cinfo; char direction[16]; - uint32_t i; + uint32_t i, n_items; - items = alloca((info->n_items + 1) * sizeof(struct spa_dict_item)); + n_items = SPA_MIN(info->n_items, 1024u); + items = alloca((n_items + 1) * sizeof(struct spa_dict_item)); cinfo = SPA_DICT(items, 0); - for (i = 0; i < info->n_items; i++) + for (i = 0; i < n_items; i++) items[cinfo.n_items++] = info->items[i]; snprintf(direction, sizeof(direction), "%s", diff --git a/src/modules/module-protocol-pulse/defs.h b/src/modules/module-protocol-pulse/defs.h index f3e25f137..f3281ae88 100644 --- a/src/modules/module-protocol-pulse/defs.h +++ b/src/modules/module-protocol-pulse/defs.h @@ -34,6 +34,9 @@ #define MAXLENGTH (4u*1024*1024) /* 4MB */ +/* pulseaudio has a 128 char limit for this but we can allow more */ +#define MAX_NAME 1024u + #define SCACHE_ENTRY_SIZE_MAX (1024*1024*16) #define MODULE_INDEX_MASK 0xfffffffu diff --git a/src/modules/module-protocol-pulse/message.c b/src/modules/module-protocol-pulse/message.c index ad32bccec..4b906c79e 100644 --- a/src/modules/module-protocol-pulse/message.c +++ b/src/modules/module-protocol-pulse/message.c @@ -532,12 +532,13 @@ static void add_stream_group(struct message *m, struct spa_dict *dict, const cha else return; - write_string(m, key); l = strlen(prefix) + strlen(id) + strlen(str) + 6; /* "-by-" , ":" and \0 */ - if (l < 0 || l > 1024) + if (l < 0 || l > 4096) return; + + write_string(m, key); b = alloca(l); - snprintf(b, l, "%s-by-%s:%s", prefix, id, str); + spa_scnprintf(b, l, "%s-by-%s:%s", prefix, id, str); write_u32(m, l); write_arbitrary(m, b, l); } diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index 9cc16353d..f02f74fef 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -753,8 +753,11 @@ static int reply_create_record_stream(struct stream *stream, struct pw_manager_o peer_index = peer->index; if (!pw_manager_object_is_source(peer)) { size_t len = (name ? strlen(name) : 5) + 10; - peer_name = tmp = alloca(len); - snprintf(tmp, len, "%s.monitor", name ? name : "sink"); + if (len <= MAX_NAME) { + peer_name = tmp = alloca(len); + spa_scnprintf(tmp, len, "%s.monitor", name ? name : "sink"); + } else + peer_name = NULL; } else { peer_name = name; } @@ -866,7 +869,7 @@ static void manager_added(void *data, struct pw_manager_object *o) size_t len = strlen(peer_name) + 10; if (len <= 1024) { char *tmp = alloca(len); - snprintf(tmp, len, "%s.monitor", peer_name); + spa_scnprintf(tmp, len, "%s.monitor", peer_name); peer_name = tmp; } } @@ -3174,9 +3177,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)); + if ((port_info = spa_alloca(card_info.n_ports, sizeof(*port_info), MAX_ALLOCA_SIZE)) == NULL) + return -errno; card_info.active_profile = SPA_ID_INVALID; n_ports = collect_port_info(card, &card_info, NULL, port_info); @@ -3315,9 +3317,9 @@ 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); + if ((dict.items = items = spa_alloca(dict.n_items, + sizeof(struct spa_dict_item), MAX_ALLOCA_SIZE)) == NULL) + return -errno; for (i = 0; i < dict.n_items; i++) { items[i].key = props->dict.items[i].key; items[i].value = NULL; @@ -3600,9 +3602,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)); + if ((profile_info = spa_alloca(card_info.n_profiles, sizeof(*profile_info), MAX_ALLOCA_SIZE)) == NULL) + return -errno; n_profiles = collect_profile_info(o, &card_info, profile_info); for (n = 0; n < n_profiles; n++) { @@ -3631,9 +3632,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)); + if ((port_info = spa_alloca(card_info.n_ports, sizeof(*port_info), MAX_ALLOCA_SIZE)) == NULL) + return -errno; card_info.active_profile = SPA_ID_INVALID; n_ports = collect_port_info(o, &card_info, NULL, port_info); @@ -3649,8 +3649,7 @@ static int fill_card_info(struct client *client, struct message *m, pi = &port_info[n]; if (pi->info && pi->n_props > 0 && - pi->n_props <= MAX_ALLOCA_SIZE / sizeof(*items)) { - items = alloca(pi->n_props * sizeof(*items)); + (items = spa_alloca(pi->n_props, sizeof(*items), MAX_ALLOCA_SIZE)) != NULL) { dict.items = items; pdict = collect_props(pi->info, &dict); } @@ -3757,7 +3756,7 @@ static int fill_sink_info(struct client *client, struct message *m, if (name == NULL) name = "unknown"; - size = strlen(name) + 10; + size = SPA_MIN(strlen(name) + 10, MAX_NAME); monitor_name = alloca(size); if (pw_manager_object_is_source(o)) snprintf(monitor_name, size, "%s", name); @@ -3838,9 +3837,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)); + if ((port_info = spa_alloca(card_info.n_ports, sizeof(*port_info), MAX_ALLOCA_SIZE)) == NULL) + return -errno; n_ports = collect_port_info(card, &card_info, &dev_info, port_info); message_put(m, @@ -3956,11 +3954,11 @@ static int fill_source_info(struct client *client, struct message *m, if (name == NULL) name = "unknown"; - size = strlen(name) + 10; + size = SPA_MIN(strlen(name) + 10, MAX_NAME); monitor_name = alloca(size); snprintf(monitor_name, size, "%s.monitor", name); - size = strlen(desc) + 20; + size = SPA_MIN(strlen(desc) + 20, MAX_NAME); monitor_desc = alloca(size); snprintf(monitor_desc, size, "Monitor of %s", desc); @@ -4036,9 +4034,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)); + if ((port_info = spa_alloca(card_info.n_ports, sizeof(*port_info), MAX_ALLOCA_SIZE)) == NULL) + return -errno; n_ports = collect_port_info(card, &card_info, &dev_info, port_info); message_put(m, @@ -4103,12 +4100,10 @@ static int fill_node_info_proplist(struct message *m, const struct spa_dict *nod n_items += client_props->n_items; } - if (n_items > MAX_ALLOCA_SIZE / sizeof(struct spa_dict_item)) - return -ENOMEM; + if ((dict.items = items = spa_alloca(n_items, sizeof(struct spa_dict_item), MAX_ALLOCA_SIZE)) == NULL) + return -errno; dict.n_items = n = 0; - dict.items = items = alloca(n_items * sizeof(struct spa_dict_item)); - spa_dict_for_each(it, node_props) items[n++] = *it; dict.n_items = n; diff --git a/src/modules/module-sendspin-recv.c b/src/modules/module-sendspin-recv.c index fe6525767..dff173eb6 100644 --- a/src/modules/module-sendspin-recv.c +++ b/src/modules/module-sendspin-recv.c @@ -560,12 +560,14 @@ static int handle_server_hello(struct client *client, struct spa_json *payload) while ((l = spa_json_object_next(payload, key, sizeof(key), &v)) > 0) { if (spa_streq(key, "server_id")) { - t = alloca(l+1); + if ((t = spa_alloca(1, l+1, 1024)) == NULL) + return -errno; spa_json_parse_stringn(v, l, t, l+1); pw_properties_set(client->props, "sendspin.server-id", t); } else if (spa_streq(key, "name")) { - t = alloca(l+1); + if ((t = spa_alloca(1, l+1, 1024)) == NULL) + return -errno; spa_json_parse_stringn(v, l, t, l+1); pw_properties_set(client->props, "sendspin.server-name", t); } @@ -579,7 +581,8 @@ static int handle_server_hello(struct client *client, struct spa_json *payload) spa_json_enter(payload, &it[0]); while ((l = spa_json_next(&it[0], &v)) > 0) { - t = alloca(l+1); + if ((t = spa_alloca(1, l+1, 128)) == NULL) + continue; spa_json_parse_stringn(v, l, t, l+1); if (spa_streq(t, "player@v1")) @@ -589,7 +592,8 @@ static int handle_server_hello(struct client *client, struct spa_json *payload) } } else if (spa_streq(key, "connection_reason")) { - t = alloca(l+1); + if ((t = spa_alloca(1, l+1, 4096)) == NULL) + return -errno; spa_json_parse_stringn(v, l, t, l+1); if (spa_streq(t, "discovery")) diff --git a/src/modules/module-sendspin-send.c b/src/modules/module-sendspin-send.c index 2c9f6df22..29ae51592 100644 --- a/src/modules/module-sendspin-send.c +++ b/src/modules/module-sendspin-send.c @@ -745,7 +745,8 @@ static int parse_player_v1_support(struct client *c, struct spa_json *payload) spa_json_enter(payload, &it[0]); while ((l = spa_json_next(&it[0], &v)) > 0) { - t = alloca(l+1); + if ((t = spa_alloca(1, l+1, 64)) == NULL) + continue; spa_json_parse_stringn(v, l, t, l+1); if (spa_streq(t, "volume")) c->supported_commands |= COMMAND_VOLUME; @@ -766,12 +767,14 @@ static int handle_client_hello(struct client *c, struct spa_json *payload) while ((l = spa_json_object_next(payload, key, sizeof(key), &v)) > 0) { if (spa_streq(key, "client_id")) { - t = alloca(l+1); + if ((t = spa_alloca(1, l+1, 1024)) == NULL) + return -errno; spa_json_parse_stringn(v, l, t, l+1); pw_properties_set(c->props, "sendspin.client-id", t); } else if (spa_streq(key, "name")) { - t = alloca(l+1); + if ((t = spa_alloca(1, l+1, 1024)) == NULL) + return -errno; spa_json_parse_stringn(v, l, t, l+1); pw_properties_set(c->props, "sendspin.client-name", t); } @@ -785,7 +788,8 @@ static int handle_client_hello(struct client *c, struct spa_json *payload) spa_json_enter(payload, &it[0]); while ((l = spa_json_next(&it[0], &v)) > 0) { - t = alloca(l+1); + if ((t = spa_alloca(1, l+1, 64)) == NULL) + continue; spa_json_parse_stringn(v, l, t, l+1); if (spa_streq(t, "player@v1")) diff --git a/src/pipewire/buffers.c b/src/pipewire/buffers.c index 5c66005b2..63e16bf43 100644 --- a/src/pipewire/buffers.c +++ b/src/pipewire/buffers.c @@ -252,6 +252,9 @@ int pw_buffers_negotiate(struct pw_context *context, uint32_t flags, if ((res = param_filter(result, &input, &output, SPA_PARAM_Meta, &b)) > 0) n_params += res; + if (n_params > 4096) + return -EINVAL; + metas = alloca(sizeof(struct spa_meta) * n_params * 2); n_metas = 0; diff --git a/src/pipewire/conf.c b/src/pipewire/conf.c index 386a08cb5..85f96b5e0 100644 --- a/src/pipewire/conf.c +++ b/src/pipewire/conf.c @@ -359,6 +359,8 @@ int pw_conf_save_state(const char *prefix, const char *name, const struct pw_pro return sfd; size_t tmp_name_size = strlen(name) + 5; + if (tmp_name_size > PATH_MAX) + return -EINVAL; tmp_name = alloca(tmp_name_size); snprintf(tmp_name, tmp_name_size, "%s.tmp", name); if ((fd = openat(sfd, tmp_name, O_CLOEXEC | O_CREAT | O_WRONLY | O_TRUNC, 0600)) < 0) { diff --git a/src/pipewire/impl-node.c b/src/pipewire/impl-node.c index e38b1640e..385cbbb24 100644 --- a/src/pipewire/impl-node.c +++ b/src/pipewire/impl-node.c @@ -31,6 +31,8 @@ PW_LOG_TOPIC_EXTERN(log_node); #define DEFAULT_SYNC_TIMEOUT ((uint64_t)(5 * SPA_NSEC_PER_SEC)) +#define MAX_COMMAND (64*1024u) + /** \cond */ struct impl { struct pw_impl_node this; @@ -1959,14 +1961,17 @@ static void node_event(void *data, const struct spa_event *event) size_t size = SPA_POD_SIZE(&event->pod); /* turn the event and all the arguments into a command */ - command = alloca(size); - memcpy(command, event, size); - command->body.body.type = SPA_TYPE_COMMAND_Node; - command->body.body.id = SPA_NODE_COMMAND_RequestProcess; + if ((command = spa_alloca(1, size, MAX_COMMAND)) != NULL) { + memcpy(command, event, size); + command->body.body.type = SPA_TYPE_COMMAND_Node; + command->body.body.id = SPA_NODE_COMMAND_RequestProcess; - /* send the request process to the driver but only on the - * server size */ - handle_request_process_command(node->driver_node, command); + /* send the request process to the driver but only on the + * server size */ + handle_request_process_command(node->driver_node, command); + } else { + pw_log_warn("%p: ignore large command", node); + } } break; default: