/*
 * 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)
 *
 */

#include "../../p_lkrg_main.h"

struct p_ed_global_variables p_ed_guard_globals;
unsigned long p_global_off_cookie;
unsigned long p_global_cnt_cookie;
struct kmem_cache *p_ed_wq_valid_cache = NULL;
struct kmem_cache *p_ed_pcfi_cache = NULL;

unsigned long p_pcfi_CPU_flags;

#define p_ed_pcfi_alloc()      kmem_cache_alloc(p_ed_pcfi_cache, GFP_ATOMIC)
#define p_ed_pcfi_free(name)   kmem_cache_free(p_ed_pcfi_cache, (void *)(name))

static int p_cmp_tasks(struct p_ed_process *p_orig, struct task_struct *p_current, char p_kill);
static unsigned int p_iterate_lkrg_tasks_paranoid(void);

static const struct p_functions_hooks {

   const char *name;
   int (*install)(int p_isra);
   void (*uninstall)(void);
   int p_fatal;
   const char *p_error_message;
   int is_isra_safe;

} p_functions_hooks_array[] = {
   {
     "security_bprm_committing_creds",
     p_install_security_bprm_committing_creds_hook,
     p_uninstall_security_bprm_committing_creds_hook,
     1,
     NULL,
     1
   },
   {
     "security_bprm_committed_creds",
     p_install_security_bprm_committed_creds_hook,
     p_uninstall_security_bprm_committed_creds_hook,
     1,
     NULL,
     1
   },
   { "call_usermodehelper",
     p_install_call_usermodehelper_hook,
     p_uninstall_call_usermodehelper_hook,
     0,
     "Won't guard usermodehelper",
     0
   },
   { "call_usermodehelper_exec",
     p_install_call_usermodehelper_exec_hook,
     p_uninstall_call_usermodehelper_exec_hook,
     0,
     "Won't enforce validation on 'call_usermodehelper_exec'",
     1
   },
   { "wake_up_new_task",
     p_install_wake_up_new_task_hook,
     p_uninstall_wake_up_new_task_hook,
     1,
     NULL,
     0
   },
   { "do_exit",
     p_install_do_exit_hook,
     p_uninstall_do_exit_hook,
     1,
     NULL,
     1
   },
   { "security_ptrace_access",
     p_install_security_ptrace_access_hook,
     p_uninstall_security_ptrace_access_hook,
     0,
     "Won't enforce validation on 'security_ptrace_access'",
     0
   },
   { "sys_setuid",
     p_install_sys_setuid_hook,
     p_uninstall_sys_setuid_hook,
     1,
     NULL,
     0
   },
   { "sys_setreuid",
     p_install_sys_setreuid_hook,
     p_uninstall_sys_setreuid_hook,
     1,
     NULL,
     0
   },
   { "sys_setresuid",
     p_install_sys_setresuid_hook,
     p_uninstall_sys_setresuid_hook,
     1,
     NULL,
     0
   },
   { "sys_setfsuid",
     p_install_sys_setfsuid_hook,
     p_uninstall_sys_setfsuid_hook,
     1,
     NULL,
     0
   },
   { "sys_setgid",
     p_install_sys_setgid_hook,
     p_uninstall_sys_setgid_hook,
     1,
     NULL,
     0
   },
   { "sys_setregid",
     p_install_sys_setregid_hook,
     p_uninstall_sys_setregid_hook,
     1,
     NULL,
     0
   },
   { "sys_setresgid",
     p_install_sys_setresgid_hook,
     p_uninstall_sys_setresgid_hook,
     1,
     NULL,
     0
   },
   { "sys_setfsgid",
     p_install_sys_setfsgid_hook,
     p_uninstall_sys_setfsgid_hook,
     1,
     NULL,
     0
   },
   { "set_current_groups",
     p_install_set_current_groups_hook,
     p_uninstall_set_current_groups_hook,
     1,
     NULL,
     1
   },
   { "generic_permission",
     p_install_generic_permission_hook,
     p_uninstall_generic_permission_hook,
     0,
     "Won't enforce validation on 'generic_permission'",
     1
   },
#ifdef CONFIG_SECURITY_SELINUX
   { "sel_write_enforce",
     p_install_sel_write_enforce_hook,
     p_uninstall_sel_write_enforce_hook,
     1,
     NULL,
     1
   },
#endif
#if defined(CONFIG_SECCOMP)
   { "seccomp",
     p_install_seccomp_hook,
     p_uninstall_seccomp_hook,
     1,
     NULL,
     1
   },
#endif
   { "sys_unshare",
     p_install_sys_unshare_hook,
     p_uninstall_sys_unshare_hook,
     1,
     NULL,
     1
   },
   /* Caps. */
   { "sys_capset",
     p_install_sys_capset_hook,
     p_uninstall_sys_capset_hook,
     1,
     NULL,
     0
   },
   { "cap_task_prctl",
     p_install_cap_task_prctl_hook,
     p_uninstall_cap_task_prctl_hook,
     1,
     NULL,
     1
   },
   /* Keyring */
   { "key_change_session_keyring",
     p_install_key_change_session_keyring_hook,
     p_uninstall_key_change_session_keyring_hook,
     1,
     NULL,
     1
   },
   { "sys_add_key",
     p_install_sys_add_key_hook,
     p_uninstall_sys_add_key_hook,
     1,
     NULL,
     0
   },
   { "sys_request_key",
     p_install_sys_request_key_hook,
     p_uninstall_sys_request_key_hook,
     1,
     NULL,
     0
   },
   { "sys_keyctl",
     p_install_sys_keyctl_hook,
     p_uninstall_sys_keyctl_hook,
     1,
     NULL,
     0
   },
#ifdef CONFIG_COMPAT
   { "compat_sys_keyctl",
     p_install_compat_sys_keyctl_hook,
     p_uninstall_compat_sys_keyctl_hook,
     1,
     NULL,
     0
   },
 #ifdef P_SYSCALL_LAYOUT_4_17
  #ifdef CONFIG_X86
   { "compat_sys_capset",
     p_install_compat_sys_capset_hook,
     p_uninstall_compat_sys_capset_hook,
     1,
     NULL,
     0
   },
   { "compat_sys_add_key",
     p_install_compat_sys_add_key_hook,
     p_uninstall_compat_sys_add_key_hook,
     1,
     NULL,
     0
   },
   { "compat_sys_request_key",
     p_install_compat_sys_request_key_hook,
     p_uninstall_compat_sys_request_key_hook,
     1,
     NULL,
     0
   },
  #endif /* CONFIG_X86 */
 #endif /* P_SYSCALL_LAYOUT_4_17 */
#endif /* CONFIG_COMPAT */
#ifdef CONFIG_X86_X32
 #ifdef P_SYSCALL_LAYOUT_4_17
   { "x32_sys_keyctl",
     p_install_x32_sys_keyctl_hook,
     p_uninstall_x32_sys_keyctl_hook,
     1,
     NULL,
     0
   },
 #endif /* P_SYSCALL_LAYOUT_4_17 */
#endif /* CONFIG_X86_X32 */
   { "override_creds",
     p_install_override_creds_hook,
     p_uninstall_override_creds_hook,
     1,
     NULL,
     0
   },
   { "revert_creds",
     p_install_revert_creds_hook,
     p_uninstall_revert_creds_hook,
     1,
     NULL,
     0
   },
   /* Namespaces. */
   { "sys_setns",
     p_install_sys_setns_hook,
     p_uninstall_sys_setns_hook,
     1,
     NULL,
     0
   },
#if P_OVL_OVERRIDE_SYNC_MODE
   /* OverlayFS
    *
    * OverlayFS might not be installed in that system - it is not critical
    * scenario. If OverlayFS is installed, used but not found (unlikely)
    * in worst case, we might have FP. Continue...
    */
   {
     P_OVL_OVERRIDE_SYNC_FUNC,
     p_install_ovl_override_sync_hook,
     p_uninstall_ovl_override_sync_hook,
     0,
     "Can't hook '" P_OVL_OVERRIDE_SYNC_FUNC "'. This is expected when OverlayFS is not used.",
     1
   },
#endif
   /* pCFI */
   { "pcfi_mark_inode_dirty",
     p_install_pcfi_mark_inode_dirty_hook,
     p_uninstall_pcfi_mark_inode_dirty_hook,
     0,
     "Won't enforce pCFI validation on 'mark_inode_dirty'",
     0
   },
   { "pcfi_schedule",
     p_install_pcfi_schedule_hook,
     p_uninstall_pcfi_schedule_hook,
     0,
     "Won't enforce pCFI validation on 'schedule'",
     1
   },
   { "pcfi___queue_work",
     p_install_pcfi___queue_work_hook,
     p_uninstall_pcfi___queue_work_hook,
     0,
     "Won't enforce validation on '__queue_work'",
     1
   },
   { "pcfi_lookup_fast",
     p_install_pcfi_lookup_fast_hook,
     p_uninstall_pcfi_lookup_fast_hook,
     0,
     "Won't enforce pCFI validation on 'lookup_fast'",
     1
   },
   { "capable",
     p_install_capable_hook,
     p_uninstall_capable_hook,
     0,
     "Won't enforce validation on 'capable'",
     1
   },
   { "scm_send",
     p_install_scm_send_hook,
     p_uninstall_scm_send_hook,
     0,
     "Won't enforce validation on 'scm_send'",
     1
   },
   { NULL, NULL, NULL, 1, NULL, 0 }
};

