/*
 * pi3's Linux kernel Runtime Guard
 *
 * Component:
 *  - Exploit detection main module
 *
 * Notes:
 *  - None
 *
 * Timeline:
 *  - Created: 06.IX.2017
 *
 * Author:
 *  - Adam 'pi3' Zabrocki (http://pi3.com.pl)
 *
 */

#ifndef P_EXPLOIT_DETECTION_MAIN_H
#define P_EXPLOIT_DETECTION_MAIN_H

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0) && defined(CONFIG_ARCH_HAS_SYSCALL_WRAPPER)

  /*
   * Linux kernel 4.17.xx introduced some changes which broke LKRG (and not only LKRG).
   * More information about it can be found here:
   *
   * https://lists.gt.net/linux/kernel/2952784
   * https://github.com/torvalds/linux/commit/d5a00528b58cdb2c71206e18bd021e34c4eab878
   *
   * In short, Linux kernel changed the exported names for syscalls (critical change
   * from the LKRG perspective). Currently, one syscall is generating up to 4 stubs
   * (depends on compilation option), e.g. sys_waitid and compat_sys_waitid can be now:
   *
   * 810f2080 T __x64_sys_waitid           # x64 64-bit-ptregs   -> C stub
   * 810f20b0 T __ia32_sys_waitid          # ia32 32-bit-ptregs  -> C stub[*]
   * 810f2470 T __ia32_compat_sys_waitid   # ia32 32-bit-ptregs  -> compat C stub
   * 810f2490 T __x32_compat_sys_waitid    # x32 64-bit-ptregs   -> compat C stub
   *
   *  [*] This stub is often unused - depends on the syscall
   *
   * Example from my Ubuntu VM (kernel 4.17.8) for sys_execve():
   *
   * ffffffffa1a9b9d0 T __ia32_compat_sys_execve
   * ffffffffa1a9ba90 T __ia32_sys_execve [*]
   * ffffffffa1a9bb30 T __x32_compat_sys_execve
   * ffffffffa1a9bb80 T __x64_sys_execve
   *
   *  [*] Unused
   *
   * But at the same time sys_setuid() can be as follow:
   *
   * ffffffffa44a69d0 T __sys_setuid  # NOT a syscall but some of the stubs can wrap it
   * ffffffffa44a6ad0 T __x64_sys_setuid
   * ffffffffa44a6af0 T __ia32_sys_setuid   [*]
   * ffffffffa452b0d0 T __x64_sys_setuid16
   * ffffffffa452b100 T __ia32_sys_setuid16
   *
   *  [*] Used
   *
   * CONFIG_COMPAT / CONFIG_IA32_EMULATION and CONFIG_X86_X32 is covered here.
   *
   */

 #ifdef CONFIG_X86_64

  #define P_SYSCALL_LAYOUT_4_17

  #define P_GET_IA32_SYSCALL_NAME(x) P_IA32_SYSCALL_PREFIX(x)
  #define P_GET_IA32_COMPAT_SYSCALL_NAME(x) P_IA32_COMPAT_SYSCALL_PREFIX(x)

  #define P_SYSCALL_PREFIX(x) P_TO_STRING(__x64_sys_ ## x)
  #define P_IA32_SYSCALL_PREFIX(x) P_TO_STRING(__ia32_sys_ ## x)
  #define P_IA32_COMPAT_SYSCALL_PREFIX(x) P_TO_STRING(__ia32_compat_sys_ ## x)
  #define P_GET_SET_ID_PREFIX(x) P_TO_STRING(__sys_ ## x)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,14,0)
  #define P_X32_COMPAT_SYSCALL_PREFIX(x) P_TO_STRING(__x64_compat_sys_ ## x)
#else
  #define P_X32_COMPAT_SYSCALL_PREFIX(x) P_TO_STRING(__x32_compat_sys_ ## x)
