From 638dfa22451b1efb16c73680a52d7832b4091b53 Mon Sep 17 00:00:00 2001 From: Ryan Brown Date: Fri, 4 Sep 2026 14:50:51 -0700 Subject: [PATCH] pipewire: guard process() against a missing port token The filter can start before the local port exists (open sequence) or run after the port was removed (close sequence). With no valid port token there is no buffer to process; skip instead of dereferencing a null token. With asserts compiled out (release build) the old code segfaulted (pw.filter_dequeue_buffer on a null token). --- include/libremidi/backends/pipewire/midi_in.hpp | 6 ++++++ include/libremidi/backends/pipewire/midi_out.hpp | 6 ++++++ include/libremidi/backends/pipewire_ump/midi_in.hpp | 6 ++++++ include/libremidi/backends/pipewire_ump/midi_out.hpp | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/include/libremidi/backends/pipewire/midi_in.hpp b/include/libremidi/backends/pipewire/midi_in.hpp index e0e13e24..fdeaf386 100644 --- a/include/libremidi/backends/pipewire/midi_in.hpp +++ b/include/libremidi/backends/pipewire/midi_in.hpp @@ -93,6 +93,12 @@ class midi_in_pipewire final assert(this->flt); assert(this->port.valid()); + // The filter can run before the local port exists (open sequence) or + // after it was removed (close sequence). With no valid port token there + // is no buffer to process; skip instead of dereferencing a null token + // (a release build with asserts compiled out would segfault). + if (!this->flt || !this->port.valid()) + return; const auto b = pw.filter_dequeue_buffer(this->port.opaque); if (!b) return; diff --git a/include/libremidi/backends/pipewire/midi_out.hpp b/include/libremidi/backends/pipewire/midi_out.hpp index cf6d5506..9ea5e1b9 100644 --- a/include/libremidi/backends/pipewire/midi_out.hpp +++ b/include/libremidi/backends/pipewire/midi_out.hpp @@ -89,6 +89,12 @@ class midi_out_pipewire int process(spa_io_position* pos) { m_process_clock.store(pos->clock.nsec, std::memory_order_relaxed); + // The filter can run before the local port exists (open sequence) or + // after it was removed (close sequence). With no valid port token there + // is no buffer to process; report idle instead of a null dereference + // (a release build with asserts compiled out would segfault). + if (!this->flt || !this->port.valid()) + return 1; const auto b = pw.filter_dequeue_buffer(this->port.opaque); if (!b) return 1; diff --git a/include/libremidi/backends/pipewire_ump/midi_in.hpp b/include/libremidi/backends/pipewire_ump/midi_in.hpp index 8c345dbc..485c61d0 100644 --- a/include/libremidi/backends/pipewire_ump/midi_in.hpp +++ b/include/libremidi/backends/pipewire_ump/midi_in.hpp @@ -100,6 +100,12 @@ class midi_in_pipewire final assert(this->flt); assert(this->port.valid()); + // The filter can run before the local port exists (open sequence) or + // after it was removed (close sequence). With no valid port token there + // is no buffer to process; skip instead of dereferencing a null token + // (a release build with asserts compiled out would segfault). + if (!this->flt || !this->port.valid()) + return; const auto b = pw.filter_dequeue_buffer(this->port.opaque); if (!b) return; diff --git a/include/libremidi/backends/pipewire_ump/midi_out.hpp b/include/libremidi/backends/pipewire_ump/midi_out.hpp index 69558427..f5b43ed7 100644 --- a/include/libremidi/backends/pipewire_ump/midi_out.hpp +++ b/include/libremidi/backends/pipewire_ump/midi_out.hpp @@ -96,6 +96,12 @@ class midi_out_pipewire int process(spa_io_position* pos) { m_process_clock.store(pos->clock.nsec, std::memory_order_relaxed); + // The filter can run before the local port exists (open sequence) or + // after it was removed (close sequence). With no valid port token there + // is no buffer to process; report idle instead of a null dereference + // (a release build with asserts compiled out would segfault). + if (!this->flt || !this->port.valid()) + return 1; const auto b = pw.filter_dequeue_buffer(this->port.opaque); if (!b) return 1;