noexec=on: enable non-executable mappings (default)
                        noexec=off: disable non-executable mappings
 
-       nosmap          [X86]
+       nosmap          [X86,PPC]
                        Disable SMAP (Supervisor Mode Access Prevention)
                        even if it is supported by processor.
 
 
 {
        int oldval = 0, ret;
 
+       allow_write_to_user(uaddr, sizeof(*uaddr));
        pagefault_disable();
 
        switch (op) {
        if (!ret)
                *oval = oldval;
 
+       prevent_write_to_user(uaddr, sizeof(*uaddr));
        return ret;
 }
 
        if (!access_ok(uaddr, sizeof(u32)))
                return -EFAULT;
 
+       allow_write_to_user(uaddr, sizeof(*uaddr));
         __asm__ __volatile__ (
         PPC_ATOMIC_ENTRY_BARRIER
 "1:     lwarx   %1,0,%3         # futex_atomic_cmpxchg_inatomic\n\
         : "cc", "memory");
 
        *uval = prev;
+       prevent_write_to_user(uaddr, sizeof(*uaddr));
         return ret;
 }
 
 
 
 #ifndef __ASSEMBLY__
 
+#include <asm/pgtable.h>
+
 void setup_kup(void);
 
 #ifdef CONFIG_PPC_KUEP
 static inline void setup_kuep(bool disabled) { }
 #endif /* CONFIG_PPC_KUEP */
 
+#ifdef CONFIG_PPC_KUAP
+void setup_kuap(bool disabled);
+#else
+static inline void setup_kuap(bool disabled) { }
+static inline void allow_user_access(void __user *to, const void __user *from,
+                                    unsigned long size) { }
+static inline void prevent_user_access(void __user *to, const void __user *from,
+                                      unsigned long size) { }
+#endif /* CONFIG_PPC_KUAP */
+
+static inline void allow_read_from_user(const void __user *from, unsigned long size)
+{
+       allow_user_access(NULL, from, size);
+}
+
+static inline void allow_write_to_user(void __user *to, unsigned long size)
+{
+       allow_user_access(to, NULL, size);
+}
+
+static inline void prevent_read_from_user(const void __user *from, unsigned long size)
+{
+       prevent_user_access(NULL, from, size);
+}
+
+static inline void prevent_write_to_user(void __user *to, unsigned long size)
+{
+       prevent_user_access(to, NULL, size);
+}
+
 #endif /* !__ASSEMBLY__ */
 
 #endif /* _ASM_POWERPC_KUP_H_ */
 
                };
        };
 
+       union {
+               struct {
 #ifdef CONFIG_PPC64
-       unsigned long ppr;
-       unsigned long __pad;    /* Maintain 16 byte interrupt stack alignment */
+                       unsigned long ppr;
+#endif
+#ifdef CONFIG_PPC_KUAP
+                       unsigned long kuap;
 #endif
+               };
+               unsigned long __pad[2]; /* Maintain 16 byte interrupt stack alignment */
+       };
 };
 #endif
 
 
 #include <asm/processor.h>
 #include <asm/page.h>
 #include <asm/extable.h>
+#include <asm/kup.h>
 
 /*
  * The fs value determines whether argument validity checking should be
 #define __put_user_size(x, ptr, size, retval)                  \
 do {                                                           \
        retval = 0;                                             \
+       allow_write_to_user(ptr, size);                         \
        switch (size) {                                         \
          case 1: __put_user_asm(x, ptr, retval, "stb"); break; \
          case 2: __put_user_asm(x, ptr, retval, "sth"); break; \
          case 8: __put_user_asm2(x, ptr, retval); break;       \
          default: __put_user_bad();                            \
        }                                                       \
+       prevent_write_to_user(ptr, size);                       \
 } while (0)
 
 #define __put_user_nocheck(x, ptr, size)                       \
        __chk_user_ptr(ptr);                                    \
        if (size > sizeof(x))                                   \
                (x) = __get_user_bad();                         \
+       allow_read_from_user(ptr, size);                        \
        switch (size) {                                         \
        case 1: __get_user_asm(x, ptr, retval, "lbz"); break;   \
        case 2: __get_user_asm(x, ptr, retval, "lhz"); break;   \
        case 8: __get_user_asm2(x, ptr, retval);  break;        \
        default: (x) = __get_user_bad();                        \
        }                                                       \
+       prevent_read_from_user(ptr, size);                      \
 } while (0)
 
 /*
 static inline unsigned long
 raw_copy_in_user(void __user *to, const void __user *from, unsigned long n)
 {
-       return __copy_tofrom_user(to, from, n);
+       unsigned long ret;
+
+       allow_user_access(to, from, n);
+       ret = __copy_tofrom_user(to, from, n);
+       prevent_user_access(to, from, n);
+       return ret;
 }
 #endif /* __powerpc64__ */
 
 static inline unsigned long raw_copy_from_user(void *to,
                const void __user *from, unsigned long n)
 {
+       unsigned long ret;
        if (__builtin_constant_p(n) && (n <= 8)) {
-               unsigned long ret = 1;
+               ret = 1;
 
                switch (n) {
                case 1:
        }
 
        barrier_nospec();
-       return __copy_tofrom_user((__force void __user *)to, from, n);
+       allow_read_from_user(from, n);
+       ret = __copy_tofrom_user((__force void __user *)to, from, n);
+       prevent_read_from_user(from, n);
+       return ret;
 }
 
 static inline unsigned long raw_copy_to_user(void __user *to,
                const void *from, unsigned long n)
 {
+       unsigned long ret;
        if (__builtin_constant_p(n) && (n <= 8)) {
-               unsigned long ret = 1;
+               ret = 1;
 
                switch (n) {
                case 1:
                        return 0;
        }
 
-       return __copy_tofrom_user(to, (__force const void __user *)from, n);
+       allow_write_to_user(to, n);
+       ret = __copy_tofrom_user(to, (__force const void __user *)from, n);
+       prevent_write_to_user(to, n);
+       return ret;
 }
 
 extern unsigned long __clear_user(void __user *addr, unsigned long size);
 
 static inline unsigned long clear_user(void __user *addr, unsigned long size)
 {
+       unsigned long ret = size;
        might_fault();
-       if (likely(access_ok(addr, size)))
-               return __clear_user(addr, size);
-       return size;
+       if (likely(access_ok(addr, size))) {
+               allow_write_to_user(addr, size);
+               ret = __clear_user(addr, size);
+               prevent_write_to_user(addr, size);
+       }
+       return ret;
 }
 
 extern long strncpy_from_user(char *dst, const char __user *src, long count);
 
        STACK_PT_REGS_OFFSET(_PPR, ppr);
 #endif /* CONFIG_PPC64 */
 
+#ifdef CONFIG_PPC_KUAP
+       STACK_PT_REGS_OFFSET(STACK_REGS_KUAP, kuap);
+#endif
+
 #if defined(CONFIG_PPC32)
 #if defined(CONFIG_BOOKE) || defined(CONFIG_40x)
        DEFINE(EXC_LVL_SIZE, STACK_EXC_LVL_FRAME_SIZE);
 
        unsigned int csum;
 
        might_sleep();
+       allow_read_from_user(src, len);
 
        *err_ptr = 0;
 
        }
 
 out:
+       prevent_read_from_user(src, len);
        return (__force __wsum)csum;
 }
 EXPORT_SYMBOL(csum_and_copy_from_user);
        unsigned int csum;
 
        might_sleep();