#endif

  #define P_COMPAT_SYSCALL_PREFIX(x) P_IA32_COMPAT_SYSCALL_PREFIX(x)
  #define P_NEW_COMPAT_SYSCALL_PREFIX(x) P_IA32_SYSCALL_PREFIX(x)

 #elif defined(CONFIG_X86_32) && LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)

  #define P_SYSCALL_LAYOUT_5_7

  /*
   * Since Linux kernel 5.7 SYSCALL_WRAPPER's "magic" was backported to x86 (32 bits)
   * arch as well. Let's correctly handle it here. More about this "magic" can be
   * found here:
   *
   * https://lore.kernel.org/lkml/20200313195144.164260-10-brgerst@gmail.com/T/
   */

  #define P_GET_IA32_SYSCALL_NAME(x) P_IA32_SYSCALL_PREFIX(x)
  #define P_GET_IA32_COMPAT_SYSCALL_NAME(x) P_IA32_COMPAT_SYSCALL_PREFIX(x)

  #define P_SYSCALL_PREFIX(x) P_TO_STRING(__ia32_sys_ ## x)
  #define P_IA32_SYSCALL_PREFIX(x) P_TO_STRING(__ia32_sys_ ## x)
  #define P_IA32_COMPAT_SYSCALL_PREFIX(x) P_TO_STRING(__ia32_compat_sys_ ## x)
  #define P_GET_SET_ID_PREFIX(x) P_TO_STRING(__sys_ ## x)
  #define P_X32_COMPAT_SYSCALL_PREFIX(x) P_TO_STRING(__x32_compat_sys_ ## x)

  #define P_COMPAT_SYSCALL_PREFIX(x) P_IA32_COMPAT_SYSCALL_PREFIX(x)
  #define P_NEW_COMPAT_SYSCALL_PREFIX(x) P_IA32_SYSCALL_PREFIX(x)

 #elif defined(CONFIG_ARM64)

  #define P_SYSCALL_LAYOUT_4_17

  #define P_GET_ARM64_SYSCALL_NAME(x) P_ARM64_SYSCALL_PREFIX(x)
  #define P_GET_ARM64_COMPAT_SYSCALL_NAME(x) P_ARM64_COMPAT_SYSCALL_PREFIX(x)

  #define P_SYSCALL_PREFIX(x) P_TO_STRING(__arm64_sys_ ## x)
  #define P_ARM64_SYSCALL_PREFIX(x) P_ARM64_COMPAT_SYSCALL_PREFIX(x)
  #define P_ARM64_COMPAT_SYSCALL_PREFIX(x) P_TO_STRING(__arm64_compat_sys_ ## x)
  #define P_GET_SET_ID_PREFIX(x) P_TO_STRING(__sys_ ## x)
  #define P_X32_COMPAT_SYSCALL_PREFIX(x) P_TO_STRING(__x32_compat_sys_ ## x)

  #define P_COMPAT_SYSCALL_PREFIX(x) P_ARM64_COMPAT_SYSCALL_PREFIX(x)
  #define P_NEW_COMPAT_SYSCALL_PREFIX(x) P_ARM64_SYSCALL_PREFIX(x)

 #else
  #define P_SYSCALL_PREFIX(x) P_TO_STRING(sys_ ## x)
  #define P_COMPAT_SYSCALL_PREFIX(x) P_TO_STRING(compat_sys_ ## x)
  #define P_GET_SET_ID_PREFIX(x) P_SYSCALL_PREFIX(x)
  #define P_X32_COMPAT_SYSCALL_PREFIX
  #define P_NEW_COMPAT_SYSCALL_PREFIX
 #endif
#else
 #define P_SYSCALL_PREFIX(x) P_TO_STRING(sys_ ## x)
 #define P_COMPAT_SYSCALL_PREFIX(x) P_TO_STRING(compat_sys_ ## x)
 #define P_GET_SET_ID_PREFIX(x) P_SYSCALL_PREFIX(x)
 #define P_X32_COMPAT_SYSCALL_PREFIX
 #define P_NEW_COMPAT_SYSCALL_PREFIX
#endif

#define P_TO_STRING(x) # x
#define P_GET_SYSCALL_NAME(x) P_SYSCALL_PREFIX(x)
#define P_GET_COMPAT_SYSCALL_NAME(x) P_COMPAT_SYSCALL_PREFIX(x)
#define P_GET_NEW_COMPAT_SYSCALL_NAME(x) P_NEW_COMPAT_SYSCALL_PREFIX(x)
#define P_GET_SET_ID_NAME(x) P_GET_SET_ID_PREFIX(x)
#define P_GET_X32_SYSCALL_NAME(x) P_X32_COMPAT_SYSCALL_PREFIX(x)

#define P_ED_PROCESS_OFF_MAX 0x1000

#define p_ed_alloc_valid()      kmem_cache_alloc(p_ed_wq_valid_cache, GFP_ATOMIC)
#define p_ed_free_valid(name)   kmem_cache_free(p_ed_wq_valid_cache, (void *)(name))


