This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
Re: [PATCH v1 1/4] lib: introduce copy_struct_from_user() helper
- From: Al Viro <viro at zeniv dot linux dot org dot uk>
- To: Linus Torvalds <torvalds at linux-foundation dot org>
- Cc: Aleksa Sarai <cyphar at cyphar dot com>, Ingo Molnar <mingo at redhat dot com>, Peter Zijlstra <peterz at infradead dot org>, Alexander Shishkin <alexander dot shishkin at linux dot intel dot com>, Jiri Olsa <jolsa at redhat dot com>, Namhyung Kim <namhyung at kernel dot org>, Christian Brauner <christian at brauner dot io>, Rasmus Villemoes <linux at rasmusvillemoes dot dk>, GNU C Library <libc-alpha at sourceware dot org>, Linux API <linux-api at vger dot kernel dot org>, Linux Kernel Mailing List <linux-kernel at vger dot kernel dot org>
- Date: Wed, 25 Sep 2019 19:04:12 +0100
- Subject: Re: [PATCH v1 1/4] lib: introduce copy_struct_from_user() helper
- References: <20190925165915.8135-1-cyphar@cyphar.com> <20190925165915.8135-2-cyphar@cyphar.com> <CAHk-=wjFeNjhtUxQ8npmXORz5RLQU7B_3wD=45eug1+MXnuYvA@mail.gmail.com> <20190925172049.skm6ohnnxpofdkzv@yavin> <CAHk-=wjagt257WHiOr2v1Bx_3q7tuzogabw_1EnodKm0vt+-WQ@mail.gmail.com>
On Wed, Sep 25, 2019 at 10:48:31AM -0700, Linus Torvalds wrote:
> On Wed, Sep 25, 2019 at 10:21 AM Aleksa Sarai <cyphar@cyphar.com> wrote:
> >
> > Just to make sure I understand, the following diff would this solve the
> > problem? If so, I'll apply it, and re-send in a few hours.
>
> Actually, looking at it more, it's still buggy.
>
> That final "size smaller than unsigned long" doesn't correctly handle
> the case of (say) a single byte in the middle of a 8-byte word.
>
> So you need to do something like this:
>
> int is_zeroed_user(const void __user *from, size_t size)
> {
> unsigned long val, mask, align;
>
> if (unlikely(!size))
> return true;
>
> if (!user_access_begin(from, size))
> return -EFAULT;
>
> align = (uintptr_t) from % sizeof(unsigned long);
> from -= align;
> size += align;
>
> mask = ~aligned_byte_mask(align);
>
> while (size >= sizeof(unsigned long)) {
> unsafe_get_user(val, (unsigned long __user *) from, err_fault);
> val &= mask;
> if (unlikely(val))
> goto done;
> mask = ~0ul;
> from += sizeof(unsigned long);
> size -= sizeof(unsigned long);
> }
>
> if (size) {
> /* (@from + @size) is unaligned. */
> unsafe_get_user(val, (unsigned long __user *) from, err_fault);
> mask &= aligned_byte_mask(size);
> val &= mask;
> }
IMO it's better to lift reading the first word out of the loop, like this:
align = (uintptr_t) from % sizeof(unsigned long);
from -= align;
unsafe_get_user(val, (unsigned long __user *) from, err_fault);
if (align) {
size += align;
val &= ~aligned_byte_mask(align);
}
while (size > sizeof(unsigned long)) {
if (unlikely(val))
goto done;
from += sizeof(unsigned long);
size -= sizeof(unsigned long);
unsafe_get_user(val, (unsigned long __user *) from, err_fault);
}
if (size != size(unsigned long))
val &= aligned_byte_mask(size);
done:
Do you see any problems with that variant?