+       allow_write_to_user(dst, len);
 
        *err_ptr = 0;
 
        }
 
 out:
+       prevent_write_to_user(dst, len);
        return (__force __wsum)csum;
 }
 EXPORT_SYMBOL(csum_and_copy_to_user);
 
 }
 
 /* Is this a bad kernel fault ? */
-static bool bad_kernel_fault(bool is_exec, unsigned long error_code,
+static bool bad_kernel_fault(struct pt_regs *regs, unsigned long error_code,
                             unsigned long address)
 {
+       int is_exec = TRAP(regs) == 0x400;
+
        /* NX faults set DSISR_PROTFAULT on the 8xx, DSISR_NOEXEC_OR_G on others */
        if (is_exec && (error_code & (DSISR_NOEXEC_OR_G | DSISR_KEYFAULT |
                                      DSISR_PROTFAULT))) {
                                    address,
                                    from_kuid(&init_user_ns, current_uid()));
        }
-       return is_exec || (address >= TASK_SIZE);
+
+       if (!is_exec && address < TASK_SIZE && (error_code & DSISR_PROTFAULT) &&
+           !search_exception_tables(regs->nip)) {
+               pr_crit_ratelimited("Kernel attempted to access user page (%lx) - exploit attempt? (uid: %d)\n",
+                                   address,
+                                   from_kuid(&init_user_ns, current_uid()));
+       }
+
+       return is_exec || (address >= TASK_SIZE) || !search_exception_tables(regs->nip);
 }
 
 static bool bad_stack_expansion(struct pt_regs *regs, unsigned long address,
 
        /*
         * The kernel should never take an execute fault nor should it
-        * take a page fault to a kernel address.
+        * take a page fault to a kernel address or a page fault to a user
+        * address outside of dedicated places
         */
-       if (unlikely(!is_user && bad_kernel_fault(is_exec, error_code, address)))
+       if (unlikely(!is_user && bad_kernel_fault(regs, error_code, address)))
                return SIGSEGV;
 
        /*
 
 #include <asm/kup.h>
 
 static bool disable_kuep = !IS_ENABLED(CONFIG_PPC_KUEP);
+static bool disable_kuap = !IS_ENABLED(CONFIG_PPC_KUAP);
 
 static int __init parse_nosmep(char *p)
 {
 }
 early_param("nosmep", parse_nosmep);
 
+static int __init parse_nosmap(char *p)
+{
+       disable_kuap = true;
+       pr_warn("Disabling Kernel Userspace Access Protection\n");
+       return 0;
+}
+early_param("nosmap", parse_nosmap);
+
 void __init setup_kup(void)
 {
        setup_kuep(disable_kuep);
+       setup_kuap(disable_kuap);
 }
 
 #define CTOR(shift) static void ctor_##shift(void *addr) \
 
 
          If you're unsure, say Y.
 
+config PPC_HAVE_KUAP
+       bool
+
+config PPC_KUAP
+       bool "Kernel Userspace Access Protection"
+       depends on PPC_HAVE_KUAP
+       default y
+       help
+         Enable support for Kernel Userspace Access Protection (KUAP)
+
+         If you're unsure, say Y.
+
 config ARCH_ENABLE_HUGEPAGE_MIGRATION
        def_bool y
        depends on PPC_BOOK3S_64 && HUGETLB_PAGE && MIGRATION