Search code examples
linuxlinux-kernelkernel-modulekprobe

register_kprobe() returns EINVAL without additional memory on containing struct


I've written a kernel module (a character device) that registers new KProbes whenever I write to the module.

I have a structure that contains struct kprobe. When I call register_kprobe(), it returns -EINVAL. But when I add a dummy character array to the (possibly some other data types as well), the KProbe registration succeeds.

Probe Registration

struct my_struct *container = kmalloc(sizeof(struct my_struct));
(container->probe).addr = (kprobe_opcode_t *) kallsyms_lookup_name("my_exported_fn"); /* my_exported_fn is in code section */
(container->probe).pre_handler = Pre_Handler;
(container->probe).post_handler = Post_Handler;
register_probe(&container->probe);
/* Returns -EINVAL if my_struct contains only `struct kprobe`. */

Not working:

struct my_struct {
     struct kprobe probe;
}

Working:

struct my_struct {
     char dummy[512]; /* At 512, it gets consistently registered. At 256, sometimes (maybe one out of 5 - 10 times get registered) */
     struct kprobe probe;
}

Why does it need this extra bit of memory to be present in the struct?


Solution

  • This could be unaligned memory access or not, but in this particular case (I mean your original code before the edit) I suspect that the data is not properly initialised. Namely, register_kprobe() calls kprobe_addr() function which in turn implies the following check:

    if ((symbol_name && addr) || (!symbol_name && !addr))
        goto invalid;
    ...
    invalid:
        return ERR_PTR(-EINVAL);
    

    So, if you indeed initialise addr and don't initialise symbol_name, the latter could be a garbage pointer under certain circumstances. Namely, kmalloc() doesn't zeroise allocated memory and, furthermore, depending on requested size, it may take memory object of a suitable size from a different pool (there are different pools to provide objects of different sizes), and when you artificially increase the size of the struct, kmalloc() has to allocate a larger object from a suitable pool. From this perspective, the probability is that such an object may not contain garbage by occasion (since larger chunks are requested less often).

    All in all, I suggest zeroising the memory chunk or using kzalloc().