On Mon, 27 Oct 2025 09:43:55 +0100 (CET)
Thomas Gleixner <[email protected]> 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


Reply via email to