From eb43532407b7fb572a20bb4c6a8cbe03a32e5919 Mon Sep 17 00:00:00 2001 From: sitiyou Date: Mon, 2 Mar 2026 20:46:25 +0800 Subject: [PATCH 1/6] output/Control: fix fail_timer duration multiplication Remove incorrect `* 1000` multiplier in fail_timer.Check() call. When the PeriodClock API was migrated to std::chrono in commit 401189984 , the fail_timer.Check() call was not updated. This caused the timer to check for a duration that was 1000 times longer than intended. --- src/output/Control.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/output/Control.cxx b/src/output/Control.cxx index d8fa73bfe3..b936517dc3 100644 --- a/src/output/Control.cxx +++ b/src/output/Control.cxx @@ -305,7 +305,7 @@ AudioOutputControl::LockUpdate(const AudioFormat audio_format, if (enabled && really_enabled) { if (force || !fail_timer.IsDefined() || - fail_timer.Check(REOPEN_AFTER * 1000)) { + fail_timer.Check(REOPEN_AFTER)) { return Open(std::move(lock), audio_format, mp); } } else if (IsOpen()) From c83cb51200edbdf4e31882a2e5cf9904d55443fc Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 11 Mar 2026 09:27:22 +0100 Subject: [PATCH 2/6] subprojects: update sqlite3 to 3.52.0-1 --- subprojects/sqlite3.wrap | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/subprojects/sqlite3.wrap b/subprojects/sqlite3.wrap index 77fae8e774..0bbb146e8b 100644 --- a/subprojects/sqlite3.wrap +++ b/subprojects/sqlite3.wrap @@ -1,14 +1,14 @@ [wrap-file] -directory = sqlite-amalgamation-3510200 -source_url = https://www.sqlite.org/2026/sqlite-amalgamation-3510200.zip -source_filename = sqlite-amalgamation-3510200.zip -source_hash = 6e2a845a493026bdbad0618b2b5a0cf48584faab47384480ed9f592d912f23ec -source_fallback_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.51.2-1/get_source/sqlite-amalgamation-3510200.zip -patch_filename = sqlite3_3.51.2-1_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.51.2-1/get_patch -patch_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sqlite3_3.51.2-1/sqlite3_3.51.2-1_patch.zip -patch_hash = eef624e9916ec7b7c62dccbf1a04a8bc6fdcacfb5b58daaba695c204e00bec67 -wrapdb_version = 3.51.2-1 +directory = sqlite-amalgamation-3520000 +source_url = https://www.sqlite.org/2026/sqlite-amalgamation-3520000.zip +source_filename = sqlite-amalgamation-3520000.zip +source_hash = 45d0fea15971dd1300e5b509f48ca134621e10d9297713b64618fbca21b325fa +source_fallback_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.52.0-1/get_source/sqlite-amalgamation-3520000.zip +patch_filename = sqlite3_3.52.0-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.52.0-1/get_patch +patch_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sqlite3_3.52.0-1/sqlite3_3.52.0-1_patch.zip +patch_hash = a4b065e5bf76df313759c95aba623456744953b432b68e0c76f87b16d839293b +wrapdb_version = 3.52.0-1 [provide] dependency_names = sqlite3 From 17b36d6d3a4ec811c1654ddf64a0268f8305bf2a Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 11 Mar 2026 09:26:33 +0100 Subject: [PATCH 3/6] decoder/sidplay: use StringAfterPrefix() Fixes out-of-bounds read due to improper use of memcmp() and makes the code simpler. Similar to commit 8925cc17d8436b6793930d11dc2168a3f66d8c94 --- src/decoder/plugins/SidplayDecoderPlugin.cxx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/decoder/plugins/SidplayDecoderPlugin.cxx b/src/decoder/plugins/SidplayDecoderPlugin.cxx index 9fb4c92603..ad4af1c402 100644 --- a/src/decoder/plugins/SidplayDecoderPlugin.cxx +++ b/src/decoder/plugins/SidplayDecoderPlugin.cxx @@ -18,6 +18,7 @@ #include "util/AllocatedString.hxx" #include "util/CharUtil.hxx" #include "util/ByteOrder.hxx" +#include "util/StringCompare.hxx" #include "Log.hxx" #include @@ -34,8 +35,6 @@ #include #include -#include - #define SUBTUNE_PREFIX "tune_" static constexpr Domain sidplay_domain("sidplay"); @@ -136,11 +135,10 @@ struct SidplayContainerPath { static unsigned ParseSubtuneName(const char *base) noexcept { - if (memcmp(base, SUBTUNE_PREFIX, sizeof(SUBTUNE_PREFIX) - 1) != 0) + base = StringAfterPrefix(base, SUBTUNE_PREFIX); + if (base == nullptr) return 0; - base += sizeof(SUBTUNE_PREFIX) - 1; - char *endptr; auto track = strtoul(base, &endptr, 10); if (endptr == base || *endptr != '.') From 08c9cef2e81cb07c43b20bcabb7769238699e016 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 11 Mar 2026 10:59:06 +0100 Subject: [PATCH 4/6] doc/developer.rst: document that iostreams, std::filesystem and std::regex is not allowed --- doc/developer.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/developer.rst b/doc/developer.rst index bd38236f22..b5de6af67f 100644 --- a/doc/developer.rst +++ b/doc/developer.rst @@ -32,6 +32,16 @@ Some example code: } +C++ Subset +========== + +The following C++ features are not allowed in MPD: + +* :code:`` (bloated) +* :code:`` (bloated, weird error handling) +* :code:`` (PCRE is better) + + Error handling ============== From 2741885a95f0085e7f3ebcc03a555e64c19549c6 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 11 Mar 2026 11:14:13 +0100 Subject: [PATCH 5/6] Main: abort if threads were created by a library during process startup A while ago, https://github.com/MusicPlayerDaemon/MPD/issues/2363 was reported ("State file not saved to file when mpd is started and stopped with systemd"). The root cause was that `libopenblas` creates worker threads before MPD's main() is invoked, and these worker threads do not have a proper signal block mask, therefore they receive SIGTERM and this will unconditionally terminate MPD without proper shutdown, leading to data loss. This kind of library behavior implemented by libopenmpt is unacceptable, and to prevent more bad surprises like this, MPD now refuses to start when linked with such a library. It aborts startup when it sees that threads have already been created outside of its control. --- NEWS | 1 + meson.build | 2 ++ src/Main.cxx | 13 +++++++++++++ src/io/linux/ProcStatus.cxx | 36 ++++++++++++++++++++++++++++++++++++ src/io/linux/ProcStatus.hxx | 13 +++++++++++++ src/io/linux/meson.build | 23 +++++++++++++++++++++++ 6 files changed, 88 insertions(+) create mode 100644 src/io/linux/ProcStatus.cxx create mode 100644 src/io/linux/ProcStatus.hxx create mode 100644 src/io/linux/meson.build diff --git a/NEWS b/NEWS index 06290f8c4c..b24f96c7d1 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ ver 0.24.9 (not yet released) - fix auto-reopen after error times out * database - upnp: refuse to build with libupnp >= 1.14.26 due to API breakage +* abort if threads were created by a library during process startup ver 0.24.8 (2026/01/26) * input diff --git a/meson.build b/meson.build index 46f7c98c26..358f54f375 100644 --- a/meson.build +++ b/meson.build @@ -480,6 +480,7 @@ subdir('src/cmdline') subdir('src/time') subdir('src/lib/icu') subdir('src/io') +subdir('src/io/linux') subdir('src/fs') subdir('src/io/fs') subdir('src/io/uring') @@ -632,6 +633,7 @@ mpd = build_target( fs_dep, net_dep, util_dep, + io_linux_dep, event_dep, thread_dep, neighbor_glue_dep, diff --git a/src/Main.cxx b/src/Main.cxx index a2bc8ce068..b3f18427a9 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -45,6 +45,10 @@ #include "config/PartitionConfig.hxx" #include "util/ScopeExit.hxx" +#ifdef __linux__ +#include "io/linux/ProcStatus.hxx" +#endif + #ifdef ENABLE_DAEMON #include "unix/Daemon.hxx" #endif @@ -691,6 +695,15 @@ mpd_main(int argc, char *argv[]) int main(int argc, char *argv[]) noexcept try { +#ifdef __linux__ + if (ProcStatusThreads() > 1) + /* threads created by libraries before main() can + cause all sorts of bugs that are difficult to + analyze; bail out quickly and refuse to run if we + detect such a thing */ + throw "Background threads were detected before MPD could initialize. This is likely caused by a misbehaving library. Aborting to prevent erroneous behavior. Please report."; +#endif + AtScopeExit() { log_deinit(); }; #ifdef _WIN32 diff --git a/src/io/linux/ProcStatus.cxx b/src/io/linux/ProcStatus.cxx new file mode 100644 index 0000000000..3d8b9fc3fd --- /dev/null +++ b/src/io/linux/ProcStatus.cxx @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: BSD-2-Clause +// author: Max Kellermann + +#include "ProcStatus.hxx" +#include "io/UniqueFileDescriptor.hxx" +#include "util/NumberParser.hxx" +#include "util/StringSplit.hxx" + +using std::string_view_literals::operator""sv; + +unsigned +ProcStatusThreads() noexcept +{ + UniqueFileDescriptor fd; + if (!fd.OpenReadOnly("/proc/self/status")) + return 0; + + char buffer[4096]; + ssize_t nbytes = fd.Read(std::as_writable_bytes(std::span{buffer})); + if (nbytes <= 0) + return 0; + + const std::string_view contents{buffer, static_cast(nbytes)}; + + static constexpr std::string_view prefix = "\nThreads:\t"sv; + const auto position = contents.find(prefix); + if (position == contents.npos) + return 0; + + const std::string_view value_string = Split(contents.substr(position + prefix.size()), '\n').first; + unsigned value; + if (!ParseIntegerTo(value_string, value)) + return 0; + + return value; +} diff --git a/src/io/linux/ProcStatus.hxx b/src/io/linux/ProcStatus.hxx new file mode 100644 index 0000000000..ffe6bfc4c4 --- /dev/null +++ b/src/io/linux/ProcStatus.hxx @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: BSD-2-Clause +// author: Max Kellermann + +#pragma once + +/** + * Attempt to determine the number of threads of the current process. + * + * @return the number of threads or 0 on error + */ +[[gnu::pure]] +unsigned +ProcStatusThreads() noexcept; diff --git a/src/io/linux/meson.build b/src/io/linux/meson.build new file mode 100644 index 0000000000..d251aedf3a --- /dev/null +++ b/src/io/linux/meson.build @@ -0,0 +1,23 @@ +if not is_linux + io_linux_dep = dependency('', required: false) + subdir_done() +endif + +io_linux = static_library( + 'io_linux', + 'ProcStatus.cxx', + include_directories: inc, + dependencies: [ + io_dep, + util_dep, + ], +) + +io_linux_dep = declare_dependency( + link_with: io_linux, + dependencies: [ + fmt_dep, + io_dep, + util_dep, + ], +) From 7a9afa059e95668c912f779219ee8fe1e44dd2aa Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 11 Mar 2026 11:54:28 +0100 Subject: [PATCH 6/6] release v0.24.9 --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index b24f96c7d1..0f2a1f62b3 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -ver 0.24.9 (not yet released) +ver 0.24.9 (2026/03/11) * input - curl: fix build failure after CURL 8.19 API change * output