kprobe not working for some functions - linux

I am trying to use kprobe to track handle_pte_fault function calls in linux kernel. I can probe handle_mm_fault but when I try to probe handle_pte_dault, kprobe's handler for handle_pte_fault doesn't print anything.
Using this I figured that i can't probe a function which is inline and also maybe static. So, I changed the definition of the handle_pte_fault function as following and recompiled the kernel.
From:
static int handle_pte_fault(struct vm_fault *vmf)
to:
noinline int handle_pte_fault(struct vm_fault *vmf)
I also added following to make sure handle_pte_fault symbol exists
EXPORT_SYMBOL_GPL(handle_pte_fault);
Still I am not able to track/probe handle_pte_fault function. Any help or explanation. Does it mean kprobe will only work for some random functions?
I am using kernel v4.13.
Below is the kernel module code for the kprobe that i am using:
#include<linux/module.h>
#include<linux/version.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/kprobes.h>
static struct kprobe kp;
static const char *probed_func = "handle_pte_fault";
static unsigned int counter = 0;
int Pre_Handler(struct kprobe *p, struct pt_regs *regs){
printk("Pre_Handler: counter=%u\n",counter++);
return 0;
}
void Post_Handler(struct kprobe *p, struct pt_regs *regs, unsigned long flags){
printk("Post_Handler: counter=%u\n",counter++);
}
int myinit(void)
{
printk("module inserted\n ");
kp.pre_handler = Pre_Handler;
kp.post_handler = Post_Handler;
kp.addr = (kprobe_opcode_t *)kallsyms_lookup_name(probed_func);
register_kprobe(&kp);
return 0;
}
void myexit(void)
{
unregister_kprobe(&kp);
printk("module removed\n ");
}
module_init(myinit);
module_exit(myexit);
MODULE_AUTHOR("User1");
MODULE_DESCRIPTION("KPROBE MODULE");
MODULE_LICENSE("GPL");

Related

How could I use `kallsyms_lookup_name` function to fix `unknown character` error when loading Linux kernel module?

I'm trying to complete a hooking sample attachment in a program for my uni assignment. The task requires to get a system call sys_rt_sigaction hooked when initiating a loadable module in Linux kernel (I use Ubuntu 18.04 LTS, kernel version is 5.0.0-23-generic). So, the case I'm struggling originates from an error could not insert module <module name>: Unknown symbol in module once I started sudo insmod <my module name>.ko.
After some googling, I see clear this problem arises due to missing sys_call_table export to run inserting as smoothly as well. Following this post, I want to cope that invoking kallsyms_lookup_name call before kicking off init procedure.
There is .c-file which provides with definitions of operations accessible by module (file name is buffer.c):
#define __KERNEL__
#define MODULE
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/syscalls.h>
#include <linux/kallsyms.h>
#include <linux/unistd.h>
void * sys_call_table = (void *) kallsyms_lookup_name("sys_call_table");// some wrongness here, but what exactly?
MODULE_LICENSE("GPL");
int (*real_rt_sigaction)(const char * path); // true syscall prototype
static int __init buffer_init_module(void);
static void __exit buffer_exit_module(void);
static int device_open(struct inode *, struct file *); // driver file opening
static int device_release(struct inode *, struct file *); // return of system resource control
static ssize_t device_read(struct file *, char *, size_t, loff_t *); // reading from driver file
static ssize_t device_write(struct file *, const char *, size_t, loff_t *); // writing into driver file
#define DEVICE_NAME "buffer"
#define BUF_LEN 80
// to be called instead
int alter_rt_sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact, size_t sigsetsize) {
printk(KERN_INFO "Syscall function hooked - you've lost control of your experience");
return 0;
}
static int Major;
static int Device_Open = 0;
static int total_open = 1;
static char Buf[BUF_LEN + 1] = "Buffer is empty, add some input\n";
static char *Msg_ptr;
static int Buf_Char = 50;
static int Bytes_Read = 0;
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
static int __init buffer_init_module(void)
{
printk(KERN_INFO
"Device initializing in progress...");
Major = register_chrdev(0, DEVICE_NAME, &fops);
if(Major < 0) {
printk("Major number hasn't been assigned - Driver registration failed\n");
return Major;
}
printk(KERN_INFO "Registration success - device major number: %d\n", Major);
real_rt_sigaction=sys_call_table[__NR_rt_sigaction];
sys_call_table[__NR_rt_sigaction]=alter_rt_sigaction; // hooking implementation
return 0;
}
static void __exit buffer_exit_module(void)
{
unregister_chrdev(Major, DEVICE_NAME);
printk(KERN_INFO "Outside the module - exit successfully completed\n");
sys_call_table[__NR_rt_sigaction]=real_rt_sigaction; // original call reset
}
static int device_open(struct inode *inode, struct file *file)
{
if(Device_Open)
return -EBUSY;
Device_Open++;
printk(KERN_INFO "Device file has been accessed %d time(s)\n", total_open++);
Msg_ptr = Buf;
try_module_get(THIS_MODULE);
Bytes_Read = 0;
return 0;
}
static int device_release(struct inode * node, struct file * filep)
{
Device_Open--;
module_put(THIS_MODULE);
printk(KERN_INFO "Device file gets close\n");
return 0;
}
static ssize_t device_read(struct file * filep, char * buffer, size_t len, loff_t * offset)
{
int got_read = Bytes_Read;
if(Bytes_Read >= Buf_Char)
return 0;
while(len && (Bytes_Read < Buf_Char)) {
put_user(Msg_ptr[Bytes_Read], buffer+Bytes_Read);
len--;
Bytes_Read++;
}
return Bytes_Read-got_read;
}
static ssize_t device_write(struct file * filep, const char * buffer, size_t len, loff_t * offset)
{
Buf_Char = 0;
if(Buf_Char >= BUF_LEN) {
return 0;
}
while(len && (Buf_Char < BUF_LEN))
{
get_user(Msg_ptr[Buf_Char], buffer+Buf_Char);
len--;
Buf_Char++;
}
return Buf_Char;
}
module_init(buffer_init_module);
module_exit(buffer_exit_module);
Additively, there is code in Makefile:
obj-m += buffer.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
The painful moment here is an error message initializer element is not constant whenever I was trying to build module via sudo make in my project folder. As I follow the beginner's tutorials and need for some basic insight, it might be highly appreciable to see any help with solution or even some ideas how to handle the same problem more effectively, indeed.