static inline unsigned int p_get_task_state(struct task_struct *p_task) {

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,14,0) || \
  (defined(RHEL_RELEASE_CODE) && RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(8,5))
   return READ_ONCE(p_task->__state);
#elif defined(ACCESS_ONCE) && !defined(READ_ONCE)
   return ACCESS_ONCE(p_task->state);
#else
   return READ_ONCE(p_task->state);
#endif

}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)
/*
 * It's temporary fix - redefine what can't be imported.
 * TODO: Need to research better way of inspecting SELinux variables!
 */
/* Policy capabilities */
enum {
    POLICYDB_CAPABILITY_NETPEER,
    POLICYDB_CAPABILITY_OPENPERM,
    POLICYDB_CAPABILITY_EXTSOCKCLASS,
    POLICYDB_CAPABILITY_ALWAYSNETWORK,
    POLICYDB_CAPABILITY_CGROUPSECLABEL,
    POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION,
    __POLICYDB_CAPABILITY_MAX
};

struct p_selinux_state {
#if defined(CONFIG_SECURITY_SELINUX_DISABLE) || LINUX_VERSION_CODE < KERNEL_VERSION(5,6,0)
    bool disabled;
#endif
#ifdef P_SELINUX_VERIFY
    bool enforcing;
#endif
    bool checkreqprot;
    bool initialized;
    bool policycap[__POLICYDB_CAPABILITY_MAX];
    struct selinux_avc *avc;
    struct selinux_ss *ss;
};
#endif

#if !defined(CONFIG_STACKTRACE) || \
    (defined(CONFIG_ARCH_STACKWALK) && (!defined(RHEL_RELEASE_CODE) || RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(9, 0)))
struct stack_trace {
    unsigned int nr_entries, max_entries;
    unsigned long *entries;
    int skip;
};
#endif

struct p_cred {

    kuid_t uid;                      /* real UID of the task */
    kgid_t gid;                      /* real GID of the task */
    kuid_t suid;                     /* saved UID of the task */
    kgid_t sgid;                     /* saved GID of the task */
    kuid_t euid;                     /* effective UID of the task */
    kgid_t egid;                     /* effective GID of the task */
    kuid_t fsuid;                    /* UID for VFS ops */
    kgid_t fsgid;                    /* GID for VFS ops */
    unsigned securebits;             /* SUID-less security management */
    kernel_cap_t cap_inheritable;    /* caps our children can inherit */
    kernel_cap_t cap_permitted;      /* caps we're permitted */
    kernel_cap_t cap_effective;      /* caps we can actually use */
    kernel_cap_t cap_bset;           /* capability bounding set */
    kernel_cap_t cap_ambient;        /* Ambient capability set */
    struct user_struct *user;        /* real user ID subscription */
    struct user_namespace *user_ns;  /* user_ns the caps and keyrings are relative to. */

};

struct p_seccomp {

   struct seccomp sec;
   int flag;
   int flag_sync_thread;

};

#ifdef P_LKRG_TASK_OFF_DEBUG
 #define P_PCFI_STACK_BUF     0x150
#else
 #define P_PCFI_STACK_BUF     0x200
#endif

#ifdef CONFIG_X86_64
 #define P_NORMALIZE_LONG 0x0101010101010101
 #define P_MASK_COUNTER   0x07FFFFFFFFFFFFFF
#else
 #define P_NORMALIZE_LONG 0x01010101
 #define P_MASK_COUNTER   0x07FFFFFF
#endif

#ifdef P_LKRG_TASK_OFF_DEBUG
#define P_LKRG_TASK_OFF_MAXBUF 256

struct p_task_off_debug {

   unsigned int p_caller;
   unsigned int p_action;
   unsigned long p_old_off;
   unsigned int p_debug_val;
   /* only for override_* API */
   struct stack_trace p_trace;
   char p_internal_buf[P_PCFI_STACK_BUF];

};
#endif

/* X86(-64)*/
#if defined(CONFIG_X86) && LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0)
#ifndef CONFIG_PAX_RANDKSTACK
 #define P_VERIFY_ADDR_LIMIT 1
#endif
/* ARM(64) */
#elif (defined(CONFIG_ARM) && LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0)) \
   || (defined(CONFIG_ARM64) && LINUX_VERSION_CODE < KERNEL_VERSION(5,11,0))
 #define P_VERIFY_ADDR_LIMIT 2
#endif

struct p_ed_process_task {

