Skip to content

Commit e497310

Browse files
KAGA-KOKOingomolnar
authored andcommitted
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
1 parent 2db48d8 commit e497310

1 file changed

Lines changed: 192 additions & 0 deletions

File tree

include/linux/uaccess.h

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#ifndef __LINUX_UACCESS_H__
33
#define __LINUX_UACCESS_H__
44

5+
#include <linux/cleanup.h>
56
#include <linux/fault-inject-usercopy.h>
67
#include <linux/instrumented.h>
78
#include <linux/minmax.h>
@@ -35,9 +36,17 @@
3536

3637
#ifdef masked_user_access_begin
3738
#define can_do_masked_user_access() 1
39+
# ifndef masked_user_write_access_begin
40+
# define masked_user_write_access_begin masked_user_access_begin
41+
# endif
42+
# ifndef masked_user_read_access_begin
43+
# define masked_user_read_access_begin masked_user_access_begin
44+
#endif
3845
#else
3946
#define can_do_masked_user_access() 0
4047
#define masked_user_access_begin(src) NULL
48+
#define masked_user_read_access_begin(src) NULL
49+
#define masked_user_write_access_begin(src) NULL
4150
#define mask_user_address(src) (src)
4251
#endif
4352

@@ -633,6 +642,189 @@ static inline void user_access_restore(unsigned long flags) { }
633642
#define user_read_access_end user_access_end
634643
#endif
635644

