/*
 * 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());
}

static int p_register_probe(struct lkrg_probe *probe)
{
   if (probe->type == LKRG_KPROBE)
      return register_kprobe(&probe->krp.kp);
   return register_kretprobe(&probe->krp);
}

static void p_unregister_probe(struct lkrg_probe *probe)
{
   if (probe->type == LKRG_KPROBE)
      unregister_kprobe(&probe->krp.kp);
   else
      unregister_kretprobe(&probe->krp);
}

int p_install_hook(struct lkrg_probe *probe, int p_isra) {

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

   if (probe->type == LKRG_KRETPROBE)
      probe->krp.maxactive = p_get_kprobe_maxactive();
   if ( (p_ret = p_register_probe(probe)) < 0) {
      if (p_isra && p_ret == -22) {
         p_print_log(P_LOG_ISSUE, "register_k[ret]probe() 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 = probe->krp.kp.symbol_name = p_isra_arg.p_isra_name;
            if ( (p_ret = p_register_probe(probe)) < 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, "register_k[ret]probe() 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 for %s", p_name);
            probe->state = LKRG_PROBE_ISRA;
         } else {
            p_print_log(P_LOG_ISSUE, "register_k[ret]probe() for %s failed and ISRA / CONSTPROP version not found!",
                        p_isra_arg.p_name);
            return p_ret;
         }
      } else {
         p_print_log(P_LOG_ISSUE, "register_k[ret]probe() for %s failed! [err=%d]", p_name, p_ret);
         return p_ret;
      }
   } else {
      probe->state = LKRG_PROBE_ON;
   }

   p_print_log(P_LOG_WATCH, "Planted k[ret]probe %s at 0x%lx", p_name, (unsigned long)probe->krp.kp.addr);

   return p_ret;
}

void p_uninstall_hook(struct lkrg_probe *probe) {

   const char *p_name = probe->krp.kp.symbol_name;

   if (probe->state == LKRG_PROBE_OFF) {
      p_print_log(P_LOG_WATCH, "k[ret]probe for %s is NOT installed", p_name);
   } else {
      p_unregister_probe(probe);
      p_print_log(P_LOG_WATCH, "Removed k[ret]probe for %s at 0x%lx nmissed %d %lu",
                  p_name, (unsigned long)probe->krp.kp.addr, probe->krp.nmissed, probe->krp.kp.nmissed);
      if (probe->state == LKRG_PROBE_ISRA) {
         // Free ISRA name buffer
         p_print_log(P_LOG_WATCH, "Freeing ISRA / CONSTPROP buffer [0x%lx]", (unsigned long)p_name);
         kfree(p_name);
      }
      probe->state = LKRG_PROBE_OFF;
   }
}
