Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
fix-lpp/fix-lpp/fix-lpp.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
130 lines (110 sloc)
3.19 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <linux/init.h> | |
#include <linux/module.h> | |
#include <linux/kernel.h> | |
#include <linux/ftrace.h> | |
#include <linux/printk.h> | |
#include <linux/kprobes.h> | |
#include <linux/fs_context.h> | |
MODULE_LICENSE("GPL"); | |
MODULE_AUTHOR("Donald Buczek <buczek@molgen.mpg.de>"); | |
MODULE_DESCRIPTION("fix-lpp"); | |
MODULE_VERSION("0.01"); | |
/* | |
* General concept to hook a kernel function with ftrace: | |
* | |
* https://www.codeproject.com/Articles/1275114/Hooking-Linux-Kernel-Functions-Part-2-How-to-Hook | |
* | |
* Idea to hack around the no longer exported kallsyms_lookup_name with register_kprobe | |
* originally from AMD developers in the comments of this thread: | |
* | |
* https://lwn.net/Articles/813350/ | |
*/ | |
/* function signature as defined in kernel source */ | |
typedef int legacy_parse_param_fn_type(struct fs_context *fc, struct fs_parameter *param); | |
static legacy_parse_param_fn_type *legacy_parse_param; | |
static legacy_parse_param_fn_type legacy_parse_param_wrapper; | |
enum legacy_fs_param { | |
LEGACY_FS_UNSET_PARAMS, | |
LEGACY_FS_MONOLITHIC_PARAMS, | |
LEGACY_FS_INDIVIDUAL_PARAMS, | |
}; | |
struct legacy_fs_context { | |
char *legacy_data; /* Data page for legacy filesystems */ | |
size_t data_size; | |
enum legacy_fs_param param_type; | |
}; | |
static __attribute__((optimize("no-optimize-sibling-calls"))) | |
int legacy_parse_param_wrapper(struct fs_context *fc, struct fs_parameter *param) | |
{ | |
struct legacy_fs_context *ctx = fc->fs_private; | |
unsigned int size = ctx->data_size; | |
size_t len = 0; | |
switch (param->type) { | |
case fs_value_is_string: | |
len = 1 + param->size; | |
fallthrough; | |
case fs_value_is_flag: | |
len += strlen(param->key); | |
break; | |
default: | |
break; | |
} | |
if (size + len + 2 > PAGE_SIZE) | |
return invalf(fc, "VFS: Legacy: Cumulative options too large"); | |
return legacy_parse_param(fc, param); | |
} | |
static void ftrace_callback(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs) | |
{ | |
if (!within_module(parent_ip, THIS_MODULE)) | |
regs->ip = (unsigned long)legacy_parse_param_wrapper; | |
} | |
static int get_symbol_via_kprobe(char *name, void **addr) | |
{ | |
struct kprobe kp; | |
int err; | |
memset(&kp, 0, sizeof(kp)); | |
kp.symbol_name = name; | |
err = register_kprobe(&kp); | |
if (err) { | |
return err; | |
} | |
*addr = kp.addr; | |
unregister_kprobe(&kp); | |
return 0; | |
} | |
static struct ftrace_ops ops = { | |
.func = ftrace_callback, | |
.flags = FTRACE_OPS_FL_SAVE_REGS|FTRACE_OPS_FL_IPMODIFY | |
}; | |
static int __init fix_lpp_init(void) | |
{ | |
int err; | |
err = get_symbol_via_kprobe("legacy_parse_param", (void **)&legacy_parse_param); | |
if (err) { | |
pr_err("fix_lpp: can't find legacy_parse_param: %d\n", err); | |
return(err); | |
} | |
err = ftrace_set_filter_ip(&ops, (unsigned long)legacy_parse_param, 0, 0); | |
if (err) { | |
pr_err("fix_lpp: ftrace_set_filter_ip failed: %d\n", err); | |
return(err); | |
} | |
err = register_ftrace_function(&ops); | |
if (err) { | |
pr_err("fix_lpp: register_ftrace_function failed: %d\n", err); | |
return(err); | |
} | |
pr_info("fix-lpp: installed\n"); | |
return 0; | |
} | |
static void __exit fix_lpp_exit(void) | |
{ | |
int err; | |
err = unregister_ftrace_function(&ops); | |
if (err) { | |
pr_err("fix_lpp: unregister_ftrace_function failed: %d\n", err); | |
} | |
pr_info("fix-lpp: removed\n"); | |
} | |
module_init(fix_lpp_init); | |
module_exit(fix_lpp_exit); |