static void p_ed_wq_valid_cache_zero(void *p_arg) {

   struct work_struct *p_struct = p_arg;

   memset(p_struct, 0, sizeof(struct work_struct));
}

int p_ed_wq_valid_cache_init(void) {

   if ( (p_ed_wq_valid_cache = kmem_cache_create("p_ed_wq_valid_cache", sizeof(struct work_struct),
                                                 0, P_LKRG_CACHE_FLAGS, p_ed_wq_valid_cache_zero)) == NULL) {
      return P_LKRG_GENERAL_ERROR;
   }

   return P_LKRG_SUCCESS;
}

static void p_ed_wq_valid_cache_delete(void) {

   flush_workqueue(system_unbound_wq);
   if (p_ed_wq_valid_cache) {
      kmem_cache_destroy(p_ed_wq_valid_cache);
      p_ed_wq_valid_cache = NULL;
   }
}

notrace void p_dump_creds(struct p_cred *p_where, const struct cred *p_from) {

   /* Get reference to cred */
   get_cred(p_from);

   /* Track process's capabilities */
   memcpy(&p_where->cap_inheritable, &p_from->cap_inheritable, sizeof(kernel_cap_t));
   memcpy(&p_where->cap_permitted, &p_from->cap_permitted, sizeof(kernel_cap_t));
   memcpy(&p_where->cap_effective, &p_from->cap_effective, sizeof(kernel_cap_t));
   memcpy(&p_where->cap_bset, &p_from->cap_bset, sizeof(kernel_cap_t));
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
   memcpy(&p_where->cap_ambient, &p_from->cap_ambient, sizeof(kernel_cap_t));
#endif

   /* Track process's IDs */
   p_set_uid(&p_where->uid, p_get_uid(&p_from->uid));
   p_set_gid(&p_where->gid, p_get_gid(&p_from->gid));
   p_set_uid(&p_where->suid, p_get_uid(&p_from->suid));
   p_set_gid(&p_where->sgid, p_get_gid(&p_from->sgid));
   p_set_uid(&p_where->euid, p_get_uid(&p_from->euid));
   p_set_gid(&p_where->egid, p_get_gid(&p_from->egid));
   p_set_uid(&p_where->fsuid, p_get_uid(&p_from->fsuid));
   p_set_gid(&p_where->fsgid, p_get_gid(&p_from->fsgid));

   /* Track process's securebits - TODO: research */
   p_where->securebits = p_from->securebits;

   /* Track process's critical pointers */
   p_where->user     = p_from->user;
   p_where->user_ns  = p_from->user_ns;

   /* Release reference to cred */
   put_cred(p_from);
}

#if defined(CONFIG_SECCOMP)
notrace void p_dump_seccomp(struct p_seccomp *p_sec, struct task_struct *p_task, char p_force) {

   P_SYM(p_get_seccomp_filter)(p_task);
   p_sec->sec.mode    = p_task->seccomp.mode;   // Mode
   p_sec->sec.filter  = p_task->seccomp.filter; // Filter
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,11,0)
   if (test_task_syscall_work(p_task,SECCOMP))
#else
   if (test_tsk_thread_flag(p_task,TIF_SECCOMP))
#endif
      p_sec->flag = 1;
   else
      p_sec->flag = 0;
   if (p_force)
      p_sec->flag_sync_thread = 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
   P_SYM(p_put_seccomp_filter)(p_task->seccomp.filter);
#else
   P_SYM(p_put_seccomp_filter)(p_task);
#endif

}
#endif

#if defined(P_VERIFY_ADDR_LIMIT)
notrace static inline unsigned long p_get_addr_limit(struct task_struct *p_task) {

/* X86(-64)*/
#if defined(CONFIG_X86)

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0) && LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0)

   return p_task->thread.addr_limit.seg;

#elif LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0)

   struct thread_info *p_ti = task_thread_info(p_task);

   return p_ti->addr_limit.seg;

#endif

/* ARM(64) */
#elif defined(CONFIG_ARM) || defined(CONFIG_ARM64)

   struct thread_info *p_ti = task_thread_info(p_task);

   return p_ti->addr_limit;

#endif
}
#endif

notrace void p_verify_addr_limit(struct p_ed_process *p_orig, struct task_struct *p_current) {

#if defined(P_VERIFY_ADDR_LIMIT)

   unsigned long p_addr_limit =
#if defined(CONFIG_X86)
                                 p_orig->p_ed_task.p_addr_limit.seg;
#elif defined(CONFIG_ARM) || defined(CONFIG_ARM64)
                                 p_orig->p_ed_task.p_addr_limit;
#endif

   /* Verify addr_limit */
   if (p_addr_limit != p_get_addr_limit(p_current)) {
      p_print_log(P_LOG_ALERT,
         "DETECT: Task: addr_limit corruption (expected 0x%lx vs. actual 0x%lx) for pid %u, name %s",
         p_addr_limit, p_get_addr_limit(p_current), task_pid_nr(p_current), p_current->comm);
      dump_stack();
      // kill this process!
      p_ed_kill_task_by_task(p_current);
   }

#endif
}

#if defined(P_VERIFY_ADDR_LIMIT)
notrace static inline void p_dump_addr_limit(mm_segment_t *p_addr_limit, struct task_struct *p_task) {
#if defined(CONFIG_X86)
   p_addr_limit->seg =
#elif defined(CONFIG_ARM) || defined(CONFIG_ARM64)
   *p_addr_limit =
#endif
                       p_get_addr_limit(p_task);
}
#endif

notrace void p_update_ed_process(struct p_ed_process *p_source, struct task_struct *p_task, char p_stack) {

   p_print_log(P_LOG_WATCH, "Updating pid %u", p_task->pid);

   rcu_read_lock();
   get_task_struct(p_task);
   /* Track process's metadata */
   p_source->p_ed_task.p_pid                      = p_task->pid;
   p_source->p_ed_task.p_cred_ptr                 = rcu_dereference(p_task->cred);
   p_source->p_ed_task.p_real_cred_ptr            = rcu_dereference(p_task->real_cred);
   if (p_stack)
      p_source->p_ed_task.p_stack                 = p_task->stack;
   /* Namespaces */
   p_source->p_ed_task.p_nsproxy                  = p_task->nsproxy;
   p_source->p_ed_task.p_ns.uts_ns                = p_task->nsproxy->uts_ns;
   p_source->p_ed_task.p_ns.ipc_ns                = p_task->nsproxy->ipc_ns;
   p_source->p_ed_task.p_ns.mnt_ns                = p_task->nsproxy->mnt_ns;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
   p_source->p_ed_task.p_ns.pid_ns_for_children   = p_task->nsproxy->pid_ns_for_children;
#else
   p_source->p_ed_task.p_ns.pid_ns                = p_task->nsproxy->pid_ns;
#endif
   p_source->p_ed_task.p_ns.net_ns                = p_task->nsproxy->net_ns;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
   p_source->p_ed_task.p_ns.cgroup_ns             = p_task->nsproxy->cgroup_ns;
#endif
   /* Creds */
   p_dump_creds(&p_source->p_ed_task.p_cred, p_source->p_ed_task.p_cred_ptr);
   p_dump_creds(&p_source->p_ed_task.p_real_cred, p_source->p_ed_task.p_real_cred_ptr);
#if defined(CONFIG_SECCOMP)
   /* Seccomp */
   p_dump_seccomp(&p_source->p_ed_task.p_sec, p_task, p_stack);
#endif
   /* addr_limit */
#if defined(P_VERIFY_ADDR_LIMIT)
   p_dump_addr_limit(&p_source->p_ed_task.p_addr_limit, p_task);
#endif
   /* Name */
   strncpy(p_source->p_ed_task.p_comm, p_task->comm, TASK_COMM_LEN);
   p_source->p_ed_task.p_comm[TASK_COMM_LEN] = 0;
   /* Should be last here to propagate potential glitching */
   wmb();
   p_source->p_ed_task.p_task            = p_task;
   put_task_struct(p_task);
   rcu_read_unlock();

}

#ifdef P_LKRG_TASK_OFF_DEBUG
struct p_lkrg_debug_off_flag_callers {

