When a virtual sink is loaded and there is only one sound card, then during a profile switch, all sinks and sources can become temporarily unavailable. In that situation, the virtual sink will become the default sink, although it is not connected to a master sink due to the move operation initiated by the profile switch. If module-always sink is loaded, it will load a null-sink in that situation. If also module-switch-on-connect is present, it will change the default sink to the new null sink and try to move the sink-inputs from the virtual sink to the null sink. This leads to a segfault because the master sink of the virtual sink is invalid. This patch fixes the issue by disallowing a moving virtual sink to become the default sink. It also fixes a bug in module-switch-on-connect, which assumed that default_sink is always set. The same applies to the source side. The is_filter_{sink,source}_moving() functions were stolen from one of Tanu's previous unapplied patches. Buglink: https://bugs.freedesktop.org/show_bug.cgi?id=100277 --- src/modules/module-switch-on-connect.c | 12 ++++++++++ src/pulsecore/core.c | 42 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/modules/module-switch-on-connect.c b/src/modules/module-switch-on-connect.c index 776c923e..e2da7222 100644 --- a/src/modules/module-switch-on-connect.c +++ b/src/modules/module-switch-on-connect.c @@ -75,6 +75,12 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void* return PA_HOOK_OK; } + /* No default sink, nothing to move away, just set the new default */ + if (!c->default_sink) { + pa_core_set_configured_default_sink(c, sink); + return PA_HOOK_OK; + } + if (c->default_sink == sink) return PA_HOOK_OK; @@ -135,6 +141,12 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, return PA_HOOK_OK; } + /* No default source, nothing to move away, just set the new default */ + if (!c->default_source) { + pa_core_set_configured_default_source(c, source); + return PA_HOOK_OK; + } + if (c->default_source == source) return PA_HOOK_OK; diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index 52e51db1..99043033 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -258,6 +258,23 @@ void pa_core_set_configured_default_source(pa_core *core, pa_source *source) { pa_core_update_default_source(core); } +/* Test if a sink is a moving filter sink */ +static bool is_filter_sink_moving(pa_sink *s) { + pa_sink *sink = s; + + if (!sink) + return false; + + while (sink->input_to_master) { + sink = sink->input_to_master->sink; + + if (!sink) + return true; + } + + return false; +} + /* a < b -> return -1 * a == b -> return 0 * a > b -> return 1 */ @@ -314,6 +331,10 @@ void pa_core_update_default_sink(pa_core *core) { best = sink; } + /* A moving filter sink cannot be the default sink */ + if (is_filter_sink_moving(best)) + best = NULL; + old_default_sink = core->default_sink; if (best == old_default_sink) @@ -332,6 +353,23 @@ void pa_core_update_default_sink(pa_core *core) { pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SINK_CHANGED], core->default_sink); } +/* Test if a source is a moving filter source */ +static bool is_filter_source_moving(pa_source *o) { + pa_source *source = o; + + if (!source) + return false; + + while (source->output_from_master) { + source = source->output_from_master->source; + + if (!source) + return true; + } + + return false; +} + /* a < b -> return -1 * a == b -> return 0 * a > b -> return 1 */ @@ -398,6 +436,10 @@ void pa_core_update_default_source(pa_core *core) { best = source; } + /* A moving filter source cannot be the default source */ + if (is_filter_source_moving(best)) + best = NULL; + old_default_source = core->default_source; if (best == old_default_source) -- 2.11.0