/*
 * pi3's Linux kernel Runtime Guard
 *
 * Component:
 *  - Generic install and uninstall functions
 *
 * Notes:
 *  - None
 *
 * Caveats:
 *  - None
 *
 * Timeline:
 *  - Created: 4.V.2020
 *
 * Author:
 *  - Mariusz Zaborski (https://oshogbo.vexillium.org/)
 *
 */

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

// to setup kretprobes maxactive
int p_get_kprobe_maxactive(void) {

/*
 * Linux default is up to max(10, 2*num_possible_cpus()) so far.
 * LKRG old default was 40.  Let's use max of these all.
 */
   return max_t(unsigned int, 40, 2*num_possible_cpus());
}

int p_install_hook(struct kretprobe *kretprobe, char *state, int p_isra) {

   int p_ret;
   const char *p_name = kretprobe->kp.symbol_name;
   struct p_isra_argument p_isra_arg;

   kretprobe->maxactive = p_get_kprobe_maxactive();
   if ( (p_ret = register_kretprobe(kretprobe)) < 0) {
      if (p_isra && p_ret == -22) {
         p_print_log(P_LOG_ISSUE, "[kretprobe] register_kretprobe() for <%s> failed! [err=%d]",
                     p_name, p_ret);
         p_print_log(P_LOG_WATCH, "Trying to find ISRA / CONSTPROP name for <%s>", p_name);
         p_isra_arg.p_name = p_name;
         p_isra_arg.p_isra_name = NULL;
         if (p_try_isra_name(&p_isra_arg)) {
            p_name = kretprobe->kp.symbol_name = p_isra_arg.p_isra_name;
            if ( (p_ret = register_kretprobe(kretprobe)) < 0) {
/*
 * We use ISSUE rather than perhaps FATAL here because p_exploit_detection.c
 * does not expose the p_fatal flag in here and some of these are non-fatal.
 */
               p_print_log(P_LOG_ISSUE, "[kretprobe] register_kretprobe() for <%s> failed! [err=%d]",
                      p_name, p_ret);
               return p_ret;
            }
            p_print_log(P_LOG_ISSUE, "ISRA / CONSTPROP version was found and hook was planted at <%s>",
                        p_name);
            (*state)++;
         } else {
            p_print_log(P_LOG_ISSUE,
                   "[kretprobe] register_kretprobe() for %s failed and ISRA / CONSTPROP version not found!",
                   p_isra_arg.p_name);
            return p_ret;
         }
      } else {
         p_print_log(P_LOG_ISSUE, "[kretprobe] register_kretprobe() for <%s> failed! [err=%d]",
                     p_name, p_ret);
         return p_ret;
      }
   }

   p_print_log(P_LOG_WATCH, "Planted [kretprobe] <%s> at: 0x%lx",
               p_name,
               (unsigned long)kretprobe->kp.addr);
   (*state)++;

   return p_ret;
}

void p_uninstall_hook(struct kretprobe *kretprobe, char *state) {

   const char *p_name = kretprobe->kp.symbol_name;

   if (!*state) {
      p_print_log(P_LOG_WATCH, "[kretprobe] <%s> at 0x%lx is NOT installed",
                  p_name,
                  (unsigned long)kretprobe->kp.addr);
   } else {
      unregister_kretprobe(kretprobe);
      p_print_log(P_LOG_WATCH, "Removing [kretprobe] <%s> at 0x%lx nmissed[%d]",
                  p_name,
                  (unsigned long)kretprobe->kp.addr,
                  kretprobe->nmissed);
      if (*state == 2) {
         // Free ISRA name buffer
         p_print_log(P_LOG_WATCH, "Freeing ISRA / CONSTPROP buffer[0x%lx]",
                     (unsigned long)kretprobe->kp.symbol_name);
         kfree(kretprobe->kp.symbol_name);
      }
      *state = 0;
   }
}
