Creation of a new hook to let LSM modules handle user-space pagefaults on
x86.
It can be used to avoid segfaulting the originating process.
If it's the case it can modify process registers before returning.

Signed-off-by: Salvatore Mesoraca <[email protected]>
Cc: [email protected]
Cc: Ingo Molnar <[email protected]>
---
 arch/x86/mm/fault.c       |  6 ++++++
 include/linux/lsm_hooks.h |  9 +++++++++
 include/linux/security.h  | 11 +++++++++++
 security/security.c       | 11 +++++++++++
 4 files changed, 37 insertions(+)

diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 8ad91a0..b75b81a 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -15,6 +15,7 @@
 #include <linux/prefetch.h>            /* prefetchw                    */
 #include <linux/context_tracking.h>    /* exception_enter(), ...       */
 #include <linux/uaccess.h>             /* faulthandler_disabled()      */
+#include <linux/security.h>            /* security_pagefault_handler   */
 
 #include <asm/cpufeature.h>            /* boot_cpu_has, ...            */
 #include <asm/traps.h>                 /* dotraplinkage, ...           */
@@ -1358,6 +1359,11 @@ static inline bool smap_violation(int error_code, struct 
pt_regs *regs)
                        local_irq_enable();
        }
 
+       if (unlikely(security_pagefault_handler_x86(regs,
+                                                   error_code,
+                                                   address)))
+               return;
+
        perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
 
        if (error_code & PF_WRITE)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 6934cc5..a42c2f8 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -487,6 +487,11 @@
  *     Check if the requested @vmflags are allowed.
  *     @vmflags contains requested the vmflags.
  *     Return 0 if the operation is allowed to continue.
+ * @pagefault_handler_x86:
+ *     Handle pagefaults on x86.
+ *     @regs contains process' registers.
+ *     @error_code contains error code for the pagefault.
+ *     @address contains the address that caused the pagefault.
  * @file_lock:
  *     Check permission before performing file locking operations.
  *     Note: this hook mediates both flock and fcntl style locks.
@@ -1487,6 +1492,9 @@
        int (*file_mprotect)(struct vm_area_struct *vma, unsigned long reqprot,
                                unsigned long prot);
        int (*check_vmflags)(vm_flags_t vmflags);
+       int (*pagefault_handler_x86)(struct pt_regs *regs,
+                                    unsigned long error_code,
+                                    unsigned long address);
        int (*file_lock)(struct file *file, unsigned int cmd);
        int (*file_fcntl)(struct file *file, unsigned int cmd,
                                unsigned long arg);
@@ -1759,6 +1767,7 @@ struct security_hook_heads {
        struct list_head mmap_file;
        struct list_head file_mprotect;
        struct list_head check_vmflags;
+       struct list_head pagefault_handler_x86;
        struct list_head file_lock;
        struct list_head file_fcntl;
        struct list_head file_set_fowner;
diff --git a/include/linux/security.h b/include/linux/security.h
index 67e33b6..bc38c83 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -303,6 +303,9 @@ int security_mmap_file(struct file *file, unsigned long 
prot,
 int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
                           unsigned long prot);
 int security_check_vmflags(vm_flags_t vmflags);
+int __maybe_unused security_pagefault_handler_x86(struct pt_regs *regs,
+                                                 unsigned long error_code,
+                                                 unsigned long address);
 int security_file_lock(struct file *file, unsigned int cmd);
 int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long 
arg);
 void security_file_set_fowner(struct file *file);
@@ -836,6 +839,14 @@ static inline int security_check_vmflags(vm_flags_t 
vmflags)
        return 0;
 }
 
+static inline int __maybe_unused security_pagefault_handler_x86(
+                                               struct pt_regs *regs,
+                                               unsigned long error_code,
+                                               unsigned long address)
+{
+       return 0;
+}
+
 static inline int security_file_lock(struct file *file, unsigned int cmd)
 {
        return 0;
diff --git a/security/security.c b/security/security.c
index 25d58f0..cf15686 100644
--- a/security/security.c
+++ b/security/security.c
@@ -910,6 +910,17 @@ int security_check_vmflags(vm_flags_t vmflags)
        return call_int_hook(check_vmflags, 0, vmflags);
 }
 
+int __maybe_unused security_pagefault_handler_x86(struct pt_regs *regs,
+                                                 unsigned long error_code,
+                                                 unsigned long address)
+{
+       return call_int_hook(pagefault_handler_x86,
+                            0,
+                            regs,
+                            error_code,
+                            address);
+}
+
 int security_file_lock(struct file *file, unsigned int cmd)
 {
        return call_int_hook(file_lock, 0, file, cmd);
-- 
1.9.1

Reply via email to