diff options
author | Dave Martin <Dave.Martin@arm.com> | 2017-06-15 10:03:38 -0400 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2017-06-20 07:42:58 -0400 |
commit | 20987de3c2c45c314e0386f724aa85f55d984ef2 (patch) | |
tree | e4239945bd5c53c4d7d7865e76d7683858bda8ff | |
parent | 8f36094802e4e6de180b36bcac4cfd9d319e1b64 (diff) |
arm64: signal: split frame link record from sigcontext structure
In order to be able to increase the amount of the data currently
written to the __reserved[] array in the signal frame, it is
necessary to overwrite the locations currently occupied by the
{fp,lr} frame link record pushed at the top of the signal stack.
In order for this to work, this patch detaches the frame link
record from struct rt_sigframe and places it separately at the top
of the signal stack. This will allow subsequent patches to insert
data between it and __reserved[].
This change relies on the non-ABI status of the placement of the
frame record with respect to struct sigframe: this status is
undocumented, but the placement is not declared or described in the
user headers, and known unwinder implementations (libgcc,
libunwind, gdb) appear not to rely on it.
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r-- | arch/arm64/kernel/signal.c | 50 |
1 files changed, 32 insertions, 18 deletions
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c index c7b6de62f9d3..1e5ed3be78ed 100644 --- a/arch/arm64/kernel/signal.c +++ b/arch/arm64/kernel/signal.c | |||
@@ -19,6 +19,7 @@ | |||
19 | 19 | ||
20 | #include <linux/compat.h> | 20 | #include <linux/compat.h> |
21 | #include <linux/errno.h> | 21 | #include <linux/errno.h> |
22 | #include <linux/kernel.h> | ||
22 | #include <linux/signal.h> | 23 | #include <linux/signal.h> |
23 | #include <linux/personality.h> | 24 | #include <linux/personality.h> |
24 | #include <linux/freezer.h> | 25 | #include <linux/freezer.h> |
@@ -41,10 +42,18 @@ | |||
41 | struct rt_sigframe { | 42 | struct rt_sigframe { |
42 | struct siginfo info; | 43 | struct siginfo info; |
43 | struct ucontext uc; | 44 | struct ucontext uc; |
45 | }; | ||
46 | |||
47 | struct frame_record { | ||
44 | u64 fp; | 48 | u64 fp; |
45 | u64 lr; | 49 | u64 lr; |
46 | }; | 50 | }; |
47 | 51 | ||
52 | struct rt_sigframe_user_layout { | ||
53 | struct rt_sigframe __user *sigframe; | ||
54 | struct frame_record __user *next_frame; | ||
55 | }; | ||
56 | |||
48 | static int preserve_fpsimd_context(struct fpsimd_context __user *ctx) | 57 | static int preserve_fpsimd_context(struct fpsimd_context __user *ctx) |
49 | { | 58 | { |
50 | struct fpsimd_state *fpsimd = ¤t->thread.fpsimd_state; | 59 | struct fpsimd_state *fpsimd = ¤t->thread.fpsimd_state; |
@@ -162,16 +171,17 @@ badframe: | |||
162 | return 0; | 171 | return 0; |
163 | } | 172 | } |
164 | 173 | ||
165 | static int setup_sigframe(struct rt_sigframe __user *sf, | 174 | static int setup_sigframe(struct rt_sigframe_user_layout *user, |
166 | struct pt_regs *regs, sigset_t *set) | 175 | struct pt_regs *regs, sigset_t *set) |
167 | { | 176 | { |
168 | int i, err = 0; | 177 | int i, err = 0; |
178 | struct rt_sigframe __user *sf = user->sigframe; | ||
169 | void *aux = sf->uc.uc_mcontext.__reserved; | 179 | void *aux = sf->uc.uc_mcontext.__reserved; |
170 | struct _aarch64_ctx *end; | 180 | struct _aarch64_ctx *end; |
171 | 181 | ||
172 | /* set up the stack frame for unwinding */ | 182 | /* set up the stack frame for unwinding */ |
173 | __put_user_error(regs->regs[29], &sf->fp, err); | 183 | __put_user_error(regs->regs[29], &user->next_frame->fp, err); |
174 | __put_user_error(regs->regs[30], &sf->lr, err); | 184 | __put_user_error(regs->regs[30], &user->next_frame->lr, err); |
175 | 185 | ||
176 | for (i = 0; i < 31; i++) | 186 | for (i = 0; i < 31; i++) |
177 | __put_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i], | 187 | __put_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i], |
@@ -209,34 +219,36 @@ static int setup_sigframe(struct rt_sigframe __user *sf, | |||
209 | return err; | 219 | return err; |
210 | } | 220 | } |
211 | 221 | ||
212 | static struct rt_sigframe __user *get_sigframe(struct ksignal *ksig, | 222 | static int get_sigframe(struct rt_sigframe_user_layout *user, |
213 | struct pt_regs *regs) | 223 | struct ksignal *ksig, struct pt_regs *regs) |
214 | { | 224 | { |
215 | unsigned long sp, sp_top; | 225 | unsigned long sp, sp_top; |
216 | struct rt_sigframe __user *frame; | ||
217 | 226 | ||
218 | sp = sp_top = sigsp(regs->sp, ksig); | 227 | sp = sp_top = sigsp(regs->sp, ksig); |
219 | 228 | ||
220 | sp = (sp - sizeof(struct rt_sigframe)) & ~15; | 229 | sp = round_down(sp - sizeof(struct frame_record), 16); |
221 | frame = (struct rt_sigframe __user *)sp; | 230 | user->next_frame = (struct frame_record __user *)sp; |
231 | |||
232 | sp = round_down(sp - sizeof(struct rt_sigframe), 16); | ||
233 | user->sigframe = (struct rt_sigframe __user *)sp; | ||
222 | 234 | ||
223 | /* | 235 | /* |
224 | * Check that we can actually write to the signal frame. | 236 | * Check that we can actually write to the signal frame. |
225 | */ | 237 | */ |
226 | if (!access_ok(VERIFY_WRITE, frame, sp_top - sp)) | 238 | if (!access_ok(VERIFY_WRITE, user->sigframe, sp_top - sp)) |
227 | frame = NULL; | 239 | return -EFAULT; |
228 | 240 | ||
229 | return frame; | 241 | return 0; |
230 | } | 242 | } |
231 | 243 | ||
232 | static void setup_return(struct pt_regs *regs, struct k_sigaction *ka, | 244 | static void setup_return(struct pt_regs *regs, struct k_sigaction *ka, |
233 | void __user *frame, int usig) | 245 | struct rt_sigframe_user_layout *user, int usig) |
234 | { | 246 | { |
235 | __sigrestore_t sigtramp; | 247 | __sigrestore_t sigtramp; |
236 | 248 | ||
237 | regs->regs[0] = usig; | 249 | regs->regs[0] = usig; |
238 | regs->sp = (unsigned long)frame; | 250 | regs->sp = (unsigned long)user->sigframe; |
239 | regs->regs[29] = regs->sp + offsetof(struct rt_sigframe, fp); | 251 | regs->regs[29] = (unsigned long)&user->next_frame->fp; |
240 | regs->pc = (unsigned long)ka->sa.sa_handler; | 252 | regs->pc = (unsigned long)ka->sa.sa_handler; |
241 | 253 | ||
242 | if (ka->sa.sa_flags & SA_RESTORER) | 254 | if (ka->sa.sa_flags & SA_RESTORER) |
@@ -250,20 +262,22 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka, | |||
250 | static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, | 262 | static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, |
251 | struct pt_regs *regs) | 263 | struct pt_regs *regs) |
252 | { | 264 | { |
265 | struct rt_sigframe_user_layout user; | ||
253 | struct rt_sigframe __user *frame; | 266 | struct rt_sigframe __user *frame; |
254 | int err = 0; | 267 | int err = 0; |
255 | 268 | ||
256 | frame = get_sigframe(ksig, regs); | 269 | if (get_sigframe(&user, ksig, regs)) |
257 | if (!frame) | ||
258 | return 1; | 270 | return 1; |
259 | 271 | ||
272 | frame = user.sigframe; | ||
273 | |||
260 | __put_user_error(0, &frame->uc.uc_flags, err); | 274 | __put_user_error(0, &frame->uc.uc_flags, err); |
261 | __put_user_error(NULL, &frame->uc.uc_link, err); | 275 | __put_user_error(NULL, &frame->uc.uc_link, err); |
262 | 276 | ||
263 | err |= __save_altstack(&frame->uc.uc_stack, regs->sp); | 277 | err |= __save_altstack(&frame->uc.uc_stack, regs->sp); |
264 | err |= setup_sigframe(frame, regs, set); | 278 | err |= setup_sigframe(&user, regs, set); |
265 | if (err == 0) { | 279 | if (err == 0) { |
266 | setup_return(regs, &ksig->ka, frame, usig); | 280 | setup_return(regs, &ksig->ka, &user, usig); |
267 | if (ksig->ka.sa.sa_flags & SA_SIGINFO) { | 281 | if (ksig->ka.sa.sa_flags & SA_SIGINFO) { |
268 | err |= copy_siginfo_to_user(&frame->info, &ksig->info); | 282 | err |= copy_siginfo_to_user(&frame->info, &ksig->info); |
269 | regs->regs[1] = (unsigned long)&frame->info; | 283 | regs->regs[1] = (unsigned long)&frame->info; |