Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions include/linux/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,32 @@ static inline void *offset_to_ptr(const int *off)
*/
#define prevent_tail_call_optimization() mb()

/*
* Sometimes a #define needs to declare a variable that is scoped
* to the statement that follows without having mismatched {}.
* with (int x = expression) {
* statements
* }
* is the same as:
* {
* int x = expression;
* statements
* }
* but lets it all be hidden from the call site, eg:
* frobnicate(x, args) {
* statements
* }
* Only a single variable can be defined, and_with() allows extra ones
* without adding an additional outer loop.
*
* The controlled scope can be terminated using return, break, continue or goto.
*/
#define with(declaration) \
for (bool _with_done = false; !_with_done; _with_done = true) \
and_with (declaration)
#define and_with(declaration) \
for (declaration; !_with_done; _with_done = true)

#include <asm/rwonce.h>

#endif /* __LINUX_COMPILER_H */
51 changes: 18 additions & 33 deletions include/linux/uaccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -647,36 +647,22 @@ static inline void user_access_restore(unsigned long flags) { }
/* 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) \
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)
#undef USER_ACCESS_GUARD
/* Cleanup wrapper functions */
static __always_inline void __scoped_user_read_access_end(const void *p)
{
user_read_access_end();
};
static __always_inline void __scoped_user_write_access_end(const void *p)
{
user_write_access_end();
};
static __always_inline void __scoped_user_rw_access_end(const void *p)
{
user_access_end();
};

/**
* __scoped_user_access_begin - Start a scoped user access
Expand Down Expand Up @@ -751,12 +737,11 @@ USER_ACCESS_GUARD(rw)
* Don't use directly. Use scoped_masked_user_$MODE_access() instead.
*/
#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); \
!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)
with (auto _tmpptr = __scoped_user_access_begin(mode, uptr, size, elbl)) \
/* Force modified pointer usage within the scope */ \
__diag_push() __diag_ignore_all("-Wshadow", "uptr is readonly copy") \
and_with (const auto uptr __cleanup(__scoped_user_##mode##_access_end) = _tmpptr) \
__diag_pop()

/**
* scoped_user_read_access_size - Start a scoped user read access with given size
Expand Down
72 changes: 42 additions & 30 deletions kernel/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -4469,10 +4469,16 @@ int restore_altstack(const stack_t __user *uss)
int __save_altstack(stack_t __user *uss, unsigned long sp)
{
struct task_struct *t = current;
int err = __put_user((void __user *)t->sas_ss_sp, &uss->ss_sp) |
__put_user(t->sas_ss_flags, &uss->ss_flags) |
__put_user(t->sas_ss_size, &uss->ss_size);
return err;

scoped_user_write_access(uss, Efault) {
unsafe_put_user((void __user *)t->sas_ss_sp, &uss->ss_sp, Efault);
unsafe_put_user(t->sas_ss_flags, &uss->ss_flags, Efault);
unsafe_put_user(t->sas_ss_size, &uss->ss_size, Efault);
}
return 0;

Efault:
return -EFAULT;
}

#ifdef CONFIG_COMPAT
Expand Down Expand Up @@ -4705,12 +4711,12 @@ SYSCALL_DEFINE3(sigaction, int, sig,

if (act) {
old_sigset_t mask;
if (!access_ok(act, sizeof(*act)) ||
__get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
__get_user(new_ka.sa.sa_restorer, &act->sa_restorer) ||
__get_user(new_ka.sa.sa_flags, &act->sa_flags) ||
__get_user(mask, &act->sa_mask))
return -EFAULT;
scoped_user_read_access(act, Efault) {
unsafe_get_user(new_ka.sa.sa_handler, &act->sa_handler, Efault);
unsafe_get_user(new_ka.sa.sa_restorer, &act->sa_restorer, Efault);
unsafe_get_user(new_ka.sa.sa_flags, &act->sa_flags, Efault);
unsafe_get_user(mask, &act->sa_mask, Efault);
}
#ifdef __ARCH_HAS_KA_RESTORER
new_ka.ka_restorer = NULL;
#endif
Expand All @@ -4720,15 +4726,18 @@ SYSCALL_DEFINE3(sigaction, int, sig,
ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);

if (!ret && oact) {
if (!access_ok(oact, sizeof(*oact)) ||
__put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
__put_user(old_ka.sa.sa_restorer, &oact->sa_restorer) ||
__put_user(old_ka.sa.sa_flags, &oact->sa_flags) ||
__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask))
return -EFAULT;
scoped_user_write_access(oact, Efault) {
unsafe_put_user(old_ka.sa.sa_handler, &oact->sa_handler, Efault);
unsafe_put_user(old_ka.sa.sa_restorer, &oact->sa_restorer, Efault);
unsafe_put_user(old_ka.sa.sa_flags, &oact->sa_flags, Efault);
unsafe_put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask, Efault);
}
}

return ret;

Efault:
return -EFAULT;
}
#endif
#ifdef CONFIG_COMPAT_OLD_SIGACTION
Expand All @@ -4742,12 +4751,12 @@ COMPAT_SYSCALL_DEFINE3(sigaction, int, sig,
compat_uptr_t handler, restorer;

if (act) {
if (!access_ok(act, sizeof(*act)) ||
__get_user(handler, &act->sa_handler) ||
__get_user(restorer, &act->sa_restorer) ||
__get_user(new_ka.sa.sa_flags, &act->sa_flags) ||
__get_user(mask, &act->sa_mask))
return -EFAULT;
scoped_user_read_access(act, Efault) {
unsafe_get_user(handler, &act->sa_handler, Efault);
unsafe_get_user(restorer, &act->sa_restorer, Efault);
unsafe_get_user(new_ka.sa.sa_flags, &act->sa_flags, Efault);
unsafe_get_user(mask, &act->sa_mask, Efault);
}

#ifdef __ARCH_HAS_KA_RESTORER
new_ka.ka_restorer = NULL;
Expand All @@ -4760,16 +4769,19 @@ COMPAT_SYSCALL_DEFINE3(sigaction, int, sig,
ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);

if (!ret && oact) {
if (!access_ok(oact, sizeof(*oact)) ||
__put_user(ptr_to_compat(old_ka.sa.sa_handler),
&oact->sa_handler) ||
__put_user(ptr_to_compat(old_ka.sa.sa_restorer),
&oact->sa_restorer) ||
__put_user(old_ka.sa.sa_flags, &oact->sa_flags) ||
__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask))
return -EFAULT;
scoped_user_write_access(oact, Efault) {
unsafe_put_user(ptr_to_compat(old_ka.sa.sa_handler),
&oact->sa_handler, Efault);
unsafe_put_user(ptr_to_compat(old_ka.sa.sa_restorer),
&oact->sa_restorer, Efault);
unsafe_put_user(old_ka.sa.sa_flags, &oact->sa_flags, Efault);
unsafe_put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask, Efault);
}
}
return ret;

Efault:
return -EFAULT;
}
#endif

Expand Down
Loading