   char p_id;
   char *p_name;

} p_debug_off_flag_callers[] = {

   { 0, "RESERVED" },
   { 1, "RESERVED" },
   { 2, "RESERVED" },
   { 3, "RESERVED" },
   { 4, "p_x32_sys_keyctl_entry" },
   { 5, "p_x32_sys_keyctl_ret" },
   { 6, "p_cap_task_prctl_entry" },
   { 7, "p_cap_task_prctl_ret" },
   { 8, "p_sys_capset_entry" },
   { 9, "p_sys_capset_ret" },
   { 10, "p_compat_sys_add_key_entry" },
   { 11, "p_compat_sys_add_key_ret" },
   { 12, "p_compat_sys_capset_entry" },
   { 13, "p_compat_sys_capset_ret" },
   { 14, "RESERVED" },
   { 15, "RESERVED" },
   { 16, "RESERVED" },
   { 17, "RESERVED" },
   { 18, "p_compat_sys_keyctl_entry" },
   { 19, "p_compat_sys_keyctl_ret" },
   { 20, "p_compat_sys_request_key_entry" },
   { 21, "p_compat_sys_request_key_ret" },
   { 22, "p_key_change_session_keyring_entry" },
   { 23, "p_key_change_session_keyring_ret" },
   { 24, "p_sys_add_key_entry" },
   { 25, "p_sys_add_key_ret" },
   { 26, "p_sys_keyctl_entry" },
   { 27, "p_sys_keyctl_ret" },
   { 28, "p_sys_request_key_entry" },
   { 29, "p_sys_request_key_ret" },
   { 30, "p_ovl_override_sync_ret" },
   { 31, "p_override_creds_entry" },
   { 32, "p_revert_creds_ret" },
   { 33, "p_seccomp_entry" },
   { 34, "p_seccomp_ret" },
   { 35, "p_seccomp_entry (TSYNC child)" },
   { 36, "p_seccomp_ret (TSYNC child)" },
   { 37, "p_set_current_groups_entry" },
   { 38, "p_set_current_groups_ret" },
   { 39, "p_security_bprm_committing_creds_entry" },
   { 40, "p_security_bprm_committed_creds_ret" },
   { 41, "p_sys_setfsgid_entry" },
   { 42, "p_sys_setfsgid_ret" },
   { 43, "p_sys_setfsuid_entry" },
   { 44, "p_sys_setfsuid_ret" },
   { 45, "p_sys_setgid_entry" },
   { 46, "p_sys_setgid_ret" },
   { 47, "p_sys_setns_entry" },
   { 48, "p_sys_setns_ret" },
   { 49, "p_sys_setregid_entry" },
   { 50, "p_sys_setregid_ret" },
   { 51, "p_sys_setresgid_entry" },
   { 52, "p_sys_setresgid_ret" },
   { 53, "p_sys_setresuid_entry" },
   { 54, "p_sys_setresuid_ret" },
   { 55, "p_sys_setreuid_entry" },
   { 56, "p_sys_setreuid_ret" },
   { 57, "p_sys_setuid_entry" },
   { 58, "p_sys_setuid_ret" },
   { 59, "p_sys_unshare_entry" },
   { 60, "p_sys_unshare_ret" },
   { 61, "p_wake_up_new_task_entry" },
   { 0, NULL }

};

struct p_lkrg_debug_off_flag_action {

   char p_id;
   char *p_name;

} p_debug_off_flag_action[] = {

   { 0, "OFF" },
   { 1, "ON" },
   { 2, "RESET" },
   { 3, "OVERRIDE OFF" },
   { 4, "OVERRIDE ON" },

};

notrace void p_debug_off_flag_off(struct p_ed_process *p_source, unsigned int p_id) {

   p_source->p_ed_task.p_off_debug_cnt++;

   if (p_source->p_ed_task.p_off_counter < P_LKRG_TASK_OFF_MAXBUF-1) {
      /* Report current event */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 0;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   } else {
      /* Mark that we are starting overriding ring-buffer */
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_caller = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_action = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_old_off = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_debug_val = -1;
      /* Reset */
      p_source->p_ed_task.p_off_counter = 0;
      /* Report current event */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 0;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   }
}

notrace void p_debug_off_flag_on(struct p_ed_process *p_source, unsigned int p_id) {

   p_source->p_ed_task.p_off_debug_cnt--;

   if (p_source->p_ed_task.p_off_counter < P_LKRG_TASK_OFF_MAXBUF-1) {
      /* Report current event */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 1;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   } else {
      /* Mark that we are starting overriding ring-buffer */
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_caller = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_action = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_old_off = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Reset */
      p_source->p_ed_task.p_off_counter = 0;
      /* Report current event */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 1;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   }
}

notrace void p_debug_off_flag_override_off(struct p_ed_process *p_source, unsigned int p_id, struct pt_regs *p_regs) {

   p_source->p_ed_task.p_off_debug_cnt++;

   if (p_source->p_ed_task.p_off_counter < P_LKRG_TASK_OFF_MAXBUF-1) {
      /* Report current event */
      /* Stack trace*/
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries =
             (unsigned long *)p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_internal_buf;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries =
          P_PCFI_STACK_BUF/sizeof(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries[0]);
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries = 0;
#if defined(CONFIG_ARCH_STACKWALK) || LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries =
          stack_trace_save(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries,
                           p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries,
                           1);
#else
      save_stack_trace(&p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace);
#endif
      /* End */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 3;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   } else {
      /* Mark that we are starting overriding ring-buffer */
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_caller = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_action = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_old_off = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_debug_val = -1;
      /* Reset */
      p_source->p_ed_task.p_off_counter = 0;
      /* Report current event */
      /* Stack trace*/
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries =
             (unsigned long *)p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_internal_buf;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries =
          P_PCFI_STACK_BUF/sizeof(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries[0]);
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries = 0;
#if defined(CONFIG_ARCH_STACKWALK) || LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries =
          stack_trace_save(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries,
                           p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries,
                           1);
#else
      save_stack_trace(&p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace);
#endif
      /* End */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 3;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   }
}

notrace void p_debug_off_flag_override_on(struct p_ed_process *p_source, unsigned int p_id, struct pt_regs *p_regs) {

   p_source->p_ed_task.p_off_debug_cnt--;

   if (p_source->p_ed_task.p_off_counter < P_LKRG_TASK_OFF_MAXBUF-1) {
      /* Report current event */
      /* Stack trace*/
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries =
             (unsigned long *)p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_internal_buf;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries =
          P_PCFI_STACK_BUF/sizeof(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries[0]);
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries = 0;
#if defined(CONFIG_ARCH_STACKWALK) || LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries =
          stack_trace_save(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries,
                           p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries,
                           1);
#else
      save_stack_trace(&p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace);
#endif
      /* End */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 4;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   } else {
      /* Mark that we are starting overriding ring-buffer */
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_caller = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_action = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_old_off = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_debug_val = -1;
      /* Reset */
      p_source->p_ed_task.p_off_counter = 0;
      /* Report current event */
      /* Stack trace*/
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries =
             (unsigned long *)p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_internal_buf;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries =
          P_PCFI_STACK_BUF/sizeof(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries[0]);
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries = 0;
#if defined(CONFIG_ARCH_STACKWALK) || LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries =
          stack_trace_save(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries,
                           p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries,
                           1);
#else
      save_stack_trace(&p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace);
#endif
      /* End */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 4;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   }
}

notrace void p_debug_off_flag_reset(struct p_ed_process *p_source, unsigned int p_id) {

   p_source->p_ed_task.p_off_debug_cnt = 0;

   if (p_source->p_ed_task.p_off_counter < P_LKRG_TASK_OFF_MAXBUF-1) {
      /* Report current event */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 2;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   } else {
      /* Mark that we are starting overriding ring-buffer */
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_caller = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_action = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_old_off = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_debug_val = -1;
      /* Reset */
      p_source->p_ed_task.p_off_counter = 0;
      /* Report current event */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 2;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   }
}

