Skip to content

Commit 81711ce

Browse files
arch/tile: handle rt_sigreturn() more cleanly
The current tile rt_sigreturn() syscall pattern uses the common idiom of loading up pt_regs with all the saved registers from the time of the signal, then anticipating the fact that we will clobber the ABI "return value" register (r0) as we return from the syscall by setting the rt_sigreturn return value to whatever random value was in the pt_regs for r0. However, this breaks in our 64-bit kernel when running "compat" tasks, since we always sign-extend the "return value" register to properly handle returned pointers that are in the upper 2GB of the 32-bit compat address space. Doing this to the sigreturn path then causes occasional random corruption of the 64-bit r0 register. Instead, we stop doing the crazy "load the return-value register" hack in sigreturn. We already have some sigreturn-specific assembly code that we use to pass the pt_regs pointer to C code. We extend that code to also set the link register to point to a spot a few instructions after the usual syscall return address so we don't clobber the saved r0. Now it no longer matters what the rt_sigreturn syscall returns, and the pt_regs structure can be cleanly and completely reloaded. Signed-off-by: Chris Metcalf <[email protected]>
1 parent bc4cf2b commit 81711ce

File tree

4 files changed

+29
-13
lines changed

4 files changed

+29
-13
lines changed

arch/tile/include/asm/signal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
#if defined(__KERNEL__) && !defined(__ASSEMBLY__)
2727
struct pt_regs;
28-
int restore_sigcontext(struct pt_regs *, struct sigcontext __user *, long *);
28+
int restore_sigcontext(struct pt_regs *, struct sigcontext __user *);
2929
int setup_sigcontext(struct sigcontext __user *, struct pt_regs *);
3030
void do_signal(struct pt_regs *regs);
3131
#endif

arch/tile/kernel/compat_signal.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,12 +290,12 @@ long compat_sys_sigaltstack(const struct compat_sigaltstack __user *uss_ptr,
290290
return ret;
291291
}
292292

293+
/* The assembly shim for this function arranges to ignore the return value. */
293294
long compat_sys_rt_sigreturn(struct pt_regs *regs)
294295
{
295296
struct compat_rt_sigframe __user *frame =
296297
(struct compat_rt_sigframe __user *) compat_ptr(regs->sp);
297298
sigset_t set;
298-
long r0;
299299

300300
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
301301
goto badframe;
@@ -308,13 +308,13 @@ long compat_sys_rt_sigreturn(struct pt_regs *regs)
308308
recalc_sigpending();
309309
spin_unlock_irq(&current->sighand->siglock);
310310

311-
if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &r0))
311+
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
312312
goto badframe;
313313

314314
if (compat_sys_sigaltstack(&frame->uc.uc_stack, NULL, regs) != 0)
315315
goto badframe;
316316

317-
return r0;
317+
return 0;
318318

319319
badframe:
320320
force_sig(SIGSEGV, current);

arch/tile/kernel/intvec_32.S

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,8 +1342,8 @@ handle_syscall:
13421342
lw r20, r20
13431343

13441344
/* Jump to syscall handler. */
1345-
jalr r20; .Lhandle_syscall_link:
1346-
FEEDBACK_REENTER(handle_syscall)
1345+
jalr r20
1346+
.Lhandle_syscall_link: /* value of "lr" after "jalr r20" above */
13471347

13481348
/*
13491349
* Write our r0 onto the stack so it gets restored instead
@@ -1352,6 +1352,9 @@ handle_syscall:
13521352
PTREGS_PTR(r29, PTREGS_OFFSET_REG(0))
13531353
sw r29, r0
13541354

1355+
.Lsyscall_sigreturn_skip:
1356+
FEEDBACK_REENTER(handle_syscall)
1357+
13551358
/* Do syscall trace again, if requested. */
13561359
lw r30, r31
13571360
andi r30, r30, _TIF_SYSCALL_TRACE
@@ -1536,9 +1539,24 @@ STD_ENTRY_LOCAL(bad_intr)
15361539
}; \
15371540
STD_ENDPROC(_##x)
15381541

1542+
/*
1543+
* Special-case sigreturn to not write r0 to the stack on return.
1544+
* This is technically more efficient, but it also avoids difficulties
1545+
* in the 64-bit OS when handling 32-bit compat code, since we must not
1546+
* sign-extend r0 for the sigreturn return-value case.
1547+
*/
1548+
#define PTREGS_SYSCALL_SIGRETURN(x, reg) \
1549+
STD_ENTRY(_##x); \
1550+
addli lr, lr, .Lsyscall_sigreturn_skip - .Lhandle_syscall_link; \
1551+
{ \
1552+
PTREGS_PTR(reg, PTREGS_OFFSET_BASE); \
1553+
j x \
1554+
}; \
1555+
STD_ENDPROC(_##x)
1556+
15391557
PTREGS_SYSCALL(sys_execve, r3)
15401558
PTREGS_SYSCALL(sys_sigaltstack, r2)
1541-
PTREGS_SYSCALL(sys_rt_sigreturn, r0)
1559+
PTREGS_SYSCALL_SIGRETURN(sys_rt_sigreturn, r0)
15421560
PTREGS_SYSCALL(sys_cmpxchg_badaddr, r1)
15431561

15441562
/* Save additional callee-saves to pt_regs, put address in r4 and jump. */

arch/tile/kernel/signal.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ SYSCALL_DEFINE3(sigaltstack, const stack_t __user *, uss,
5252
*/
5353

5454
int restore_sigcontext(struct pt_regs *regs,
55-
struct sigcontext __user *sc, long *pr0)
55+
struct sigcontext __user *sc)
5656
{
5757
int err = 0;
5858
int i;
@@ -75,17 +75,15 @@ int restore_sigcontext(struct pt_regs *regs,
7575

7676
regs->faultnum = INT_SWINT_1_SIGRETURN;
7777

78-
err |= __get_user(*pr0, &sc->gregs[0]);
7978
return err;
8079
}
8180

82-
/* sigreturn() returns long since it restores r0 in the interrupted code. */
81+
/* The assembly shim for this function arranges to ignore the return value. */
8382
SYSCALL_DEFINE1(rt_sigreturn, struct pt_regs *, regs)
8483
{
8584
struct rt_sigframe __user *frame =
8685
(struct rt_sigframe __user *)(regs->sp);
8786
sigset_t set;
88-
long r0;
8987

9088
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
9189
goto badframe;
@@ -98,13 +96,13 @@ SYSCALL_DEFINE1(rt_sigreturn, struct pt_regs *, regs)
9896
recalc_sigpending();
9997
spin_unlock_irq(&current->sighand->siglock);
10098

101-
if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &r0))
99+
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
102100
goto badframe;
103101

104102
if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->sp) == -EFAULT)
105103
goto badframe;
106104

107-
return r0;
105+
return 0;
108106

109107
badframe:
110108
force_sig(SIGSEGV, current);

0 commit comments

Comments
 (0)