diff mbox series

[4/8] snd: make snd_map_bufs() deal with ITER_UBUF

Message ID 20230328173613.555192-5-axboe@kernel.dk (mailing list archive)
State Mainlined, archived
Headers show
Series Turn single segment imports into ITER_UBUF | expand

Commit Message

Jens Axboe March 28, 2023, 5:36 p.m. UTC
This probably doesn't make any sense, as it's reliant on passing in
different things in multiple segments. Most likely we can just make
this go away as it's already checking for ITER_IOVEC upon entry, and
it looks like nr_segments == 2 is the smallest legal value. IOW, any
attempt to readv/writev with 1 segment would fail with -EINVAL already.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
 sound/core/pcm_native.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

Comments

Linus Torvalds March 28, 2023, 5:50 p.m. UTC | #1
On Tue, Mar 28, 2023 at 10:36 AM Jens Axboe <axboe@kernel.dk> wrote:
>
> @@ -3516,23 +3516,28 @@ static void __user **snd_map_bufs(struct snd_pcm_runtime *runtime,
>                                   struct iov_iter *iter,
>                                   snd_pcm_uframes_t *frames, int max_segs)
>  {
> +       int nr_segs = iovec_nr_user_vecs(iter);

This has a WARN_ON_ONCE() for !user_backed, but then..

>         void __user **bufs;
> +       struct iovec iov;
>         unsigned long i;
>
>         if (!iter->user_backed)
>                 return ERR_PTR(-EFAULT);

here the code tries to deal with it.

So I think the two should probably be switched around.

                 Linus
Jens Axboe March 28, 2023, 5:52 p.m. UTC | #2
On 3/28/23 11:50 AM, Linus Torvalds wrote:
> On Tue, Mar 28, 2023 at 10:36 AM Jens Axboe <axboe@kernel.dk> wrote:
>>
>> @@ -3516,23 +3516,28 @@ static void __user **snd_map_bufs(struct snd_pcm_runtime *runtime,
>>                                   struct iov_iter *iter,
>>                                   snd_pcm_uframes_t *frames, int max_segs)
>>  {
>> +       int nr_segs = iovec_nr_user_vecs(iter);
> 
> This has a WARN_ON_ONCE() for !user_backed, but then..
> 
>>         void __user **bufs;
>> +       struct iovec iov;
>>         unsigned long i;
>>
>>         if (!iter->user_backed)
>>                 return ERR_PTR(-EFAULT);
> 
> here the code tries to deal with it.
> 
> So I think the two should probably be switched around.

True, it was actually like that before I refactored it to include
that common helper. I'll swap them around, thanks.
Al Viro March 28, 2023, 6:52 p.m. UTC | #3
On Tue, Mar 28, 2023 at 11:52:10AM -0600, Jens Axboe wrote:
> On 3/28/23 11:50 AM, Linus Torvalds wrote:
> > On Tue, Mar 28, 2023 at 10:36 AM Jens Axboe <axboe@kernel.dk> wrote:
> >>
> >> @@ -3516,23 +3516,28 @@ static void __user **snd_map_bufs(struct snd_pcm_runtime *runtime,
> >>                                   struct iov_iter *iter,
> >>                                   snd_pcm_uframes_t *frames, int max_segs)
> >>  {
> >> +       int nr_segs = iovec_nr_user_vecs(iter);
> > 
> > This has a WARN_ON_ONCE() for !user_backed, but then..
> > 
> >>         void __user **bufs;
> >> +       struct iovec iov;
> >>         unsigned long i;
> >>
> >>         if (!iter->user_backed)
> >>                 return ERR_PTR(-EFAULT);
> > 
> > here the code tries to deal with it.
> > 
> > So I think the two should probably be switched around.
> 
> True, it was actually like that before I refactored it to include
> that common helper. I'll swap them around, thanks.

Umm...  That looks really weird - if nothing else, it seems that this
thing quietly ignores the ->iov_len on all but the first iovec.

Might make sense to ask ALSA folks what the hell is going on there;
it's readv()/writev() on pcm device, and it looks like userland ABI
is really perverted here... ;-/
Jens Axboe March 28, 2023, 7:28 p.m. UTC | #4
On 3/28/23 12:52 PM, Al Viro wrote:
> On Tue, Mar 28, 2023 at 11:52:10AM -0600, Jens Axboe wrote:
>> On 3/28/23 11:50 AM, Linus Torvalds wrote:
>>> On Tue, Mar 28, 2023 at 10:36 AM Jens Axboe <axboe@kernel.dk> wrote:
>>>>
>>>> @@ -3516,23 +3516,28 @@ static void __user **snd_map_bufs(struct snd_pcm_runtime *runtime,
>>>>                                   struct iov_iter *iter,
>>>>                                   snd_pcm_uframes_t *frames, int max_segs)
>>>>  {
>>>> +       int nr_segs = iovec_nr_user_vecs(iter);
>>>
>>> This has a WARN_ON_ONCE() for !user_backed, but then..
>>>
>>>>         void __user **bufs;
>>>> +       struct iovec iov;
>>>>         unsigned long i;
>>>>
>>>>         if (!iter->user_backed)
>>>>                 return ERR_PTR(-EFAULT);
>>>
>>> here the code tries to deal with it.
>>>
>>> So I think the two should probably be switched around.
>>
>> True, it was actually like that before I refactored it to include
>> that common helper. I'll swap them around, thanks.
> 
> Umm...  That looks really weird - if nothing else, it seems that this
> thing quietly ignores the ->iov_len on all but the first iovec.

I agree, but this is how it currently works...

> Might make sense to ask ALSA folks what the hell is going on there;
> it's readv()/writev() on pcm device, and it looks like userland ABI
> is really perverted here... ;-/

I have sent them email separately to confirm that the only cases
that makes sense here is nr_segs >= 2. But the ABI is what it is,
however horrible it may be :/
diff mbox series

Patch

diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 925d96343148..05913d68923a 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -3516,23 +3516,28 @@  static void __user **snd_map_bufs(struct snd_pcm_runtime *runtime,
 				  struct iov_iter *iter,
 				  snd_pcm_uframes_t *frames, int max_segs)
 {
+	int nr_segs = iovec_nr_user_vecs(iter);
 	void __user **bufs;
+	struct iovec iov;
 	unsigned long i;
 
 	if (!iter->user_backed)
 		return ERR_PTR(-EFAULT);
-	if (!iter->nr_segs)
+	if (!nr_segs)
 		return ERR_PTR(-EINVAL);
-	if (iter->nr_segs > max_segs || iter->nr_segs != runtime->channels)
+	if (nr_segs > max_segs || nr_segs != runtime->channels)
 		return ERR_PTR(-EINVAL);
-	if (!frame_aligned(runtime, iter->iov->iov_len))
+	iov = iov_iter_iovec(iter);
+	if (!frame_aligned(runtime, iov.iov_len))
 		return ERR_PTR(-EINVAL);
-	bufs = kmalloc_array(iter->nr_segs, sizeof(void *), GFP_KERNEL);
+	bufs = kmalloc_array(nr_segs, sizeof(void *), GFP_KERNEL);
 	if (bufs == NULL)
 		return ERR_PTR(-ENOMEM);
-	for (i = 0; i < iter->nr_segs; ++i)
+	bufs[0] = iov.iov_base;
+	/* we know it's an ITER_IOVEC is nr_segs > 1 */
+	for (i = 1; i < nr_segs; ++i)
 		bufs[i] = iter->iov[i].iov_base;
-	*frames = bytes_to_samples(runtime, iter->iov->iov_len);
+	*frames = bytes_to_samples(runtime, iov.iov_len);
 	return bufs;
 }