How to get pgdir in xv6?

I'm trying to add a system call to xv6 that provides the translated physical address given a virtual address. Combining the following function definitions, I have written a system call. Functions used:
static pte_t *
walkpgdir(pde_t *pgdir, const void *va, int alloc) -- vm.c
char*
uva2ka(pde_t *pgdir, char *uva) --vm.c
Here is my code:
proc.c
int addr_translate(char* virtual_address)
{
int physical_address;
pde_t *pgdir,*pgtab,*pde;
//must initialise pgdir
pde = &pgdir[PDX(virtual_address)];
if(*pde & PTE_P){
pgtab = (pte_t*)P2V(PTE_ADDR(*pde));
}
else
{
cprintf("\n PTE Not Present! - Invalid Virtual address\n");
return -1;
}
cprintf("\n ----------------- \n");
cprintf(" Page Directory Entry (PDE): %d\n",*pde);
cprintf(" PTE_P : %d\n",PTE_P);
cprintf("\n ----------------- \n");
//uva2ka
pte_t *pte;
pte = &pgtab[PTX(virtual_address)];
physical_address=(char*)P2V(PTE_ADDR(*pte));
cprintf(" --PHYSICAL ADDRESS-- %d\n",physical_address);
return 0;
}
sysproc.c
char*
sys_addr_translation(char *s)
{
argstr(0, &s);
addr_translate(s);
return s;
}
addr_translation.c (From where I'm invoking my system call)
#include "types.h"
#include "stat.h"
#include "user.h"
int main(int argc,char *argv[])
{
printf(1,"\n ----ADDRESS TRANSLATION----\n");
char *virtual_addr;
virtual_addr=argv[1];
printf(1,"\n Entered virtual address: %s\n",virtual_addr);
printf(1,"\n Status from system call: %s",addr_translation(virtual_addr));
exit();
}
Basically, I'm taking the virtual address as an argument from the user and passing it to my system call. I'm retrieving the virtual address using argstr() in my system call.
The part where I'm stuck is, I'm not able to understand how to initialise pgdir (page directory) in my add_translate() function in proc.c.
For the line: physical_address=(char*)P2V(PTE_ADDR(*pte));
I think this should be V2P, because what you want is physical address, right?
:)
You can use struct proc *curproc = myproc(); and then you'll have access to the pgdir through curproc->pgdir.

Why can't kprobe probe some functions in the kernel?