notrace void p_debug_off_flag_dump_ring_buffer(struct p_ed_process *p_source) {

   unsigned int p_tmp;

   p_print_log(P_LOG_WATCH, "OFF debug: normalization[0x%lx] cookie[0x%lx]",
               p_global_cnt_cookie,
               p_global_off_cookie);
   p_print_log(P_LOG_WATCH, "Process[%d | %s] Parent[%d | %s] has TSYNC[%d] and [%d] entries:",
               p_source->p_ed_task.p_pid,
               p_source->p_ed_task.p_comm,
               p_source->p_ed_task.p_task->real_parent->pid,
               p_source->p_ed_task.p_task->real_parent->comm,
               p_source->p_ed_task.p_sec.flag_sync_thread,
               p_source->p_ed_task.p_off_counter);

   if (p_source->p_ed_task.p_off_counter < 3 &&
       p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_caller == -1 &&
       p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_action == -1 &&
       p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_old_off == -1 &&
       p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_debug_val == -1) {
      /* OK, ring buffer was overwritten. Dump a few entries before overwrite: */
      p_print_log(P_LOG_WATCH, "Before overwrite:");
      for (p_tmp = P_LKRG_TASK_OFF_MAXBUF-1-6; p_tmp < P_LKRG_TASK_OFF_MAXBUF-1; p_tmp++) {
         p_print_log(P_LOG_WATCH, " => caller[%s] action[%s] old_off[0x%lx] debug_val[%d]",
                     p_debug_off_flag_callers[p_source->p_ed_task.p_off_debug[p_tmp].p_caller].p_name,
                     p_debug_off_flag_action[p_source->p_ed_task.p_off_debug[p_tmp].p_action].p_name,
                     p_source->p_ed_task.p_off_debug[p_tmp].p_old_off,
                     p_source->p_ed_task.p_off_debug[p_tmp].p_debug_val);
         if (p_source->p_ed_task.p_off_debug[p_tmp].p_action == 3 ||
             p_source->p_ed_task.p_off_debug[p_tmp].p_action == 4) {
            p_print_log(P_LOG_WATCH, "Stack trace:");
#if defined(CONFIG_ARCH_STACKWALK) || LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)
            stack_trace_print(p_source->p_ed_task.p_off_debug[p_tmp].p_trace.entries,
                              p_source->p_ed_task.p_off_debug[p_tmp].p_trace.nr_entries,
                              0);
#else
            print_stack_trace(&p_source->p_ed_task.p_off_debug[p_tmp].p_trace, 0);
#endif
         }
      }
      p_print_log(P_LOG_WATCH, "=== RING BUFFER OVERRIDE ===");
   }

   for (p_tmp = 0; p_tmp < p_source->p_ed_task.p_off_counter; p_tmp++) {
      p_print_log(P_LOG_WATCH, " => caller[%s] action[%s] old_off[0x%lx] debug_val[%d]",
                  p_debug_off_flag_callers[p_source->p_ed_task.p_off_debug[p_tmp].p_caller].p_name,
                  p_debug_off_flag_action[p_source->p_ed_task.p_off_debug[p_tmp].p_action].p_name,
                  p_source->p_ed_task.p_off_debug[p_tmp].p_old_off,
                  p_source->p_ed_task.p_off_debug[p_tmp].p_debug_val);
      if (p_source->p_ed_task.p_off_debug[p_tmp].p_action == 3 ||
          p_source->p_ed_task.p_off_debug[p_tmp].p_action == 4) {
         p_print_log(P_LOG_WATCH, "Stack trace:");
#if defined(CONFIG_ARCH_STACKWALK) || LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)
         stack_trace_print(p_source->p_ed_task.p_off_debug[p_tmp].p_trace.entries,
                           p_source->p_ed_task.p_off_debug[p_tmp].p_trace.nr_entries,
                           0);
#else
         print_stack_trace(&p_source->p_ed_task.p_off_debug[p_tmp].p_trace, 0);
#endif
      }
   }
}
#endif

static inline void p_ed_is_off_off(struct p_ed_process *p_source, long p_val, int *p_ret) {

   if (unlikely(p_val != p_global_cnt_cookie)) {
      p_print_log(P_LOG_ALERT, "DETECT: Task: 'off' flag corruption for pid %u, name %s",
         p_source->p_ed_task.p_pid, p_source->p_ed_task.p_comm);
#ifdef P_LKRG_TASK_OFF_DEBUG
      p_print_log(P_LOG_WATCH, "'off' flag[0x%lx] (normalization via 0x%lx)",
         p_val, p_global_cnt_cookie);
      p_debug_off_flag_dump_ring_buffer(p_source);
      dump_stack();
#else
      p_debug_log(P_LOG_DEBUG, "'off' flag[0x%lx] (normalization via 0x%lx)",
         p_val, p_global_cnt_cookie);
#endif
      // kill this process!
      rcu_read_lock();
      if (p_ret)
         (*p_ret)++;
      else
         p_ed_kill_task_by_task(p_source->p_ed_task.p_task);
      rcu_read_unlock();
   }
}

inline void p_validate_off_flag(struct p_ed_process *p_source, long p_val, int *p_ret) {

   if (likely(p_val == p_global_cnt_cookie))
      return;

   while (p_val > p_global_cnt_cookie) {
      p_val -= p_global_cnt_cookie;
      if (unlikely(p_val > (p_global_cnt_cookie << 3)))
         break;
   }

   p_ed_is_off_off(p_source, p_val, p_ret);
}

#if P_OVL_OVERRIDE_SYNC_MODE
notrace int p_verify_ovl_override_sync(struct p_ed_process *p_source) {

   register unsigned long p_off = p_source->p_ed_task.p_off ^ p_global_off_cookie; // Decode

   p_validate_off_flag(p_source,p_off,NULL);   // Validate
#if P_OVL_OVERRIDE_SYNC_MODE == 2
   return p_off == 3 * p_global_cnt_cookie;
#else
   return p_off == 2 * p_global_cnt_cookie;
#endif
}
#endif

notrace void p_ed_is_off_off_wrap(struct p_ed_process *p_source) {

   register unsigned long p_off = p_source->p_ed_task.p_off ^ p_global_off_cookie; // Decode
   p_ed_is_off_off(p_source,p_off,NULL);
}

notrace void p_ed_validate_off_flag_wrap(struct p_ed_process *p_source) {

   register unsigned long p_off = p_source->p_ed_task.p_off ^ p_global_off_cookie; // Decode
   p_validate_off_flag(p_source,p_off,NULL);   // Validate
}

notrace void p_set_ed_process_on(struct p_ed_process *p_source) {

   register unsigned long p_off = p_source->p_ed_task.p_off ^ p_global_off_cookie; // Decode

#if defined(CONFIG_SECCOMP)
   if (p_source->p_ed_task.p_sec.flag_sync_thread) {
      p_set_ed_process_override_on(p_source);
   } else {
#endif
      p_off -= p_global_cnt_cookie;               // Normalize
      p_ed_is_off_off(p_source,p_off,NULL);       // Validate

      p_source->p_ed_task.p_off = p_off ^ p_global_off_cookie; // Encode
      p_source->p_ed_task.p_off_count = 0;
#if defined(CONFIG_SECCOMP)
   }
#endif
}

notrace void p_set_ed_process_off(struct p_ed_process *p_source) {

   register unsigned long p_off = p_source->p_ed_task.p_off ^ p_global_off_cookie; // Decode

#if defined(CONFIG_SECCOMP)
   if (p_source->p_ed_task.p_sec.flag_sync_thread) {
      p_set_ed_process_override_off(p_source);
   } else {
#endif
      p_ed_is_off_off(p_source,p_off,NULL);       // Validate
      p_off += p_global_cnt_cookie;               // Normalize

      p_source->p_ed_task.p_off = p_off ^ p_global_off_cookie;
#if defined(CONFIG_SECCOMP)
   }
#endif
}

notrace void p_set_ed_process_override_on(struct p_ed_process *p_source) {

   register unsigned long p_off = p_source->p_ed_task.p_off ^ p_global_off_cookie; // Decode

   p_validate_off_flag(p_source,p_off,NULL);   // Validate
   p_off -= p_global_cnt_cookie;               // Normalize

   p_source->p_ed_task.p_off = p_off ^ p_global_off_cookie; // Encode
   if (p_off == p_global_cnt_cookie)
      p_source->p_ed_task.p_off_count = 0;
}

notrace void p_set_ed_process_override_off(struct p_ed_process *p_source) {

   register unsigned long p_off = p_source->p_ed_task.p_off ^ p_global_off_cookie; // Decode

   p_validate_off_flag(p_source,p_off,NULL);   // Validate
   p_off += p_global_cnt_cookie;               // Normalize

   p_source->p_ed_task.p_off = p_off ^ p_global_off_cookie;
}

notrace void p_reset_ed_flags(struct p_ed_process *p_source) {

   p_source->p_ed_task.p_off = p_global_cnt_cookie ^ p_global_off_cookie;
   p_source->p_ed_task.p_off_count = 0;

}

int p_dump_task_f(void *p_arg) {

   struct task_struct *p_task = (struct task_struct *)p_arg;
   struct p_ed_process *p_tmp;
   struct rb_root *p_root;

   if ( (p_tmp = p_alloc_ed_pids()) == NULL) {
      p_print_log(P_LOG_FAULT, "Can't allocate memory for tracking pid %u, name %s", p_task->pid, p_task->comm);
      return P_LKRG_GENERAL_ERROR;
   }

   p_update_ed_process(p_tmp, p_task, 1);
//   p_set_ed_process_on(p_tmp);
   p_tmp->p_ed_task.p_off = p_global_cnt_cookie ^ p_global_off_cookie;
   p_tmp->p_ed_task.p_off_count = 0;
#ifdef P_LKRG_TASK_OFF_DEBUG
   p_tmp->p_ed_task.p_off_debug_cnt = p_tmp->p_ed_task.p_off_counter = 0;
#endif

   p_rb_init_ed_pid_node(&p_tmp->p_rb);
   p_root = p_rb_hash_tree_lookup(p_tmp->p_ed_task.p_pid);
   if (p_rb_add_ed_pid(p_root, p_tmp->p_ed_task.p_pid, p_tmp)) {
      p_print_log(P_LOG_WATCH, "pid %u is already inserted", p_tmp->p_ed_task.p_pid);
      p_free_ed_pids(p_tmp);
      return 1;
   } else {
      p_print_log(P_LOG_WATCH, "Inserting pid %u", p_tmp->p_ed_task.p_pid);
   }

   return P_LKRG_SUCCESS;
}

int p_remove_task_pid_f(pid_t p_arg) {

   struct p_ed_process *p_tmp;
   struct rb_root *p_root;

   p_root = p_rb_hash_tree_lookup(p_arg);
   if ( (p_tmp = p_rb_find_ed_pid(p_root, p_arg)) == NULL) {
      // This process is not on the list!
      return P_LKRG_GENERAL_ERROR;
   }

   p_rb_del_ed_pid(p_root, p_tmp);
   p_print_log(P_LOG_WATCH, "Removing pid %u", p_arg);

   return P_LKRG_SUCCESS;
}

