security: fix multiple issues in pulse module implementations

- module-zeroconf-publish: guard spa_hook_remove of impl_listener with
  a flag to prevent operating on uninitialized hook when unload is called
  after a partial load failure; bail out of create_service when
  pw_properties_new fails to prevent NULL dereference in publish_service
- module-device-restore: add missing NULL check after message_alloc in
  emit_event; make manager_events static const
- module-jackdbus-detect: fix memory leak on error paths in prepare by
  using goto out instead of early return; free props/sink_props/source_props
  in unload
- module-roc-sink-input: add missing valid_args whitelist
- module-rtp-recv: add missing valid_args whitelist
- module-rtp-send: add missing valid_args whitelist
- module-gsettings: add missing NULL check after strdup in load_group

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Wim Taymans 2026-04-30 16:50:30 +02:00
parent 8e596bd85f
commit ef2541a1ef
7 changed files with 72 additions and 9 deletions

View file

@ -118,6 +118,8 @@ static void emit_event(struct subscribe *s, uint32_t type, uint32_t idx)
{
struct client *client = s->client;
struct message *msg = message_alloc(client->impl, -1, 0);
if (msg == NULL)
return;
pw_log_info("[%s] EVENT index:%u name:%s %d/%d", client->name,
s->data->module->index, s->data->module->info->name, type, idx);
@ -161,7 +163,7 @@ static void manager_updated(void *data, struct pw_manager_object *object)
}
}
struct pw_manager_events manager_events = {
static const struct pw_manager_events manager_events = {
PW_VERSION_MANAGER_EVENTS,
.added = manager_updated,
.updated = manager_updated,

View file

@ -115,6 +115,10 @@ static int load_group(struct module_gsettings_data *d, const struct info *info)
return -errno;
g->name = strdup(info->name);
if (g->name == NULL) {
free(g);
return -errno;
}
g->module = module_create(d->module->impl, info->module[i], info->args[i]);
if (g->module == NULL) {
pw_log_info("can't create module:%s args:%s: %m",

View file

@ -86,13 +86,13 @@ static int module_jackdbus_detect_load(struct module *module)
spa_json_builder_array_push(&b, "{");
pw_properties_serialize_dict(b.f, &data->props->dict, 0);
spa_json_builder_object_push(&b, "source.props", "{");
spa_json_builder_object_push(&b, "source.props", "{");
pw_properties_serialize_dict(b.f, &data->source_props->dict, 0);
spa_json_builder_pop(&b, "}");
spa_json_builder_object_push(&b, "sink.props", "{");
spa_json_builder_pop(&b, "}");
spa_json_builder_object_push(&b, "sink.props", "{");
pw_properties_serialize_dict(b.f, &data->sink_props->dict, 0);
spa_json_builder_pop(&b, "}");
spa_json_builder_pop(&b, "}");
spa_json_builder_pop(&b, "}");
spa_json_builder_pop(&b, "}");
spa_json_builder_close(&b);
data->mod = pw_context_load_module(module->impl->context,
@ -120,6 +120,10 @@ static int module_jackdbus_detect_unload(struct module *module)
d->mod = NULL;
}
pw_properties_free(d->props);
pw_properties_free(d->sink_props);
pw_properties_free(d->source_props);
return 0;
}
@ -172,7 +176,7 @@ static int module_jackdbus_detect_prepare(struct module * const module)
spa_zero(info);
if ((res = module_args_to_audioinfo_keys(module->impl, props, NULL, NULL,
"sink_channels", "sink_channel_map", &info)) < 0) {
return res;
goto out;
} else {
audioinfo_to_properties(&info, sink_props);
}
@ -194,7 +198,7 @@ static int module_jackdbus_detect_prepare(struct module * const module)
spa_zero(info);
if ((res = module_args_to_audioinfo_keys(module->impl, props, NULL, NULL,
"source_channels", "source_channel_map", &info)) < 0) {
return res;
goto out;
} else {
audioinfo_to_properties(&info, source_props);
}

View file

@ -199,8 +199,22 @@ out:
return res;
}
static const char* const valid_args[] = {
"sink",
"sink_input_properties",
"resampler_profile",
"fec_code",
"sess_latency_msec",
"local_ip",
"local_source_port",
"local_repair_port",
"local_control_port",
NULL
};
DEFINE_MODULE_INFO(module_roc_sink_input) = {
.name = "module-roc-sink-input",
.valid_args = valid_args,
.prepare = module_roc_sink_input_prepare,
.load = module_roc_sink_input_load,
.unload = module_roc_sink_input_unload,

View file

@ -168,8 +168,17 @@ out:
return res;
}
static const char* const valid_args[] = {
"sink",
"sap_address",
"latency_msec",
"stream_properties",
NULL
};
DEFINE_MODULE_INFO(module_rtp_recv) = {
.name = "module-rtp-recv",
.valid_args = valid_args,
.prepare = module_rtp_recv_prepare,
.load = module_rtp_recv_load,
.unload = module_rtp_recv_unload,

View file

@ -266,8 +266,27 @@ out:
return res;
}
static const char* const valid_args[] = {
"source",
"format",
"channels",
"rate",
"destination_ip",
"source_ip",
"port",
"mtu",
"loop",
"ttl",
"inhibit_auto_suspend",
"stream_name",
"stream_properties",
"enable_opus",
NULL
};
DEFINE_MODULE_INFO(module_rtp_send) = {
.name = "module-rtp-send",
.valid_args = valid_args,
.prepare = module_rtp_send_prepare,
.load = module_rtp_send_load,
.unload = module_rtp_send_unload,

View file

@ -72,6 +72,8 @@ struct module_zeroconf_publish_data {
struct pw_zeroconf *zeroconf;
struct spa_hook zeroconf_listener;
unsigned int impl_listening:1;
/* lists of services */
struct spa_list pending;
struct spa_list published;
@ -299,6 +301,11 @@ static struct service *create_service(struct module_zeroconf_publish_data *d, st
fill_service_data(d, s, o);
if (s->props == NULL) {
spa_list_remove(&s->link);
return NULL;
}
pw_log_debug("service %p: created for object %p", s, o);
return s;
@ -475,6 +482,7 @@ static int module_zeroconf_publish_load(struct module *module)
&zeroconf_events, data);
impl_add_listener(module->impl, &data->impl_listener, &impl_events, data);
data->impl_listening = true;
return 0;
}
@ -484,7 +492,10 @@ static int module_zeroconf_publish_unload(struct module *module)
struct module_zeroconf_publish_data *d = module->user_data;
struct service *s;
spa_hook_remove(&d->impl_listener);
if (d->impl_listening) {
spa_hook_remove(&d->impl_listener);
d->impl_listening = false;
}
unpublish_all_services(d);