All of lore.kernel.org
 help / color / mirror / Atom feed
From: Takashi Iwai <tiwai@suse.de>
To: butt3rflyh4ck <butterflyhuangxx@gmail.com>
Cc: alsa-devel@alsa-project.org, LKML <linux-kernel@vger.kernel.org>,
	pierre-louis.bossart@linux.intel.com, tiwai@suse.com,
	broonie@kernel.org
Subject: Re: A divide error bug in snd_pcm_write
Date: Mon, 26 Sep 2022 12:21:58 +0200	[thread overview]
Message-ID: <87v8pa306x.wl-tiwai@suse.de> (raw)
In-Reply-To: <CAFcO6XNk5Wtjju=DBOcJr46miBbaWT7jL+zjhWMp+xnz7k5K9A@mail.gmail.com>

On Mon, 26 Sep 2022 11:26:17 +0200,
butt3rflyh4ck wrote:
> 
> Hi, there is a divide error bug in snd_pcm_write in
> sound/core/pcm_native.c in the latest kernel.
> 
> ##Root Cause
> When open the device of /dev/snd/pcmC#D#p, there would attach a
> runtime to pcm->substream via snd_pcm_open_substream. see the code
> below:
> ```
> int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
>     struct file *file,
>     struct snd_pcm_substream **rsubstream)
> {
> ......
> 
>  runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
> if (runtime == NULL)
> return -ENOMEM;
> 
> size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status));
> runtime->status = alloc_pages_exact(size, GFP_KERNEL);
> if (runtime->status == NULL) {
> kfree(runtime);
> return -ENOMEM;
> }
> memset(runtime->status, 0, size);
> 
> size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control));
> runtime->control = alloc_pages_exact(size, GFP_KERNEL);
> if (runtime->control == NULL) {
> free_pages_exact(runtime->status,
>       PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)));
> kfree(runtime);
> return -ENOMEM;
> }
> memset(runtime->control, 0, size);
> 
> init_waitqueue_head(&runtime->sleep);
> init_waitqueue_head(&runtime->tsleep);
> 
> runtime->status->state = SNDRV_PCM_STATE_OPEN;
> mutex_init(&runtime->buffer_mutex);
> atomic_set(&runtime->buffer_accessing, 0);
> 
> substream->runtime = runtime;
> substream->private_data = pcm->private_data;
> substream->ref_count = 1;
> substream->f_flags = file->f_flags;
> substream->pid = get_pid(task_pid(current));
> pstr->substream_opened++;
> *rsubstream = substream;
> return 0;
> }
> ```
> It would kzmalloc a new runtime. And initialize runtime simply.
> If we write some data to the device. it would call snd_pcm_write or
> snd_pcm_writev. and read some date from the device, it would call
> snd_pcm_read or snd_pcm_readv.
> Anyway, the four function would use data of runtime, but some data of
> runtime is NULL not be initialized, see the code below:
> ```
> static ssize_t snd_pcm_write(struct file *file, const char __user *buf,
>     size_t count, loff_t * offset)
> {
> struct snd_pcm_file *pcm_file;
> struct snd_pcm_substream *substream;
> struct snd_pcm_runtime *runtime;
> snd_pcm_sframes_t result;
> 
> pcm_file = file->private_data;
> substream = pcm_file->substream;
> if (PCM_RUNTIME_CHECK(substream))
> return -ENXIO;
> runtime = substream->runtime;
> if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
>    runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
> return -EBADFD;
> if (!frame_aligned(runtime, count))    ///////  [1]
> return -EINVAL;
> count = bytes_to_frames(runtime, count);    /////// [2]
> result = snd_pcm_lib_write(substream, buf, count);
> if (result > 0)
> result = frames_to_bytes(runtime, result);
> return result;
> }
> ```
> [1] call frame_aligned to aligned.
> ```
> static inline int frame_aligned(struct snd_pcm_runtime *runtime, ssize_t bytes)
> {
> return bytes % runtime->byte_align == 0;
> }
> ```
> but runtime->byte_align is NULL.
> 
> [2] call bytes_to_frames.
> ```
> static inline ssize_t frames_to_bytes(struct snd_pcm_runtime *runtime,
> snd_pcm_sframes_t size)
> {
> return size * runtime->frame_bits / 8;
> }
> ```
> but runtime->frame_bits is NULL.
> 
> ##reproduce it
> [ 1189.305083][ T4656] divide error: 0000 [#1] PREEMPT SMP
> [ 1189.305600][ T4656] CPU: 1 PID: 4656 Comm: snd_pcm_write Not
> tainted 6.0.0-rc7 #16
> [ 1189.306157][ T4656] Hardware name: QEMU Standard PC (i440FX + PIIX,
> 1996), BIOS 1.13.0-1ubuntu1 04/01/2014
> [ 1189.306760][ T4656] RIP: 0010:snd_pcm_write+0x33/0xa0
> [ 1189.307155][ T4656] Code: 8b 38 48 85 ff 74 72 48 8b 9f c0 00 00 00
> 48 85 db 74 66 48 8b 83 00 01 00 00 f7 00 f7 ff ff ff 74 6b 48 89 d0
> 48 89 d1 31 d2 <48> f7 b3 91
> [ 1189.308553][ T4656] RSP: 0018:ffffc9000adc7e68 EFLAGS: 00010246
> [ 1189.309034][ T4656] RAX: 0000000000000000 RBX: ffff888048ec2000
> RCX: 0000000000000000
> [ 1189.309583][ T4656] RDX: 0000000000000000 RSI: 0000000000000000
> RDI: ffff888046fc9c00
> [ 1189.310163][ T4656] RBP: 0000000000000000 R08: 0000000000000000
> R09: 0000000000020026
> [ 1189.310679][ T4656] R10: 0000000000000001 R11: 0000000000000000
> R12: 0000000000000000
> [ 1189.311226][ T4656] R13: ffffc9000adc7f08 R14: 0000000000000000
> R15: 0000000000000000
> [ 1189.311754][ T4656] FS:  00000000012d8880(0000)
> GS:ffff88807ec00000(0000) knlGS:0000000000000000
> [ 1189.312350][ T4656] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [ 1189.312780][ T4656] CR2: 0000000020000000 CR3: 000000004b496000
> CR4: 00000000000006e0
> [ 1189.313234][ T4656] Call Trace:
> [ 1189.313424][ T4656]  <TASK>
> [ 1189.313597][ T4656]  vfs_write+0xe6/0x4d0
> [ 1189.313836][ T4656]  ksys_write+0x60/0xe0
> [ 1189.314071][ T4656]  do_syscall_64+0x35/0xb0
> [ 1189.314324][ T4656]  entry_SYSCALL_64_after_hwframe+0x63/0xcd
> [ 1189.314734][ T4656] RIP: 0033:0x44dc3d
> [ 1189.315007][ T4656] Code: 28 c3 e8 36 29 00 00 66 0f 1f 44 00 00 f3
> 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b
> 4c 24 08 0f 05 <48> 3d 01 f8
> [ 1189.316338][ T4656] RSP: 002b:00007ffcbdf5ef38 EFLAGS: 00000246
> ORIG_RAX: 0000000000000001
> [ 1189.317012][ T4656] RAX: ffffffffffffffda RBX: 0000000000400530
> RCX: 000000000044dc3d
> [ 1189.317693][ T4656] RDX: 0000000000000000 RSI: 0000000000000000
> RDI: 0000000000000003
> [ 1189.318172][ T4656] RBP: 00007ffcbdf5ef50 R08: 0000000082000000
> R09: 0000000000000000
> [ 1189.318648][ T4656] R10: 000000000000ffff R11: 0000000000000246
> R12: 00000000004030a0
> [ 1189.319182][ T4656] R13: 0000000000000000 R14: 00000000004c5018
> R15: 0000000000000000

The question is how the code passes the check before [1]:

 if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
     runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
	return -EBADFD;

The uninitialized state should have been with SNDRV_PCM_STATE_OPEN,
and runtime->byte_align is set up at snd_pcm_hw_params() followed by
snd_pcm-set_state() to chage the state to SNDRV_PCM_STATE_SETUP.

Which kernel version are you testing?


Takashi

WARNING: multiple messages have this Message-ID (diff)
From: Takashi Iwai <tiwai@suse.de>
To: butt3rflyh4ck <butterflyhuangxx@gmail.com>
Cc: perex@perex.cz, tiwai@suse.com,
	pierre-louis.bossart@linux.intel.com, broonie@kernel.org,
	alsa-devel@alsa-project.org, LKML <linux-kernel@vger.kernel.org>
Subject: Re: A divide error bug in snd_pcm_write
Date: Mon, 26 Sep 2022 12:21:58 +0200	[thread overview]
Message-ID: <87v8pa306x.wl-tiwai@suse.de> (raw)
In-Reply-To: <CAFcO6XNk5Wtjju=DBOcJr46miBbaWT7jL+zjhWMp+xnz7k5K9A@mail.gmail.com>

On Mon, 26 Sep 2022 11:26:17 +0200,
butt3rflyh4ck wrote:
> 
> Hi, there is a divide error bug in snd_pcm_write in
> sound/core/pcm_native.c in the latest kernel.
> 
> ##Root Cause
> When open the device of /dev/snd/pcmC#D#p, there would attach a
> runtime to pcm->substream via snd_pcm_open_substream. see the code
> below:
> ```
> int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
>     struct file *file,
>     struct snd_pcm_substream **rsubstream)
> {
> ......
> 
>  runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
> if (runtime == NULL)
> return -ENOMEM;
> 
> size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status));
> runtime->status = alloc_pages_exact(size, GFP_KERNEL);
> if (runtime->status == NULL) {
> kfree(runtime);
> return -ENOMEM;
> }
> memset(runtime->status, 0, size);
> 
> size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control));
> runtime->control = alloc_pages_exact(size, GFP_KERNEL);
> if (runtime->control == NULL) {
> free_pages_exact(runtime->status,
>       PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)));
> kfree(runtime);
> return -ENOMEM;
> }
> memset(runtime->control, 0, size);
> 
> init_waitqueue_head(&runtime->sleep);
> init_waitqueue_head(&runtime->tsleep);
> 
> runtime->status->state = SNDRV_PCM_STATE_OPEN;
> mutex_init(&runtime->buffer_mutex);
> atomic_set(&runtime->buffer_accessing, 0);
> 
> substream->runtime = runtime;
> substream->private_data = pcm->private_data;
> substream->ref_count = 1;
> substream->f_flags = file->f_flags;
> substream->pid = get_pid(task_pid(current));
> pstr->substream_opened++;
> *rsubstream = substream;
> return 0;
> }
> ```
> It would kzmalloc a new runtime. And initialize runtime simply.
> If we write some data to the device. it would call snd_pcm_write or
> snd_pcm_writev. and read some date from the device, it would call
> snd_pcm_read or snd_pcm_readv.
> Anyway, the four function would use data of runtime, but some data of
> runtime is NULL not be initialized, see the code below:
> ```
> static ssize_t snd_pcm_write(struct file *file, const char __user *buf,
>     size_t count, loff_t * offset)
> {
> struct snd_pcm_file *pcm_file;
> struct snd_pcm_substream *substream;
> struct snd_pcm_runtime *runtime;
> snd_pcm_sframes_t result;
> 
> pcm_file = file->private_data;
> substream = pcm_file->substream;
> if (PCM_RUNTIME_CHECK(substream))
> return -ENXIO;
> runtime = substream->runtime;
> if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
>    runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
> return -EBADFD;
> if (!frame_aligned(runtime, count))    ///////  [1]
> return -EINVAL;
> count = bytes_to_frames(runtime, count);    /////// [2]
> result = snd_pcm_lib_write(substream, buf, count);
> if (result > 0)
> result = frames_to_bytes(runtime, result);
> return result;
> }
> ```
> [1] call frame_aligned to aligned.
> ```
> static inline int frame_aligned(struct snd_pcm_runtime *runtime, ssize_t bytes)
> {
> return bytes % runtime->byte_align == 0;
> }
> ```
> but runtime->byte_align is NULL.
> 
> [2] call bytes_to_frames.
> ```
> static inline ssize_t frames_to_bytes(struct snd_pcm_runtime *runtime,
> snd_pcm_sframes_t size)
> {
> return size * runtime->frame_bits / 8;
> }
> ```
> but runtime->frame_bits is NULL.
> 
> ##reproduce it
> [ 1189.305083][ T4656] divide error: 0000 [#1] PREEMPT SMP
> [ 1189.305600][ T4656] CPU: 1 PID: 4656 Comm: snd_pcm_write Not
> tainted 6.0.0-rc7 #16
> [ 1189.306157][ T4656] Hardware name: QEMU Standard PC (i440FX + PIIX,
> 1996), BIOS 1.13.0-1ubuntu1 04/01/2014
> [ 1189.306760][ T4656] RIP: 0010:snd_pcm_write+0x33/0xa0
> [ 1189.307155][ T4656] Code: 8b 38 48 85 ff 74 72 48 8b 9f c0 00 00 00
> 48 85 db 74 66 48 8b 83 00 01 00 00 f7 00 f7 ff ff ff 74 6b 48 89 d0
> 48 89 d1 31 d2 <48> f7 b3 91
> [ 1189.308553][ T4656] RSP: 0018:ffffc9000adc7e68 EFLAGS: 00010246
> [ 1189.309034][ T4656] RAX: 0000000000000000 RBX: ffff888048ec2000
> RCX: 0000000000000000
> [ 1189.309583][ T4656] RDX: 0000000000000000 RSI: 0000000000000000
> RDI: ffff888046fc9c00
> [ 1189.310163][ T4656] RBP: 0000000000000000 R08: 0000000000000000
> R09: 0000000000020026
> [ 1189.310679][ T4656] R10: 0000000000000001 R11: 0000000000000000
> R12: 0000000000000000
> [ 1189.311226][ T4656] R13: ffffc9000adc7f08 R14: 0000000000000000
> R15: 0000000000000000
> [ 1189.311754][ T4656] FS:  00000000012d8880(0000)
> GS:ffff88807ec00000(0000) knlGS:0000000000000000
> [ 1189.312350][ T4656] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [ 1189.312780][ T4656] CR2: 0000000020000000 CR3: 000000004b496000
> CR4: 00000000000006e0
> [ 1189.313234][ T4656] Call Trace:
> [ 1189.313424][ T4656]  <TASK>
> [ 1189.313597][ T4656]  vfs_write+0xe6/0x4d0
> [ 1189.313836][ T4656]  ksys_write+0x60/0xe0
> [ 1189.314071][ T4656]  do_syscall_64+0x35/0xb0
> [ 1189.314324][ T4656]  entry_SYSCALL_64_after_hwframe+0x63/0xcd
> [ 1189.314734][ T4656] RIP: 0033:0x44dc3d
> [ 1189.315007][ T4656] Code: 28 c3 e8 36 29 00 00 66 0f 1f 44 00 00 f3
> 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b
> 4c 24 08 0f 05 <48> 3d 01 f8
> [ 1189.316338][ T4656] RSP: 002b:00007ffcbdf5ef38 EFLAGS: 00000246
> ORIG_RAX: 0000000000000001
> [ 1189.317012][ T4656] RAX: ffffffffffffffda RBX: 0000000000400530
> RCX: 000000000044dc3d
> [ 1189.317693][ T4656] RDX: 0000000000000000 RSI: 0000000000000000
> RDI: 0000000000000003
> [ 1189.318172][ T4656] RBP: 00007ffcbdf5ef50 R08: 0000000082000000
> R09: 0000000000000000
> [ 1189.318648][ T4656] R10: 000000000000ffff R11: 0000000000000246
> R12: 00000000004030a0
> [ 1189.319182][ T4656] R13: 0000000000000000 R14: 00000000004c5018
> R15: 0000000000000000

The question is how the code passes the check before [1]:

 if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
     runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
	return -EBADFD;

The uninitialized state should have been with SNDRV_PCM_STATE_OPEN,
and runtime->byte_align is set up at snd_pcm_hw_params() followed by
snd_pcm-set_state() to chage the state to SNDRV_PCM_STATE_SETUP.

Which kernel version are you testing?


Takashi

  reply	other threads:[~2022-09-26 10:22 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-09-26  9:26 A divide error bug in snd_pcm_write butt3rflyh4ck
2022-09-26 10:21 ` Takashi Iwai [this message]
2022-09-26 10:21   ` Takashi Iwai
2022-09-26 17:16   ` butt3rflyh4ck
2022-09-26 17:16     ` butt3rflyh4ck
2022-09-26 18:01     ` Takashi Iwai
2022-09-26 18:01       ` Takashi Iwai
2022-09-29 14:52       ` butt3rflyh4ck
2022-09-29 14:52         ` butt3rflyh4ck
2022-09-29 16:14         ` Takashi Iwai
2022-09-29 16:14           ` Takashi Iwai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87v8pa306x.wl-tiwai@suse.de \
    --to=tiwai@suse.de \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@kernel.org \
    --cc=butterflyhuangxx@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pierre-louis.bossart@linux.intel.com \
    --cc=tiwai@suse.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.