static unsigned int p_iterate_processes(int (*p_func)(void *), char p_ver) {

   int p_ret;
   unsigned int p_err = 0;
   struct task_struct *p_ptmp, *p_tmp;
   unsigned long p_flags;

   p_tasks_read_lock(&p_flags);
   rcu_read_lock();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
   for_each_process_thread(p_ptmp, p_tmp) {
#else
   // tasklist_lock
   do_each_thread(p_ptmp, p_tmp) {
#endif

      get_task_struct(p_tmp);
      /* do not touch kernel threads or the global init */
      if (!p_is_ed_task(p_tmp)) {
         put_task_struct(p_tmp);
         continue;
      }

      if ( (p_ret = p_func(p_tmp)) != 0) {
         p_err++;
         if (likely(p_ver)) {
            if (spin_is_locked(&p_tmp->sighand->siglock)) {
               p_regs_set_ip(task_pt_regs(p_tmp), -1);
            } else {
               p_ed_kill_task_by_task(p_tmp);
            }
         }
      }
      put_task_struct(p_tmp);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
   }
#else
   // tasklist_unlock
   } while_each_thread(p_ptmp, p_tmp);
#endif
   rcu_read_unlock();
   p_tasks_read_unlock(&p_flags);

   return p_err;
}

static unsigned int p_iterate_lkrg_tasks_paranoid(void) {

   int p_ret = 0;
   unsigned int i;
   struct rb_node *p_node;
   struct p_ed_process *p_tmp;
   struct task_struct *p_task = NULL;
   unsigned long p_flags;

   for (i=0; i<RB_HASH_SIZE; i++) {
      p_tasks_read_lock_raw(&p_rb_hash[i].p_lock.lock);
      rcu_read_lock();
      for (p_node = rb_first(&p_rb_hash[i].p_tree.tree); p_node; p_node = rb_next(p_node)) {
         p_tmp = rb_entry(p_node, struct p_ed_process, p_rb);
         if ( (p_task = pid_task(find_vpid(p_tmp->p_ed_task.p_pid), PIDTYPE_PID)) != NULL) {
            get_task_struct(p_task);
            /* do not touch kernel threads or the global init */
            if (p_is_ed_task(p_task) && p_get_task_state(p_task) != TASK_DEAD) {
               if (p_cmp_tasks(p_tmp, p_task, 0x0)) {
                  p_ret++;
                  if (spin_is_locked(&p_task->sighand->siglock)) {
                     p_regs_set_ip(task_pt_regs(p_task), -1);
                  } else {
                     p_ed_kill_task_by_task(p_task);
                  }
               }
            }
            put_task_struct(p_task);
         }
      }
      rcu_read_unlock();
      p_tasks_read_unlock_raw(&p_rb_hash[i].p_lock.lock);
   }

   /* Before leaving, verify current task */
   p_tasks_read_lock(&p_flags);
   if (p_is_ed_task(current)) {
      p_validate_task_f(current);
   }
   p_tasks_read_unlock(&p_flags);

   return p_ret;
}

int p_cmp_creds(struct p_cred *p_orig, const struct cred *p_current_cred, struct task_struct *p_current, char p_opt) {

   int p_ret = 0;

#define P_CMP_CRED(eq, get, cred) \
   if (!eq(p_orig->cred, p_current_cred->cred)) { \
      if (p_opt) { \
         p_print_log(P_LOG_ALERT, \
            "DETECT: Task: " #cred " corruption (expected %u vs. actual %u) for pid %u, name %s", \
            get(&p_orig->cred), get(&p_current_cred->cred), \
            task_pid_nr(p_current), p_current->comm); \
      } \
      p_ret++; \
   }

   /* *UID */
   P_CMP_CRED(uid_eq, p_get_uid, uid)
   P_CMP_CRED(uid_eq, p_get_uid, euid)
   P_CMP_CRED(uid_eq, p_get_uid, suid)
   P_CMP_CRED(uid_eq, p_get_uid, fsuid)

   /* *GID */
   P_CMP_CRED(gid_eq, p_get_gid, gid)
   P_CMP_CRED(gid_eq, p_get_gid, egid)
   P_CMP_CRED(gid_eq, p_get_gid, sgid)
   P_CMP_CRED(gid_eq, p_get_gid, fsgid)

#define P_CMP_PTR(orig, curr, name) \
   if (orig != curr) { \
      if (p_opt) { \
         if (P_CTRL(p_log_level) >= P_LOG_WATCH) \
            p_print_log(P_LOG_ALERT, \
               "DETECT: Task: " name " pointer corruption (expected 0x%lx vs. actual 0x%lx) for pid %u, name %s", \
               (unsigned long)orig, (unsigned long)curr, task_pid_nr(p_current), p_current->comm); \
         else \
            p_print_log(P_LOG_ALERT, \
               "DETECT: Task: " name " pointer corruption for pid %u, name %s", \
               task_pid_nr(p_current), p_current->comm); \
      } \
      p_ret++; \
   }

   /* Namespaces */
#define P_NS_ESCAPE "Namespace escape: "
   P_CMP_PTR(p_orig->user_ns, p_current_cred->user_ns, P_NS_ESCAPE "user_ns")

   return p_ret;
}

