Skip to content
Permalink
main
Switch branches/tags

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?
Go to file
 
 
Cannot retrieve contributors at this time
#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);