mirror of https://github.com/openwall/lkrg.git
Add support for x86/static_call used by TRACEPOINTs since kernel 5.10+
Since kernel 5.10 tracepoints don't use JUMP_LABEL engine for .text kernel modification. Linux kernel introduced 'static_call' as a replacement for global function pointers. It uses code patching to allow direct calls to be used instead of indirect calls. Related Linux kernel commits:e6d6c071f2
1e7e478838 (diff-d7873f00dcd8c46df3e1e57b3225ff91036c83d5d7339d410b468418fc9a32a4)
Currently, only x86(-64) architecture has implementation for static calls. This commit should address #69
This commit is contained in:
parent
294c8895de
commit
cccc01dd05
1
Makefile
1
Makefile
|
@ -40,6 +40,7 @@ $(TARGET)-objs += src/modules/ksyms/p_resolve_ksym.o \
|
|||
src/modules/database/JUMP_LABEL/p_arch_jump_label_transform_apply/p_arch_jump_label_transform_apply.o \
|
||||
src/modules/database/FTRACE/p_ftrace_modify_all_code/p_ftrace_modify_all_code.o \
|
||||
src/modules/database/FTRACE/p_ftrace_enable_sysctl/p_ftrace_enable_sysctl.o \
|
||||
src/modules/database/TRACEPOINT/p_arch_static_call_transform/p_arch_static_call_transform.o \
|
||||
src/modules/database/p_database.o \
|
||||
src/modules/notifiers/p_notifiers.o \
|
||||
src/modules/self-defense/hiding/p_hiding.o \
|
||||
|
|
|
@ -42,7 +42,7 @@ static struct kretprobe p_ftrace_modify_all_code_kretprobe = {
|
|||
|
||||
/*
|
||||
* We do not need to protect this variables since ftrace_modify_all_code() is executed
|
||||
* under ftrace log. LKRG is synchronizing with it...
|
||||
* under ftrace lock. LKRG is synchronizing with it...
|
||||
*
|
||||
* ... unless I overlooked some code-path...
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* pi3's Linux kernel Runtime Guard
|
||||
*
|
||||
* Component:
|
||||
* - Tracepoints: hook 'arch_static_call_transform' function.
|
||||
*
|
||||
* Notes:
|
||||
* - Since kernel 5.10 tracepoints don't use JUMP_LABEL engine for .text
|
||||
kernel modifications.
|
||||
*
|
||||
* Caveats:
|
||||
* - None
|
||||
*
|
||||
* Timeline:
|
||||
* - Created: 22.IV.2021
|
||||
*
|
||||
* Author:
|
||||
* - Adam 'pi3' Zabrocki (http://pi3.com.pl)
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../../../p_lkrg_main.h"
|
||||
|
||||
#if defined(P_LKRG_CI_ARCH_STATIC_CALL_TRANSFORM_H)
|
||||
|
||||
char p_arch_static_call_transform_kretprobe_state = 0;
|
||||
|
||||
static struct kretprobe p_arch_static_call_transform_kretprobe = {
|
||||
.kp.symbol_name = "arch_static_call_transform",
|
||||
.handler = p_arch_static_call_transform_ret,
|
||||
.entry_handler = p_arch_static_call_transform_entry,
|
||||
.data_size = sizeof(struct p_arch_static_call_transform_data),
|
||||
/* Probe up to 40 instances concurrently. */
|
||||
.maxactive = 40,
|
||||
};
|
||||
|
||||
unsigned long p_tracepoint_tmp_text;
|
||||
struct module *p_module1;
|
||||
struct module *p_module2;
|
||||
unsigned int p_module1_idx;
|
||||
unsigned int p_module2_idx;
|
||||
|
||||
notrace int p_arch_static_call_transform_entry(struct kretprobe_instance *p_ri, struct pt_regs *p_regs) {
|
||||
|
||||
unsigned long p_site = p_regs_get_arg1(p_regs);
|
||||
unsigned long p_tramp = p_regs_get_arg2(p_regs);
|
||||
unsigned int p_tmp;
|
||||
|
||||
p_debug_kprobe_log(
|
||||
"p_arch_static_call_transform_entry: comm[%s] Pid:%d\n",current->comm,current->pid);
|
||||
|
||||
p_module1_idx = p_module2_idx = p_tracepoint_tmp_text = 0;
|
||||
p_module1 = p_module2 = NULL;
|
||||
|
||||
if (p_tramp) {
|
||||
|
||||
p_print_log(P_LKRG_INFO,
|
||||
"[TRACEPOINT] New modification: code[0x%lx]!\n",
|
||||
(unsigned long)p_tramp);
|
||||
|
||||
if (P_SYM(p_core_kernel_text)(p_tramp)) {
|
||||
|
||||
p_tracepoint_tmp_text++;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
|
||||
} else if ( (p_module1 = P_SYM(p_module_text_address)(p_tramp)) != NULL) {
|
||||
#else
|
||||
} else if ( (p_module1 = __module_text_address(p_tramp)) != NULL) {
|
||||
#endif
|
||||
for (p_tmp = 0; p_tmp < p_db.p_module_list_nr; p_tmp++) {
|
||||
if (p_db.p_module_list_array[p_tmp].p_mod == p_module1) {
|
||||
/*
|
||||
* OK, we found this module on our internal tracking list.
|
||||
*/
|
||||
p_module1_idx = p_tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
* We shouldn't be here...
|
||||
*/
|
||||
p_print_log(P_LKRG_ERR,
|
||||
"[TRACEPOINT] Not a .text section! [0x%lx]\n",p_tramp);
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_HAVE_STATIC_CALL_INLINE) && p_site) {
|
||||
|
||||
p_print_log(P_LKRG_INFO,
|
||||
"[TRACEPOINT] New modification: code[0x%lx]!\n",
|
||||
(unsigned long)p_site);
|
||||
|
||||
if (P_SYM(p_core_kernel_text)(p_site)) {
|
||||
|
||||
p_tracepoint_tmp_text++;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
|
||||
} else if ( (p_module2 = P_SYM(p_module_text_address)(p_site)) != NULL) {
|
||||
#else
|
||||
} else if ( (p_module2 = __module_text_address(p_site)) != NULL) {
|
||||
#endif
|
||||
for (p_tmp = 0; p_tmp < p_db.p_module_list_nr; p_tmp++) {
|
||||
if (p_db.p_module_list_array[p_tmp].p_mod == p_module2) {
|
||||
/*
|
||||
* OK, we found this module on our internal tracking list.
|
||||
*/
|
||||
p_module2_idx = p_tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
* We shouldn't be here...
|
||||
*/
|
||||
p_print_log(P_LKRG_ERR,
|
||||
"[TRACEPOINT] Not a .text section! [0x%lx]\n",p_site);
|
||||
}
|
||||
}
|
||||
|
||||
/* A dump_stack() here will give a stack backtrace */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
notrace int p_arch_static_call_transform_ret(struct kretprobe_instance *ri, struct pt_regs *p_regs) {
|
||||
|
||||
unsigned int p_tmp;
|
||||
unsigned char p_flag = 0;
|
||||
|
||||
if (p_tracepoint_tmp_text) {
|
||||
/*
|
||||
* We do not require to take any locks neither to copy entire .text section to temporary memory
|
||||
* since at this state it is static. Just recompute the hash.
|
||||
*/
|
||||
p_db.kernel_stext.p_hash = p_lkrg_fast_hash((unsigned char *)p_db.kernel_stext.p_addr,
|
||||
(unsigned int)p_db.kernel_stext.p_size);
|
||||
#if defined(P_LKRG_JUMP_LABEL_STEXT_DEBUG)
|
||||
memcpy(p_db.kernel_stext_copy,p_db.kernel_stext.p_addr,p_db.kernel_stext.p_size);
|
||||
p_db.kernel_stext_copy[p_db.kernel_stext.p_size] = 0;
|
||||
#endif
|
||||
|
||||
p_print_log(P_LKRG_INFO,
|
||||
"[TRACEPOINT] Updating kernel core .text section hash!\n");
|
||||
|
||||
}
|
||||
|
||||
if (p_module1) {
|
||||
|
||||
p_print_log(P_LKRG_INFO,
|
||||
"[TRACEPOINT] Updating module's core .text section hash module[%s : 0x%lx]!\n",
|
||||
p_db.p_module_list_array[p_module1_idx].p_name,
|
||||
(unsigned long)p_db.p_module_list_array[p_module1_idx].p_mod);
|
||||
|
||||
p_db.p_module_list_array[p_module1_idx].p_mod_core_text_hash =
|
||||
p_lkrg_fast_hash((unsigned char *)p_db.p_module_list_array[p_module1_idx].p_module_core,
|
||||
(unsigned int)p_db.p_module_list_array[p_module1_idx].p_core_text_size);
|
||||
|
||||
/*
|
||||
* Because we have modified individual module's hash, we need to update
|
||||
* 'global' module's list hash as well
|
||||
*/
|
||||
p_db.p_module_list_hash = p_lkrg_fast_hash((unsigned char *)p_db.p_module_list_array,
|
||||
(unsigned int)p_db.p_module_list_nr * sizeof(p_module_list_mem));
|
||||
|
||||
|
||||
/*
|
||||
* Because we update module's .text section hash we need to update KOBJs as well.
|
||||
*/
|
||||
for (p_tmp = 0; p_tmp < p_db.p_module_kobj_nr; p_tmp++) {
|
||||
if (p_db.p_module_kobj_array[p_tmp].p_mod == p_module1) {
|
||||
p_db.p_module_kobj_array[p_tmp].p_mod_core_text_hash =
|
||||
p_db.p_module_list_array[p_module1_idx].p_mod_core_text_hash;
|
||||
p_flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!p_flag) {
|
||||
p_print_log(P_LKRG_ERR,
|
||||
"[TRACEPOINT] Updated module's list hash for module[%s] but can't find the same module in KOBJs list!\n",
|
||||
p_db.p_module_list_array[p_module1_idx].p_name);
|
||||
p_print_log(P_LKRG_INFO,"module[%s : 0x%lx]!\n",
|
||||
p_db.p_module_list_array[p_module1_idx].p_name,
|
||||
(unsigned long)p_db.p_module_list_array[p_module1_idx].p_mod);
|
||||
} else {
|
||||
/*
|
||||
* Because we have modified individual module's hash, we need to update
|
||||
* 'global' module's list hash as well
|
||||
*/
|
||||
p_db.p_module_kobj_hash = p_lkrg_fast_hash((unsigned char *)p_db.p_module_kobj_array,
|
||||
(unsigned int)p_db.p_module_kobj_nr * sizeof(p_module_kobj_mem));
|
||||
}
|
||||
}
|
||||
|
||||
if (p_module2) {
|
||||
|
||||
p_print_log(P_LKRG_INFO,
|
||||
"[TRACEPOINT] Updating module's core .text section hash module[%s : 0x%lx]!\n",
|
||||
p_db.p_module_list_array[p_module2_idx].p_name,
|
||||
(unsigned long)p_db.p_module_list_array[p_module2_idx].p_mod);
|
||||
|
||||
p_db.p_module_list_array[p_module2_idx].p_mod_core_text_hash =
|
||||
p_lkrg_fast_hash((unsigned char *)p_db.p_module_list_array[p_module2_idx].p_module_core,
|
||||
(unsigned int)p_db.p_module_list_array[p_module2_idx].p_core_text_size);
|
||||
|
||||
/*
|
||||
* Because we have modified individual module's hash, we need to update
|
||||
* 'global' module's list hash as well
|
||||
*/
|
||||
p_db.p_module_list_hash = p_lkrg_fast_hash((unsigned char *)p_db.p_module_list_array,
|
||||
(unsigned int)p_db.p_module_list_nr * sizeof(p_module_list_mem));
|
||||
|
||||
|
||||
/*
|
||||
* Because we update module's .text section hash we need to update KOBJs as well.
|
||||
*/
|
||||
for (p_tmp = 0; p_tmp < p_db.p_module_kobj_nr; p_tmp++) {
|
||||
if (p_db.p_module_kobj_array[p_tmp].p_mod == p_module2) {
|
||||
p_db.p_module_kobj_array[p_tmp].p_mod_core_text_hash =
|
||||
p_db.p_module_list_array[p_module2_idx].p_mod_core_text_hash;
|
||||
p_flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!p_flag) {
|
||||
p_print_log(P_LKRG_ERR,
|
||||
"[TRACEPOINT] Updated module's list hash for module[%s] but can't find the same module in KOBJs list!\n",
|
||||
p_db.p_module_list_array[p_module2_idx].p_name);
|
||||
p_print_log(P_LKRG_INFO,"module[%s : 0x%lx]!\n",
|
||||
p_db.p_module_list_array[p_module2_idx].p_name,
|
||||
(unsigned long)p_db.p_module_list_array[p_module2_idx].p_mod);
|
||||
} else {
|
||||
/*
|
||||
* Because we have modified individual module's hash, we need to update
|
||||
* 'global' module's list hash as well
|
||||
*/
|
||||
p_db.p_module_kobj_hash = p_lkrg_fast_hash((unsigned char *)p_db.p_module_kobj_array,
|
||||
(unsigned int)p_db.p_module_kobj_nr * sizeof(p_module_kobj_mem));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int p_install_arch_static_call_transform_hook(void) {
|
||||
|
||||
int p_tmp;
|
||||
|
||||
if ( (p_tmp = register_kretprobe(&p_arch_static_call_transform_kretprobe)) != 0) {
|
||||
p_print_log(P_LKRG_ERR, "[kretprobe] register_kretprobe() for <%s> failed! [err=%d]\n",
|
||||
p_arch_static_call_transform_kretprobe.kp.symbol_name,
|
||||
p_tmp);
|
||||
return P_LKRG_GENERAL_ERROR;
|
||||
}
|
||||
p_print_log(P_LKRG_INFO, "Planted [kretprobe] <%s> at: 0x%lx\n",
|
||||
p_arch_static_call_transform_kretprobe.kp.symbol_name,
|
||||
(unsigned long)p_arch_static_call_transform_kretprobe.kp.addr);
|
||||
p_arch_static_call_transform_kretprobe_state = 1;
|
||||
|
||||
return P_LKRG_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void p_uninstall_arch_static_call_transform_hook(void) {
|
||||
|
||||
if (!p_arch_static_call_transform_kretprobe_state) {
|
||||
p_print_log(P_LKRG_INFO, "[kretprobe] <%s> at 0x%lx is NOT installed\n",
|
||||
p_arch_static_call_transform_kretprobe.kp.symbol_name,
|
||||
(unsigned long)p_arch_static_call_transform_kretprobe.kp.addr);
|
||||
} else {
|
||||
unregister_kretprobe(&p_arch_static_call_transform_kretprobe);
|
||||
p_print_log(P_LKRG_INFO, "Removing [kretprobe] <%s> at 0x%lx nmissed[%d]\n",
|
||||
p_arch_static_call_transform_kretprobe.kp.symbol_name,
|
||||
(unsigned long)p_arch_static_call_transform_kretprobe.kp.addr,
|
||||
p_arch_static_call_transform_kretprobe.nmissed);
|
||||
p_arch_static_call_transform_kretprobe_state = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* pi3's Linux kernel Runtime Guard
|
||||
*
|
||||
* Component:
|
||||
* - Tracepoints: hook 'arch_static_call_transform' function.
|
||||
*
|
||||
* Notes:
|
||||
* - Since kernel 5.10 tracepoints don't use JUMP_LABEL engine for .text
|
||||
kernel modifications.
|
||||
*
|
||||
* Caveats:
|
||||
* - None
|
||||
*
|
||||
* Timeline:
|
||||
* - Created: 22.IV.2021
|
||||
*
|
||||
* Author:
|
||||
* - Adam 'pi3' Zabrocki (http://pi3.com.pl)
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_HAVE_STATIC_CALL
|
||||
|
||||
#ifndef P_LKRG_CI_ARCH_STATIC_CALL_TRANSFORM_H
|
||||
#define P_LKRG_CI_ARCH_STATIC_CALL_TRANSFORM_H
|
||||
|
||||
/* per-instance private data */
|
||||
struct p_arch_static_call_transform_data {
|
||||
ktime_t entry_stamp;
|
||||
};
|
||||
|
||||
extern p_lkrg_counter_lock p_jl_lock;
|
||||
|
||||
int p_arch_static_call_transform_ret(struct kretprobe_instance *ri, struct pt_regs *p_regs);
|
||||
int p_arch_static_call_transform_entry(struct kretprobe_instance *p_ri, struct pt_regs *p_regs);
|
||||
int p_install_arch_static_call_transform_hook(void);
|
||||
void p_uninstall_arch_static_call_transform_hook(void);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -104,6 +104,14 @@ int p_register_arch_metadata(void) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_HAVE_STATIC_CALL)
|
||||
if (p_install_arch_static_call_transform_hook()) {
|
||||
p_print_log(P_LKRG_ERR,
|
||||
"ERROR: Can't hook 'arch_jump_label_transform' function :(\n");
|
||||
return P_LKRG_GENERAL_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
return P_LKRG_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -127,6 +135,9 @@ int p_unregister_arch_metadata(void) {
|
|||
#if defined(CONFIG_FUNCTION_TRACER)
|
||||
p_uninstall_ftrace_enable_sysctl_hook();
|
||||
#endif
|
||||
#if defined(CONFIG_HAVE_STATIC_CALL)
|
||||
p_uninstall_arch_static_call_transform_hook();
|
||||
#endif
|
||||
|
||||
return P_LKRG_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -94,6 +94,13 @@ typedef struct p_cpu_info {
|
|||
#include "FTRACE/p_ftrace_enable_sysctl/p_ftrace_enable_sysctl.h"
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_HAVE_STATIC_CALL)
|
||||
/*
|
||||
* Since kernel 5.10+ TRACEPOINTs don't use *_JUMP_LABEL engine on x86(-64)
|
||||
*/
|
||||
#include "TRACEPOINT/p_arch_static_call_transform/p_arch_static_call_transform.h"
|
||||
#endif
|
||||
|
||||
enum p_jump_label_state {
|
||||
|
||||
P_JUMP_LABEL_NONE,
|
||||
|
|
Loading…
Reference in New Issue