   unsigned long p_off;
   struct task_struct *p_task;
   pid_t p_pid;
   char p_comm[TASK_COMM_LEN+1];
   const struct cred *p_cred_ptr;
   const struct cred *p_real_cred_ptr;
   struct p_cred p_cred;
   struct p_cred p_real_cred;
#if defined(CONFIG_SECCOMP)
   struct p_seccomp p_sec;
#endif
#if defined(P_VERIFY_ADDR_LIMIT)
   mm_segment_t p_addr_limit;
#endif
   struct nsproxy *p_nsproxy;
   struct nsproxy p_ns;
   void *p_stack;
   unsigned int p_off_count;
#ifdef P_LKRG_TASK_OFF_DEBUG
   struct p_task_off_debug p_off_debug[P_LKRG_TASK_OFF_MAXBUF];
   unsigned int p_off_counter;
   int p_off_debug_cnt;
#endif

};

#ifdef CONFIG_SECURITY_SELINUX
struct p_ed_guard_selinux {

#ifdef P_SELINUX_VERIFY
   int p_selinux_enforcing;
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)
   int p_selinux_enabled;
#endif

};
#endif

struct p_ed_global_variables {

#ifdef CONFIG_SECURITY_SELINUX
   p_lkrg_counter_lock p_selinux_lock;
   struct p_ed_guard_selinux p_selinux;
#endif

};

#include "p_rb_ed_trees/p_rb_ed_pids/p_rb_ed_pids_tree.h"
#include "syscalls/p_install.h"
#include "syscalls/exec/p_security_bprm_committing_creds/p_security_bprm_committing_creds.h"
#include "syscalls/exec/p_security_bprm_committed_creds/p_security_bprm_committed_creds.h"
#include "syscalls/p_call_usermodehelper/p_usermode_kernel_dep.h"
#include "syscalls/p_call_usermodehelper/p_call_usermodehelper.h"
#include "syscalls/p_call_usermodehelper_exec/p_call_usermodehelper_exec.h"
#include "syscalls/p_wake_up_new_task/p_wake_up_new_task.h"
#include "syscalls/p_do_exit/p_do_exit.h"
#include "syscalls/p_sys_setuid/p_sys_setuid.h"
#include "syscalls/p_sys_setreuid/p_sys_setreuid.h"
#include "syscalls/p_sys_setresuid/p_sys_setresuid.h"
#include "syscalls/p_sys_setfsuid/p_sys_setfsuid.h"
#include "syscalls/p_sys_setgid/p_sys_setgid.h"
#include "syscalls/p_sys_setregid/p_sys_setregid.h"
#include "syscalls/p_sys_setresgid/p_sys_setresgid.h"
#include "syscalls/p_sys_setfsgid/p_sys_setfsgid.h"
#include "syscalls/p_set_current_groups/p_set_current_groups.h"
#include "syscalls/p_generic_permission/p_generic_permission.h"
#include "syscalls/p_sel_write_enforce/p_sel_write_enforce.h"
#include "syscalls/p_seccomp/p_seccomp.h"
#include "syscalls/p_sys_unshare/p_sys_unshare.h"
#include "syscalls/caps/p_sys_capset/p_sys_capset.h"
#include "syscalls/caps/p_cap_task_prctl/p_cap_task_prctl.h"
#include "syscalls/keyring/p_key_change_session_keyring/p_key_change_session_keyring.h"
#include "syscalls/keyring/p_sys_add_key/p_sys_add_key.h"
#include "syscalls/keyring/p_sys_request_key/p_sys_request_key.h"
#include "syscalls/keyring/p_sys_keyctl/p_sys_keyctl.h"
#include "syscalls/p_security_ptrace_access/p_security_ptrace_access.h"
#include "syscalls/compat/p_compat_sys_keyctl/p_compat_sys_keyctl.h"
#include "syscalls/compat/p_compat_sys_capset/p_compat_sys_capset.h"
#include "syscalls/compat/p_compat_sys_add_key/p_compat_sys_add_key.h"
#include "syscalls/compat/p_compat_sys_request_key/p_compat_sys_request_key.h"
#include "syscalls/__x32/p_x32_sys_keyctl/p_x32_sys_keyctl.h"
/* Override creds */
#include "syscalls/override/p_override_creds/p_override_creds.h"
#include "syscalls/override/p_revert_creds/p_revert_creds.h"
/* Namespaces */
#include "syscalls/p_sys_setns/p_sys_setns.h"
/* OverlayFS */
#include "syscalls/override/overlayfs/p_ovl_override_sync/p_ovl_override_sync.h"
/* pCFI */
#include "syscalls/pCFI/p_mark_inode_dirty/p_mark_inode_dirty.h"
#include "syscalls/pCFI/p_schedule/p_schedule.h"
#include "syscalls/pCFI/p___queue_work/p___queue_work.h"
#include "syscalls/pCFI/p_lookup_fast/p_lookup_fast.h"
#include "syscalls/p_capable/p_capable.h"
#include "syscalls/p_scm_send/p_scm_send.h"