static int p_cmp_tasks(struct p_ed_process *p_orig, struct task_struct *p_current, char p_kill) {

   const char p_opt = 1; /* for uses of the P_CMP_PTR() macro */
   int p_ret = 0, p_killed = 0;
   register long p_off = p_orig->p_ed_task.p_off ^ p_global_off_cookie;
   const struct cred *p_current_cred = NULL;
   const struct cred *p_current_real_cred = NULL;

   if (p_off - p_global_cnt_cookie) {
      if (p_kill)
         p_validate_off_flag(p_orig,p_off,NULL);     // Validate
      else
         p_validate_off_flag(p_orig,p_off,&p_ret);   // Validate

      p_orig->p_ed_task.p_off_count++;

      if (p_orig->p_ed_task.p_off_count > P_ED_PROCESS_OFF_MAX) {
         /* That's weird and it might be a potentially compromised process */
         p_print_log(P_LOG_WATCH, "Validation was off %u times in a row for pid %u, name %s",
            p_orig->p_ed_task.p_off_count, p_orig->p_ed_task.p_pid, p_orig->p_ed_task.p_comm);
      }
      return 0;
   }


   if (p_orig->p_ed_task.p_task != p_current) {
      /*
       * On heavily loaded SMP machines, in a very rare corner case situation,
       * it is possible to hit an annoying kretprobe glitch.
       * If you are one of the "lucky" guys who hit this problem for one of the processes
       * being monitored by the Exploit Detection (ED) feature, you are a good candidate
       * to hit another problem. If at some point kernel decided to reuse already unused
       * pid (from the process which was affected by the glitching scenario) you are an even
       * better candidate to hit a potential little race condition in a function return
       * from do_fork().
       * When p_do_fork_ret() is invoked it tries to get a spin_lock for internal ED database
       * (which is red-black tree identified by p_rb_ed_pids_lock lock). If another CPU/core is
       * executing ED validation routine p_ed_enforce_validation() it might be faster to get
       * this lock before p_do_fork_ret() does (race condition). In that case, p_do_fork_ret()
       * does a busy wait, and the other CPU does verification. If you are the most "lucky" guy
       * who hit the glitching problem and pid reuse scenario for the process which supposed to
       * do attributes update for ED database from the p_do_fork_ret() function you will generate
       * a one-time FP. As soon as FP happens and verification routine unlocks the ED database,
       * p_do_fork_ret() kicks-in and fixes attributes, and FP will never happen again.
       * This is a very rare corner-case situation which can only be possible if you meet all
       * the problems together affecting exactly the same process (kretprobe glitch + pid reuse
       * + race condition on a heavily loaded SMP machine). It is possible to mitigate this
       * problem and we do it here.
       */
#define P_PRINT_LOG_GLITCH(p_orig, p_current) \
      p_print_log(P_LOG_FAULT, "Tracked task unexpectedly replaced (kretprobe glitch?) with new pid %u, name %s", \
         task_pid_nr(p_current), p_current->comm); \
      if (P_CTRL(p_log_level) >= P_LOG_WATCH) \
         p_print_log(P_LOG_WATCH, "Tracked task unexpectedly replaced (kretprobe glitch?) from address 0x%lx, pid %u, name %s " \
            "to address 0x%lx, pid %u, name %s", \
            (unsigned long)p_orig->p_ed_task.p_task, p_orig->p_ed_task.p_pid, p_orig->p_ed_task.p_comm, \
            (unsigned long)p_current, task_pid_nr(p_current), p_current->comm);
      P_PRINT_LOG_GLITCH(p_orig, p_current)
      return 0;
   }

   /* Validate stack first */
   if (p_ed_pcfi_validate_sp(p_current, p_orig, p_get_thread_sp(p_current))) {
      if (p_kill) {
         p_pcfi_kill_task_by_task(p_current);
         p_killed = 1;
      }
      p_ret++;
   }

   /*
    * Fetch pointers first
    */
   p_current_cred = rcu_dereference(p_current->cred);
   /* Get reference to cred */
   get_cred(p_current_cred);
   p_current_real_cred = rcu_dereference(p_current->real_cred);
   /* Get reference to real_cred */
   get_cred(p_current_real_cred);

   if (p_orig->p_ed_task.p_cred_ptr != p_current_cred) {
      if (p_cmp_creds(&p_orig->p_ed_task.p_cred, p_current_cred, p_current, 0x0)) {
         P_CMP_PTR(p_orig->p_ed_task.p_cred_ptr, p_current_cred, "cred")
      }
   }

   if (p_orig->p_ed_task.p_real_cred_ptr != p_current_real_cred) {
      if (p_cmp_creds(&p_orig->p_ed_task.p_real_cred, p_current_real_cred, p_current, 0x0)) {
         P_CMP_PTR(p_orig->p_ed_task.p_real_cred_ptr, p_current_real_cred, "real_cred")
      }
   }

   p_ret += p_cmp_creds(&p_orig->p_ed_task.p_cred, p_current_cred, p_current, 0x1);
   p_ret += p_cmp_creds(&p_orig->p_ed_task.p_real_cred, p_current_real_cred, p_current, 0x1);

   /* Namespaces */
   P_CMP_PTR(p_orig->p_ed_task.p_nsproxy, p_current->nsproxy, P_NS_ESCAPE "nsproxy")
   P_CMP_PTR(p_orig->p_ed_task.p_ns.uts_ns, p_current->nsproxy->uts_ns, P_NS_ESCAPE "uts_ns")
   P_CMP_PTR(p_orig->p_ed_task.p_ns.ipc_ns, p_current->nsproxy->ipc_ns, P_NS_ESCAPE "ipc_ns")
   P_CMP_PTR(p_orig->p_ed_task.p_ns.mnt_ns, p_current->nsproxy->mnt_ns, P_NS_ESCAPE "mnt_ns")
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
   P_CMP_PTR(p_orig->p_ed_task.p_ns.pid_ns_for_children, p_current->nsproxy->pid_ns_for_children, P_NS_ESCAPE "pid_ns_for_children")
#else
   P_CMP_PTR(p_orig->p_ed_task.p_ns.pid_ns, p_current->nsproxy->pid_ns, P_NS_ESCAPE "pid_ns")
#endif
   P_CMP_PTR(p_orig->p_ed_task.p_ns.net_ns, p_current->nsproxy->net_ns, P_NS_ESCAPE "net_ns")
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
   P_CMP_PTR(p_orig->p_ed_task.p_ns.cgroup_ns, p_current->nsproxy->cgroup_ns, P_NS_ESCAPE "cgroup_ns")
#endif

#if defined(CONFIG_SECCOMP)
   /* Seccomp */
   if (p_orig->p_ed_task.p_sec.flag) { // SECCOMP was enabled so it make sense to compare...
      P_SYM(p_get_seccomp_filter)(p_current);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,11,0)
      if (test_task_syscall_work(p_current,SECCOMP) != p_orig->p_ed_task.p_sec.flag) {
#else
      if (test_tsk_thread_flag(p_current,TIF_SECCOMP) != p_orig->p_ed_task.p_sec.flag) {
#endif
         p_print_log(P_LOG_ALERT, "DETECT: Task: TIF_SECCOMP flag corruption (expected %u vs. actual %u) for pid %u, name %s",
            p_orig->p_ed_task.p_sec.flag,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,11,0)
            test_task_syscall_work(p_current, SECCOMP),
#else
            test_tsk_thread_flag(p_current, TIF_SECCOMP),
#endif
            task_pid_nr(p_current), p_current->comm);
         p_ret++;
      }

      if (p_orig->p_ed_task.p_sec.sec.mode != p_current->seccomp.mode) {
         if (p_current->seccomp.mode < 0 || p_current->seccomp.mode > 2
             || p_orig->p_ed_task.p_sec.sec.mode < 0 || p_orig->p_ed_task.p_sec.sec.mode > 2) {
            p_print_log(P_LOG_ALERT, "DETECT: Task: seccomp mode corruption (expected %u vs. actual %u) for pid %u, name %s",
               p_orig->p_ed_task.p_sec.sec.mode, p_current->seccomp.mode, task_pid_nr(p_current), p_current->comm);
         } else {
            static const char * const p_sec_strings[] = {"SECCOMP_MODE_DISABLED", "SECCOMP_MODE_STRICT", "SECCOMP_MODE_FILTER"};
            p_print_log(P_LOG_ALERT, "DETECT: Task: seccomp mode corruption (expected %s vs. actual %s) for pid %u, name %s",
               p_sec_strings[p_orig->p_ed_task.p_sec.sec.mode], p_sec_strings[p_current->seccomp.mode],
               task_pid_nr(p_current), p_current->comm);
         }
         p_ret++;
      }

      P_CMP_PTR(p_orig->p_ed_task.p_sec.sec.filter, p_current->seccomp.filter, "seccomp filter")

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
      P_SYM(p_put_seccomp_filter)(p_current->seccomp.filter);
#else
      P_SYM(p_put_seccomp_filter)(p_current);
#endif
   }
#endif

   /* Release reference to cred */
   put_cred(p_current_cred);
   /* Release reference to real_cred */
   put_cred(p_current_real_cred);

   return p_killed ? -p_ret : p_ret;
}

int p_validate_task_f(void *p_arg) {

   int p_ret = P_LKRG_SUCCESS;
   struct p_ed_process *p_tmp;
   struct task_struct *p_task = (struct task_struct *)p_arg;

   rcu_read_lock();
   get_task_struct(p_task);

   if ( (p_tmp = p_find_ed_by_pid(task_pid_nr(p_task))) == NULL) {
      // This process is not on the list!
      if (p_get_task_state(p_task) != TASK_DEAD) {
         p_ret = P_LKRG_GENERAL_ERROR;
         p_print_log(P_LOG_WATCH, "Can't find in internal tracking list pid %u, name %s", task_pid_nr(p_task), p_task->comm);
      }
      goto p_validate_task_out;
   }

   if (p_cmp_tasks(p_tmp, p_task, 1) > 0) {
      // kill this process!
      p_ed_kill_task_by_task(p_task);
   }

p_validate_task_out:

   put_task_struct(p_task);
   rcu_read_unlock();

   return p_ret;
}

#ifdef CONFIG_SECURITY_SELINUX
static void p_validate_selinux(void) {
   unsigned long p_flags;

   do {
      p_lkrg_counter_lock_lock(&p_ed_guard_globals.p_selinux_lock, &p_flags);
      if (!p_lkrg_counter_lock_val_read(&p_ed_guard_globals.p_selinux_lock))
         break;
      p_lkrg_counter_lock_unlock(&p_ed_guard_globals.p_selinux_lock, &p_flags);
      schedule();
   } while(1);

#if (!defined(RHEL_RELEASE_CODE) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)) || \
     (defined(RHEL_RELEASE_CODE) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 3))
   if (p_ed_guard_globals.p_selinux.p_selinux_enabled != *P_SYM(p_selinux_enabled)) {
      p_print_log(P_LOG_ALERT, "BLOCK: SELinux: selinux_enabled corruption (expected %u vs. actual %u), restoring",
         p_ed_guard_globals.p_selinux.p_selinux_enabled, *P_SYM(p_selinux_enabled));
/* FIXME: should obey kint_enforce here */
      *P_SYM(p_selinux_enabled) = p_ed_guard_globals.p_selinux.p_selinux_enabled;
   }
#endif

#ifdef P_SELINUX_VERIFY
   if (p_selinux_state_changed()) {
      p_print_log(P_LOG_ALERT, "DETECT: SELinux: selinux_state->enforcing corruption (expected %u vs. actual %u)",
         p_ed_guard_globals.p_selinux.p_selinux_enforcing, p_selinux_state_enforcing());

      switch (P_CTRL(p_kint_enforce)) {

         /* Panic */
         case 2:
           // OK, we need to crash the kernel now
           p_panic("SELinux: selinux_state->enforcing corruption (expected %u vs. actual %u)",
              p_ed_guard_globals.p_selinux.p_selinux_enforcing, p_selinux_state_enforcing());
           break;

         case 1:
           p_print_log(P_LOG_ALERT, "BLOCK: SELinux: Restoring selinux_state->enforcing to %u",
              p_ed_guard_globals.p_selinux.p_selinux_enforcing);
           p_selinux_state_restore();
           break;

         case 0:
           p_print_log(P_LOG_ALERT, "ALLOW: SELinux: Accepting new value for selinux_state->enforcing");
           p_selinux_state_update();
           break;

      }

   }
#endif
   p_lkrg_counter_lock_unlock(&p_ed_guard_globals.p_selinux_lock, &p_flags);
}
#endif

static void p_ed_wq_valid_work(struct work_struct *p_work) {

#ifdef CONFIG_SECURITY_SELINUX
   // SELinux
   p_validate_selinux();
#endif

   /* Free the worker struct */
   if (p_work) {
      p_ed_free_valid(p_work);
   }
}

