Re: [patch V5 07/12] uaccess: Provide scoped user access regions
From: David Laight <hidden>
Date: 2025-11-07 19:18:04
Also in:
linux-arm-kernel, linux-fsdevel, linux-riscv, linux-s390, lkml
On Mon, 27 Oct 2025 09:43:55 +0100 (CET) Thomas Gleixner [off-list ref] wrote:
User space access regions are tedious and require similar code patterns all over the place:
...
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 code doesn't work if the 'from' (above) is 'const foo __user *from'. Due to assigning away constness. The changes below fix the build, I suspect the code is then correct. ...
+/* Define RW variant so the below _mode macro expansion works */ +#define masked_user_rw_access_begin(u) masked_user_access_begin(u) +#define user_rw_access_begin(u, s) user_access_begin(u, s) +#define user_rw_access_end() user_access_end() + +/* Scoped user access */ +#define USER_ACCESS_GUARD(_mode) \
#define USER_ACCESS_GUARD(_mode, void) (but change all the void below to a different name...)
+static __always_inline void __user * \
+class_user_##_mode##_begin(void __user *ptr) \
+{ \
+ return ptr; \
+} \
+ \
+static __always_inline void \
+class_user_##_mode##_end(void __user *ptr) \
+{ \
+ user_##_mode##_access_end(); \
+} \
+ \
+DEFINE_CLASS(user_ ##_mode## _access, void __user *, \
+ class_user_##_mode##_end(_T), \
+ class_user_##_mode##_begin(ptr), void __user *ptr) \
+ \
+static __always_inline class_user_##_mode##_access_t \
+class_user_##_mode##_access_ptr(void __user *scope) \
+{ \
+ return scope; \
+}
+
+USER_ACCESS_GUARD(read)
+USER_ACCESS_GUARD(write)
+USER_ACCESS_GUARD(rw)USER_ACCESS_GUARD(read, const void) USER_ACCESS_GUARD(write, void) USER_ACCESS_GUARD(rw, void)
+#undef USER_ACCESS_GUARD
...
+#define __scoped_user_access(mode, uptr, size, elbl) \ +for (bool done = false; !done; done = true) \ + for (void __user *_tmpptr = __scoped_user_access_begin(mode, uptr, size, elbl); \
for (typeof(uptr) _tmpptr = ...
+ !done; done = true) \ + for (CLASS(user_##mode##_access, scope)(_tmpptr); !done; done = true) \ + /* Force modified pointer usage within the scope */ \ + for (const typeof(uptr) uptr = _tmpptr; !done; done = true) +
David