extern struct p_ed_global_variables p_ed_guard_globals;
extern unsigned long p_pcfi_CPU_flags;

//unsigned int p_iterate_processes(int (*p_func)(void *), char p_ver);
int p_dump_task_f(void *p_arg);
int p_remove_task_pid_f(pid_t p_arg);

void p_verify_addr_limit(struct p_ed_process *p_orig, struct task_struct *p_current);
void p_update_ed_process(struct p_ed_process *p_source, struct task_struct *p_task, char p_stack);
void p_set_ed_process_on(struct p_ed_process *p_source);
void p_set_ed_process_off(struct p_ed_process *p_source);
void p_ed_is_off_off_wrap(struct p_ed_process *p_source);
void p_ed_validate_off_flag_wrap(struct p_ed_process *p_source);
/* For override */
void p_set_ed_process_override_on(struct p_ed_process *p_source);
void p_set_ed_process_override_off(struct p_ed_process *p_source);
void p_reset_ed_flags(struct p_ed_process *p_source);
#if P_OVL_OVERRIDE_SYNC_MODE
/* For OverlayFS */
int p_verify_ovl_override_sync(struct p_ed_process *p_source);
#endif

int p_validate_task_f(void *p_arg);

//void p_ed_pcfi_cpu(unsigned char p_kill);
void p_ed_validate_current(void);
void p_ed_enforce_validation(void);
unsigned int p_ed_enforce_validation_paranoid(void);
int p_ed_enforce_pcfi(struct task_struct *p_task, struct p_ed_process *p_orig, struct pt_regs *p_regs);
int p_ed_pcfi_validate_sp(struct task_struct *p_task, struct p_ed_process *p_orig, unsigned long p_sp);

int p_exploit_detection_init(void);
void p_exploit_detection_exit(void);

int p_selinux_state_init(void);
void p_selinux_state_restore(void);
void p_selinux_state_update(void);
int p_selinux_state_changed(void);
int p_selinux_state_enforcing(void);

#ifdef P_LKRG_TASK_OFF_DEBUG
void p_debug_off_flag_off(struct p_ed_process *p_source, unsigned int p_id);
void p_debug_off_flag_on(struct p_ed_process *p_source, unsigned int p_id);
void p_debug_off_flag_reset(struct p_ed_process *p_source, unsigned int p_id);
void p_debug_off_flag_override_off(struct p_ed_process *p_source, unsigned int p_id, struct pt_regs *p_regs);
void p_debug_off_flag_override_on(struct p_ed_process *p_source, unsigned int p_id, struct pt_regs *p_regs);
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)
#define p_force_sig(sig) force_sig((sig))
#else
#define p_force_sig(sig) force_sig((sig), current)
#endif

/* Task integrity lock API */
static inline void p_tasks_read_lock_raw(rwlock_t *p_arg) {

   read_lock(p_arg);
}

static inline void p_tasks_read_unlock_raw(rwlock_t *p_arg) {

   read_unlock(p_arg);
}

static inline void p_tasks_write_lock_noirq(rwlock_t *p_arg) {

   write_lock(p_arg);
}

static inline void p_tasks_write_unlock_noirq(rwlock_t *p_arg) {

   write_unlock(p_arg);
}

static inline void p_tasks_read_lock(unsigned long *p_flags) {

   read_lock(p_rb_hash_lock_lookup(task_pid_nr(current)));
}

static inline int p_tasks_read_trylock(unsigned long *p_flags) {

//   local_irq_save(*p_flags);
   return read_trylock(p_rb_hash_lock_lookup(task_pid_nr(current))) ? 1 : ({ /* local_irq_restore(*p_flags); */ 0; });
}

static inline void p_tasks_read_unlock(unsigned long *p_flags) {

   read_unlock(p_rb_hash_lock_lookup(task_pid_nr(current)));
}

