Skip to content

Commit

Permalink
s390/signal: set correct address space control
Browse files Browse the repository at this point in the history
If user space is running in primary mode it can switch to secondary
or access register mode, this is used e.g. in the clock_gettime code
of the vdso. If a signal is delivered to the user space process while
it has been running in access register mode the signal handler is
executed in access register mode as well which will result in a crash
most of the time.

Set the address space control bits in the PSW to the default for the
execution of the signal handler and make sure that the previous
address space control is restored on signal return. Take care
that user space can not switch to the kernel address space by
modifying the registers in the signal frame.

Cc: stable@vger.kernel.org
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
  • Loading branch information
Martin Schwidefsky committed Nov 12, 2012
1 parent 77b6706 commit fa968ee
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 7 deletions.
2 changes: 1 addition & 1 deletion arch/s390/include/asm/compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#define PSW32_MASK_CC 0x00003000UL
#define PSW32_MASK_PM 0x00000f00UL

#define PSW32_MASK_USER 0x00003F00UL
#define PSW32_MASK_USER 0x0000FF00UL

#define PSW32_ADDR_AMODE 0x80000000UL
#define PSW32_ADDR_INSN 0x7FFFFFFFUL
Expand Down
4 changes: 2 additions & 2 deletions arch/s390/include/uapi/asm/ptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ typedef struct
#define PSW_MASK_EA 0x00000000UL
#define PSW_MASK_BA 0x00000000UL

#define PSW_MASK_USER 0x00003F00UL
#define PSW_MASK_USER 0x0000FF00UL

#define PSW_ADDR_AMODE 0x80000000UL
#define PSW_ADDR_INSN 0x7FFFFFFFUL
Expand Down Expand Up @@ -269,7 +269,7 @@ typedef struct
#define PSW_MASK_EA 0x0000000100000000UL
#define PSW_MASK_BA 0x0000000080000000UL

#define PSW_MASK_USER 0x00003F8180000000UL
#define PSW_MASK_USER 0x0000FF8180000000UL

#define PSW_ADDR_AMODE 0x0000000000000000UL
#define PSW_ADDR_INSN 0xFFFFFFFFFFFFFFFFUL
Expand Down
14 changes: 12 additions & 2 deletions arch/s390/kernel/compat_signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,10 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
(__u64)(regs32.psw.mask & PSW32_MASK_USER) << 32 |
(__u64)(regs32.psw.addr & PSW32_ADDR_AMODE);
/* Check for invalid user address space control. */
if ((regs->psw.mask & PSW_MASK_ASC) >= (psw_kernel_bits & PSW_MASK_ASC))
regs->psw.mask = (psw_user_bits & PSW_MASK_ASC) |
(regs->psw.mask & ~PSW_MASK_ASC);
regs->psw.addr = (__u64)(regs32.psw.addr & PSW32_ADDR_INSN);
for (i = 0; i < NUM_GPRS; i++)
regs->gprs[i] = (__u64) regs32.gprs[i];
Expand Down Expand Up @@ -481,7 +485,10 @@ static int setup_frame32(int sig, struct k_sigaction *ka,

/* Set up registers for signal handler */
regs->gprs[15] = (__force __u64) frame;
regs->psw.mask |= PSW_MASK_BA; /* force amode 31 */
/* Force 31 bit amode and default user address space control. */
regs->psw.mask = PSW_MASK_BA |
(psw_user_bits & PSW_MASK_ASC) |
(regs->psw.mask & ~PSW_MASK_ASC);
regs->psw.addr = (__force __u64) ka->sa.sa_handler;

regs->gprs[2] = map_signal(sig);
Expand Down Expand Up @@ -549,7 +556,10 @@ static int setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info,

/* Set up registers for signal handler */
regs->gprs[15] = (__force __u64) frame;
regs->psw.mask |= PSW_MASK_BA; /* force amode 31 */
/* Force 31 bit amode and default user address space control. */
regs->psw.mask = PSW_MASK_BA |
(psw_user_bits & PSW_MASK_ASC) |
(regs->psw.mask & ~PSW_MASK_ASC);
regs->psw.addr = (__u64) ka->sa.sa_handler;

regs->gprs[2] = map_signal(sig);
Expand Down
14 changes: 12 additions & 2 deletions arch/s390/kernel/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
/* Use regs->psw.mask instead of psw_user_bits to preserve PER bit. */
regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
(user_sregs.regs.psw.mask & PSW_MASK_USER);
/* Check for invalid user address space control. */
if ((regs->psw.mask & PSW_MASK_ASC) >= (psw_kernel_bits & PSW_MASK_ASC))
regs->psw.mask = (psw_user_bits & PSW_MASK_ASC) |
(regs->psw.mask & ~PSW_MASK_ASC);
/* Check for invalid amode */
if (regs->psw.mask & PSW_MASK_EA)
regs->psw.mask |= PSW_MASK_BA;
Expand Down Expand Up @@ -273,7 +277,10 @@ static int setup_frame(int sig, struct k_sigaction *ka,

/* Set up registers for signal handler */
regs->gprs[15] = (unsigned long) frame;
regs->psw.mask |= PSW_MASK_EA | PSW_MASK_BA; /* 64 bit amode */
/* Force default amode and default user address space control. */
regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA |
(psw_user_bits & PSW_MASK_ASC) |
(regs->psw.mask & ~PSW_MASK_ASC);
regs->psw.addr = (unsigned long) ka->sa.sa_handler | PSW_ADDR_AMODE;

regs->gprs[2] = map_signal(sig);
Expand Down Expand Up @@ -346,7 +353,10 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,

/* Set up registers for signal handler */
regs->gprs[15] = (unsigned long) frame;
regs->psw.mask |= PSW_MASK_EA | PSW_MASK_BA; /* 64 bit amode */
/* Force default amode and default user address space control. */
regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA |
(psw_user_bits & PSW_MASK_ASC) |
(regs->psw.mask & ~PSW_MASK_ASC);
regs->psw.addr = (unsigned long) ka->sa.sa_handler | PSW_ADDR_AMODE;

regs->gprs[2] = map_signal(sig);
Expand Down

0 comments on commit fa968ee

Please sign in to comment.