]> git.alsa-project.org Git - alsa-lib.git/commitdiff
pcm: fix and optimize snd_pcm_areas_copy function
authorJaroslav Kysela <perex@perex.cz>
Wed, 20 Mar 2013 19:37:50 +0000 (20:37 +0100)
committerJaroslav Kysela <perex@perex.cz>
Wed, 20 Mar 2013 19:43:34 +0000 (20:43 +0100)
The memcpy() function in snd_pcm_area_copy() should not be called
with the overlapped areas. Alex discovered - using own LD_PRELOAD checked
for memcpy() input - that the memcpy() is called with src == dst.

For some special plugin combos (rate+softvol+hw for example), the same
areas with same offsets can be asked to be copied (softvol). The collapse
check uses own areas created on heap, causing dst_area == src_area &&
dst_offset == src_offset check bypassed.

Two fixes are in this patch:

- use assert to check the memcpy() input for future triggers
- bypass the snd_pcm_area_copy() call for collapsed identical areas

Reported-by: Alexander Kruppa <akruppa@gmail.com>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
src/pcm/pcm.c

index 5053a7bcbde5895c2fae281bcfea0182c499340a..05737d9994f7e55ea96eb5a73d573efa6ae78bf9 100644 (file)
@@ -2711,6 +2711,8 @@ int snd_pcm_area_copy(const snd_pcm_channel_area_t *dst_area, snd_pcm_uframes_t
            dst_area->step == (unsigned int) width) {
                size_t bytes = samples * width / 8;
                samples -= bytes * 8 / width;
+               assert(src < dst || src >= dst + bytes);
+               assert(dst < src || dst >= src + bytes);
                memcpy(dst, src, bytes);
                if (samples == 0)
                        return 0;
@@ -2845,17 +2847,21 @@ int snd_pcm_areas_copy(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_
                                break;
                }
                if (chns > 1 && chns * width == step) {
-                       /* Collapse the areas */
-                       snd_pcm_channel_area_t s, d;
-                       s.addr = src_start->addr;
-                       s.first = src_start->first;
-                       s.step = width;
-                       d.addr = dst_start->addr;
-                       d.first = dst_start->first;
-                       d.step = width;
-                       snd_pcm_area_copy(&d, dst_offset * chns,
-                                         &s, src_offset * chns, 
-                                         frames * chns, format);
+                       if (src_offset != dst_offset ||
+                           src_start->addr != dst_start->addr ||
+                           src_start->first != dst_start->first) {
+                               /* Collapse the areas */
+                               snd_pcm_channel_area_t s, d;
+                               s.addr = src_start->addr;
+                               s.first = src_start->first;
+                               s.step = width;
+                               d.addr = dst_start->addr;
+                               d.first = dst_start->first;
+                               d.step = width;
+                               snd_pcm_area_copy(&d, dst_offset * chns,
+                                                 &s, src_offset * chns, 
+                                                 frames * chns, format);
+                       }
                        channels -= chns;
                } else {
                        snd_pcm_area_copy(dst_start, dst_offset,