Some architectures grand full access to userspace regardless of the
address/len passed to user_access_begin(), but other architectures
only grand access to the requested area.
For exemple, on 32 bits powerpc (book3s/32), access is granted by
segments of 256 Mbytes.
Modify filldir() and filldir64() to request the real area they need
to get access to, i.e. the area covering the parent dirent (if any)
and the contiguous current dirent.
Fixes: 9f79b78ef744 ("Convert filldir[64]() from __put_user() to unsafe_put_user()")
Signed-off-by: Christophe Leroy <redacted>
---
v2: have user_access_begin() cover both parent dirent (if any) and current dirent
---
fs/readdir.c | 50 ++++++++++++++++++++++++++++----------------------
1 file changed, 28 insertions(+), 22 deletions(-)
@@ -319,19 +322,22 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen,buf->error=-EINVAL;/* only used if we fail.. */if(reclen>buf->count)return-EINVAL;-dirent=buf->previous;-if(dirent&&signal_pending(current))+dirent0=buf->previous;+if(dirent0&&signal_pending(current))return-EINTR;-/*-*Note!Thisrange-checks'previous'(whichmaybeNULL).-*Therealrangewascheckedingetdents-*/-if(!user_access_begin(dirent,sizeof(*dirent)))-gotoefault;-if(dirent)-unsafe_put_user(offset,&dirent->d_off,efault_end);dirent=buf->current_dir;+if(dirent0){+intsz=(void__user*)dirent+reclen-+(void__user*)dirent0;++if(!user_access_begin(dirent0,sz))+gotoefault;+unsafe_put_user(offset,&dirent0->d_off,efault_end);+}else{+if(!user_access_begin(dirent,reclen))+gotoefault;+}unsafe_put_user(ino,&dirent->d_ino,efault_end);unsafe_put_user(reclen,&dirent->d_reclen,efault_end);unsafe_put_user(d_type,&dirent->d_type,efault_end);
At the moment, bad_kuap_fault() reports a fault only if a bad access
to userspace occurred while access to userspace was not granted.
But if a fault occurs for a write outside the allowed userspace
segment(s) that have been unlocked, bad_kuap_fault() fails to
detect it and the kernel loops forever in do_page_fault().
Fix it by checking that the accessed address is within the allowed
range.
Fixes: a68c31fc01ef ("powerpc/32s: Implement Kernel Userspace Access Protection")
Cc: stable@vger.kernel.org
Signed-off-by: Christophe Leroy <redacted>
---
v2: added missing address parametre to bad_kuap_fault() in asm/kup.h
---
arch/powerpc/include/asm/book3s/32/kup.h | 9 +++++++--
arch/powerpc/include/asm/book3s/64/kup-radix.h | 3 ++-
arch/powerpc/include/asm/kup.h | 6 +++++-
arch/powerpc/include/asm/nohash/32/kup-8xx.h | 3 ++-
arch/powerpc/mm/fault.c | 2 +-
5 files changed, 17 insertions(+), 6 deletions(-)
@@ -233,7 +233,7 @@ static bool bad_kernel_fault(struct pt_regs *regs, unsigned long error_code,// Read/write fault in a valid region (the exception table search passed// above), but blocked by KUAP is bad, it can never succeed.-if(bad_kuap_fault(regs,is_write))+if(bad_kuap_fault(regs,address,is_write))returntrue;// What's left? Kernel fault on user in well defined regions (extable
NULL addr is a user address. Don't waste time checking it. If
someone tries to access it, it will SIGFAULT the same way as for
address 1, so no need to make it special.
The special case is when not doing a write, in that case we want
to drop the entire function. This is now handled by 'dir' param
and not by the nulity of 'to' anymore.
Also make beginning of prevent_user_access() similar
to beginning of allow_user_access(), and tell the compiler
that writing in kernel space or with a 0 length is unlikely
Signed-off-by: Christophe Leroy <redacted>
---
v2: no change
---
arch/powerpc/include/asm/book3s/32/kup.h | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
In preparation of implementing user_access_begin and friends
on powerpc, the book3s/32 version of prevent_user_access() need
to be prepared for user_access_end().
user_access_end() doesn't provide the address and size which
were passed to user_access_begin(), required by prevent_user_access()
to know which segment to modify.
The list of segments which where unprotected by allow_user_access()
are available in current->kuap. But we don't want prevent_user_access()
to read this all the time, especially everytime it is 0 (for instance
because the access was not a write access).
Implement a special direction case named KUAP_SELF. In this case only,
the addr and end are retrieved from current->kuap.
Signed-off-by: Christophe Leroy <redacted>
---
v2: no change
---
arch/powerpc/include/asm/book3s/32/kup.h | 25 ++++++++++++++++++------
arch/powerpc/include/asm/kup.h | 1 +
2 files changed, 20 insertions(+), 6 deletions(-)
@@ -77,20 +77,21 @@ static inline void set_kuap(unsigned long value)isync();}-staticinlinevoidallow_user_access(void__user*to,constvoid__user*from,-unsignedlongsize)+static__always_inlinevoidallow_user_access(void__user*to,constvoid__user*from,+unsignedlongsize,unsignedlongdir){// This is written so we can resolve to a single case at build time-if(__builtin_constant_p(to)&&to==NULL)+BUILD_BUG_ON(!__builtin_constant_p(dir));+if(dir==KUAP_R)set_kuap(AMR_KUAP_BLOCK_WRITE);-elseif(__builtin_constant_p(from)&&from==NULL)+elseif(dir==KUAP_W)set_kuap(AMR_KUAP_BLOCK_READ);elseset_kuap(0);}staticinlinevoidprevent_user_access(void__user*to,constvoid__user*from,-unsignedlongsize)+unsignedlongsize,unsignedlongdir){set_kuap(AMR_KUAP_BLOCKED);}
Today, when a function like strncpy_from_user() is called,
the userspace access protection is de-activated and re-activated
for every word read.
By implementing user_access_begin and friends, the protection
is de-activated at the beginning of the copy and re-activated at the
end.
Implement user_access_begin(), user_access_end() and
unsafe_get_user(), unsafe_put_user() and unsafe_copy_to_user()
For the time being, we keep user_access_save() and
user_access_restore() as nops.
Signed-off-by: Christophe Leroy <redacted>
---
v2: no change
---
arch/powerpc/include/asm/uaccess.h | 92 ++++++++++++++++++++++++++----
1 file changed, 82 insertions(+), 10 deletions(-)
Patch looks better, but those names are horrid.
Please just use "prev" for the previous dirent, and leave the current one
as "dirent".
I think that's going to be a lot more legible. But no numbering.
Linus
On Wed, Jan 22, 2020 at 10:24 AM Linus Torvalds
[off-list ref] wrote:
Patch looks better, but those names are horrid.
Hmm.
A bit more re-organization also allows us to do the unsafe_put_user()
unconditionally.
In particular, if we remove 'previous' as a pointer from the filldir
data structure, and replace it with 'prev_reclen', then we can do
prev_reclen = buf->prev_reclen;
dirent = buf->current_dir;
prev = (void __user *) dirent - prev_reclen;
if (!user_access_begin(prev, reclen + prev_reclen))
goto efault;
and instead of checking that 'previous' pointer for NULL, we just
check prev_reclen for not being zero.
Yes, it replaces a few other
lastdirent = buf.previous;
with the slightly more complicated
lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
but on the whole it makes the _important_ code more streamlined, and
avoids having to have those if-else cases.
Something like the attached.
COMPLETELY UNTESTED! It compiles for me. The generated assembly looks
ok from a quick look.
Christophe, does this work for you on your ppc test-case?
Side note: I think verify_dirent_name() should check that 'len' is in
the appropriate range too, because right now a corrupted filesystem is
only noticed for a zero length. But a negative one, or one where the
reclen calculations would overflow, is not noticed.
Most filesystems have the source of 'len' being something like an
'unsigned char' so that it's pretty bounded anyway, which is likely
why nobody cared when we added that check, but..
Linus
On Wed, Jan 22, 2020 at 12:00 PM Linus Torvalds
[off-list ref] wrote:
A bit more re-organization also allows us to do the unsafe_put_user()
unconditionally.
I meant the "user_access_begin()", of course.
Code was right, explanation was wrong.
That said, with this model, we _could_ make the
unsafe_put_user(offset, &prev->d_off, efault_end);
be unconditional too, since now 'prev' will actually be a valid
pointer - it will match 'dirent' if there was no prev.
But since we want to test whether we had a previous entry anyway (for
the signal handling latency issue), making the write to the previous
d_reclen unconditional (and then overwriting it the next iteration)
doesn't actually buy us anything.
It was the user_access_begin() I'd rather have unconditional, since
otherwise it gets duplicated in two (very slightly) different versions
and we have unnecessary code bloat.
Linus
[ Talking to myself ]
On Wed, Jan 22, 2020 at 12:00 PM Linus Torvalds
[off-list ref] wrote:
COMPLETELY UNTESTED! It compiles for me. The generated assembly looks
ok from a quick look.
Some more testing shows that objtool is unhappy about how we do that
signal_pending(current) inside the user access region.
I didn't notice because my test builds were with sane kernel
configurations so that I could look at the generated code.
But with KASAN enabled, the signal check causes accesses that KASAN
wants to check, and I get
objtool: filldir()+0x395: call to __kasan_check_read() with UACCESS enabled
warnings.
So that patch of mine isn't acceptable for silly reasons, and the
signal check itself would need to be done outside of the user access
area.
That actually makes the whole "let's do the &prev->d_off setting
unconditionally" much more interesting.
So here's a slightly updated patch that does exactly that, and avoids
the objtool warning.
It actually generates better code than the last one too, because now
we don't duplicate the user_access_end() for the EINTR case.
So test this one instead, please.
Linus
[ Talking to myself ]
On Wed, Jan 22, 2020 at 12:00 PM Linus Torvalds
[off-list ref] wrote:
quoted
COMPLETELY UNTESTED! It compiles for me. The generated assembly looks
ok from a quick look.
So here's a slightly updated patch that does exactly that, and avoids
the objtool warning.
It actually generates better code than the last one too, because now
we don't duplicate the user_access_end() for the EINTR case.
So test this one instead, please.
This patch works on my ppc board, thanks
Christophe
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2020-01-23 10:59:31
Christophe Leroy [off-list ref] writes:
In preparation of implementing user_access_begin and friends
on powerpc, the book3s/32 version of prevent_user_access() need
to be prepared for user_access_end().
user_access_end() doesn't provide the address and size which
were passed to user_access_begin(), required by prevent_user_access()
to know which segment to modify.
The list of segments which where unprotected by allow_user_access()
are available in current->kuap. But we don't want prevent_user_access()
to read this all the time, especially everytime it is 0 (for instance
because the access was not a write access).
Implement a special direction case named KUAP_SELF. In this case only,
the addr and end are retrieved from current->kuap.
Can we call it KUAP_CURRENT?
ie. "use the KUAP state in current"
cheers
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2020-01-23 11:56:25
Hi Christophe,
This patch is independent of the rest of the series AFAICS, and it looks
like Linus has modified it quite a bit down thread.
So I'll take patches 2-6 via powerpc and assume this patch will go via
Linus or Al or elsewhere.
Also a couple of minor spelling fixes below.
cheers
Christophe Leroy [off-list ref] writes:
Some architectures grand full access to userspace regardless of the
^
grant
address/len passed to user_access_begin(), but other architectures
only grand access to the requested area.
^
grant
For exemple, on 32 bits powerpc (book3s/32), access is granted by
^
example
quoted hunk
segments of 256 Mbytes.
Modify filldir() and filldir64() to request the real area they need
to get access to, i.e. the area covering the parent dirent (if any)
and the contiguous current dirent.
Fixes: 9f79b78ef744 ("Convert filldir[64]() from __put_user() to unsafe_put_user()")
Signed-off-by: Christophe Leroy <redacted>
---
v2: have user_access_begin() cover both parent dirent (if any) and current dirent
---
fs/readdir.c | 50 ++++++++++++++++++++++++++++----------------------
1 file changed, 28 insertions(+), 22 deletions(-)
@@ -319,19 +322,22 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen,buf->error=-EINVAL;/* only used if we fail.. */if(reclen>buf->count)return-EINVAL;-dirent=buf->previous;-if(dirent&&signal_pending(current))+dirent0=buf->previous;+if(dirent0&&signal_pending(current))return-EINTR;-/*-*Note!Thisrange-checks'previous'(whichmaybeNULL).-*Therealrangewascheckedingetdents-*/-if(!user_access_begin(dirent,sizeof(*dirent)))-gotoefault;-if(dirent)-unsafe_put_user(offset,&dirent->d_off,efault_end);dirent=buf->current_dir;+if(dirent0){+intsz=(void__user*)dirent+reclen-+(void__user*)dirent0;++if(!user_access_begin(dirent0,sz))+gotoefault;+unsafe_put_user(offset,&dirent0->d_off,efault_end);+}else{+if(!user_access_begin(dirent,reclen))+gotoefault;+}unsafe_put_user(ino,&dirent->d_ino,efault_end);unsafe_put_user(reclen,&dirent->d_reclen,efault_end);unsafe_put_user(d_type,&dirent->d_type,efault_end);
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2020-01-23 12:05:58
Christophe Leroy [off-list ref] writes:
Today, when a function like strncpy_from_user() is called,
the userspace access protection is de-activated and re-activated
for every word read.
By implementing user_access_begin and friends, the protection
is de-activated at the beginning of the copy and re-activated at the
end.
Implement user_access_begin(), user_access_end() and
unsafe_get_user(), unsafe_put_user() and unsafe_copy_to_user()
For the time being, we keep user_access_save() and
user_access_restore() as nops.
That means we will run with user access enabled in a few more places, but
it's only used sparingly AFAICS:
kernel/trace/trace_branch.c: unsigned long flags = user_access_save();
lib/ubsan.c: unsigned long flags = user_access_save();
lib/ubsan.c: unsigned long ua_flags = user_access_save();
mm/kasan/common.c: unsigned long flags = user_access_save();
And we don't have objtool checking that user access enablement isn't
leaking in the first place, so I guess it's OK for us not to implement
these to begin with?
cheers
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2020-01-23 12:31:10
Michael Ellerman [off-list ref] writes:
Christophe Leroy [off-list ref] writes:
quoted
Today, when a function like strncpy_from_user() is called,
the userspace access protection is de-activated and re-activated
for every word read.
By implementing user_access_begin and friends, the protection
is de-activated at the beginning of the copy and re-activated at the
end.
Implement user_access_begin(), user_access_end() and
unsafe_get_user(), unsafe_put_user() and unsafe_copy_to_user()
For the time being, we keep user_access_save() and
user_access_restore() as nops.
That means we will run with user access enabled in a few more places, but
it's only used sparingly AFAICS:
kernel/trace/trace_branch.c: unsigned long flags = user_access_save();
lib/ubsan.c: unsigned long flags = user_access_save();
lib/ubsan.c: unsigned long ua_flags = user_access_save();
mm/kasan/common.c: unsigned long flags = user_access_save();
And we don't have objtool checking that user access enablement isn't
leaking in the first place, so I guess it's OK for us not to implement
these to begin with?
It looks like we can implement them on on all three KUAP
implementations.
For radix and 8xx we just return/set the relevant SPR.
For book3s/32/kup.h I think we'd just need to add a KUAP_CURRENT case to
allow_user_access()?
cheers
Hi Christophe,
This patch is independent of the rest of the series AFAICS
And of course having hit send I immediately realise that's not true.
Without this, book3s/32 fails booting. (And without patch 2, it even
hangs, looping forever in do_page_fault()).
quoted
So I'll take patches 2-6 via powerpc and assume this patch will go via
Linus or Al or elsewhere.
So I guess I'll wait and see what happens with patch 1.
We could eventually opt out user_access_begin() for
CONFIG_PPC_BOOK3S_32, then you could take patches 3 and 6. That's enough
to have user_access_begin() and stuff for 8xx and RADIX.
Patch 2 should be taken as well as a fix, and can be kept independant of
the series (once we have patch 1, we normally don't hit the problem
fixed by patch 2).
Won't don't need patch 4 until we want user_access_begin() supported by
book3s/32
However, I'm about to send out a v3 with a different approach. It
modifies the core part where user_access_begin() is returning an opaque
value used by user_access_end(). And it also tells user_access_begin()
whether it's a read or a write, so that we can limit unlocking to write
acccesses on book3s/32, and fine grain rights on book3s/64.
Maybe you would prefer this change on top of first step, in which case
I'll be able to make a v4 rebasing all this on top of patch 3 and 6 of
v3 series. Tell me what you prefer.
Christophe
On Thu, Jan 23, 2020 at 4:00 AM Michael Ellerman [off-list ref] wrote:
So I guess I'll wait and see what happens with patch 1.
I've committed my fixes to filldir[64]() directly - they really were
fixing me being lazy about the range, and the name length checking
really is a theoretical "access wrong user space pointer" issue with
corrupted filesystems regardless (even though I suspect it's entirely
theoretical - even a corrupt filesystem hopefully won't be passing in
negative directory entry lengths or something like that).
The "pass in read/write" part I'm not entirely convinced about.
Honestly, if this is just for ppc32 and nobody else really needs it,
make the ppc32s thing always just enable both user space reads and
writes. That's the semantics for x86 and arm as is, I'm not convinced
that we should complicate this for a legacy platform.
Linus
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2020-01-24 10:42:46
Linus Torvalds [off-list ref] writes:
On Thu, Jan 23, 2020 at 4:00 AM Michael Ellerman [off-list ref] wrote:
quoted
So I guess I'll wait and see what happens with patch 1.
I've committed my fixes to filldir[64]() directly - they really were
fixing me being lazy about the range, and the name length checking
really is a theoretical "access wrong user space pointer" issue with
corrupted filesystems regardless (even though I suspect it's entirely
theoretical - even a corrupt filesystem hopefully won't be passing in
negative directory entry lengths or something like that).
Great, thanks.
The "pass in read/write" part I'm not entirely convinced about.
Honestly, if this is just for ppc32 and nobody else really needs it,
make the ppc32s thing always just enable both user space reads and
writes. That's the semantics for x86 and arm as is, I'm not convinced
that we should complicate this for a legacy platform.
We can use the read/write info on Power9 too. That's a niche platform
but hopefully not legacy status yet :P
But it's entirely optional, as you say we can just enable read/write if
we aren't passed the read/write info from the upper-level API.
I think our priority should be getting objtool going on powerpc to check
our user access regions are well contained. Once we have that working
maybe then we can look at plumbing the direction through
user_access_begin() etc.
cheers
Today, when a function like strncpy_from_user() is called,
the userspace access protection is de-activated and re-activated
for every word read.
By implementing user_access_begin and friends, the protection
is de-activated at the beginning of the copy and re-activated at the
end.
Implement user_access_begin(), user_access_end() and
unsafe_get_user(), unsafe_put_user() and unsafe_copy_to_user()
For the time being, we keep user_access_save() and
user_access_restore() as nops.
That means we will run with user access enabled in a few more places, but
it's only used sparingly AFAICS:
kernel/trace/trace_branch.c: unsigned long flags = user_access_save();
lib/ubsan.c: unsigned long flags = user_access_save();
lib/ubsan.c: unsigned long ua_flags = user_access_save();
mm/kasan/common.c: unsigned long flags = user_access_save();
And we don't have objtool checking that user access enablement isn't
leaking in the first place, so I guess it's OK for us not to implement
these to begin with?
It looks like we can implement them on on all three KUAP
implementations.
For radix and 8xx we just return/set the relevant SPR.
For book3s/32/kup.h I think we'd just need to add a KUAP_CURRENT case to
allow_user_access()?
Can't do that, we don't want to keep the info in current->thread.kuap
after user_access_save(), otherwise we might unexpectedly re-open access
through an interrupt.
And if we use KUAP_CURRENT case of prevent_user_access(), it means we'll
read current->thread.kuap twice.
So, just regenerate addr and end from the flags, and use
allow_user_access() and prevent_user_access() as usual.
I'll have it in v4
Christophe