On Wed, Jul 1, 2020 at 1:51 PM Linus Torvalds <torva...@linux-foundation.org> wrote: > > On Wed, Jul 1, 2020 at 1:36 PM Andy Lutomirski <l...@amacapital.net> wrote: > > > > We ought to be able to do it the way I described and get decent code > > generation too. > > No, we really can't. > > Each access really needs to jump to an exception label. Otherwise any > time you have multiple operations (think "strncpy()" and friends) you > have to test in between each access. > > That is why *fundamnetally* the interface to "unsafe_get/put_user()" > takes a label for the error case. There is absolutely no way to make > any other interface work efficiently.
You inspired me to mock it up. I don't think I did anything special here, except that I mocked up unsafe_put_user() and a fudged it a little bit because I'm using gcc 9.3.1 which doesn't support asm goto outputs. Code like this: if (unsafe_put_user(&a, user_a)) goto error; if (unsafe_put_user(&b, user_b)) goto error; if (unsafe_put_user(&c, user_c)) goto error; if (unsafe_put_user(&d, user_d)) goto error; generates a series of movs. The conditions are entirely omitted from the generated assembly output because gcc is smart enough to figure out that the return value of unsafe_put_user() indicates which way the asm goto went. I don't think I could generate better output by hand than gcc generated from my test. So I stand by my claim. :) Each access does need to jump, but that jump can be entirely within the exception entry, and we don't need to generate any actual jump instructions. --Andy
#define EFAULT 14 static int unsafe_put_user(int *value, int *ptr) { asm goto ("1: movl %[value], %[mem]\n\t" ".pushsection __ex_table\n\t" ".long 1b - .\n\t" ".long err - .\n\t" ".popsection" :: [value] "rmi" (*value), [mem] "m" (*ptr) :: err ); return 0; err: return -EFAULT; } int test(int *user_a, int *user_b, int *user_c, int *user_d) { int a = 1, b = 2, c = 3, d = 4; if (unsafe_put_user(&a, user_a)) goto error; if (unsafe_put_user(&b, user_b)) goto error; if (unsafe_put_user(&c, user_c)) goto error; if (unsafe_put_user(&d, user_d)) goto error; return 0; error: return -EFAULT; }