| From efb6402c3c4a7c26d97c92d70186424097b6e366 Mon Sep 17 00:00:00 2001 |
| From: Takashi Iwai <tiwai@suse.de> |
| Date: Fri, 18 Mar 2022 09:20:36 +0100 |
| Subject: ALSA: oss: Fix PCM OSS buffer allocation overflow |
| |
| From: Takashi Iwai <tiwai@suse.de> |
| |
| commit efb6402c3c4a7c26d97c92d70186424097b6e366 upstream. |
| |
| We've got syzbot reports hitting INT_MAX overflow at vmalloc() |
| allocation that is called from snd_pcm_plug_alloc(). Although we |
| apply the restrictions to input parameters, it's based only on the |
| hw_params of the underlying PCM device. Since the PCM OSS layer |
| allocates a temporary buffer for the data conversion, the size may |
| become unexpectedly large when more channels or higher rates is given; |
| in the reported case, it went over INT_MAX, hence it hits WARN_ON(). |
| |
| This patch is an attempt to avoid such an overflow and an allocation |
| for too large buffers. First off, it adds the limit of 1MB as the |
| upper bound for period bytes. This must be large enough for all use |
| cases, and we really don't want to handle a larger temporary buffer |
| than this size. The size check is performed at two places, where the |
| original period bytes is calculated and where the plugin buffer size |
| is calculated. |
| |
| In addition, the driver uses array_size() and array3_size() for |
| multiplications to catch overflows for the converted period size and |
| buffer bytes. |
| |
| Reported-by: syzbot+72732c532ac1454eeee9@syzkaller.appspotmail.com |
| Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Cc: <stable@vger.kernel.org> |
| Link: https://lore.kernel.org/r/00000000000085b1b305da5a66f3@google.com |
| Link: https://lore.kernel.org/r/20220318082036.29699-1-tiwai@suse.de |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| sound/core/oss/pcm_oss.c | 12 ++++++++---- |
| sound/core/oss/pcm_plugin.c | 5 ++++- |
| 2 files changed, 12 insertions(+), 5 deletions(-) |
| |
| --- a/sound/core/oss/pcm_oss.c |
| +++ b/sound/core/oss/pcm_oss.c |
| @@ -774,6 +774,11 @@ static int snd_pcm_oss_period_size(struc |
| |
| if (oss_period_size < 16) |
| return -EINVAL; |
| + |
| + /* don't allocate too large period; 1MB period must be enough */ |
| + if (oss_period_size > 1024 * 1024) |
| + return -ENOMEM; |
| + |
| runtime->oss.period_bytes = oss_period_size; |
| runtime->oss.period_frames = 1; |
| runtime->oss.periods = oss_periods; |
| @@ -1042,10 +1047,9 @@ static int snd_pcm_oss_change_params_loc |
| goto failure; |
| } |
| #endif |
| - oss_period_size *= oss_frame_size; |
| - |
| - oss_buffer_size = oss_period_size * runtime->oss.periods; |
| - if (oss_buffer_size < 0) { |
| + oss_period_size = array_size(oss_period_size, oss_frame_size); |
| + oss_buffer_size = array_size(oss_period_size, runtime->oss.periods); |
| + if (oss_buffer_size <= 0) { |
| err = -EINVAL; |
| goto failure; |
| } |
| --- a/sound/core/oss/pcm_plugin.c |
| +++ b/sound/core/oss/pcm_plugin.c |
| @@ -61,7 +61,10 @@ static int snd_pcm_plugin_alloc(struct s |
| } |
| if ((width = snd_pcm_format_physical_width(format->format)) < 0) |
| return width; |
| - size = frames * format->channels * width; |
| + size = array3_size(frames, format->channels, width); |
| + /* check for too large period size once again */ |
| + if (size > 1024 * 1024) |
| + return -ENOMEM; |
| if (snd_BUG_ON(size % 8)) |
| return -ENXIO; |
| size /= 8; |