static inline void p_tasks_write_lock(unsigned long *p_flags) {

   write_lock_irqsave(p_rb_hash_lock_lookup(task_pid_nr(current)), *p_flags);
}

static inline int p_tasks_write_trylock(unsigned long *p_flags) {

   return write_trylock_irqsave(p_rb_hash_lock_lookup(task_pid_nr(current)), *p_flags);
}

static inline void p_tasks_write_unlock(unsigned long *p_flags) {

   write_unlock_irqrestore(p_rb_hash_lock_lookup(task_pid_nr(current)), *p_flags);
}

static inline void p_tasks_write_lock_by_pid(pid_t p_arg, unsigned long *p_flags) {

   write_lock_irqsave(p_rb_hash_lock_lookup(p_arg), *p_flags);
}

static inline void p_tasks_write_unlock_by_pid(pid_t p_arg, unsigned long *p_flags) {

   write_unlock_irqrestore(p_rb_hash_lock_lookup(p_arg), *p_flags);
}
/* End */

static inline unsigned int p_is_ed_task(struct task_struct *p_task) {

   return p_task->mm && !is_global_init(p_task);
}

static inline int p_kill_task_by_task(struct task_struct *p_task) {

   p_print_log(P_LOG_ALERT, "BLOCK: Task: Killing pid %u, name %s",
               task_pid_nr(p_task), p_task->comm);

   return send_sig_info(SIGKILL, SEND_SIG_PRIV, p_task);

//   do_send_sig_info(SIGKILL, SEND_SIG_FORCED, p_task, true);

}

static inline int p_kill_task_by_pid(pid_t p_pid) {

   return p_kill_task_by_task(pid_task(find_vpid(p_pid), PIDTYPE_PID));

//   do_send_sig_info(SIGKILL, SEND_SIG_FORCED, p_task, true);

}

static inline int p_ed_kill_task_by_task(struct task_struct *p_task) {

   switch (P_CTRL(p_pint_enforce)) {

      /* Panic */
      case 2:
         // OK, we need to crash the kernel now
         p_panic("Task: Unexpected changes for pid %u, name %s",
            task_pid_nr(p_task), p_task->comm);
         break;

      /* Kill task */
      case 1:
         return p_kill_task_by_task(p_task);
         break;

      /* Log and accept */
      case 0:
         do {
            struct p_ed_process *p_tmp;

            if ( (p_tmp = p_find_ed_by_pid(task_pid_nr(current))) != NULL) {
               p_update_ed_process(p_tmp, p_task, 1);
               p_print_log(P_LOG_ALERT, "ALLOW: Task: Accepting unexpected changes for pid %u, name %s",
                  task_pid_nr(p_task), p_task->comm);
            } else {
               p_print_log(P_LOG_FAULT, "Can't find the corrupted process pid %u, name %s",
                  task_pid_nr(p_task), p_task->comm);
            }
         } while(0);
         break;

   }

   return 0;
}

static inline int p_pcfi_kill_task_by_task(struct task_struct *p_task) {

   switch (P_CTRL(p_pcfi_enforce)) {

      /* Panic */
      case 2:
         // OK, we need to crash the kernel now
         p_panic("Task: pCFI validation failed for pid %u, name %s",
            task_pid_nr(p_task), p_task->comm);
         break;

      /* Kill task */
      case 1:
         return p_kill_task_by_task(p_task);
         break;

      /* Log */
      case 0:
         p_print_log(P_LOG_ALERT, "ALLOW: Task: pCFI validation failed for pid %u, name %s",
            task_pid_nr(p_task), p_task->comm);
         break;

   }

   return 0;
}

/*
 * First CPU specific code
 */
#ifdef CONFIG_X86
 #include "arch/x86/p_ed_x86_arch.h"
#elif defined(CONFIG_ARM)
 #include "arch/arm/p_ed_arm_arch.h"
#elif defined(CONFIG_ARM64)
 #include "arch/arm64/p_ed_arm64_arch.h"
#else
 #error "!!! UNSUPPORTED ARCHITECTURE !!!"
#endif

static inline unsigned int p_ed_pcfi_cpu(unsigned char p_kill) {

#ifdef CONFIG_X86
   unsigned int p_ret;

   p_ret = p_ed_pcfi_x86_validate_wp(p_kill);
   p_ret += p_ed_pcfi_x86_validate_smXp(p_kill);

   return p_ret;

#elif defined(CONFIG_ARM)

   return 0;

#elif defined(CONFIG_ARM64)

   return 0;

#endif

}

#endif
