diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2017-05-27 00:29:34 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2017-05-27 15:38:17 -0400 |
commit | bcfe8ad8ef55a1cf3c935c2667e8e5ae598b3b7e (patch) | |
tree | b94e3ebf18142c1c2fde4d7d1da4d68035100b86 | |
parent | 613763a1f056211522bac77ff39f25706e678fdd (diff) |
do_sigaltstack(): lift copying to/from userland into callers
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | kernel/signal.c | 107 |
1 files changed, 46 insertions, 61 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index ca92bcfeb322..d1eed0d7ca64 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -3113,78 +3113,68 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact) | |||
3113 | } | 3113 | } |
3114 | 3114 | ||
3115 | static int | 3115 | static int |
3116 | do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long sp) | 3116 | do_sigaltstack (const stack_t *ss, stack_t *oss, unsigned long sp) |
3117 | { | 3117 | { |
3118 | stack_t oss; | 3118 | struct task_struct *t = current; |
3119 | int error; | ||
3120 | 3119 | ||
3121 | oss.ss_sp = (void __user *) current->sas_ss_sp; | 3120 | if (oss) { |
3122 | oss.ss_size = current->sas_ss_size; | 3121 | memset(oss, 0, sizeof(stack_t)); |
3123 | oss.ss_flags = sas_ss_flags(sp) | | 3122 | oss->ss_sp = (void __user *) t->sas_ss_sp; |
3124 | (current->sas_ss_flags & SS_FLAG_BITS); | 3123 | oss->ss_size = t->sas_ss_size; |
3124 | oss->ss_flags = sas_ss_flags(sp) | | ||
3125 | (current->sas_ss_flags & SS_FLAG_BITS); | ||
3126 | } | ||
3125 | 3127 | ||
3126 | if (uss) { | 3128 | if (ss) { |
3127 | void __user *ss_sp; | 3129 | void __user *ss_sp = ss->ss_sp; |
3128 | size_t ss_size; | 3130 | size_t ss_size = ss->ss_size; |
3129 | unsigned ss_flags; | 3131 | unsigned ss_flags = ss->ss_flags; |
3130 | int ss_mode; | 3132 | int ss_mode; |
3131 | 3133 | ||
3132 | error = -EFAULT; | 3134 | if (unlikely(on_sig_stack(sp))) |
3133 | if (!access_ok(VERIFY_READ, uss, sizeof(*uss))) | 3135 | return -EPERM; |
3134 | goto out; | ||
3135 | error = __get_user(ss_sp, &uss->ss_sp) | | ||
3136 | __get_user(ss_flags, &uss->ss_flags) | | ||
3137 | __get_user(ss_size, &uss->ss_size); | ||
3138 | if (error) | ||
3139 | goto out; | ||
3140 | |||
3141 | error = -EPERM; | ||
3142 | if (on_sig_stack(sp)) | ||
3143 | goto out; | ||
3144 | 3136 | ||
3145 | ss_mode = ss_flags & ~SS_FLAG_BITS; | 3137 | ss_mode = ss_flags & ~SS_FLAG_BITS; |
3146 | error = -EINVAL; | 3138 | if (unlikely(ss_mode != SS_DISABLE && ss_mode != SS_ONSTACK && |
3147 | if (ss_mode != SS_DISABLE && ss_mode != SS_ONSTACK && | 3139 | ss_mode != 0)) |
3148 | ss_mode != 0) | 3140 | return -EINVAL; |
3149 | goto out; | ||
3150 | 3141 | ||
3151 | if (ss_mode == SS_DISABLE) { | 3142 | if (ss_mode == SS_DISABLE) { |
3152 | ss_size = 0; | 3143 | ss_size = 0; |
3153 | ss_sp = NULL; | 3144 | ss_sp = NULL; |
3154 | } else { | 3145 | } else { |
3155 | error = -ENOMEM; | 3146 | if (unlikely(ss_size < MINSIGSTKSZ)) |
3156 | if (ss_size < MINSIGSTKSZ) | 3147 | return -ENOMEM; |
3157 | goto out; | ||
3158 | } | 3148 | } |
3159 | 3149 | ||
3160 | current->sas_ss_sp = (unsigned long) ss_sp; | 3150 | t->sas_ss_sp = (unsigned long) ss_sp; |
3161 | current->sas_ss_size = ss_size; | 3151 | t->sas_ss_size = ss_size; |
3162 | current->sas_ss_flags = ss_flags; | 3152 | t->sas_ss_flags = ss_flags; |
3163 | } | ||
3164 | |||
3165 | error = 0; | ||
3166 | if (uoss) { | ||
3167 | error = -EFAULT; | ||
3168 | if (!access_ok(VERIFY_WRITE, uoss, sizeof(*uoss))) | ||
3169 | goto out; | ||
3170 | error = __put_user(oss.ss_sp, &uoss->ss_sp) | | ||
3171 | __put_user(oss.ss_size, &uoss->ss_size) | | ||
3172 | __put_user(oss.ss_flags, &uoss->ss_flags); | ||
3173 | } | 3153 | } |
3174 | 3154 | return 0; | |
3175 | out: | ||
3176 | return error; | ||
3177 | } | 3155 | } |
3156 | |||
3178 | SYSCALL_DEFINE2(sigaltstack,const stack_t __user *,uss, stack_t __user *,uoss) | 3157 | SYSCALL_DEFINE2(sigaltstack,const stack_t __user *,uss, stack_t __user *,uoss) |
3179 | { | 3158 | { |
3180 | return do_sigaltstack(uss, uoss, current_user_stack_pointer()); | 3159 | stack_t new, old; |
3160 | int err; | ||
3161 | if (uss && copy_from_user(&new, uss, sizeof(stack_t))) | ||
3162 | return -EFAULT; | ||
3163 | err = do_sigaltstack(uss ? &new : NULL, uoss ? &old : NULL, | ||
3164 | current_user_stack_pointer()); | ||
3165 | if (!err && uoss && copy_to_user(uoss, &old, sizeof(stack_t))) | ||
3166 | err = -EFAULT; | ||
3167 | return err; | ||
3181 | } | 3168 | } |
3182 | 3169 | ||
3183 | int restore_altstack(const stack_t __user *uss) | 3170 | int restore_altstack(const stack_t __user *uss) |
3184 | { | 3171 | { |
3185 | int err = do_sigaltstack(uss, NULL, current_user_stack_pointer()); | 3172 | stack_t new; |
3173 | if (copy_from_user(&new, uss, sizeof(stack_t))) | ||
3174 | return -EFAULT; | ||
3175 | (void)do_sigaltstack(&new, NULL, current_user_stack_pointer()); | ||
3186 | /* squash all but EFAULT for now */ | 3176 | /* squash all but EFAULT for now */ |
3187 | return err == -EFAULT ? err : 0; | 3177 | return 0; |
3188 | } | 3178 | } |
3189 | 3179 | ||
3190 | int __save_altstack(stack_t __user *uss, unsigned long sp) | 3180 | int __save_altstack(stack_t __user *uss, unsigned long sp) |
@@ -3207,29 +3197,24 @@ COMPAT_SYSCALL_DEFINE2(sigaltstack, | |||
3207 | { | 3197 | { |
3208 | stack_t uss, uoss; | 3198 | stack_t uss, uoss; |
3209 | int ret; | 3199 | int ret; |
3210 | mm_segment_t seg; | ||
3211 | 3200 | ||
3212 | if (uss_ptr) { | 3201 | if (uss_ptr) { |
3213 | compat_stack_t uss32; | 3202 | compat_stack_t uss32; |
3214 | |||
3215 | memset(&uss, 0, sizeof(stack_t)); | ||
3216 | if (copy_from_user(&uss32, uss_ptr, sizeof(compat_stack_t))) | 3203 | if (copy_from_user(&uss32, uss_ptr, sizeof(compat_stack_t))) |
3217 | return -EFAULT; | 3204 | return -EFAULT; |
3218 | uss.ss_sp = compat_ptr(uss32.ss_sp); | 3205 | uss.ss_sp = compat_ptr(uss32.ss_sp); |
3219 | uss.ss_flags = uss32.ss_flags; | 3206 | uss.ss_flags = uss32.ss_flags; |
3220 | uss.ss_size = uss32.ss_size; | 3207 | uss.ss_size = uss32.ss_size; |
3221 | } | 3208 | } |
3222 | seg = get_fs(); | 3209 | ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, |
3223 | set_fs(KERNEL_DS); | ||
3224 | ret = do_sigaltstack((stack_t __force __user *) (uss_ptr ? &uss : NULL), | ||
3225 | (stack_t __force __user *) &uoss, | ||
3226 | compat_user_stack_pointer()); | 3210 | compat_user_stack_pointer()); |
3227 | set_fs(seg); | ||
3228 | if (ret >= 0 && uoss_ptr) { | 3211 | if (ret >= 0 && uoss_ptr) { |
3229 | if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(compat_stack_t)) || | 3212 | compat_stack_t old; |
3230 | __put_user(ptr_to_compat(uoss.ss_sp), &uoss_ptr->ss_sp) || | 3213 | memset(&old, 0, sizeof(old)); |
3231 | __put_user(uoss.ss_flags, &uoss_ptr->ss_flags) || | 3214 | old.ss_sp = ptr_to_compat(uoss.ss_sp); |
3232 | __put_user(uoss.ss_size, &uoss_ptr->ss_size)) | 3215 | old.ss_flags = uoss.ss_flags; |
3216 | old.ss_size = uoss.ss_size; | ||
3217 | if (copy_to_user(uoss_ptr, &old, sizeof(compat_stack_t))) | ||
3233 | ret = -EFAULT; | 3218 | ret = -EFAULT; |
3234 | } | 3219 | } |
3235 | return ret; | 3220 | return ret; |