lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Fri, 12 May 2017 18:00:44 -0700
From:   Linus Torvalds <torvalds@...ux-foundation.org>
To:     Al Viro <viro@...iv.linux.org.uk>
Cc:     Linux Kernel Mailing List <linux-kernel@...r.kernel.org>,
        "linux-arch@...r.kernel.org" <linux-arch@...r.kernel.org>
Subject: Re: [git pull] uaccess-related bits of vfs.git

So I should have asked earlier, but I was feeling rather busy during
the early merge window..

On Sun, Apr 30, 2017 at 8:45 PM, Al Viro <viro@...iv.linux.org.uk> wrote:
>         uaccess unification pile.  It's _not_ the end of uaccess work, but
> the next batch of that will go into the next cycle.  This one mostly takes
> copy_from_user() and friends out of arch/* and gets the zero-padding behaviour
> in sync for all architectures.
>         Dealing with the nocache/writethrough mess is for the next cycle;
> fortunately, that's x86-only.

So I'm actually more interested to hear if you have any pending work
on cleaning up the __get/put_user() mess we have. Is that on your
radar at all?

In particular, right now, both __get_user() and __put_user() are nasty
and broken interfaces.

It *used* to be that they were designed to just generate a single
instruction. That's not what they currently do at all, due to the
whole SMAP/PAN on x86 and arm.

For example, on x86, right now a single __put_user() call ends up
generating something like

    #APP
    # 58 "./arch/x86/include/asm/smap.h" 1
            661:

    662:
    .skip -(((6651f-6641f)-(662b-661b)) > 0) * ((6651f-6641f)-(662b-661b)),0x90
    663:
    .pushsection .altinstructions,"a"
     .long 661b - .
     .long 6641f - .
     .word ( 9*32+20)
     .byte 663b-661b
     .byte 6651f-6641f
     .byte 663b-662b
    .popsection
    .pushsection .altinstr_replacement, "ax"
    6641:
            .byte 0x0f,0x01,0xcb
    6651:
            .popsection
    # 0 "" 2
    #NO_APP
            xorl        %eax, %eax      # __pu_err
    #APP
    # 269 "fs/readdir.c" 1

    1:  movq %rcx,8(%rdi)       # offset, MEM[(struct __large_struct *)_16]
    2:
    .section .fixup,"ax"
    3:  mov $-14,%eax   #, __pu_err
            jmp 2b
    .previous
     .pushsection "__ex_table","a"
     .balign 4
     .long (1b) - .
     .long (3b) - .
     .long (ex_handler_default) - .
     .popsection

    # 0 "" 2
    # 52 "./arch/x86/include/asm/smap.h" 1
            661:

    662:
    .skip -(((6651f-6641f)-(662b-661b)) > 0) * ((6651f-6641f)-(662b-661b)),0x90
    663:
    .pushsection .altinstructions,"a"
     .long 661b - .
     .long 6641f - .
     .word ( 9*32+20)
     .byte 663b-661b
     .byte 6651f-6641f
     .byte 663b-662b
    .popsection
    .pushsection .altinstr_replacement, "ax"
    6641:
            .byte 0x0f,0x01,0xca
    6651:
            .popsection
    # 0 "" 2
    #NO_APP

and while much of that is out-of-line and in different sections, it
basically means that it's insane how we inline these things.

Yes, yes, the inlined part of the above horror-story ends up being just

        clac
        xor    %eax,%eax
        mov    %rcx,0x8(%rdi)
        stac

and everything else is just boiler-plate for the alt-instruction
handling and the exception handling.

But even that small thing is rather debatable from a performance
angle: the actual cost of __put_user() these days is not the function
call, but the clac/stac (on modern x86) and the PAN bit games (on
arm).

So I'm actually inclined to just say "We should make
__get_user/__put_user() just be aliases for the normal
get_user/put_user() model".

The *correct* way to do fast user space accesses when you hoist error
checking outside is to use

        if (!access_ok(..))
                goto efault;
        user_access_begin();
        ..
        ... loop over or otherwise do multiple "raw" accessess ...
        unsafe_get/put_user(c, ptr, got_fault);
        unsafe_get/put_user(c, ptr, got_fault);
        ...
        user_access_end();
        return 0;

got_fault:
        user_access_end();
efault:
        return -EFAULT;

which is more boilerplate, but ends up generating much better code.
And for "unsafe_put_user()" in particular, we actually could teach gcc
to use "asm goto" to really improve code generation. I have patches
for that if you want to look.

I'm attaching an example patch for "filldir()" that does that modern
thing. It almost had the right form as-is anyway, and under some loads
(eg "find") filldir actually shows up in profiles too.\

And just from a code generation standpoint, look at what the attached
patch does:

[torvalds@i7 linux]$ diff -u fs/readdir.s ~/readdir.s | diffstat
 readdir.s |  548 ++++++++++++++++++++++----------------------------------------
 1 file changed, 201 insertions(+), 347 deletions(-)

just from getting rid of that crazy repeated SMAP overhead.

Yeah, yeah, doing diffstat on generated assembly files is all kinds of
crazy, but it's an example of what kind of odd garbage we currently
generate.

But the *first* thing I'd like to do would be to just get rid of
__get_user/__put_user as a thing. It really does generate nasty code,
and we might as well just make it do that function call.

Because what we do now isn't right. If we care about performance, the
"__xyz_user" versions are wrong (use unsafe_xyz_user() instead). And
if you don't care about performance, you should use the regular
xyz_user() functions that do an ok job by just calling the right-sized
helper function instead of inlining the crud.

Hmm?

                          Linus

View attachment "patch.diff" of type "text/plain" (1378 bytes)

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