]> git.hungrycats.org Git - linux/commitdiff
powerpc/tm: Block signal return setting invalid MSR state
authorMichael Neuling <mikey@neuling.org>
Thu, 19 Nov 2015 04:44:44 +0000 (15:44 +1100)
committerJiri Slaby <jslaby@suse.cz>
Fri, 12 Feb 2016 07:57:56 +0000 (08:57 +0100)
commit d2b9d2a5ad5ef04ff978c9923d19730cb05efd55 upstream.

Currently we allow both the MSR T and S bits to be set by userspace on
a signal return.  Unfortunately this is a reserved configuration and
will cause a TM Bad Thing exception if attempted (via rfid).

This patch checks for this case in both the 32 and 64 bit signals
code.  If both T and S are set, we mark the context as invalid.

Found using a syscall fuzzer.

Fixes: 2b0a576d15e0 ("powerpc: Add new transactional memory state to the signal context")
Signed-off-by: Michael Neuling <mikey@neuling.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
arch/powerpc/include/asm/reg.h
arch/powerpc/kernel/signal_32.c
arch/powerpc/kernel/signal_64.c

index 390e09872b77aac410df84f5bcc15b1534276b04..3ce6b7b5ca19d267c296b390d46d0af12ccbe361 100644 (file)
 #define MSR_TS_T       __MASK(MSR_TS_T_LG)     /*  Transaction Transactional */
 #define MSR_TS_MASK    (MSR_TS_T | MSR_TS_S)   /* Transaction State bits */
 #define MSR_TM_ACTIVE(x) (((x) & MSR_TS_MASK) != 0) /* Transaction active? */
+#define MSR_TM_RESV(x) (((x) & MSR_TS_MASK) == MSR_TS_MASK) /* Reserved */
 #define MSR_TM_TRANSACTIONAL(x)        (((x) & MSR_TS_MASK) == MSR_TS_T)
 #define MSR_TM_SUSPENDED(x)    (((x) & MSR_TS_MASK) == MSR_TS_S)
 
index 7fce77b89f6db496f2f683151ae68f87dac17c5d..3678e5097c5970d0e763788a5727b1111eb9ce9e 100644 (file)
@@ -867,6 +867,15 @@ static long restore_tm_user_regs(struct pt_regs *regs,
                return 1;
 #endif /* CONFIG_SPE */
 
+       /* Get the top half of the MSR from the user context */
+       if (__get_user(msr_hi, &tm_sr->mc_gregs[PT_MSR]))
+               return 1;
+       msr_hi <<= 32;
+       /* If TM bits are set to the reserved value, it's an invalid context */
+       if (MSR_TM_RESV(msr_hi))
+               return 1;
+       /* Pull in the MSR TM bits from the user context */
+       regs->msr = (regs->msr & ~MSR_TS_MASK) | (msr_hi & MSR_TS_MASK);
        /* Now, recheckpoint.  This loads up all of the checkpointed (older)
         * registers, including FP and V[S]Rs.  After recheckpointing, the
         * transactional versions should be loaded.
@@ -876,11 +885,6 @@ static long restore_tm_user_regs(struct pt_regs *regs,
        current->thread.tm_texasr |= TEXASR_FS;
        /* This loads the checkpointed FP/VEC state, if used */
        tm_recheckpoint(&current->thread, msr);
-       /* Get the top half of the MSR */
-       if (__get_user(msr_hi, &tm_sr->mc_gregs[PT_MSR]))
-               return 1;
-       /* Pull in MSR TM from user context */
-       regs->msr = (regs->msr & ~MSR_TS_MASK) | ((msr_hi<<32) & MSR_TS_MASK);
 
        /* This loads the speculative FP/VEC state, if used */
        if (msr & MSR_FP) {
index 4456779dba1c6603667eb3068651259405d8aa91..1c43da49fb1c91a2945e6e3ce6806aba56b8b436 100644 (file)
@@ -420,6 +420,10 @@ static long restore_tm_sigcontexts(struct pt_regs *regs,
 
        /* get MSR separately, transfer the LE bit if doing signal return */
        err |= __get_user(msr, &sc->gp_regs[PT_MSR]);
+       /* Don't allow reserved mode. */
+       if (MSR_TM_RESV(msr))
+               return -EINVAL;
+
        /* pull in MSR TM from user context */
        regs->msr = (regs->msr & ~MSR_TS_MASK) | (msr & MSR_TS_MASK);