645+
/* Define RW variant so the below _mode macro expansion works */
646+
#define masked_user_rw_access_begin(u) masked_user_access_begin(u)
647+
#define user_rw_access_begin(u, s) user_access_begin(u, s)
648+
#define user_rw_access_end() user_access_end()
649+
650+
/* Scoped user access */
651+
#define USER_ACCESS_GUARD(_mode) \
652+
static __always_inline void __user * \
653+
class_user_##_mode##_begin(void __user *ptr) \
654+
{ \
655+
return ptr; \
656+
} \
657+
\
658+
static __always_inline void \
659+
class_user_##_mode##_end(void __user *ptr) \
660+
{ \
661+
user_##_mode##_access_end(); \
662+
} \
663+
\
664+
DEFINE_CLASS(user_ ##_mode## _access, void __user *, \
665+
class_user_##_mode##_end(_T), \
666+
class_user_##_mode##_begin(ptr), void __user *ptr) \
667+
\
668+
static __always_inline class_user_##_mode##_access_t \
669+
class_user_##_mode##_access_ptr(void __user *scope) \
670+
{ \
671+
return scope; \
672+
}
673+
674+
USER_ACCESS_GUARD(read)
675+
USER_ACCESS_GUARD(write)
676+
USER_ACCESS_GUARD(rw)
677+
#undef USER_ACCESS_GUARD
678+
679+
/**
680+
* __scoped_user_access_begin - Start a scoped user access
681+
* @mode: The mode of the access class (read, write, rw)
682+
* @uptr: The pointer to access user space memory
683+
* @size: Size of the access
684+
* @elbl: Error label to goto when the access region is rejected
685+
*
686+
* Internal helper for __scoped_user_access(). Don't use directly.
687+
*/
688+
#define __scoped_user_access_begin(mode, uptr, size, elbl) \
689+
({ \
690+
typeof(uptr) __retptr; \
691+
\
692+
if (can_do_masked_user_access()) { \
693+
__retptr = masked_user_##mode##_access_begin(uptr); \
694+
} else { \
695+
__retptr = uptr; \
696+
if (!user_##mode##_access_begin(uptr, size)) \
697+
goto elbl; \
698+
} \
699+
__retptr; \
700+
})
701+
702+
/**
703+
* __scoped_user_access - Open a scope for user access
704+
* @mode: The mode of the access class (read, write, rw)
705+
* @uptr: The pointer to access user space memory
706+
* @size: Size of the access
707+
* @elbl: Error label to goto when the access region is rejected. It
708+
* must be placed outside the scope
709+
*
710+
* If the user access function inside the scope requires a fault label, it
711+
* can use @elbl or a different label outside the scope, which requires
712+
* that user access which is implemented with ASM GOTO has been properly
713+
* wrapped. See unsafe_get_user() for reference.
714+
*
715+
* scoped_user_rw_access(ptr, efault) {
716+
* unsafe_get_user(rval, &ptr->rval, efault);
717+
* unsafe_put_user(wval, &ptr->wval, efault);
718+
* }
719+
* return 0;
720+
* efault:
721+
* return -EFAULT;
722+
*
723+
* The scope is internally implemented as a autoterminating nested for()
724+
* loop, which can be left with 'return', 'break' and 'goto' at any
725+
* point.
726+
*
727+
* When the scope is left user_##@_mode##_access_end() is automatically
728+
* invoked.
729+
*
730+
* When the architecture supports masked user access and the access region
731+
* which is determined by @uptr and @size is not a valid user space
732+
* address, i.e. < TASK_SIZE, the scope sets the pointer to a faulting user
733+
* space address and does not terminate early. This optimizes for the good
734+
* case and lets the performance uncritical bad case go through the fault.
735+
*
736+
* The eventual modification of the pointer is limited to the scope.
737+
* Outside of the scope the original pointer value is unmodified, so that
738+
* the original pointer value is available for diagnostic purposes in an
739+
* out of scope fault path.
740+
*
741+
* Nesting scoped user access into a user access scope is invalid and fails
742+
* the build. Nesting into other guards, e.g. pagefault is safe.
743+
*
744+
* The masked variant does not check the size of the access and relies on a
745+
* mapping hole (e.g. guard page) to catch an out of range pointer, the
746+
* first access to user memory inside the scope has to be within
747+
* @uptr ... @uptr + PAGE_SIZE - 1
748+
*
749+
* Don't use directly. Use scoped_masked_user_$MODE_access() instead.
750+
*/
751+
#define __scoped_user_access(mode, uptr, size, elbl) \
752+
for (bool done = false; !done; done = true) \
753+
for (void __user *_tmpptr = __scoped_user_access_begin(mode, uptr, size, elbl); \
754+
!done; done = true) \
755+
for (CLASS(user_##mode##_access, scope)(_tmpptr); !done; done = true) \
756+
/* Force modified pointer usage within the scope */ \
757+
for (const typeof(uptr) uptr = _tmpptr; !done; done = true)
758+
759+
/**
760+
* scoped_user_read_access_size - Start a scoped user read access with given size
761+
* @usrc: Pointer to the user space address to read from
762+
* @size: Size of the access starting from @usrc
763+
* @elbl: Error label to goto when the access region is rejected
764+
*
765+
* For further information see __scoped_user_access() above.
766+
*/
767+
#define scoped_user_read_access_size(usrc, size, elbl) \
768+
__scoped_user_access(read, usrc, size, elbl)
769+
770+
/**
771+
* scoped_user_read_access - Start a scoped user read access
772+
* @usrc: Pointer to the user space address to read from
773+
* @elbl: Error label to goto when the access region is rejected
774+
*
775+
* The size of the access starting from @usrc is determined via sizeof(*@usrc)).
776+
*
777+
* For further information see __scoped_user_access() above.
778+
*/
779+
#define scoped_user_read_access(usrc, elbl) \
780+
scoped_user_read_access_size(usrc, sizeof(*(usrc)), elbl)
781+
782+
/**
783+
* scoped_user_write_access_size - Start a scoped user write access with given size
784+
* @udst: Pointer to the user space address to write to
785+
* @size: Size of the access starting from @udst
786+
* @elbl: Error label to goto when the access region is rejected
787+
*
788+
* For further information see __scoped_user_access() above.
789+
*/
790+
#define scoped_user_write_access_size(udst, size, elbl) \
791+
__scoped_user_access(write, udst, size, elbl)
792+
793+
/**
794+
* scoped_user_write_access - Start a scoped user write access
795+
* @udst: Pointer to the user space address to write to
796+
* @elbl: Error label to goto when the access region is rejected
797+
*
798+
* The size of the access starting from @udst is determined via sizeof(*@udst)).
799+
*
800+
* For further information see __scoped_user_access() above.
801+
*/
802+
#define scoped_user_write_access(udst, elbl) \
803+
scoped_user_write_access_size(udst, sizeof(*(udst)), elbl)
804+
805+
/**
806+
* scoped_user_rw_access_size - Start a scoped user read/write access with given size
807+
* @uptr Pointer to the user space address to read from and write to
808+
* @size: Size of the access starting from @uptr
809+
* @elbl: Error label to goto when the access region is rejected
810+
*
811+
* For further information see __scoped_user_access() above.
812+
*/
813+
#define scoped_user_rw_access_size(uptr, size, elbl) \
814+
__scoped_user_access(rw, uptr, size, elbl)
815+
816+
/**
817+
* scoped_user_rw_access - Start a scoped user read/write access
818+
* @uptr Pointer to the user space address to read from and write to
819+
* @elbl: Error label to goto when the access region is rejected
820+
*
821+
* The size of the access starting from @uptr is determined via sizeof(*@uptr)).
822+
*
823+
* For further information see __scoped_user_access() above.
824+
*/
825+
#define scoped_user_rw_access(uptr, elbl) \
826+
scoped_user_rw_access_size(uptr, sizeof(*(uptr)), elbl)
827+
636828
#ifdef CONFIG_HARDENED_USERCOPY
637829
void __noreturn usercopy_abort(const char *name, const char *detail,
638830
bool to_user, unsigned long offset,

0 commit comments

Comments
 (0)