Blob Blame History Raw
From 0796f5a2d6bf8e175a16d7f58cd0a18783fb4590 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 21 Oct 2008 20:00:36 +0200
Subject: [PATCH] Try to catch certain driver errors

... by verifying return values of snd_pcm_avail_update() and
snd_pcm_begin_mmap() for their sanenness.
---
 src/modules/alsa-util.c          |   60 ++++++++++++++++++++++++++++++++++++++
 src/modules/alsa-util.h          |    3 ++
 src/modules/module-alsa-sink.c   |    6 ++--
 src/modules/module-alsa-source.c |    6 ++--
 4 files changed, 69 insertions(+), 6 deletions(-)

diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c
index ffe7795..39cea49 100644
--- a/src/modules/alsa-util.c
+++ b/src/modules/alsa-util.c
@@ -30,6 +30,7 @@
 
 #include <pulse/sample.h>
 #include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
 
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
@@ -1109,3 +1110,62 @@ pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) {
 
     return item;
 }
+
+snd_pcm_sframes_t pa_alsa_safe_avail_update(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss) {
+    snd_pcm_sframes_t n;
+    size_t k;
+
+    pa_assert(pcm);
+    pa_assert(hwbuf_size > 0);
+    pa_assert(ss);
+
+    /* Some ALSA driver expose weird bugs, let's inform the user about
+     * what is going on */
+
+    n = snd_pcm_avail_update(pcm);
+
+    if (n <= 0)
+        return n;
+
+    k = (size_t) n * pa_frame_size(ss);
+
+    if (k >= hwbuf_size * 3 ||
+        k >= pa_bytes_per_second(ss)*10)
+        pa_log("snd_pcm_avail_update() returned a value that is exceptionally large: %lu bytes (%lu ms) "
+               "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.",
+               (unsigned long) k, (unsigned long) pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC);
+
+    return n;
+}
+
+int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss) {
+    int r;
+    snd_pcm_uframes_t before;
+    size_t k;
+
+    pa_assert(pcm);
+    pa_assert(areas);
+    pa_assert(offset);
+    pa_assert(frames);
+    pa_assert(hwbuf_size > 0);
+    pa_assert(ss);
+
+    before = *frames;
+
+    r = snd_pcm_mmap_begin(pcm, areas, offset, frames);
+
+    if (r < 0)
+        return r;
+
+    k = (size_t) *frames * pa_frame_size(ss);
+
+    if (*frames > before ||
+        k >= hwbuf_size * 3 ||
+        k >= pa_bytes_per_second(ss)*10)
+
+        pa_log("snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms) "
+               "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.",
+               (unsigned long) k, (unsigned long) pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC);
+
+    return r;
+}
diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h
index b66adc1..aaa01c7 100644
--- a/src/modules/alsa-util.h
+++ b/src/modules/alsa-util.h
@@ -92,4 +92,7 @@ int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents);
 
 pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll);
 
+snd_pcm_sframes_t pa_alsa_safe_avail_update(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss);
+int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss);
+
 #endif
diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c
index 4044de1..af83103 100644
--- a/src/modules/module-alsa-sink.c
+++ b/src/modules/module-alsa-sink.c
@@ -261,7 +261,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {
         /* First we determine how many samples are missing to fill the
          * buffer up to 100% */
 
-        if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+        if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
 
             if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
                 continue;
@@ -299,7 +299,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {
 
 /*             pa_log_debug("%lu frames to write", (unsigned long) frames); */
 
-            if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) {
+            if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
 
                 if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
                     continue;
@@ -374,7 +374,7 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) {
 
         snd_pcm_hwsync(u->pcm_handle);
 
-        if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+        if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
 
             if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
                 continue;
diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c
index a743776..dd6ca97 100644
--- a/src/modules/module-alsa-source.c
+++ b/src/modules/module-alsa-source.c
@@ -255,7 +255,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {
 
         snd_pcm_hwsync(u->pcm_handle);
 
-        if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+        if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
 
             if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
                 continue;
@@ -282,7 +282,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {
 
 /*             pa_log_debug("%lu frames to read", (unsigned long) frames); */
 
-            if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) {
+            if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
 
                 if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
                     continue;
@@ -353,7 +353,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) {
 
         snd_pcm_hwsync(u->pcm_handle);
 
-        if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+        if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
 
             if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
                 continue;
-- 
1.6.0.3