static void p_ed_validate_globals(void) {

   struct work_struct *p_worker;

   if (P_CTRL(p_kint_validate)) {

      /* Validate globals... */
      // ...

      /* Prepare for validation which requires 'sleeping' */
      while ( (p_worker = p_ed_alloc_valid()) == NULL); // Should never be NULL
      INIT_WORK(p_worker, p_ed_wq_valid_work);
      /* schedule for execution */
      queue_work(system_unbound_wq, p_worker);

   }
}

void p_ed_validate_current(void) {

   if (!P_CTRL(p_pint_validate))
      return;

   if (p_is_ed_task(current)) {
      p_validate_task_f(current);
   }
}

void p_ed_enforce_validation(void) {

   unsigned long p_flags;

   p_ed_pcfi_cpu(0);

   switch (P_CTRL(p_pint_validate)) {

      case 3:
        p_ed_enforce_validation_paranoid();
        break;

      case 2:
      case 1:
        p_tasks_read_lock(&p_flags);
        if (p_is_ed_task(current)) {
           p_validate_task_f(current);
        }
        p_tasks_read_unlock(&p_flags);
        break;

      case 0:
        break;
   }

   /* Validate critical globals */
//   p_ed_validate_globals();
}

unsigned int p_ed_enforce_validation_paranoid(void) {

   unsigned int p_ret = 0;

   p_ed_pcfi_cpu(0);

   if (!P_CTRL(p_pint_validate))
      goto p_ed_enforce_validation_paranoid_globals;

   /* Validate processes and threads */
   p_ret = p_iterate_lkrg_tasks_paranoid();

p_ed_enforce_validation_paranoid_globals:

   /* Validate critical globals */
   p_ed_validate_globals();

   return p_ret;
}

static void p_ed_pcfi_cache_zero(void *p_arg) {

   unsigned long *p_page = p_arg;

   memset(p_page, 0, P_PCFI_STACK_BUF);
}

static int p_ed_pcfi_cache_init(void) {

   if ( (p_ed_pcfi_cache = kmem_cache_create("p_ed_pcfi_cache", P_PCFI_STACK_BUF,
                                            0, P_LKRG_CACHE_FLAGS, p_ed_pcfi_cache_zero)) == NULL) {
      return P_LKRG_GENERAL_ERROR;
   }

   return P_LKRG_SUCCESS;
}

static void p_ed_pcfi_cache_delete(void) {

   if (p_ed_pcfi_cache) {
      kmem_cache_destroy(p_ed_pcfi_cache);
      p_ed_pcfi_cache = NULL;
   }
}

static inline int p_is_obj_on_stack(struct task_struct *p_task, const void *p_addr) {

   void *p_stack = p_task->stack; //task_stack_page(p_task);

   return (p_addr >= p_stack) && (p_addr < (p_stack + THREAD_SIZE));
}

int p_ed_enforce_pcfi(struct task_struct *p_task, struct p_ed_process *p_orig, struct pt_regs *p_regs) {

   unsigned int i = 0;
//   unsigned long p_flags;
   struct stack_trace p_trace;
#ifndef CONFIG_UNWINDER_ORC
   const void *p_fp = (const void *)p_regs_get_fp(p_regs);
#endif
#ifdef CONFIG_X86
#if defined(CONFIG_UNWINDER_ORC)
   struct unwind_state p_state;
#else
   struct stack_frame p_frame;
#endif
#elif defined(CONFIG_ARM64)
#  if LINUX_VERSION_CODE >= KERNEL_VERSION(5,19,0)
   struct unwind_state p_frame;
#  else
   struct stackframe p_frame;
#  endif
#elif defined(CONFIG_ARM)
   struct stackframe p_frame;
   const void *p_sp = (const void *)thread_saved_sp(p_task);
   unsigned long p_high, p_low;
#endif
   unsigned int p_offset = 1;
   char p_sym1[KSYM_SYMBOL_LEN];
   char p_not_valid = 0;
   unsigned long p_sp_addr;

   if (in_interrupt())
      p_sp_addr = p_get_thread_sp(p_task);
   else
      p_sp_addr = p_regs_get_sp(p_regs);

   if (p_ed_pcfi_validate_sp(p_task,p_orig,p_sp_addr)) {
      // kill this process!
      p_pcfi_kill_task_by_task(p_task);
      p_not_valid = 1;
      goto p_ed_enforce_pcfi_out;
   }

   if (!(P_CTRL(p_pcfi_validate) & 0x2)) {
      goto p_ed_enforce_pcfi_out;
   }

   if (p_orig) {
      if (p_task != p_orig->p_ed_task.p_task) {
         P_PRINT_LOG_GLITCH(p_orig, p_task)
         goto p_ed_enforce_pcfi_out;
      }
   }

#ifndef CONFIG_UNWINDER_ORC
   if (!p_is_obj_on_stack(p_task, p_fp)) {
      p_print_log(P_LOG_FAULT, "Frame pointer is not on the stack, so CFI is not enforced");
      goto p_ed_enforce_pcfi_out;
   }
#endif

   while ( (p_trace.entries = p_ed_pcfi_alloc()) == NULL); // Should never be NULL

   /* Current logic always call this function with IRQ disabled */
//   local_irq_save(p_flags);

   p_trace.max_entries = P_PCFI_STACK_BUF/sizeof(p_trace.entries[0]);
   p_trace.nr_entries = 0;


#if defined(CONFIG_X86)

#if defined(CONFIG_UNWINDER_ORC)

   if (p_trace.nr_entries < p_trace.max_entries)
      p_trace.entries[p_trace.nr_entries++] = p_regs_get_ip(p_regs);

   do {
      unsigned long p_addr;

      for (unwind_start(&p_state, p_task, p_regs, NULL);
           !unwind_done(&p_state) && p_trace.nr_entries < p_trace.max_entries;
           unwind_next_frame(&p_state)) {
         p_addr = unwind_get_return_address(&p_state);
         if (!p_addr)
            break;
         p_trace.entries[p_trace.nr_entries++] = p_addr;
      }
   } while(0);

#else

   if (p_trace.nr_entries < p_trace.max_entries)
      p_trace.entries[p_trace.nr_entries++] = p_regs_get_ip(p_regs);

   while (p_trace.nr_entries < p_trace.max_entries) {
      p_frame.next_frame = NULL;
      p_frame.return_address = 0;

      if ((unsigned long)p_fp < p_regs_get_sp(p_regs) || !p_is_obj_on_stack(p_task, p_fp))
         break;

      memcpy(&p_frame, p_fp, sizeof(struct stack_frame));
      if (p_frame.return_address) {
         p_trace.entries[p_trace.nr_entries++] = p_frame.return_address;
      }

      if (p_fp == p_frame.next_frame)
         break;

      p_fp = p_frame.next_frame;
    }

#endif

#elif defined(CONFIG_ARM)

   p_frame.fp = (unsigned long)p_fp;
   p_frame.sp = (unsigned long)p_sp;
   p_frame.pc = instruction_pointer(p_regs);

   if (current != p_task) {
      if (p_trace.nr_entries < p_trace.max_entries) {
         if (p_frame.pc) {
            p_trace.entries[p_trace.nr_entries++] = p_frame.pc;
         }
      }
   }

   while (p_trace.nr_entries < p_trace.max_entries) {
      p_fp = (void *)p_frame.fp;
      p_low = p_frame.sp;
      p_high = ALIGN(p_low, THREAD_SIZE);

/*
      if ((unsigned long)p_fp < p_regs_get_sp(p_regs) ||
          !p_is_obj_on_stack(current, p_fp) ||
          (unsigned long)(p_fp) & 0xf)
         break;
*/

      if ((unsigned long)p_fp < p_low + 12 || (unsigned long)p_fp > p_high - 4)
         break;

      p_frame.fp = *(unsigned long *)(p_fp - 12);
      p_frame.sp = *(unsigned long *)(p_fp - 8);
      p_frame.pc = *(unsigned long *)(p_fp - 4);

      if (p_frame.pc) {
         p_trace.entries[p_trace.nr_entries++] = p_frame.pc;
      }

      if (!p_frame.fp && !p_frame.pc)
         break;
   }

#elif defined(CONFIG_ARM64)

   p_frame.fp = (unsigned long)p_fp;
   p_frame.pc = p_regs_get_ip(p_regs);

   if (p_trace.nr_entries < p_trace.max_entries) {
      if (p_frame.pc) {
         p_trace.entries[p_trace.nr_entries++] = p_frame.pc;
      }
   }

   while (p_trace.nr_entries < p_trace.max_entries) {
      p_fp = (void *)p_frame.fp;

      if ((unsigned long)p_fp < p_regs_get_sp(p_regs) ||
          !p_is_obj_on_stack(current, p_fp) ||
          (unsigned long)(p_fp) & 0xf)
         break;

      p_frame.fp = *(unsigned long *)(p_fp);
      p_frame.pc = *(unsigned long *)(p_fp + 8);

      if (p_frame.pc) {
         p_trace.entries[p_trace.nr_entries++] = p_frame.pc;
      }

      if (!p_frame.fp && !p_frame.pc)
         break;
   }

#else

   goto p_ed_enforce_pcfi_unlock_out;

#endif

   if (p_trace.nr_entries) {

   //p_not_valid = 0;
      for (i = 0; i < p_trace.nr_entries-p_offset; i++) {
         if (!P_SYM(p___kernel_text_address)(p_trace.entries[i])) {
            if (p_trace.nr_entries-p_offset > 4 && i > 4) {
               memset(p_sym1,0,KSYM_SYMBOL_LEN);
               sprint_symbol_no_offset(p_sym1,p_trace.entries[i-1]);
               if (!strncmp(p_sym1,"ret_from_fork",0xd)) {
                  memset(p_sym1,0,KSYM_SYMBOL_LEN);
                  sprint_symbol_no_offset(p_sym1,p_trace.entries[i-2]);
                  if (!strncmp(p_sym1,"kthread",0x7)) {
                     continue;
                  } else {
                     p_not_valid = 1;
                     break;
                  }
               }
               p_not_valid = 1;
               break;
            } else {
               p_not_valid = 1;
               break;
            }
         }
      }
   }

   if (p_not_valid) {
      p_print_log(P_LOG_ALERT, "DETECT: Task: Invalid stack trace for pid %u, name %s",
         task_pid_nr(p_task), p_task->comm);
      if (P_CTRL(p_log_level) >= P_LOG_WATCH) {
         p_print_log(P_LOG_WATCH, "Invalid frame is %u (0x%lx) out of %u for pid %u, name %s",
            i, p_trace.entries[i], p_trace.nr_entries, task_pid_nr(p_task), p_task->comm);
#ifdef CONFIG_STACKTRACE
/*
 * stack_trace_print() uses unprefixed printk(), which means default severity,
 * so we do the same here to match it regardless of what it is.
 */
         printk("--- STACK TRACE START ---\n");
#if defined(CONFIG_ARCH_STACKWALK) || LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)
         stack_trace_print(p_trace.entries, p_trace.nr_entries, 0);
#else
         print_stack_trace(&p_trace, 0);
#endif
         printk("--- STACK TRACE END ---\n");
#endif
      }
      p_pcfi_kill_task_by_task(p_task);
   }

#if !defined(CONFIG_X86) && !defined(CONFIG_ARM64) && !defined(CONFIG_ARM)

p_ed_enforce_pcfi_unlock_out:

#endif

   /* Current logic always call this function with IRQ disabled */
//   local_irq_restore(p_flags);

   p_ed_pcfi_free(p_trace.entries);


p_ed_enforce_pcfi_out:

   return p_not_valid;
}

