[PATCH] ALSA: firewire-motu: fix invalid memory access when operating hwdep character device
ALSA firewire-motu driver recently got support for event notification via ALSA HwDep interface for register DSP models. However, when polling ALSA HwDep cdev, the driver can cause null pointer dereference for the other models due to accessing to unallocated memory or uninitialized memory.
This commit fixes the bug by check the type of model before accessing to the memory.
Fixes: 634ec0b2906e ("ALSA: firewire-motu: notify event for parameter change in register DSP model") Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/motu/motu-hwdep.c | 72 ++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 27 deletions(-)
diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c index 9c2e457ce692..ae2d01ddc8d3 100644 --- a/sound/firewire/motu/motu-hwdep.c +++ b/sound/firewire/motu/motu-hwdep.c @@ -16,6 +16,47 @@
#include "motu.h"
+static bool has_dsp_event(struct snd_motu *motu) +{ + if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) + return (snd_motu_register_dsp_message_parser_count_event(motu) > 0); + else + return false; +} + +// NOTE: Take care of page fault due to accessing to userspace. +static long copy_dsp_event_to_user(struct snd_motu *motu, char __user *buf, long count, + struct snd_firewire_event_motu_register_dsp_change *event) +{ + if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) { + size_t consumed = 0; + u32 __user *ptr; + u32 ev; + + // Header is filled later. + consumed += sizeof(*event); + + while (consumed < count && + snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) { + ptr = (u32 __user *)(buf + consumed); + if (put_user(ev, ptr)) + return -EFAULT; + consumed += sizeof(ev); + } + + event->type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE; + event->count = (consumed - sizeof(*event)) / 4; + if (copy_to_user(buf, &event, sizeof(*event))) + return -EFAULT; + + count = consumed; + } else { + count = 0; + } + + return count; +} + static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { @@ -25,8 +66,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
spin_lock_irq(&motu->lock);
- while (!motu->dev_lock_changed && motu->msg == 0 && - snd_motu_register_dsp_message_parser_count_event(motu) == 0) { + while (!motu->dev_lock_changed && motu->msg == 0 && !has_dsp_event(motu)) { prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE); spin_unlock_irq(&motu->lock); schedule(); @@ -55,31 +95,10 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, count = min_t(long, count, sizeof(event)); if (copy_to_user(buf, &event, count)) return -EFAULT; - } else if (snd_motu_register_dsp_message_parser_count_event(motu) > 0) { - size_t consumed = 0; - u32 __user *ptr; - u32 ev; - + } else if (has_dsp_event(motu)) { spin_unlock_irq(&motu->lock);
- // Header is filled later. - consumed += sizeof(event.motu_register_dsp_change); - - while (consumed < count && - snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) { - ptr = (u32 __user *)(buf + consumed); - if (put_user(ev, ptr)) - return -EFAULT; - consumed += sizeof(ev); - } - - event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE; - event.motu_register_dsp_change.count = - (consumed - sizeof(event.motu_register_dsp_change)) / 4; - if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change))) - return -EFAULT; - - count = consumed; + count = copy_dsp_event_to_user(motu, buf, count, &event.motu_register_dsp_change); }
return count; @@ -94,8 +113,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_wait(file, &motu->hwdep_wait, wait);
spin_lock_irq(&motu->lock); - if (motu->dev_lock_changed || motu->msg || - snd_motu_register_dsp_message_parser_count_event(motu) > 0) + if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu)) events = EPOLLIN | EPOLLRDNORM; else events = 0;
On Wed, 20 Oct 2021 06:25:55 +0200, Takashi Sakamoto wrote:
ALSA firewire-motu driver recently got support for event notification via ALSA HwDep interface for register DSP models. However, when polling ALSA HwDep cdev, the driver can cause null pointer dereference for the other models due to accessing to unallocated memory or uninitialized memory.
This commit fixes the bug by check the type of model before accessing to the memory.
Fixes: 634ec0b2906e ("ALSA: firewire-motu: notify event for parameter change in register DSP model") Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp
Wouldn't it be simpler to add the flag check in snd_motu_register_dsp_message_parser_count_event() and return 0 if SND_MOTU_SPEC_REGISTER_DSP isn't set there?
thanks,
Takashi
sound/firewire/motu/motu-hwdep.c | 72 ++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 27 deletions(-)
diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c index 9c2e457ce692..ae2d01ddc8d3 100644 --- a/sound/firewire/motu/motu-hwdep.c +++ b/sound/firewire/motu/motu-hwdep.c @@ -16,6 +16,47 @@
#include "motu.h"
+static bool has_dsp_event(struct snd_motu *motu) +{
- if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)
return (snd_motu_register_dsp_message_parser_count_event(motu) > 0);
- else
return false;
+}
+// NOTE: Take care of page fault due to accessing to userspace. +static long copy_dsp_event_to_user(struct snd_motu *motu, char __user *buf, long count,
struct snd_firewire_event_motu_register_dsp_change *event)
+{
- if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
size_t consumed = 0;
u32 __user *ptr;
u32 ev;
// Header is filled later.
consumed += sizeof(*event);
while (consumed < count &&
snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) {
ptr = (u32 __user *)(buf + consumed);
if (put_user(ev, ptr))
return -EFAULT;
consumed += sizeof(ev);
}
event->type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE;
event->count = (consumed - sizeof(*event)) / 4;
if (copy_to_user(buf, &event, sizeof(*event)))
return -EFAULT;
count = consumed;
- } else {
count = 0;
- }
- return count;
+}
static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { @@ -25,8 +66,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
spin_lock_irq(&motu->lock);
- while (!motu->dev_lock_changed && motu->msg == 0 &&
snd_motu_register_dsp_message_parser_count_event(motu) == 0) {
- while (!motu->dev_lock_changed && motu->msg == 0 && !has_dsp_event(motu)) { prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE); spin_unlock_irq(&motu->lock); schedule();
@@ -55,31 +95,10 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, count = min_t(long, count, sizeof(event)); if (copy_to_user(buf, &event, count)) return -EFAULT;
- } else if (snd_motu_register_dsp_message_parser_count_event(motu) > 0) {
size_t consumed = 0;
u32 __user *ptr;
u32 ev;
- } else if (has_dsp_event(motu)) { spin_unlock_irq(&motu->lock);
// Header is filled later.
consumed += sizeof(event.motu_register_dsp_change);
while (consumed < count &&
snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) {
ptr = (u32 __user *)(buf + consumed);
if (put_user(ev, ptr))
return -EFAULT;
consumed += sizeof(ev);
}
event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE;
event.motu_register_dsp_change.count =
(consumed - sizeof(event.motu_register_dsp_change)) / 4;
if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change)))
return -EFAULT;
count = consumed;
count = copy_dsp_event_to_user(motu, buf, count, &event.motu_register_dsp_change);
}
return count;
@@ -94,8 +113,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_wait(file, &motu->hwdep_wait, wait);
spin_lock_irq(&motu->lock);
- if (motu->dev_lock_changed || motu->msg ||
snd_motu_register_dsp_message_parser_count_event(motu) > 0)
- if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu)) events = EPOLLIN | EPOLLRDNORM; else events = 0;
-- 2.30.2
Hi,
On Wed, Oct 20, 2021 at 07:40:46AM +0200, Takashi Iwai wrote:
On Wed, 20 Oct 2021 06:25:55 +0200, Takashi Sakamoto wrote:
ALSA firewire-motu driver recently got support for event notification via ALSA HwDep interface for register DSP models. However, when polling ALSA HwDep cdev, the driver can cause null pointer dereference for the other models due to accessing to unallocated memory or uninitialized memory.
This commit fixes the bug by check the type of model before accessing to the memory.
Fixes: 634ec0b2906e ("ALSA: firewire-motu: notify event for parameter change in register DSP model") Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp
Wouldn't it be simpler to add the flag check in snd_motu_register_dsp_message_parser_count_event() and return 0 if SND_MOTU_SPEC_REGISTER_DSP isn't set there?
Indeed. It's simpler than my patch.
When posted, I considered about pre-condition in context of design by contract. I programmed motu-register-dsp-message-parser.c so that callers of extern functions should guarantee the target is register DSP models.
+// NOTE: Take care of page fault due to accessing to userspace. +static long copy_dsp_event_to_user(struct snd_motu *motu, char __user *buf, long count,
struct snd_firewire_event_motu_register_dsp_change *event)
+{
- if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
size_t consumed = 0;
u32 __user *ptr;
u32 ev;
// Header is filled later.
consumed += sizeof(*event);
while (consumed < count &&
snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) {
ptr = (u32 __user *)(buf + consumed);
if (put_user(ev, ptr))
return -EFAULT;
consumed += sizeof(ev);
}
event->type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE;
event->count = (consumed - sizeof(*event)) / 4;
if (copy_to_user(buf, &event, sizeof(*event)))
The second argument should have been 'event'...
Anyway I'll use a bit more time to consider about what is better to fix the bug.
Thanks
Takashi Sakamoto
Hi Takashi,
I love your patch! Yet something to improve:
[auto build test ERROR on tiwai-sound/for-next] [also build test ERROR on next-20211025] [cannot apply to v5.15-rc6] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Takashi-Sakamoto/ALSA-firewire-motu... base: https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next config: arc-randconfig-r043-20211020 (attached as .config) compiler: arc-elf-gcc (GCC) 11.2.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/0day-ci/linux/commit/0105a381c2c6677118fca8f30b96778d590e... git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Takashi-Sakamoto/ALSA-firewire-motu-fix-invalid-memory-access-when-operating-hwdep-character-device/20211020-122834 git checkout 0105a381c2c6677118fca8f30b96778d590e2173 # save the attached .config to linux build tree mkdir build_dir COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=arc SHELL=/bin/bash sound/firewire/motu/
If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot lkp@intel.com
All errors (new ones prefixed by >>):
In file included from include/asm-generic/current.h:5, from arch/arc/include/asm/current.h:20, from include/linux/sched.h:12, from include/linux/ratelimit.h:6, from include/linux/dev_printk.h:16, from include/linux/device.h:15, from sound/firewire/motu/motu.h:11, from sound/firewire/motu/motu-hwdep.c:17: In function 'check_copy_size', inlined from 'copy_to_user' at include/linux/uaccess.h:199:6, inlined from 'copy_dsp_event_to_user' at sound/firewire/motu/motu-hwdep.c:49:7, inlined from 'hwdep_read' at sound/firewire/motu/motu-hwdep.c:101:11:
include/linux/thread_info.h:211:25: error: call to '__bad_copy_from' declared with attribute error: copy source size is too small
211 | __bad_copy_from(); | ^~~~~~~~~~~~~~~~~
vim +/__bad_copy_from +211 include/linux/thread_info.h
b0377fedb65280 Al Viro 2017-06-29 202 9dd819a15162f8 Kees Cook 2019-09-25 203 static __always_inline __must_check bool b0377fedb65280 Al Viro 2017-06-29 204 check_copy_size(const void *addr, size_t bytes, bool is_source) b0377fedb65280 Al Viro 2017-06-29 205 { b0377fedb65280 Al Viro 2017-06-29 206 int sz = __compiletime_object_size(addr); b0377fedb65280 Al Viro 2017-06-29 207 if (unlikely(sz >= 0 && sz < bytes)) { b0377fedb65280 Al Viro 2017-06-29 208 if (!__builtin_constant_p(bytes)) b0377fedb65280 Al Viro 2017-06-29 209 copy_overflow(sz, bytes); b0377fedb65280 Al Viro 2017-06-29 210 else if (is_source) b0377fedb65280 Al Viro 2017-06-29 @211 __bad_copy_from(); b0377fedb65280 Al Viro 2017-06-29 212 else b0377fedb65280 Al Viro 2017-06-29 213 __bad_copy_to(); b0377fedb65280 Al Viro 2017-06-29 214 return false; b0377fedb65280 Al Viro 2017-06-29 215 } 6d13de1489b6bf Kees Cook 2019-12-04 216 if (WARN_ON_ONCE(bytes > INT_MAX)) 6d13de1489b6bf Kees Cook 2019-12-04 217 return false; b0377fedb65280 Al Viro 2017-06-29 218 check_object_size(addr, bytes, is_source); b0377fedb65280 Al Viro 2017-06-29 219 return true; b0377fedb65280 Al Viro 2017-06-29 220 } b0377fedb65280 Al Viro 2017-06-29 221
--- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
participants (3)
-
kernel test robot
-
Takashi Iwai
-
Takashi Sakamoto