clinuxlinux-kernelfutex

Kernel Module, Call get_futex_key


I am working on a Linux kernel module, and I want to call function get_futex_key. How can I do this? The eventual goal is to use kernel functions to return a list of threads that hold a given futex.

I followed this tutorial to create a "hello world" module. It uses the printk function. I understand that it is possible to use printk because it is one of the symbols visible to the kernel.

# cat /proc/kallsyms | grep printk
...
ffffffff9ebb9c32 T printk
...

Function get_futex_key is also visible in /proc/kallsyms. It is defined in futex.c, not futex.h. See this source for example.

# cat /proc/kallsyms | grep "get_futex_key"
ffffffff9e3223c0 t get_futex_key_refs.isra.11
ffffffff9e322a30 t get_futex_key

Assume that 0x20566a0 is a uaddr obtained with the use of strace.

# strace -p  117589
strace: Process 117589 attached
futex(0x20566a0, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 0, NULL, 0xffffffff^Cstrace: Process 117589 detached

I have tried the following code as fut.c.

#include <linux/module.h>    // included for all kernel modules
#include <linux/kernel.h>    // included for KERN_INFO
#include <linux/init.h>      // included for __init and __exit macros
#include <linux/futex.h>
#include <kernel/futex.c>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("FutexTestAuthor");
MODULE_DESCRIPTION("Futex Examiner");

static int __init hello_init(void)
{
    union futex_key key1 = FUTEX_KEY_INIT;
    printk(KERN_INFO "Get futex key\n");
    //futex_key key1 = FUTEX_KEY_INIT;
    get_futex_key(0x20566a0, 0, &key1, VERIFY_READ);
    printk(KERN_INFO "key1 = %p\n", &key1);
    return 0;    // Non-zero return means that the module couldn't be loaded.
}

static void __exit hello_cleanup(void)
{
    printk(KERN_INFO "Cleaning up futex module.\n");
}

module_init(hello_init);
module_exit(hello_cleanup);

The Makefile

obj-m += fut.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

Output of make

# make
make -C /lib/modules/4.15.0-204-generic/build M=/home/myusername/git/LinuxExamples/kernel/fut modules
make[1]: Entering directory '/usr/src/linux-headers-4.15.0-204-generic'
  CC [M]  /home/myusername/git/LinuxExamples/kernel/fut/fut.o
/home/myusername/git/LinuxExamples/kernel/fut/fut.c:5:10: fatal error: kernel/futex.c: No such file or directory
 #include <kernel/futex.c>
          ^~~~~~~~~~~~~~~~
compilation terminated.
scripts/Makefile.build:340: recipe for target '/home/myusername/git/LinuxExamples/kernel/fut/fut.o' failed
make[2]: *** [/home/myusername/git/LinuxExamples/kernel/fut/fut.o] Error 1
Makefile:1596: recipe for target '_module_/home/myusername/git/LinuxExamples/kernel/fut' failed
make[1]: *** [_module_/home/myusername/git/LinuxExamples/kernel/fut] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-4.15.0-204-generic'
Makefile:4: recipe for target 'all' failed
make: *** [all] Error 2

Solution

  • The get_futex_key() function is not exported by the kernel. You can notice this in two ways:

    1. Looking at kernel sources, exported functions usually have a EXPORT_SYMBOL(name) after their definition. This one doesn't.
    2. In the output of /proc/kallsyms, exported functions have an uppercase T symbol, while others have a lowercase t. This one has the latter.

    You cannot create a module that uses get_futex_key() directly as it will fail linking since the kernel does not export it. You also cannot and should never include .c files in your module, only .h (header) files. In fact, most of the times, .c files won't even be available for you to include in the first place, but only .h files will.

    With the above said, you can either look at the source code of get_futex_key() to understand how it works and re-implement part of its functionality in your module, or take a look at this other question for some hacks that will allow you to get ahold of unexported symbols from modules: Proper way of getting the address of non-exported kernel symbols in a Linux kernel module