2022-03-15 17:02:00 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Here ' s a sample kernel module showing the use of fprobe to dump a
* stack trace and selected registers when kernel_clone ( ) is called .
*
* For more information on theory of operation of kprobes , see
* Documentation / trace / kprobes . rst
*
* You will see the trace data in / var / log / messages and on the console
* whenever kernel_clone ( ) is invoked to create a new process .
*/
# define pr_fmt(fmt) "%s: " fmt, __func__
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/fprobe.h>
# include <linux/sched/debug.h>
# include <linux/slab.h>
# define BACKTRACE_DEPTH 16
# define MAX_SYMBOL_LEN 4096
2022-06-06 10:56:59 +03:00
static struct fprobe sample_probe ;
2022-06-07 19:11:02 +03:00
static unsigned long nhit ;
2022-03-15 17:02:00 +03:00
static char symbol [ MAX_SYMBOL_LEN ] = " kernel_clone " ;
module_param_string ( symbol , symbol , sizeof ( symbol ) , 0644 ) ;
2022-06-24 01:31:35 +03:00
MODULE_PARM_DESC ( symbol , " Probed symbol(s), given by comma separated symbols or a wildcard pattern. " ) ;
2022-03-15 17:02:00 +03:00
static char nosymbol [ MAX_SYMBOL_LEN ] = " " ;
module_param_string ( nosymbol , nosymbol , sizeof ( nosymbol ) , 0644 ) ;
2022-06-24 01:31:35 +03:00
MODULE_PARM_DESC ( nosymbol , " Not-probed symbols, given by a wildcard pattern. " ) ;
2022-03-15 17:02:00 +03:00
static bool stackdump = true ;
module_param ( stackdump , bool , 0644 ) ;
2022-06-24 01:31:35 +03:00
MODULE_PARM_DESC ( stackdump , " Enable stackdump. " ) ;
2022-06-07 19:11:02 +03:00
static bool use_trace = false ;
module_param ( use_trace , bool , 0644 ) ;
2022-06-24 01:31:35 +03:00
MODULE_PARM_DESC ( use_trace , " Use trace_printk instead of printk. This is only for debugging. " ) ;
2022-03-15 17:02:00 +03:00
static void show_backtrace ( void )
{
unsigned long stacks [ BACKTRACE_DEPTH ] ;
unsigned int len ;
len = stack_trace_save ( stacks , BACKTRACE_DEPTH , 2 ) ;
stack_trace_print ( stacks , len , 24 ) ;
}
2023-02-01 18:56:38 +03:00
static int sample_entry_handler ( struct fprobe * fp , unsigned long ip ,
2023-06-06 15:39:55 +03:00
unsigned long ret_ip ,
2023-02-01 18:56:38 +03:00
struct pt_regs * regs , void * data )
2022-03-15 17:02:00 +03:00
{
2022-06-07 19:11:02 +03:00
if ( use_trace )
/*
* This is just an example , no kernel code should call
* trace_printk ( ) except when actively debugging .
*/
trace_printk ( " Enter <%pS> ip = 0x%p \n " , ( void * ) ip , ( void * ) ip ) ;
else
pr_info ( " Enter <%pS> ip = 0x%p \n " , ( void * ) ip , ( void * ) ip ) ;
nhit + + ;
2022-03-15 17:02:00 +03:00
if ( stackdump )
show_backtrace ( ) ;
2023-02-01 18:56:38 +03:00
return 0 ;
2022-03-15 17:02:00 +03:00
}
2023-06-06 15:39:55 +03:00
static void sample_exit_handler ( struct fprobe * fp , unsigned long ip ,
unsigned long ret_ip , struct pt_regs * regs ,
2023-02-01 18:56:01 +03:00
void * data )
2022-03-15 17:02:00 +03:00
{
2023-06-06 15:39:55 +03:00
unsigned long rip = ret_ip ;
2022-03-15 17:02:00 +03:00
2022-06-07 19:11:02 +03:00
if ( use_trace )
/*
* This is just an example , no kernel code should call
* trace_printk ( ) except when actively debugging .
*/
trace_printk ( " Return from <%pS> ip = 0x%p to rip = 0x%p (%pS) \n " ,
( void * ) ip , ( void * ) ip , ( void * ) rip , ( void * ) rip ) ;
else
pr_info ( " Return from <%pS> ip = 0x%p to rip = 0x%p (%pS) \n " ,
( void * ) ip , ( void * ) ip , ( void * ) rip , ( void * ) rip ) ;
nhit + + ;
2022-03-15 17:02:00 +03:00
if ( stackdump )
show_backtrace ( ) ;
}
static int __init fprobe_init ( void )
{
char * p , * symbuf = NULL ;
const char * * syms ;
int ret , count , i ;
sample_probe . entry_handler = sample_entry_handler ;
sample_probe . exit_handler = sample_exit_handler ;
if ( strchr ( symbol , ' * ' ) ) {
/* filter based fprobe */
ret = register_fprobe ( & sample_probe , symbol ,
nosymbol [ 0 ] = = ' \0 ' ? NULL : nosymbol ) ;
goto out ;
} else if ( ! strchr ( symbol , ' , ' ) ) {
symbuf = symbol ;
ret = register_fprobe_syms ( & sample_probe , ( const char * * ) & symbuf , 1 ) ;
goto out ;
}
/* Comma separated symbols */
symbuf = kstrdup ( symbol , GFP_KERNEL ) ;
if ( ! symbuf )
return - ENOMEM ;
p = symbuf ;
count = 1 ;
while ( ( p = strchr ( + + p , ' , ' ) ) ! = NULL )
count + + ;
pr_info ( " %d symbols found \n " , count ) ;
syms = kcalloc ( count , sizeof ( char * ) , GFP_KERNEL ) ;
if ( ! syms ) {
kfree ( symbuf ) ;
return - ENOMEM ;
}
p = symbuf ;
for ( i = 0 ; i < count ; i + + )
syms [ i ] = strsep ( & p , " , " ) ;
ret = register_fprobe_syms ( & sample_probe , syms , count ) ;
kfree ( syms ) ;
kfree ( symbuf ) ;
out :
if ( ret < 0 )
pr_err ( " register_fprobe failed, returned %d \n " , ret ) ;
else
pr_info ( " Planted fprobe at %s \n " , symbol ) ;
return ret ;
}
static void __exit fprobe_exit ( void )
{
unregister_fprobe ( & sample_probe ) ;
2022-06-07 19:11:02 +03:00
pr_info ( " fprobe at %s unregistered. %ld times hit, %ld times missed \n " ,
symbol , nhit , sample_probe . nmissed ) ;
2022-03-15 17:02:00 +03:00
}
module_init ( fprobe_init )
module_exit ( fprobe_exit )
MODULE_LICENSE ( " GPL " ) ;