From 1008f918c97fe776ea4eb9313ef925cd66eec820 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jun 2026 18:48:02 +0100 Subject: [PATCH 1/7] Add note about new --with-metadata-pipe and --with-metadata-multicast configuration flags. --- scripts/shairport-sync.conf | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 86009806..52ab212e 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -325,13 +325,16 @@ dsp = // How to deal with metadata, including artwork // For this section to be operative, Shairport Sync must be built with at one (or more) of the following configuration flags: // --with-metadata, --with-dbus-interface, --with-mpris-interface or --with-mqtt-client. -// In those cases, "enabled" and "include_cover_art" will both be "yes" by default +// In those cases, "enabled" and "include_cover_art" will both be "yes" by default. +// To get metadata through a unix pipe, Shairport Sync must be built with the --with-metadata-pipe configuration flag. +// To get metadata through a multicast port, Shairport Sync must be built with the --with-metadata-multicast configuration flag. + metadata = { // enabled = "yes"; // set this to yes to get Shairport Sync to solicit metadata from the source and to pass it on via a pipe // include_cover_art = "yes"; // set to "yes" to get Shairport Sync to solicit cover art from the source and pass it via the pipe. You must also set "enabled" to "yes". // cover_art_cache_directory = "/tmp/shairport-sync/.cache/coverart"; // artwork will be stored in this directory if the dbus or MPRIS interfaces are enabled or if the MQTT client is in use. Set it to "" to prevent caching, which may be useful on some systems -// pipe_name = "/tmp/shairport-sync-metadata"; +// pipe_name = "/tmp/shairport-sync-metadata"; // pipe_timeout = 5000; // wait for this number of milliseconds for a blocked pipe to unblock before giving up // progress_interval = 0.0; // if non-zero, progress 'phbt' messages will be sent at the interval specified in seconds. A 'phb0' message will also be sent when the first audio frame of a play session is about to be played. // Each message consists of the RTPtime of a a frame of audio and the exact system time when it is to be played. The system time, in nanoseconds, is based the CLOCK_MONOTONIC_RAW of the machine -- if available -- or CLOCK_MONOTONIC otherwise. From 37fff1ac39ba636001bae0345042bd4d710c1e94 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jun 2026 18:51:25 +0100 Subject: [PATCH 2/7] Add Jack deprecation note to the configuration file. [skip ci] --- scripts/shairport-sync.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 52ab212e..aa8d35e2 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -236,7 +236,7 @@ pulseaudio = // default_channel_layouts = "alsa"; // Set to "alsa" (default) for the alsa-compatible channel layouts (see "PA_CHANNEL_MAP_ALSA"), or set to "pulseaudio" if you want PulseAudio's own channel layouts ("PA_CHANNEL_MAP_DEFAULT") to be used instead. }; -// Parameters for the JACK Audio Connection Kit backend. +// Parameters for the JACK Audio Connection Kit backend. Please note that this is now deprecated. // For this section to be operative, Shairport Sync must be built with the following configuration flag: // --with-jack jack = From 5920deae13bb88ca53d8d02fdf2118d14caba58c Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sun, 28 Jun 2026 17:06:40 +0100 Subject: [PATCH 3/7] Stop allocating large buffers on the stack, as it will overflow on, e.g. Alpine and embedded Linuxes, where the stack frame allocations are smaller. Use mallocs instead. --- ap2_buffered_audio_processor.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/ap2_buffered_audio_processor.c b/ap2_buffered_audio_processor.c index e7fa237f..47f89c5d 100644 --- a/ap2_buffered_audio_processor.c +++ b/ap2_buffered_audio_processor.c @@ -151,7 +151,6 @@ void *rtp_buffered_audio_processor(void *arg) { debug(1, "cannot allocate an audio buffer of %zu bytes!", buffered_audio->buffer_max_size); pthread_cleanup_push(malloc_cleanup, &buffered_audio->buffer); - // pthread_mutex_lock(&conn->buffered_audio_mutex); buffered_audio->toq = buffered_audio->buffer; buffered_audio->eoq = buffered_audio->buffer; @@ -161,10 +160,20 @@ void *rtp_buffered_audio_processor(void *arg) { "ap2_buf_rdr_%d", conn->connection_number); pthread_cleanup_push(thread_cleanup, buffered_reader_thread); + const size_t buffer_packet_size = 16 * 1024; // it looks as if 4096 is the largest size (?) + uint8_t *packet = malloc(buffer_packet_size); + if (packet == NULL) + debug(1, "cannot allocate an audio packet buffer of %zu bytes!", buffer_packet_size); + pthread_cleanup_push(malloc_cleanup, &packet); + const size_t leading_free_space_length = 256; // leave this many bytes free to make room for prefixes that might be added later - uint8_t packet[32 * 1024]; - unsigned char m[32 * 1024 + leading_free_space_length]; + + unsigned char *m = malloc(buffer_packet_size + leading_free_space_length); + if (m == NULL) + debug(1, "cannot allocate an audio m buffer of %zu bytes!", buffer_packet_size + leading_free_space_length); + pthread_cleanup_push(malloc_cleanup, &m); + // unsigned char m[32 * 1024 + leading_free_space_length]; unsigned char *payload_pointer = NULL; unsigned long long payload_length = 0; @@ -365,7 +374,7 @@ void *rtp_buffered_audio_processor(void *arg) { unsigned int f = 0; for (f = 0; f < MAX_DEFERRED_FLUSH_REQUESTS; f++) { if ((conn->ap2_deferred_flush_requests[f].inUse != 0) && - (conn->ap2_deferred_flush_requests[f].active = 0)) { + (conn->ap2_deferred_flush_requests[f].active == 0)) { debug(1, "deferred flush cancelled by an immediate flush: flushFromTS: %12u, " "flushFromSeq: %12u, " @@ -690,6 +699,8 @@ void *rtp_buffered_audio_processor(void *arg) { } while (finished == 0); // debug(1, "Connection %d: rtp_buffered_audio_processor PID %d exiting", conn->connection_number, // syscall(SYS_gettid)); + pthread_cleanup_pop(1); // m + pthread_cleanup_pop(1); // packet pthread_cleanup_pop(1); // buffered_tcp_reader thread creation pthread_cleanup_pop(1); // buffer malloc pthread_cleanup_pop(1); // not_full_cv From 012c2a656968610015c81de2cdd81b269fba657e Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sun, 28 Jun 2026 17:08:11 +0100 Subject: [PATCH 4/7] If the minimum DAC queue size is not changed from maxint, assume it has never been set and indicate that it is not available. --- player.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/player.c b/player.c index b9802d08..edc7f347 100644 --- a/player.c +++ b/player.c @@ -4093,7 +4093,11 @@ void *player_thread_func(void *arg) { statistics_item(" Late", "%*" PRIu64 "", 6, conn->late_packets); statistics_item("Too Late", "%*" PRIu64 "", 8, conn->too_late_packets); statistics_item("Resend Reqs", "%*" PRIu64 "", 11, conn->resend_requests); - statistics_item("Min DAC Queue", "%*" PRIu64 "", 13, minimum_dac_queue_size); + if (minimum_dac_queue_size == UINT64_MAX) { + statistics_item("Min DAC Queue", " n/a"); // same size as below, right justified + } else { + statistics_item("Min DAC Queue", "%*" PRIu64 "", 13, minimum_dac_queue_size); + } statistics_item("Min Buffers", "%*" PRIu32 "", 11, minimum_buffer_occupancy); statistics_item("Max Buffers", "%*" PRIu32 "", 11, maximum_buffer_occupancy); #ifdef CONFIG_AIRPLAY_2 From 69f5e78050dea2cf34c0ddbdbf108ea11bdd896e Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sun, 28 Jun 2026 17:08:56 +0100 Subject: [PATCH 5/7] Add a few tools and do a recompile for more comportable troubleshooting. --- docker/Dockerfile | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 106e07f5..a790be7f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -120,6 +120,25 @@ WORKDIR / ##### DEV ##### FROM shairport-sync AS dev RUN apk add bash +RUN apk add gdb +RUN apk add valgrind +RUN ulimit -c unlimited + + +WORKDIR /shairport-sync/build + +# Rebuild with no optimisation and debug symbols in place + +RUN CFLAGS="-O0 -g" CXXFLAGS="-O0 -g" ../configure --sysconfdir=/etc --with-alsa --with-pulseaudio --with-soxr --with-avahi --with-ssl=openssl \ + --with-airplay-2 --with-metadata --with-dummy --with-pipe --with-dbus-interface \ + --with-stdout --with-mpris-interface --with-mqtt-client \ + --with-convolution --with-pipewire +RUN make clean +RUN make +RUN DESTDIR=install make install +WORKDIR / + + RUN addgroup shairport-sync && \ adduser -D shairport-sync -G shairport-sync && \ From 6db0ba16a44e0a15d133451c351fb598497dd25c Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sun, 28 Jun 2026 17:23:14 +0100 Subject: [PATCH 6/7] Add sys-nice request to docker compose file and comment out unnecessary stuff. --- docker/docker-compose.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 398b2d2b..2cc5737d 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -5,20 +5,22 @@ services: image: mikebrady/shairport-sync:latest network_mode: host # Required for AirPlay 2 restart: unless-stopped + cap_add: + - SYS_NICE # environment: # PULSE_SERVER: unix:/tmp/pulseaudio.socket # Path for PulseAudio socket # PULSE_COOKIE: /tmp/pulseaudio.cookie # Path for PulseAudio cookie # XDG_RUNTIME_DIR: /tmp # Path for pipewire # ENABLE_AVAHI: 0 # Disable DBus and Avahi daemon inside the container devices: - - "/dev/snd" # ALSA device, omit if using PulseAudio + - "/dev/snd" # For access to ALSA devices-- unnecessary unless you are outputting to an ALSA hardware device # volumes: # - ./volumes/shairport-sync/shairport-sync.conf:/etc/shairport-sync.conf # Customised Shairport Sync configuration file. # - /run/user/1000/pulse/native:/tmp/pulseaudio.socket # PulseAudio socket when using that backend # - /run/user/1000/pipewire-0:/tmp/pipewire-0 # Pipewire socket when using pipewire # - /var/run/dbus:/var/run/dbus # DBus when ENABLE_AVAHI set to 0 # - /var/run/avahi-daemon:/var/run/avahi-daemon # Avahi socket when ENABLE_AVAHI set to 0 - # command: -o pw # You can specify the desired output with command: + # command: -o pipewire # Add any options for Shairport Sync here, e.g. output backend PipeWire logging: options: max-size: "200k" From 89b78b8927e7afed9fe78c2dbdfec34c05bbb5f3 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sun, 28 Jun 2026 17:34:25 +0100 Subject: [PATCH 7/7] Update RELEASENOTES-DEVELOPMENT.md for version 5.1-dev-52 [skip ci] Document bug fixes and minor improvements in release notes. --- RELEASENOTES-DEVELOPMENT.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 019c2c76..e93a6f5d 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,13 @@ +Version 5.1-dev-52-g6db0ba16 +== +**Bug Fixes** +* Fix a condition that could cause a stack frame overflow under rare conditions. The problem was fixed by increased use of `malloc` in place of stack-based allocation. Thanks to [Arthur Loureiro](https://github.com/ARLBR10) for reporting the [issue](https://github.com/mikebrady/shairport-sync/issues/2221). + +**Minor Improvements** +* Add a `sys-nice` request to the sample Docker compose file. +* Add a few tools and do a recompile for more comfortable troubleshooting. +* If the minimum DAC queue size is not changed from `UINT64_MAX`, assume it has never been set and indicate that it is not available (`"n/a"`) in the statistics log. + Version 5.1-dev-38-g3780b504 == **Bug Fixes**