parisc: Fix kernel panic due invalid values in IAOQ0 or IAOQ1 [Linux 4.9.187]

This Linux kernel change "parisc: Fix kernel panic due invalid values in IAOQ0 or IAOQ1" is included in the Linux 4.9.187 release. This change is authored by Helge Deller <deller [at] gmx.de> on Tue Jul 16 21:43:11 2019 +0200. The commit for this change in Linux stable tree is d0c4b05 (patch) which is from upstream commit 10835c8. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 10835c8.

parisc: Fix kernel panic due invalid values in IAOQ0 or IAOQ1

commit 10835c854685393a921b68f529bf740fa7c9984d upstream.

On parisc the privilege level of a process is stored in the lowest two bits of
the instruction pointers (IAOQ0 and IAOQ1). On Linux we use privilege level 0
for the kernel and privilege level 3 for user-space. So userspace should not be
allowed to modify IAOQ0 or IAOQ1 of a ptraced process to change it's privilege
level to e.g. 0 to try to gain kernel privileges.

This patch prevents such modifications by always setting the two lowest bits to
one (which relates to privilege level 3 for user-space) if IAOQ0 or IAOQ1 are
modified via ptrace calls in the native and compat ptrace paths.

Link: https://bugs.gentoo.org/481768
Reported-by: Jeroen Roovers <jer@gentoo.org>
Cc: <stable@vger.kernel.org>
Tested-by: Rolf Eike Beer <eike-kernel@sf-tec.de>
Signed-off-by: Helge Deller <deller@gmx.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

There are 28 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 arch/parisc/kernel/ptrace.c | 28 ++++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c
index 8a0c72a..e204fc4 100644
--- a/arch/parisc/kernel/ptrace.c
+++ b/arch/parisc/kernel/ptrace.c
@@ -170,6 +170,9 @@ long arch_ptrace(struct task_struct *child, long request,
        if ((addr & (sizeof(unsigned long)-1)) ||
             addr >= sizeof(struct pt_regs))
            break;
+       if (addr == PT_IAOQ0 || addr == PT_IAOQ1) {
+           data |= 3; /* ensure userspace privilege */
+       }
        if ((addr >= PT_GR1 && addr <= PT_GR31) ||
                addr == PT_IAOQ0 || addr == PT_IAOQ1 ||
                (addr >= PT_FR0 && addr <= PT_FR31 + 4) ||
@@ -231,16 +234,18 @@ long arch_ptrace(struct task_struct *child, long request,

 static compat_ulong_t translate_usr_offset(compat_ulong_t offset)
 {
-   if (offset < 0)
-       return sizeof(struct pt_regs);
-   else if (offset <= 32*4)    /* gr[0..31] */
-       return offset * 2 + 4;
-   else if (offset <= 32*4+32*8)   /* gr[0..31] + fr[0..31] */
-       return offset + 32*4;
-   else if (offset < sizeof(struct pt_regs)/2 + 32*4)
-       return offset * 2 + 4 - 32*8;
+   compat_ulong_t pos;
+
+   if (offset < 32*4)  /* gr[0..31] */
+       pos = offset * 2 + 4;
+   else if (offset < 32*4+32*8)    /* fr[0] ... fr[31] */
+       pos = (offset - 32*4) + PT_FR0;
+   else if (offset < sizeof(struct pt_regs)/2 + 32*4) /* sr[0] ... ipsw */
+       pos = (offset - 32*4 - 32*8) * 2 + PT_SR0 + 4;
    else
-       return sizeof(struct pt_regs);
+       pos = sizeof(struct pt_regs);
+
+   return pos;
 }

 long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
@@ -284,9 +289,12 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
            addr = translate_usr_offset(addr);
            if (addr >= sizeof(struct pt_regs))
                break;
+           if (addr == PT_IAOQ0+4 || addr == PT_IAOQ1+4) {
+               data |= 3; /* ensure userspace privilege */
+           }
            if (addr >= PT_FR0 && addr <= PT_FR31 + 4) {
                /* Special case, fp regs are 64 bits anyway */
-               *(__u64 *) ((char *) task_regs(child) + addr) = data;
+               *(__u32 *) ((char *) task_regs(child) + addr) = data;
                ret = 0;
            }
            else if ((addr >= PT_GR1+4 && addr <= PT_GR31+4) ||

Leave a Reply

Your email address will not be published. Required fields are marked *