I tried to probe a simple function (e.g. myfunc) which I added in the kernel as following:
I created a file (myfile.c) under ~/source/kernel/ i.e. ~/source/kernel/myfile.c
I added a simple system call mysyscall and a local function myfunc in this file.
mysyscall function calls myfunc function.
I can get the address of the function using
cat /proc/kallsyms | grep myfunc
But the kprobe handler doesn't get called when I call the myfunc.
I can probe the system call 'mysyscall'. But when I try to probe 'myfunc', the handler doesn't get called.
Can anyone please explain why this is the behavior? Thanks.
As asked by Eugene, below is the code for kprobe and, mysyscall & myfunc.
The kprobe handler doesn't get called in the following code. But if i uncomment Line B and comment A in kprobe code given below, then kprobe handler gets called.
I used kernel version 4.8.
I added ~/source/kernel/myfile.c to write mysyscall and myfunc as given below:
#include <linux/linkage.h>
#include <linux/export.h>
#include <linux/time.h>
#include <asm/uaccess.h>
#include <linux/printk.h>
#include <linux/slab.h>
extern int myfunc(int ax)
{
int x = 6;
return x;
}
asmlinkage int* sys_mysyscall(int bx){
int *retval;
int ret = 0;
printk(KERN_ALERT "Hello World!\n");
ret = myfunc(10);
retval = kmalloc(sizeof(int), GFP_KERNEL);
*retval = 55;
printk("sum: %d\n", *retval);
printk("myfunc return value: %d\n", ret);
return retval;
}
EXPORT_SYMBOL(sys_mysyscall);
kprobe module code is as below:
#include<linux/module.h>
#include<linux/version.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/kprobes.h>
//Line A
static const char *probed_func = "myfunc";
//Line B
//static const char *probed_func = "sys_mysyscall";
static unsigned int counter = 0;
int Pre_Handler(struct kprobe *p, struct pt_regs *regs){
printk("Pre_Handler: counter=%u\n",counter++);
return 0;
}
void Post_Handler(struct kprobe *p, struct pt_regs *regs, unsigned long flags){
printk("Post_Handler: counter=%u\n",counter++);
}
static struct kprobe kp;
int myinit(void)
{
int error;
printk("module inserted\n ");
kp.pre_handler = Pre_Handler;
kp.post_handler = Post_Handler;
kp.addr = (kprobe_opcode_t *)kallsyms_lookup_name(probed_func);
error = register_kprobe(&kp);
if(error)
{
pr_err("can't register_kprobe :(\n");
return error;
}
else
{
printk("probe registration successful\n");
}
return 0;
}
void myexit(void)
{
unregister_kprobe(&kp);
printk("module removed\n ");
}
module_init(myinit);
module_exit(myexit);
MODULE_AUTHOR("psin");
MODULE_DESCRIPTION("KPROBE MODULE");
MODULE_LICENSE("GPL");
I use a kernel module to call mysyscall as below:
sys_mysyscall(12);//12 is some random integer as parameter

Getting error: implicit declaration of function 'proc_create'

I am getting the "implicit declaration of function 'proc_create'" error while compiling my driver module.I want to create a entry in /proc and print the number of programs which are using the module. Can you please let me know what is wrong in here?? Here is my code.
#include<linux/module.h>
#include<linux/fs.h>
#define HELLO_MAJOR 234
static int debug_enable = 0;
static int no_of_access;
module_param(debug_enable, int, 0);
MODULE_PARM_DESC(debug_enable, "Enable module debug mode.");
struct file_operations hello_fops;
struct proc_dir_entry *proc_file_entry;
<File operation functions...>
<Incremented global_counter in the file open operation.>
static int hello1_read_proc(char *buf, char **start, off_t offset,
int count, int *eof, void *data)
{
int len=0;
len += sprintf(buf+len, no_of_access);
*eof=1;
return len;
}
static int __init hello_init(void)
{
int ret;
proc_file_entry = proc_create("examples/hello1", 0,NULL, hello1_read_proc);
if(proc_file_entry == NULL)
return -ENOMEM;
printk("\nProc file entry for hello1 has been created !!!\n");
}
static void __exit hello_exit(void)
{
printk("Hello Example Exit\n");
remove_proc_entry("exmaples/hello1", NULL);
unregister_chrdev(HELLO_MAJOR,"hello1");
}
Thanks in advance.
You also need to include <linux/proc_fs.h>

Which is the correct way to register a new net_device?

i'm trying to register a new net_device in linux...i can alloc and register it correctly and ifconfig shows it. The problem arrives when i try to put the interface up:
ifconfig my_dev up
A kernel freeze occurs...the problem is present only on x86 machines and i can't figure out the reason...on a pcc machine all works well. The code is very simple:
static struct net_device *my_dev;
static int veth_dev_init(struct net_device *dev);
static int veth_open(struct net_device *dev);
static int veth_close(struct net_device *dev);
static int veth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static struct veth_priv
{
...
};
static struct net_device_ops veth_ops =
{
.ndo_init = veth_dev_init,
.ndo_open = veth_open,
.ndo_stop = veth_close,
.ndo_do_ioctl = veth_ioctl
};
static int __init veth_init()
{
my_dev = alloc_netdev(sizeof(struct veth_priv), "my_dev", ether_setup);
if (my_dev == NULL)
return -ENOMEM;
my_dev->netdev_ops = &veth_ops;
register_netdev(my_dev);
return 0;
}
static void __exit veth_exit()
{
unregister_netdev(my_dev);
free_netdev(my_dev);
}
module_init(veth_init);
module_exit(veth_exit);
The first four functions veth_dev_init, veth_open, veth_close and veth_ioctl simply return 0.
Maybe is there a missing field in veth_ops structure?
Thank you all!
Yea, you missed one element in struct net_device_ops
Add .ndo_start_xmit also, And the function must return NETDEV_TX_OK or NETDEV_TX_BUSY.
use as follows
static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
{
return NETDEV_TX_OK;
}
And also change the open as
static int veth_open(struct net_device *dev)
{
memcpy(dev->dev_addr, "\0ABCD0", ETH_ALEN);
netif_start_queue(dev);
return 0;
}
Then in veth_ops
static struct net_device_ops veth_ops = {
.ndo_init = veth_dev_init,
.ndo_open = veth_open,
.ndo_stop = veth_close,
.ndo_start_xmit = veth_xmit,
.ndo_do_ioctl = veth_ioctl,
};
Then after inserting the module
give ifconfig my_dev 192.168.10.98 ...

Resources