In preparation of implementing user_access_begin and friends
on powerpc, the book3s/32 version of prevent_user_access() need
to be prepared for user_access_end().

user_access_end() doesn't provide the address and size which
were passed to user_access_begin(), required by prevent_user_access()
to know which segment to modify.

The list of segments which where unprotected by allow_user_access()
are available in current->kuap. But we don't want prevent_user_access()
to read this all the time, especially everytime it is 0 (for instance
because the access was not a write access).

Implement a special direction case named KUAP_SELF. In this case only,
the addr and end are retrieved from current->kuap.

Signed-off-by: Christophe Leroy <christophe.le...@c-s.fr>
---
v2: no change
---
 arch/powerpc/include/asm/book3s/32/kup.h | 25 ++++++++++++++++++------
 arch/powerpc/include/asm/kup.h           |  1 +
 2 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/arch/powerpc/include/asm/book3s/32/kup.h 
b/arch/powerpc/include/asm/book3s/32/kup.h
index 3c1798e56b55..a99fc3428ac9 100644
--- a/arch/powerpc/include/asm/book3s/32/kup.h
+++ b/arch/powerpc/include/asm/book3s/32/kup.h
@@ -117,6 +117,7 @@ static __always_inline void allow_user_access(void __user 
*to, const void __user
                return;
 
        end = min(addr + size, TASK_SIZE);
+
        current->thread.kuap = (addr & 0xf0000000) | ((((end - 1) >> 28) + 1) & 
0xf);
        kuap_update_sr(mfsrin(addr) & ~SR_KS, addr, end);       /* Clear Ks */
 }
@@ -127,15 +128,27 @@ static __always_inline void prevent_user_access(void 
__user *to, const void __us
        u32 addr, end;
 
        BUILD_BUG_ON(!__builtin_constant_p(dir));
-       if (!(dir & KUAP_W))
-               return;
 
-       addr = (__force u32)to;
+       if (dir == KUAP_SELF) {
+               u32 kuap = current->thread.kuap;
 
-       if (unlikely(addr >= TASK_SIZE || !size))
-               return;
+               if (unlikely(!kuap))
+                       return;
+
+               addr = kuap & 0xf0000000;
+               end = kuap << 28;
+       } else {
+               if (!(dir & KUAP_W))
+                       return;
+
+               addr = (__force u32)to;
+
+               if (unlikely(addr >= TASK_SIZE || !size))
+                       return;
+
+               end = min(addr + size, TASK_SIZE);
+       }
 
-       end = min(addr + size, TASK_SIZE);
        current->thread.kuap = 0;
        kuap_update_sr(mfsrin(addr) | SR_KS, addr, end);        /* set Ks */
 }
diff --git a/arch/powerpc/include/asm/kup.h b/arch/powerpc/include/asm/kup.h
index ff57bfcb88f7..4229e749dcf4 100644
--- a/arch/powerpc/include/asm/kup.h
+++ b/arch/powerpc/include/asm/kup.h
@@ -5,6 +5,7 @@
 #define KUAP_R         1
 #define KUAP_W         2
 #define KUAP_RW                (KUAP_R | KUAP_W)
+#define KUAP_SELF      4
 
 #ifdef CONFIG_PPC64
 #include <asm/book3s/64/kup-radix.h>
-- 
2.25.0

Reply via email to