int p_ed_pcfi_validate_sp(struct task_struct *p_task, struct p_ed_process *p_orig, unsigned long p_sp) {

   unsigned long p_stack = (p_orig) ? (unsigned long) p_orig->p_ed_task.p_stack : 0x0;
   register unsigned long p_stack_offset;
   int p_not_valid = 0;

   if (!P_CTRL(p_pcfi_validate)) {
      return P_LKRG_SUCCESS;
   }

#define P_PRINT_LOG_STACK(what) \
   if (P_CTRL(p_log_level) >= P_LOG_WATCH) \
      p_print_log(P_LOG_ALERT, "DETECT: Task: Stack " what " (base 0x%lx, sp 0x%lx) for pid %u, name %s", \
         p_stack, p_sp, task_pid_nr(p_task), p_task->comm); \
   else \
      p_print_log(P_LOG_ALERT, "DETECT: Task: Stack " what " for pid %u, name %s", \
         task_pid_nr(p_task), p_task->comm); \
   p_not_valid = 1; \

   /*
    * Validate alignment - this test should be passed even if we have a glitching problem
    */
   if (unlikely(p_sp & (sizeof(long) - 1))) {
      P_PRINT_LOG_STACK("pointer misaligned")
   }

   if (!p_stack) {
      return p_not_valid ? P_LKRG_GENERAL_ERROR : P_LKRG_SUCCESS;
   }

   if (p_task != p_orig->p_ed_task.p_task) {
      P_PRINT_LOG_GLITCH(p_orig, p_task)
      return P_LKRG_SUCCESS;
   }

   /*
    * Validate stack base
    */
   if (unlikely((p_sp & ~(THREAD_SIZE - 1)) != (p_stack & ~(THREAD_SIZE - 1)))) {
      P_PRINT_LOG_STACK("base invalid")
   }

   /*
    * Validate if stack is coming from the valid range (CONFIG_VMAP_STACK)
    */

   // TODO

   /*
    * Validate current size of the stack.
    */

   p_stack_offset = p_sp - p_stack;
   if (unlikely(p_stack_offset >= THREAD_SIZE)) {
      P_PRINT_LOG_STACK("size mismatch")
   }

   return p_not_valid ? P_LKRG_GENERAL_ERROR : P_LKRG_SUCCESS;
}


int p_exploit_detection_init(void) {

   int p_ret;
   const struct p_functions_hooks *p_fh_it;

   p_global_off_cookie = (unsigned long)get_random_long();
   p_global_cnt_cookie = (unsigned long)get_random_long();

   p_global_off_cookie |= P_NORMALIZE_LONG;
   p_global_cnt_cookie |= P_NORMALIZE_LONG;
   p_global_cnt_cookie &= P_MASK_COUNTER;

   if (p_ed_pcfi_cache_init()) {
      p_print_log(P_LOG_FATAL, "Can't initialize pCFI cache");
      p_ret = P_LKRG_GENERAL_ERROR;
      goto p_exploit_detection_init_out;
   }

   if (p_ed_wq_valid_cache_init()) {
      p_print_log(P_LOG_FATAL, "Can't initialize cache for data integrity WQ");
      p_ret = P_LKRG_GENERAL_ERROR;
      goto p_exploit_detection_init_out;
   }

   P_SYM_INIT(__kernel_text_address, int (*)(unsigned long))
   P_SYM_INIT(mm_find_pmd, pmd_t *(*)(struct mm_struct *, unsigned long))
#if defined(CONFIG_SECCOMP)
   P_SYM_INIT(get_seccomp_filter, void (*)(struct task_struct *))
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
#define p___put_seccomp_filter p_put_seccomp_filter
   P_SYM_INIT(__put_seccomp_filter, void (*)(struct seccomp_filter *))
#else
   P_SYM_INIT(put_seccomp_filter, void (*)(struct task_struct *))
#endif
#endif

#ifdef CONFIG_SECURITY_SELINUX
#if (!defined(RHEL_RELEASE_CODE) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)) || \
     (defined(RHEL_RELEASE_CODE) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 3))
   P_SYM_INIT(selinux_enabled, int *)
#endif
   // SELinux information
#ifdef P_SELINUX_VERIFY
   if (p_selinux_state_init()) {
      p_print_log(P_LOG_FATAL, "Can't initialize SELinux");
      p_ret = P_LKRG_GENERAL_ERROR;
      goto p_exploit_detection_init_out;
   }
#elif defined(CONFIG_GCC_PLUGIN_RANDSTRUCT)
   p_print_log(P_LOG_ISSUE, "Can't enforce SELinux validation (CONFIG_GCC_PLUGIN_RANDSTRUCT detected)");
#endif
#if (!defined(RHEL_RELEASE_CODE) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)) || \
     (defined(RHEL_RELEASE_CODE) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 3))
   p_ed_guard_globals.p_selinux.p_selinux_enabled = *P_SYM(p_selinux_enabled);
#endif
   p_lkrg_counter_lock_init(&p_ed_guard_globals.p_selinux_lock);
#endif

   if (p_init_rb_ed_pids()) {
      p_print_log(P_LOG_FATAL, "Can't initialize PIDs cache and RB tree");
      p_ret = P_LKRG_GENERAL_ERROR;
      goto p_exploit_detection_init_out;
   }

   // Dump processes and threads
   p_iterate_processes(p_dump_task_f,0x0);

   p_ret = P_LKRG_SUCCESS;

   for (p_fh_it = p_functions_hooks_array; p_fh_it->name != NULL; p_fh_it++) {
      if (p_fh_it->install(p_fh_it->is_isra_safe)) {
         if (!p_fh_it->p_fatal) {
             p_print_log(P_LOG_ISSUE, "%s", p_fh_it->p_error_message);
             continue;
         }
         p_print_log(P_LOG_FATAL, "Can't hook %s", p_fh_it->name);
         p_ret = P_LKRG_GENERAL_ERROR;
         p_exploit_detection_exit();
         break;
      }
   }

p_exploit_detection_init_out:

   return p_ret;

p_sym_error:
   return P_LKRG_GENERAL_ERROR;
}


void p_exploit_detection_exit(void) {

   const struct p_functions_hooks *p_fh_it;

#if !defined(P_LKRG_DEBUG_BUILD)
   lockdep_off();
#endif

   for (p_fh_it = p_functions_hooks_array; p_fh_it->name != NULL; p_fh_it++) {
      p_fh_it->uninstall();
   }

   mb();

   /* Delete cache for ED wq validation */
   p_ed_wq_valid_cache_delete();
   /* Delete cache for ED CFI validation */
   p_ed_pcfi_cache_delete();
   /* Before deleting cache i should clean each entry! */
   p_delete_rb_ed_pids();

#if !defined(P_LKRG_DEBUG_BUILD)
   lockdep_on();
#endif

   p_print_log(P_LOG_WATCH, "kmem_cache 'p_ed_pids' destroyed");
}
