pcm: fix and optimize snd_pcm_areas_copy function

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>
This commit is contained in:
Jaroslav Kysela 2013-03-20 20:37:50 +01:00
parent 28493d9d69
commit 009193a345

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