On Mon, Oct 07, 2019 at 11:26:35AM -0700, Linus Torvalds wrote: > The good news is that right now x86 is the only architecture that does > that user_access_begin(), so we don't need to worry about anything > else. Apparently the ARM people haven't had enough performance > problems with the PAN bit for them to care. Take a look at this: static inline unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n) { unsigned long ret; if (__builtin_constant_p(n) && (n <= 8)) { ret = 1; switch (n) { case 1: barrier_nospec(); __get_user_size(*(u8 *)to, from, 1, ret); break; case 2: barrier_nospec(); __get_user_size(*(u16 *)to, from, 2, ret); break; case 4: barrier_nospec(); __get_user_size(*(u32 *)to, from, 4, ret); break; case 8: barrier_nospec(); __get_user_size(*(u64 *)to, from, 8, ret); break; } if (ret == 0) return 0; } barrier_nospec(); allow_read_from_user(from, n); ret = __copy_tofrom_user((__force void __user *)to, from, n); prevent_read_from_user(from, n); return ret; } That's powerpc. And while the constant-sized bits are probably pretty useless there as well, note the allow_read_from_user()/prevent_read_from_user() part. Looks suspiciously similar to user_access_begin()/user_access_end()... The difference is, they have separate "for read" and "for write" primitives and they want the range in their user_access_end() analogue. Separating the read and write isn't a problem for callers (we want them close to the actual memory accesses). Passing the range to user_access_end() just might be tolerable, unless it makes you throw up...