Hello out there, I have an issue with a linked list which holds pointers to memory pages for a kprobe. The linked list get initialised but I cannot use it within the kprobe.

I use main or head pointer to hold everything in one place:

struct kprobe_head {
        struct list_head        *p_mem_cache;   
        struct kprobe_fsops     *pfops;
        struct kprobe           *pProbe;
};

and the pointer to access everything:

static struct kprobe_head *pHead;

then I have the structure which this list is made of:

struct kprobe_mem_cache {

        void                    *p_page;
        __u32                   size;
        __u8                    dirty;
        struct list_head        list_element;
};


the list looks like:
                    ____________________   _________________
| |<-| |<-|
pHead->p_mem_cache->| kprobe_mem_cache |->| krobe_mem_cache |->| ...
                    --------------------  -------------------

the p_page pointer is the pointer to the alloacted page. Therefore each element of that linked list holds a pointer to allocated memory segment.

in the __init function I do, among other things, allocate some memory for that structure, thought a static struct would be also sufficient.

/* create the head storage record */
pHead = (struct kprobe_head*) kzalloc(sizeof(struct kprobe_head), GFP_KERNEL);
        if(pHead == NULL)
                return -ENOMEM;
        
then I create the linked list with this function:

static __u8 _kprobe_setup_cache_elements( struct kprobe_head *pHead, __u16 elements, __u32 size)
{
        unsigned int    count   = 0;
        __u8            rc      = 0;
        struct list_head local_head;

        INIT_LIST_HEAD(&local_head);

        if(pHead != NULL) {

                pr_err("create list with %p\n", pHead);
                //INIT_LIST_HEAD(pHead->p_mem_cache);

                for(count=0; count<elements; count++) {
struct kprobe_mem_cache *new = kzalloc(sizeof(struct kprobe_mem_cache), GFP_KERNEL);
                        if(NULL == new) {
                                pr_err("error: kzalloc issue");
                                return -ENOMEM;
                        }

                        /* allocate memory for one page */
                        new->p_page = vmalloc(size);
                        if(NULL == new->p_page) {
                                pr_err("error: vmalloc issue");
                                return -ENOMEM;
                        }
                        else {
pr_err("List Element %d added Size: %u addr: %p \n", count, size, new->p_page);
                        }

                        new->size    = size;
                        new->dirty   = 0;

                        list_add_tail(&new->list_element, &local_head);
                }

                pHead->p_mem_cache = &local_head;
        }
        return rc;
}

right after that, still in the __init function I traverse trough this linked list, just to see if its work.

if(pHead->p_mem_cache != NULL)
        {
                pr_err("within loop %p  %p\n", pHead, pHead->p_mem_cache);
                list_for_each(local_head, pHead->p_mem_cache) {
local_page = list_entry(local_head, struct kprobe_mem_cache, list_element);
                        if(local_page->size)
                                pr_err("address: %u \n", local_page->size );
                }
        }


And here it works.

The problem now is that this is a kprobe kernel module and I defined a kprobe function as pre_handler function called submit_bio_pre which is evoked whenever the block layer function submit_bio is called by the kernel. Within that function however the access to that linked list failed

int submit_bio_pre(struct kprobe *p_submit_bio, struct pt_regs *regs)
{
        int rc                          = 0;
        struct bio *bio                 = NULL;
        static unsigned int  len        = 0;
        static unsigned int counter;
        struct kprobe_mem_cache  *tmp  = NULL;
        struct kprobe_mem_cache *local_page = NULL;
        struct list_head *local_head = NULL;
        
        bio = (struct bio*) regs_get_kernel_argument(regs, 0);

        if(pHead != NULL)
                pr_err("#### pHead is initialized %p #####\n", pHead);

        if(pHead->p_mem_cache != NULL)
pr_err("#### pHead->p_mem_cache is initialized %p #######\n", pHead->p_mem_cache);

        if(pHead->p_mem_cache != NULL && counter == 0)
        {
                spin_lock(&sl);
                pr_err("within loop submit_bio_pre \n");
                list_for_each(local_head, pHead->p_mem_cache) {
local_page = list_entry(local_head, struct kprobe_mem_cache, list_element);
                        if(local_page->size)
                                pr_err("address bio: %u \n", local_page->size );
                }
                spin_unlock(&sl);
                counter++;
        }

As it seems is the head pointer valid and has the same address as in the init function. Also the head pointer to the linked list is a valid one. But traversing through the linked list is not possible any more. As you can see I added a spin_lock to that kprobe function, albeit I only read from the linked list. This function does something completely different normally anyway.

When I load this module, the initialisation of the linked list works, also the following walk through the list, but within the kprobe function sometimes I can see the first and second pr_err. But after that the kernel breaks:

[ 191.460196] Unable to handle kernel paging request at virtual address 000323bfa8c17bf5

[ 191.669571] Call trace: [ 191.672021] submit_bio_pre+0xcc/0x150 [ 191.676641] kprobe_breakpoint_handler+0x100/0x190 [ 191.681445] call_break_hook+0x68/0x80
[  191.685201]  brk_handler+0x1c/0x60

So can anybody tell me, what is the reason that the linked list doesn't work within the kprobe?

Thank you in advance

BR Christian


_______________________________________________
Kernelnewbies mailing list
Kernelnewbies@kernelnewbies.org
https://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies

Reply via email to