]> git.ipfire.org Git - thirdparty/kernel/linux.git/commit
uaccess: Provide scoped user access regions
authorThomas Gleixner <tglx@linutronix.de>
Mon, 27 Oct 2025 08:43:55 +0000 (09:43 +0100)
committerIngo Molnar <mingo@kernel.org>
Tue, 4 Nov 2025 07:27:52 +0000 (08:27 +0100)
commite497310b4ffb559e1149ee89470d5c518d234ddf
tree40c72f7aac51c3ccdcb7b237f1e6a000104deefc
parent2db48d8bf87d3cb9d968e73623efc1c5a02523e7
uaccess: Provide scoped user access regions

User space access regions are tedious and require similar code patterns all
over the place:

      if (!user_read_access_begin(from, sizeof(*from)))
return -EFAULT;
unsafe_get_user(val, from, Efault);
user_read_access_end();
return 0;
Efault:
user_read_access_end();
return -EFAULT;

This got worse with the recent addition of masked user access, which
optimizes the speculation prevention:

if (can_do_masked_user_access())
from = masked_user_read_access_begin((from));
else if (!user_read_access_begin(from, sizeof(*from)))
return -EFAULT;
unsafe_get_user(val, from, Efault);
user_read_access_end();
return 0;
Efault:
user_read_access_end();
return -EFAULT;

There have been issues with using the wrong user_*_access_end() variant in
the error path and other typical Copy&Pasta problems, e.g. using the wrong
fault label in the user accessor which ends up using the wrong accesss end
variant.

These patterns beg for scopes with automatic cleanup. The resulting outcome
is:
     scoped_user_read_access(from, Efault)
unsafe_get_user(val, from, Efault);
return 0;
  Efault:
return -EFAULT;

The scope guarantees the proper cleanup for the access mode is invoked both
in the success and the failure (fault) path.

The scoped_user_$MODE_access() macros are implemented as self terminating
nested for() loops. Thanks to Andrew Cooper for pointing me at them. The
scope can therefore be left with 'break', 'goto' and 'return'.  Even
'continue' "works" due to the self termination mechanism. Both GCC and
clang optimize all the convoluted macro maze out and the above results with
clang in:

 b80: f3 0f 1e fa                  endbr64
 b84: 48 b8 ef cd ab 89 67 45 23 01  movabs $0x123456789abcdef,%rax
 b8e: 48 39 c7                    cmp    %rax,%rdi
 b91: 48 0f 47 f8                  cmova  %rax,%rdi
 b95: 90                           nop
 b96: 90                           nop
 b97: 90                           nop
 b98: 31 c9                        xor    %ecx,%ecx
 b9a: 8b 07                        mov    (%rdi),%eax
 b9c: 89 06                        mov    %eax,(%rsi)
 b9e: 85 c9                        test   %ecx,%ecx
 ba0: 0f 94 c0                     sete   %al
 ba3: 90                           nop
 ba4: 90                           nop
 ba5: 90                           nop
 ba6: c3                           ret

Which looks as compact as it gets. The NOPs are placeholder for STAC/CLAC.
GCC emits the fault path seperately:

 bf0: f3 0f 1e fa                  endbr64
 bf4: 48 b8 ef cd ab 89 67 45 23 01  movabs $0x123456789abcdef,%rax
 bfe: 48 39 c7                     cmp    %rax,%rdi
 c01: 48 0f 47 f8                  cmova  %rax,%rdi
 c05: 90                           nop
 c06: 90                           nop
 c07: 90                           nop
 c08: 31 d2                        xor    %edx,%edx
 c0a: 8b 07                        mov    (%rdi),%eax
 c0c: 89 06                        mov    %eax,(%rsi)
 c0e: 85 d2                        test   %edx,%edx
 c10: 75 09                        jne    c1b <afoo+0x2b>
 c12: 90                           nop
 c13: 90                           nop
 c14: 90                           nop
 c15: b8 01 00 00 00               mov    $0x1,%eax
 c1a: c3                           ret
 c1b: 90                           nop
 c1c: 90                           nop
 c1d: 90                           nop
 c1e: 31 c0                        xor    %eax,%eax
 c20: c3                           ret

The fault labels for the scoped*() macros and the fault labels for the
actual user space accessors can be shared and must be placed outside of the
scope.

If masked user access is enabled on an architecture, then the pointer
handed in to scoped_user_$MODE_access() can be modified to point to a
guaranteed faulting user address. This modification is only scope local as
the pointer is aliased inside the scope. When the scope is left the alias
is not longer in effect. IOW the original pointer value is preserved so it
can be used e.g. for fixup or diagnostic purposes in the fault path.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Link: https://patch.msgid.link/20251027083745.546420421@linutronix.de
include/linux/uaccess.h