427 lines
13 KiB
C
427 lines
13 KiB
C
#include "monitor_kernel.h"
|
|
|
|
unsigned char w_arg2k_w_arg(void *ptr, watch_arg warg,
|
|
kernel_watch_arg *k_watch_arg) {
|
|
// k_watch_arg init
|
|
k_watch_arg->task_id = warg.task_id;
|
|
strncpy(k_watch_arg->name, warg.name, MAX_NAME_LEN + 1); // name
|
|
k_watch_arg->name[MAX_NAME_LEN + 1] = '\0'; // just in case
|
|
k_watch_arg->kptr = ptr;
|
|
k_watch_arg->length_byte = warg.length_byte;
|
|
k_watch_arg->threshold = warg.threshold;
|
|
k_watch_arg->unsigned_flag = warg.unsigned_flag;
|
|
k_watch_arg->greater_flag = warg.greater_flag;
|
|
return 0;
|
|
}
|
|
|
|
/// @brief get a valuable timer
|
|
/// @param time_ns
|
|
/// @return kernel_watch_timer *, NULL means fail
|
|
kernel_watch_timer *get_timer(unsigned long long time_ns) {
|
|
int i = 0;
|
|
kernel_watch_timer *timer = NULL;
|
|
// chose a timer
|
|
for (i = 0; i < kernel_wtimer_num; i++) {
|
|
timer = &kernel_wtimer_list[i];
|
|
|
|
if (TIMER_EMPTY(timer)) {
|
|
break;
|
|
}
|
|
if ((timer->time_ns == time_ns) && (!TIMER_FILLED(timer))) {
|
|
break;
|
|
}
|
|
}
|
|
// if all timer is full
|
|
if (i >= MAX_TIMER_NUM) {
|
|
printk(KERN_ERR "No timer available\n");
|
|
return NULL;
|
|
}
|
|
// if a new timer, init it
|
|
if (i > kernel_wtimer_num - 1) {
|
|
printk(KERN_INFO "New timer\n");
|
|
|
|
kernel_wtimer_list[i].time_ns = time_ns;
|
|
kernel_wtimer_list[i].sentinel = 0;
|
|
|
|
kernel_wtimer_list[i].kt = ktime_set(0, (unsigned long)time_ns); // ns
|
|
// CLOCK_MONOTONIC: time since boot | HRTIMER_MODE_REL : relative time
|
|
hrtimer_init(&(kernel_wtimer_list[i].hr_timer), CLOCK_MONOTONIC,
|
|
HRTIMER_MODE_REL);
|
|
kernel_wtimer_list[i].hr_timer.function =
|
|
check_variable_cb; // callback function
|
|
|
|
kernel_wtimer_num = i + 1;
|
|
}
|
|
printk(KERN_INFO "now, we have %d timers\n", kernel_wtimer_num);
|
|
return &kernel_wtimer_list[i];
|
|
}
|
|
|
|
/// @brief hrTimer add watch
|
|
/// @param timer
|
|
/// @param k_watch_arg
|
|
/// @return 0 is success
|
|
unsigned char timer_add_watch(kernel_watch_timer *timer,
|
|
kernel_watch_arg k_watch_arg) {
|
|
if (TIMER_FILLED(timer)) {
|
|
printk(KERN_ERR "Timer is full\n");
|
|
return -1;
|
|
}
|
|
memcpy(&timer->k_watch_args[timer->sentinel], &k_watch_arg,
|
|
sizeof(k_watch_arg));
|
|
// timer->k_watch_args[timer->sentinel] = k_watch_arg;
|
|
timer->sentinel++;
|
|
return 0;
|
|
}
|
|
|
|
unsigned char timer_del_watch_by_pid(kernel_watch_timer *timer, pid_t pid) {
|
|
int i = 0;
|
|
for (i = 0; i < timer->sentinel; i++) {
|
|
// if pid match, delete it and move the last one to this position, check
|
|
// again
|
|
if (timer->k_watch_args[i].task_id == pid) {
|
|
if (i != timer->sentinel - 1) {
|
|
memcpy(&timer->k_watch_args[i],
|
|
&timer->k_watch_args[timer->sentinel - 1],
|
|
sizeof(kernel_watch_arg));
|
|
}
|
|
timer->sentinel--;
|
|
i--;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// @brief transfer user space address to kernel space address
|
|
/// change static global "kaddr" and "page" value
|
|
/// @param pid: process id
|
|
/// @param kaddr: user space address
|
|
/// @return kernel space address + offset
|
|
void *convert_user_space_ptr(pid_t pid, unsigned long addr) {
|
|
struct task_struct *task;
|
|
struct mm_struct *mm;
|
|
int ret;
|
|
|
|
// unsigned long aligned_addr = 0;
|
|
// unsigned long offset = 0;
|
|
|
|
watch_local_memory *node;
|
|
|
|
// if (addr < TASK_SIZE || addr > -PAGE_SIZE)
|
|
// {
|
|
// printk(KERN_ERR "Invalid address\n");
|
|
// return NULL;
|
|
// }
|
|
|
|
// for get_user_pages_remote
|
|
unsigned long aligned_addr = addr & PAGE_MASK;
|
|
unsigned long offset = addr & ~PAGE_MASK;
|
|
|
|
printk(KERN_INFO "%s\n", __FUNCTION__);
|
|
|
|
node = kmalloc(sizeof(watch_local_memory), GFP_KERNEL);
|
|
node->task_id = pid;
|
|
|
|
// Find the task with pid
|
|
rcu_read_lock();
|
|
task = pid_task(find_vpid(pid), PIDTYPE_PID);
|
|
rcu_read_unlock();
|
|
|
|
if (!task) {
|
|
printk(KERN_ERR "Cannot find task for PID %d\n", pid);
|
|
kfree(node); // careful there is kfree
|
|
return NULL;
|
|
}
|
|
// Get memory descriptor
|
|
mm = get_task_mm(task);
|
|
if (!mm) {
|
|
printk(KERN_ERR "Cannot get memory descriptor\n");
|
|
kfree(node); // careful there is kfree
|
|
return NULL;
|
|
}
|
|
down_read(&task->mm->mmap_lock);
|
|
ret = get_user_pages_remote(task->mm, aligned_addr, 1, FOLL_FORCE,
|
|
&(node->page), NULL, NULL);
|
|
up_read(&task->mm->mmap_lock);
|
|
|
|
if (ret != 1) {
|
|
printk(KERN_ERR "Cannot get user page\n");
|
|
kfree(node); // careful there is kfree
|
|
return NULL;
|
|
}
|
|
// Map the page to kernel space
|
|
node->kaddr = kmap(node->page);
|
|
list_add_tail(&node->entry, &watch_local_memory_list); // add to list
|
|
// printk(KERN_INFO "node->kaddr: %p, aligned_addr: %ld, offset: %ld\n",
|
|
// node->kaddr, aligned_addr, offset);
|
|
return (void *)((unsigned long)(node->kaddr) + offset);
|
|
}
|
|
|
|
/// @brief free page in watch_local_memory_list with task_id
|
|
/// @param task_id
|
|
void free_page_list(pid_t task_id) {
|
|
watch_local_memory *node, *next;
|
|
list_for_each_entry_safe(node, next, &watch_local_memory_list, entry) {
|
|
if (node == NULL)
|
|
break;
|
|
if (node->task_id == task_id) {
|
|
// unmap and release the page
|
|
if (node->kaddr)
|
|
kunmap(node->kaddr);
|
|
if (node->page)
|
|
put_page(node->page);
|
|
list_del(&node->entry);
|
|
kfree(node); // careful there is kfree
|
|
}
|
|
}
|
|
}
|
|
|
|
/// @brief free all page in watch_local_memory_list
|
|
/// @param
|
|
void free_all_page_list(void) {
|
|
watch_local_memory *node, *next;
|
|
list_for_each_entry_safe(node, next, &watch_local_memory_list, entry) {
|
|
if (node == NULL)
|
|
break;
|
|
// unmap and release the page
|
|
if (node->kaddr)
|
|
kunmap(node->kaddr);
|
|
if (node->page)
|
|
put_page(node->page);
|
|
list_del(&node->entry);
|
|
kfree(node); // careful there is kfree
|
|
}
|
|
}
|
|
|
|
/// @brief hrTimer handler
|
|
enum hrtimer_restart check_variable_cb(struct hrtimer *timer) {
|
|
kernel_watch_timer *k_watch_timer =
|
|
container_of(timer, kernel_watch_timer, hr_timer);
|
|
int i = 0, j = 0;
|
|
int buffer[TIMER_MAX_WATCH_NUM]; // Buffer to store the messages
|
|
|
|
// check all watched kernel_watch_arg
|
|
for (i = 0; i < k_watch_timer->sentinel; i++) {
|
|
if (read_and_compare(&k_watch_timer->k_watch_args[i])) {
|
|
// snprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer), "
|
|
// name: %s, threshold: %lld, pid: %d\n",
|
|
// k_watch_timer->k_watch_args[i].name,
|
|
// k_watch_timer->k_watch_args[i].threshold,
|
|
// k_watch_timer->k_watch_args[i].task_id);
|
|
buffer[j] = i;
|
|
j++;
|
|
|
|
// printk(KERN_INFO "j: name %s, threshold: %lld\n",
|
|
// k_watch_timer->k_watch_args[i].name,
|
|
// k_watch_timer->k_watch_args[i].threshold);
|
|
// printk(KERN_INFO "j: %d\n", j);
|
|
}
|
|
}
|
|
if (j > 0) // if any threshold reached
|
|
{
|
|
printk("-------------------------------------\n");
|
|
printk("-------------watch monitor-----------\n");
|
|
printk("Threshold reached:\n");
|
|
|
|
for (i = 0; i < j; i++) {
|
|
printk(" name: %s, threshold: %lld, pid: %d\n",
|
|
k_watch_timer->k_watch_args[buffer[i]].name, //! todo
|
|
k_watch_timer->k_watch_args[buffer[i]].threshold,
|
|
k_watch_timer->k_watch_args[buffer[i]].task_id);
|
|
}
|
|
print_task_stack();
|
|
// restart timer after 1s
|
|
hrtimer_forward(timer, timer->base->get_time(), ktime_set(1, 0)); //! todo
|
|
printk("-------------------------------------\n");
|
|
} else {
|
|
// keep frequency
|
|
hrtimer_forward(timer, timer->base->get_time(), k_watch_timer->kt);
|
|
}
|
|
return HRTIMER_RESTART; // restart timer
|
|
}
|
|
|
|
/// @brief start hrTimer
|
|
/// @param timeout: timeout in us
|
|
/// @return 0 is success
|
|
// int start_hrTimer(unsigned long timeout)
|
|
// {
|
|
// printk("HrTimer Start\n");
|
|
|
|
// kt = ktime_set(0, (unsigned long)timeout); // us -> ns
|
|
// // CLOCK_MONOTONIC: time since boot | HRTIMER_MODE_REL : relative time
|
|
// hrtimer_init(&hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
// hr_timer.function = check_variable_cb;
|
|
// // mode the same as hrtimer_init
|
|
// hrtimer_start(&hr_timer, kt, HRTIMER_MODE_REL);
|
|
// return 0;
|
|
// }
|
|
|
|
/// @brief start all hrTimer
|
|
/// @param
|
|
void start_all_hrTimer(void) {
|
|
int i = 0;
|
|
kernel_watch_timer *timer = NULL;
|
|
for (i = 0; i < kernel_wtimer_num; i++) {
|
|
timer = &(kernel_wtimer_list[i]);
|
|
TIMER_START(timer);
|
|
}
|
|
printk("HrTimer start,module keep %d hrtimer for now\n", kernel_wtimer_num);
|
|
}
|
|
|
|
/// @brief cancel hrTimer
|
|
/// @param
|
|
void cancel_all_hrTimer(void) {
|
|
int i = 0;
|
|
kernel_watch_timer *timer = NULL;
|
|
for (i = 0; i < kernel_wtimer_num; i++) {
|
|
timer = &(kernel_wtimer_list[i]);
|
|
TIMER_CANCEL(timer);
|
|
}
|
|
|
|
printk("HrTimer cancel,module keep %d hrtimer for now\n", kernel_wtimer_num);
|
|
}
|
|
|
|
// for read_and_compare
|
|
typedef unsigned char (*compare_func)(void *, long long);
|
|
|
|
unsigned char compare_1_byte_signed(void *ptr, long long threshold) {
|
|
// printk("compare_1_byte_signed: value %d, biss: %lld\n", *(char *)ptr,
|
|
// threshold);
|
|
return *(char *)ptr > threshold;
|
|
}
|
|
unsigned char compare_1_byte_unsigned(void *ptr, long long threshold) {
|
|
// printk("compare_1_byte_unsigned: value %d, biss: %lld\n", *(unsigned char
|
|
// *)ptr, threshold);
|
|
return *(unsigned char *)ptr > threshold;
|
|
}
|
|
unsigned char compare_2_byte_signed(void *ptr, long long threshold) {
|
|
// printk("compare_2_byte_signed: value %d, biss: %lld\n", *(short int *)ptr,
|
|
// threshold);
|
|
return *(short int *)ptr > threshold;
|
|
}
|
|
unsigned char compare_2_byte_unsigned(void *ptr, long long threshold) {
|
|
// printk("compare_2_byte_unsigned: value %d, biss: %lld\n", *(unsigned short
|
|
// int *)ptr, threshold);
|
|
return *(unsigned short int *)ptr > threshold;
|
|
}
|
|
unsigned char compare_4_byte_signed(void *ptr, long long threshold) {
|
|
// printk("compare_4_byte_signed: value %d, biss: %lld\n", *(int *)ptr,
|
|
// threshold);
|
|
return *(int *)ptr > threshold;
|
|
}
|
|
unsigned char compare_4_byte_unsigned(void *ptr, long long threshold) {
|
|
// printk("compare_4_byte_unsigned: value %d, biss: %lld\n", *(unsigned int
|
|
// *)ptr, threshold);
|
|
return *(unsigned int *)ptr > threshold;
|
|
}
|
|
unsigned char compare_8_byte_signed(void *ptr, long long threshold) {
|
|
// printk("compare_8_byte_signed: value %lld, biss: %lld\n", *(long long
|
|
// *)ptr, threshold);
|
|
return *(long long *)ptr > threshold;
|
|
}
|
|
unsigned char compare_8_byte_unsigned(void *ptr, long long threshold) {
|
|
// printk("compare_8_byte_unsigned: value %lld, biss: %lld\n", *(unsigned long
|
|
// long *)ptr, threshold);
|
|
return *(unsigned long long *)ptr > threshold;
|
|
}
|
|
// list of compare functions
|
|
static compare_func compare_funcs[8] = {
|
|
compare_1_byte_signed, compare_2_byte_signed, compare_4_byte_signed,
|
|
compare_8_byte_signed, compare_1_byte_unsigned, compare_2_byte_unsigned,
|
|
compare_4_byte_unsigned, compare_8_byte_unsigned};
|
|
|
|
static int func_indices[2][9] = {{0, 0, 1, 0, 2, 0, 0, 0, 3},
|
|
{0, 4, 5, 0, 6, 0, 0, 0, 7}};
|
|
|
|
/// @brief read k_arg->kptr and compare with threshold
|
|
/// @param k_arg
|
|
/// @return result of compare
|
|
unsigned char read_and_compare(kernel_watch_arg *k_arg) {
|
|
void *ptr = k_arg->kptr;
|
|
int len = k_arg->length_byte;
|
|
unsigned char is_unsigned = k_arg->unsigned_flag;
|
|
long long threshold = k_arg->threshold;
|
|
|
|
unsigned char result = 0;
|
|
|
|
// if (len != 1 && len != 2 && len != 4 && len != 8)
|
|
// {
|
|
// printk(KERN_ERR "Invalid length\n");
|
|
// return 0;
|
|
// }
|
|
|
|
result = compare_funcs[func_indices[is_unsigned][len]](ptr, threshold);
|
|
|
|
// printk(KERN_INFO "read_and_compare: name %s, value %d, biss: %lld, result:
|
|
// %d \n", k_arg->name, *(int *)ptr,
|
|
// threshold, result);
|
|
|
|
if (k_arg->greater_flag)
|
|
return result;
|
|
else
|
|
return !result;
|
|
}
|
|
|
|
/// @brief init kallsyms_lookup_name
|
|
/// @param
|
|
/// @return 0 is success
|
|
int fn_kallsyms_lookup_name_init(void) {
|
|
register_kprobe(&kprobe_kallsyms_lookup_name);
|
|
diag_kallsyms_lookup_name = (void *)kprobe_kallsyms_lookup_name.addr;
|
|
unregister_kprobe(&kprobe_kallsyms_lookup_name);
|
|
|
|
printk("xby-debug, diag_kallsyms_lookup_name is %p\n",
|
|
diag_kallsyms_lookup_name);
|
|
|
|
if (!diag_kallsyms_lookup_name) {
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned char del_all_kwarg_by_pid(pid_t pid) {
|
|
int i = 0;
|
|
kernel_watch_timer *timer = NULL;
|
|
|
|
printk(KERN_INFO "del kwarg...");
|
|
|
|
for (i = 0; i < kernel_wtimer_num; i++) {
|
|
timer = &(kernel_wtimer_list[i]);
|
|
timer_del_watch_by_pid(timer, pid);
|
|
}
|
|
for (i = 0; i < kernel_wtimer_num; i++) {
|
|
timer = &(kernel_wtimer_list[i]);
|
|
if (TIMER_NO_KWARG(timer)) // no available kwarg
|
|
{
|
|
if (i != kernel_wtimer_num - 1) {
|
|
memcpy(timer, &kernel_wtimer_list[kernel_wtimer_num - 1],
|
|
sizeof(kernel_watch_timer));
|
|
}
|
|
kernel_wtimer_num--;
|
|
i--;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// @brief clear watch with pid
|
|
/// @param pid
|
|
void clear_watch(pid_t pid) {
|
|
printk(KERN_INFO "clear pid %d 's watch variable\n", pid);
|
|
cancel_all_hrTimer(); // just in case
|
|
del_all_kwarg_by_pid(pid); // delete all kwarg with pid
|
|
free_page_list(pid); // free page with pid
|
|
start_all_hrTimer(); // restart timer
|
|
}
|
|
|
|
/// @brief clear all watch and reset kernel_wtimer_list/kernel_wtimer_num
|
|
/// @param
|
|
void clear_all_watch(void) {
|
|
printk(KERN_INFO "clear all watch variable\n");
|
|
// unmap and release the page
|
|
free_all_page_list();
|
|
// cancel timer
|
|
cancel_all_hrTimer();
|
|
// clear timer
|
|
kernel_wtimer_num = 0;
|
|
memset(kernel_wtimer_list, 0, sizeof(kernel_wtimer_list));
|
|
} |