[PATCH 2/5] uaccess: Add speculation barrier to copy_from_user_iter()

Christophe Leroy posted 5 patches 3 months, 2 weeks ago
There is a newer version of this series
[PATCH 2/5] uaccess: Add speculation barrier to copy_from_user_iter()
Posted by Christophe Leroy 3 months, 2 weeks ago
The results of "access_ok()" can be mis-speculated.  The result is that
you can end speculatively:

	if (access_ok(from, size))
		// Right here

For the same reason as done in copy_from_user() by
commit 74e19ef0ff80 ("uaccess: Add speculation barrier to
copy_from_user()"), add a speculation barrier to copy_from_user_iter().

See commit 74e19ef0ff80 ("uaccess: Add speculation barrier to
copy_from_user()") for more details.

Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
 lib/iov_iter.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index f9193f952f49..ebf524a37907 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -50,6 +50,13 @@ size_t copy_from_user_iter(void __user *iter_from, size_t progress,
 	if (should_fail_usercopy())
 		return len;
 	if (access_ok(iter_from, len)) {
+		/*
+		 * Ensure that bad access_ok() speculation will not
+		 * lead to nasty side effects *after* the copy is
+		 * finished:
+		 */
+		barrier_nospec();
+
 		to += progress;
 		instrument_copy_from_user_before(to, iter_from, len);
 		res = raw_copy_from_user(to, iter_from, len);
-- 
2.49.0
Re: [PATCH 2/5] uaccess: Add speculation barrier to copy_from_user_iter()
Posted by David Laight 3 months, 2 weeks ago
On Sun, 22 Jun 2025 11:52:40 +0200
Christophe Leroy <christophe.leroy@csgroup.eu> wrote:

> The results of "access_ok()" can be mis-speculated.  The result is that
> you can end speculatively:
> 
> 	if (access_ok(from, size))
> 		// Right here
> 
> For the same reason as done in copy_from_user() by
> commit 74e19ef0ff80 ("uaccess: Add speculation barrier to
> copy_from_user()"), add a speculation barrier to copy_from_user_iter().

I'm sure I sent a patch to change this code to used the 'masked' functions.
Probably ought to be done at the same time.
Would have been early feb, about the time I suggested:

+#ifdef masked_user_access_begin
+#define masked_user_read_access_begin(from, size) \
+	((*(from) = masked_user_access_begin(*(from))), 1)
+#define masked_user_write_access_begin(from, size) \
+	((*(from) = masked_user_access_begin(*(from))), 1)
+#else
+#define masked_user_read_access_begin(from, size) \
+	user_read_access_begin(*(from), size)
+#define masked_user_write_access_begin(from, size) \
+	user_write_access_begin(*(from), size)
+#endif

allowing:
-		if (!user_read_access_begin(from, sizeof(*from)))
+		if (!masked_user_read_access_begin(&from, sizeof(*from)))

	David


> 
> See commit 74e19ef0ff80 ("uaccess: Add speculation barrier to
> copy_from_user()") for more details.
> 
> Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
> ---
>  lib/iov_iter.c | 7 +++++++
>  1 file changed, 7 insertions(+)
> 
> diff --git a/lib/iov_iter.c b/lib/iov_iter.c
> index f9193f952f49..ebf524a37907 100644
> --- a/lib/iov_iter.c
> +++ b/lib/iov_iter.c
> @@ -50,6 +50,13 @@ size_t copy_from_user_iter(void __user *iter_from, size_t progress,
>  	if (should_fail_usercopy())
>  		return len;
>  	if (access_ok(iter_from, len)) {
> +		/*
> +		 * Ensure that bad access_ok() speculation will not
> +		 * lead to nasty side effects *after* the copy is
> +		 * finished:
> +		 */
> +		barrier_nospec();
> +
>  		to += progress;
>  		instrument_copy_from_user_before(to, iter_from, len);
>  		res = raw_copy_from_user(to, iter_from, len);
Re: [PATCH 2/5] uaccess: Add speculation barrier to copy_from_user_iter()
Posted by Linus Torvalds 3 months, 2 weeks ago
On Sun, 22 Jun 2025 at 02:52, Christophe Leroy
<christophe.leroy@csgroup.eu> wrote:
>
> The results of "access_ok()" can be mis-speculated.

Hmm. This code is critical. I think it should be converted to use that
masked address thing if we have to add it here.

And at some point this access_ok() didn't even exist, because we check
the addresses at iter creation time. So this one might be a "belt and
suspenders" check, rather than something critical.

(Although I also suspect that when we added ITER_UBUF we might have
created cases where those user addresses aren't checked at iter
creation time any more).

             Linus
Re: [PATCH 2/5] uaccess: Add speculation barrier to copy_from_user_iter()
Posted by Christophe Leroy 3 months, 2 weeks ago

Le 22/06/2025 à 18:57, Linus Torvalds a écrit :
> On Sun, 22 Jun 2025 at 02:52, Christophe Leroy
> <christophe.leroy@csgroup.eu> wrote:
>>
>> The results of "access_ok()" can be mis-speculated.
> 
> Hmm. This code is critical. I think it should be converted to use that
> masked address thing if we have to add it here.

Ok, I'll add it.

> 
> And at some point this access_ok() didn't even exist, because we check
> the addresses at iter creation time. So this one might be a "belt and
> suspenders" check, rather than something critical.
> 
> (Although I also suspect that when we added ITER_UBUF we might have
> created cases where those user addresses aren't checked at iter
> creation time any more).
> 

Let's take the follow path as an exemple:

snd_pcm_ioctl(SNDRV_PCM_IOCTL_WRITEI_FRAMES)
   snd_pcm_common_ioctl()
     snd_pcm_xferi_frames_ioctl()
       snd_pcm_lib_write()
         __snd_pcm_lib_xfer()
           default_write_copy()
             copy_from_iter()
               _copy_from_iter()
                 __copy_from_iter()
                   iterate_and_advance()
                     iterate_and_advance2()
                       iterate_iovec()
                         copy_from_user_iter()

As far as I can see, none of those functions check the accessibility of 
the iovec. Am I missing something ?

Christophe
Re: [PATCH 2/5] uaccess: Add speculation barrier to copy_from_user_iter()
Posted by Linus Torvalds 3 months, 2 weeks ago
On Mon, 23 Jun 2025 at 22:49, Christophe Leroy
<christophe.leroy@csgroup.eu> wrote:
>
> >
> > (Although I also suspect that when we added ITER_UBUF we might have
> > created cases where those user addresses aren't checked at iter
> > creation time any more).
> >
>
> Let's take the follow path as an exemple:
>
> snd_pcm_ioctl(SNDRV_PCM_IOCTL_WRITEI_FRAMES)
>    snd_pcm_common_ioctl()
>      snd_pcm_xferi_frames_ioctl()
>        snd_pcm_lib_write()
>          __snd_pcm_lib_xfer()
>            default_write_copy()
>              copy_from_iter()
>                _copy_from_iter()
>                  __copy_from_iter()
>                    iterate_and_advance()
>                      iterate_and_advance2()
>                        iterate_iovec()
>                          copy_from_user_iter()
>
> As far as I can see, none of those functions check the accessibility of
> the iovec. Am I missing something ?

So we still to do this checking at creation time (see import_iovec ->
__import_iovec, and import_ubuf).

In the path you give as an example, the check happens at that
"do_transfer()" stage when it does

        err = import_ubuf(type, (__force void __user *)data, bytes, &iter);

but yeah, it's very non-obvious (see __snd_pcm_lib_xfer(), which calls
writer() which is either interleaved_copy() or noninterleaved_copy(),
and then they do that do_transfer() thing which does that
import_ubuf() thing.

So *because* you were supposed to have checked your iov_iters
beforehand, the actual iter code itself at some point just used
__copy_to_user() directly with no checking at all.

And that all was really *much* too subtle, and Al fixed this a few
years ago (see commit 09fc68dc66f7: "iov_iter: saner checks on
copyin/copyout")

                  Linus
Re: [PATCH 2/5] uaccess: Add speculation barrier to copy_from_user_iter()
Posted by David Laight 3 months, 2 weeks ago
On Tue, 24 Jun 2025 07:49:03 +0200
Christophe Leroy <christophe.leroy@csgroup.eu> wrote:

> Le 22/06/2025 à 18:57, Linus Torvalds a écrit :
> > On Sun, 22 Jun 2025 at 02:52, Christophe Leroy
> > <christophe.leroy@csgroup.eu> wrote:  
> >>
> >> The results of "access_ok()" can be mis-speculated.  
> > 
> > Hmm. This code is critical. I think it should be converted to use that
> > masked address thing if we have to add it here.  
> 
> Ok, I'll add it.
> 
> > 
> > And at some point this access_ok() didn't even exist, because we check
> > the addresses at iter creation time. So this one might be a "belt and
> > suspenders" check, rather than something critical.
> > 
> > (Although I also suspect that when we added ITER_UBUF we might have
> > created cases where those user addresses aren't checked at iter
> > creation time any more).
> >   
> 
> Let's take the follow path as an exemple:
> 
> snd_pcm_ioctl(SNDRV_PCM_IOCTL_WRITEI_FRAMES)
>    snd_pcm_common_ioctl()
>      snd_pcm_xferi_frames_ioctl()
>        snd_pcm_lib_write()
>          __snd_pcm_lib_xfer()
>            default_write_copy()
>              copy_from_iter()
>                _copy_from_iter()
>                  __copy_from_iter()
>                    iterate_and_advance()
>                      iterate_and_advance2()
>                        iterate_iovec()
>                          copy_from_user_iter()
> 
> As far as I can see, none of those functions check the accessibility of 
> the iovec. Am I missing something ?

The import_ubuf() in do_transfer() ought to contain one.
But really you want the one in copy_from_user_iter() rather than the outer one.

Mind you that code is horrid.
The code only ever copies a single buffer, so could be much shorter.
And is that deep call chain really needed for the very common case of one buffer.

	David


> 
> Christophe
Re: [PATCH 2/5] uaccess: Add speculation barrier to copy_from_user_iter()
Posted by David Laight 3 months, 2 weeks ago
On Sun, 22 Jun 2025 09:57:20 -0700
Linus Torvalds <torvalds@linux-foundation.org> wrote:

> On Sun, 22 Jun 2025 at 02:52, Christophe Leroy
> <christophe.leroy@csgroup.eu> wrote:
> >
> > The results of "access_ok()" can be mis-speculated.
> 
> Hmm. This code is critical. I think it should be converted to use that
> masked address thing if we have to add it here.

If access_ok() is mis-speculated then you get a read from the user-specified
kernel address - I don't think that matters.
The hacker would need to find somewhere where the read value was used
in a test or memory access so that side effects (typically cache line
evictions) can be detected.
But copy_from_user_iter() is pretty much always used for 'data' not
'control pane' - so you'd be hard pushed to find somewhere 'useful'.
Not only that the cpu would have to return from copy_from_user_iter()
before correcting the mis-speculation.
I can't imagine that happening - even without all the 'return thunk' stuff.

The same might be true for copy_from_user().
It might only be get_user() that actually has any chance of being exploited.

> 
> And at some point this access_ok() didn't even exist, because we check
> the addresses at iter creation time. So this one might be a "belt and
> suspenders" check, rather than something critical.

IIRC there was a patch to move the access_ok() much nearer the use copy.
But it didn't go as far as removing the one from import_iovec().
Although removing that one might make sense.
(I've also looked about whether the 'direction' is needed in the 'iter'.
98% of the code knows what it should be - and may contain pointless
checks, but some bits seem to rely on it.)

	David

> 
> (Although I also suspect that when we added ITER_UBUF we might have
> created cases where those user addresses aren't checked at iter
> creation time any more).
> 
>              Linus