renamed module
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
module:=bsl_sys_tracer
|
||||
module:=syscall_counter
|
||||
|
||||
obj-m := $(module).o
|
||||
KVERSION := $(shell uname -r)
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
/**
|
||||
* Mit Hilfe von Kernel-Tracepoints Syscalls zählen.
|
||||
*
|
||||
* Kompilieren: make
|
||||
* Laden: sudo make install
|
||||
* Testen: cat /sys/kernel/debug/syscall_counter
|
||||
* sudo make test
|
||||
* Entladen: sudo make uninstall
|
||||
* Kernel-Log: sudo dmesg -w
|
||||
*
|
||||
* Autor: Magnus Küderli
|
||||
* Lizenz: GPL
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/tracepoint-defs.h>
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
|
||||
// === OPTIONS ===
|
||||
// alternative to spinlocks with less performance loss
|
||||
#define USE_ATOMICS
|
||||
|
||||
// header generated by ./gen_syscall_names.sh called in make
|
||||
// optional because it wasnt provided by default
|
||||
#define WANT_SYSCALL_NAMES
|
||||
// === === === ===
|
||||
|
||||
#ifdef WANT_SYSCALL_NAMES
|
||||
# include "syscall_names.h"
|
||||
# define MAX_SYSCALLS SYSCALL_MAX_ID
|
||||
#else
|
||||
# define MAX_SYSCALLS 512 // default value
|
||||
#endif
|
||||
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Magnus Küderli");
|
||||
MODULE_DESCRIPTION("Count number of syscalls per id and expose via debugfs");
|
||||
|
||||
|
||||
// === Parameter ===
|
||||
static bool enabled = true;
|
||||
|
||||
static int param_set(const char* val, const struct kernel_param *kp) {
|
||||
int val_num;
|
||||
int base = 10;
|
||||
|
||||
int err = kstrtoint(val, base, &val_num);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
if (val_num && !(val_num == 1)) {
|
||||
// invalid input
|
||||
return -EINVAL;
|
||||
}
|
||||
err = param_set_bool(val, kp);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// successfully updated parameter
|
||||
printk(KERN_INFO "%s\n", val_num ? "Counter enabled!" : "Counter disabled!");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int param_get(char* buffer, const struct kernel_param *kp) {
|
||||
// write value into buffer
|
||||
int err = param_get_bool(buffer, kp);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops param_ops = {
|
||||
.set = param_set,
|
||||
.get = param_get,
|
||||
};
|
||||
|
||||
module_param_cb(enabled, ¶m_ops, &enabled, 0644);
|
||||
MODULE_PARM_DESC(enabled, "Toggle to disable the syscall counter (default: true)");
|
||||
|
||||
// === === === === =
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef USE_ATOMICS
|
||||
unsigned long syscalls[MAX_SYSCALLS];
|
||||
spinlock_t syscalls_lock;
|
||||
|
||||
#else
|
||||
atomic64_t syscalls[MAX_SYSCALLS];
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
struct tracepoint *tp;
|
||||
|
||||
|
||||
// function to hook into tracepoint
|
||||
static void probe_sys_enter(void *data, struct pt_regs *regs, long id)
|
||||
{
|
||||
if (!enabled) {
|
||||
// we stopped counting
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
id >= MAX_SYSCALLS ||
|
||||
id < 0
|
||||
) {
|
||||
printk(KERN_INFO "Probe recieved syscall id out of bounds\n");
|
||||
// id out of bounds;
|
||||
return;
|
||||
}
|
||||
|
||||
// overflow is not realistic (~> 500 years on busy sys)
|
||||
#ifndef USE_ATOMICS
|
||||
unsigned long flags;
|
||||
|
||||
// irqsave to save flags so a multiple interrupts on the same core dont deadlock
|
||||
spin_lock_irqsave(&syscalls_lock, flags);
|
||||
syscalls[id]++;
|
||||
|
||||
spin_unlock_irqrestore(&syscalls_lock, flags);
|
||||
#else
|
||||
atomic64_inc(&syscalls[id]);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static int syscall_counter_show(struct seq_file *m, void *v)
|
||||
{
|
||||
/*
|
||||
seq_printf(m, "Systemaufruf-Zähler:\n");
|
||||
seq_printf(m, "--------------------\n");
|
||||
seq_printf(m, "Nr Anzahl Name\n");
|
||||
*/
|
||||
for (long id = 0; id < MAX_SYSCALLS; id++) {
|
||||
#ifdef WANT_SYSCALL_NAMES
|
||||
char const * const name = syscall_name(id);
|
||||
#else
|
||||
char const * const name = "unknown";
|
||||
#endif
|
||||
|
||||
#ifndef USE_ATOMICS
|
||||
spin_lock(&syscalls_lock);
|
||||
unsigned long value = syscalls[id];
|
||||
spin_unlock(&syscalls_lock);
|
||||
#else
|
||||
unsigned long value = atomic64_read(&syscalls[id]);
|
||||
#endif
|
||||
// ohne name, da Header fehlt
|
||||
seq_printf(m, "%-4ld %-8lu %s\n", id, value, name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int syscall_counter_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, syscall_counter_show, NULL);
|
||||
}
|
||||
|
||||
|
||||
static void lookup_sys_enter_tp(struct tracepoint *tp, void *tp_ptr) {
|
||||
// cast to tp pointer from void
|
||||
struct tracepoint **found = tp_ptr;
|
||||
|
||||
if (!strcmp(tp->name, "sys_enter")) {
|
||||
*found = tp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int find_sys_enter_tp(struct tracepoint **tp_ptr)
|
||||
{
|
||||
*tp_ptr = NULL; // setzt tp auf NULL
|
||||
|
||||
for_each_kernel_tracepoint(lookup_sys_enter_tp, tp_ptr);
|
||||
|
||||
if (!*tp_ptr) {
|
||||
// tp wasnt found, tp_ptr still null
|
||||
return -ENOENT; // error no entry found
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int register_tp(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = find_sys_enter_tp(&tp);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = tracepoint_probe_register_prio(
|
||||
tp,
|
||||
probe_sys_enter,
|
||||
NULL, // data (inp to probe funct)
|
||||
0 // prio (0 is highest)
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void unregister_tp(void)
|
||||
{
|
||||
tracepoint_probe_unregister(
|
||||
tp,
|
||||
probe_sys_enter,
|
||||
NULL
|
||||
);
|
||||
|
||||
tracepoint_synchronize_unregister(); // gegen use-after-free, invalide mem zugriffe nach entladen
|
||||
}
|
||||
|
||||
|
||||
// === Debug fs ===
|
||||
static struct dentry *dbg_file;
|
||||
|
||||
static const struct file_operations syscall_fops = {
|
||||
.open = syscall_counter_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int __init mod_syscall_tracepoint_init(void)
|
||||
{
|
||||
int failure;
|
||||
|
||||
#ifndef USE_ATOMICS
|
||||
memset(&syscalls, 0, sizeof(syscalls));
|
||||
spin_lock_init(&syscalls_lock);
|
||||
#else
|
||||
for (int i = 0; i < MAX_SYSCALLS; i++) {
|
||||
atomic64_set(&syscalls[i], 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
dbg_file = debugfs_create_file(
|
||||
"syscall_counter",
|
||||
0444, // octal permission numbering
|
||||
NULL, // parent dir - here its root of fs
|
||||
NULL, // optional data thingy
|
||||
&syscall_fops // file operations
|
||||
);
|
||||
|
||||
failure = register_tp();
|
||||
if (failure) {
|
||||
debugfs_remove(dbg_file);
|
||||
return failure;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "BSL sys_tracer loaded successfully!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit mod_syscall_tracepoint_exit(void)
|
||||
{
|
||||
debugfs_remove(dbg_file);
|
||||
unregister_tp();
|
||||
printk(KERN_INFO "BSL sys_tracer unloaded successfully!\n");
|
||||
}
|
||||
|
||||
module_init(mod_syscall_tracepoint_init);
|
||||
module_exit(mod_syscall_tracepoint_exit);
|
||||
Reference in New Issue
Block a user