Computer Science Operating Systems

Modifying The Linux Kernel – New Syscalls

In this article, we will learn how to modify the linux kernel, add our own unique system calls and finally build the kernel with our added functionality.


Before we get to modify the linux kernel we will have to download it somehow.
I am going to specify the steps I took, so make sure you follow precisely to gurantee the same results.

  • Download a virtual machine software such as Vimware \ VirtualBox
  • Download a Ubuntu 18.04 image http://releases.ubuntu.com/18.04/
  • Set up your virtual OS from the virtual machine software using the image you downloaded

Once Ubuntu has loaded, open the terminal and follow along

  • Installing prerequisites
>> sudo sed -i "s/# deb-src/deb-src/g" /etc/apt/sources.list
>> sudo apt update -y
>> sudo apt install -y build-essential libncurses-dev bison flex
>> sudo apt install -y libssl-dev libelf-dev
  • Download Linux source code
>> cd ~
>> apt source linux
  • Changing permissions and renaming the folder
>> sudo chown -R student:student ~/linux-4.15.0/
>> mv ~/linux-4.15.0 ~/linux-4.15.18-custom
  • Configuring the kernel building process
>> cd ~/linux-4.15.18-custom
>> cp -f /boot/config-$(uname -r) .config
>> geany .config
# search the CONFIG_LOCALVERSION parameter and set it to "-custom"
>> yes '' | make localmodconfig
>> yes '' | make oldconfig
  • Compiling the kernel
>> make -j $(nproc)
  • Install kernel modules and image
>> sudo make modules_install
>> sudo make install
  • Configuring GRUB
>> sudo geany /etc/default/grub

Once the file is opened do the following

  • Set ‘GRUB_DEFAULT’ to “Ubuntu, with Linux4.15.18-custom”
  • Set ‘GRUB_TIMEOUT_STYLE to menu
  • Set ‘GRUB_TIMEOUT’ to 5
  • Add the line: “GRUB_DISABLE_SUBMENUE=y” at the end of the

To finish off, we will have to generate the GRUB config file and reboot with

>> sudo update-grub
>> sudo reboot

Once the OS booted, make sure that you loaded the custom kernel

>> uname -r

Should result with “4.15.18-custom”

That’s it, we have done with the prerequisites, it’s time to code.


The functionality we are about to add is called process weights.
As the name suggests, we will assign every process a weight that will represent how “heavy” it is.

Two behaviors that we want to maintain are:

  • When a process is forked, the child process will have the same weight as his father
  • The init process weight will be 0

The system calls we are about to implement will be able to

  • Set the weight of the current process
  • Get the total weight of the current process recursively

First, we will need to somehow tell each process “FYI, you have a weight now”.
In order to do so, open ~/linux-4.15.18-custom/include/linux/sched.h
And Inside the struct task_struct add an attribute of int weight

struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
/*
* For reasons of header soup (see current_thread_info()), this
* must be the first element of task_struct.
*/
struct thread_info thread_info;
#endif
/* -1 unrunnable, 0 runnable, >0 stopped: */
int weight; #line 569
volatile long state;
/*
* This begins the randomizable portion of task_struct. Only
* scheduling-critical items should be added above here.
*/
randomized_struct_fields_start

Now, we would like to tell every process what’s his initial weight, in order to do that, under the same directory as before, open init_task.h — go to the macro definition of INIT_TASK and add an initialization to the weight attribute you just added.

#define INIT_TASK(tsk) \
{ \
INIT_TASK_TI(tsk) \
.weight = 0, \ #line 228
.state = 0, \
.stack = init_stack, \
...

In the last section, we were able to tell each process that it has a new attribute called weight and that this attribute should initialize to 0 whenever a new process is created.

In this section, we will focus on setting up the ground for our new system calls.

Navigate to ~/linux-4.15.18-custom/arch/x86/entry/syscalls/ and open syscall_64.tbl 
Scroll to the bottom of the file and reserve your syscalls numbers

...
332 common statx sys_statx
333 common hello sys_hello
334 common set_weight sys_set_weight
335 common get_total_weight sys_get_total_weight
...

Now we will create our system call signature. go to syscalls.h under the same directory and scroll to the bottom of the file

...
asmlinkage long sys_pkey_free(int pkey);
asmlinkage long sys_statx(int dfd, const char __user *path, unsigned flags, unsigned mask, struct statx __user *buffer);
asmlinkage long sys_hello(void);
asmlinkage long sys_set_weight(int weight); #line 944
asmlinkage long sys_get_total_weight(void);

#endif

We have set everything up, the only thing missing is the implementation of these new syscalls.

Navigate to ~/linux-4.15.18-custom/kernel and create a new file called syscalls_weight.c .
Don’t forget to go into Makefile under the same directory and add your new file to the building process

# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the linux kernel.
#
obj-y = fork.o exec_domain.o panic.o \
cpu.o exit.o softirq.o resource.o \
sysctl.o sysctl_binary.o capability.o ptrace.o user.o \
signal.o sys.o umh.o workqueue.o pid.o task_work.o \
extable.o params.o \
kthread.o sys_ni.o nsproxy.o \
notifier.o ksysfs.o cred.o reboot.o \
async.o range.o smpboot.o ucount.o hello_syscall.o syscalls_weight.o
...

Open the file you just created syscalls_weight.c and let’s implement our new system calls.

First, include the following libraries

#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/sched.h>

And let’s start with the implementation of sys_set_weight

asmlinkage long sys_get_weight(int weight){
if(weight < 0){
return -EINVAL;
}
current->weight = weight;
return 0;
}

Few things to notice

  • current is a pointer to the current task running
  • The convention with syscalls is to return 0 if it was successful and return a negative value if some error occurred and that’s exactly what we did (considering that we don’t want to allow a negative weight).

Moving on to the next syscall implementation, we will first define another function that will help us

int traverse_children_sum_weight(struct task_struct *root_task){
struct task_struct *task;
struct list_head *list;
int sum = root_task->weight;

list_for_each(list, &root_task->children){
task = list_entry(list, struct task_struct, sibling);
sum += traverse_children_sum_weight(task, true);
}
return sum;

Then, our syscall implementation will be

asmlinkage long sys_get_total_weight(void){
return traverse_children_sum_weight(current);
}

We did it. all you have to do now is to build your kernel, restart your machine and you are free to use these brand new kernel features that you just created.

To build and restart, simply execute the following

make -j $(nproc)
sudo cp -f arch/x86/boot/bzImage /boot/vmlinuz-4.15.18-custom
sudo reboot

Can you think of some new interesting features you can add? Maybe it will be your next coding project.


In case you want to receive my new stories to your inbox, subscribe with your email

3 comments on “Modifying The Linux Kernel – New Syscalls

  1. James Pizagno

    This is really exciting. However, when I run ‘make -j $(nproc)’, I get the following error. Maybe you can advise what to do?

    CC arch/x86/kernel/apic/x2apic_uv_x.o
    arch/x86/kernel/apic/x2apic_uv_x.c: In function ‘set_block_size’:
    arch/x86/kernel/apic/x2apic_uv_x.c:429:3: error: implicit declaration of function ‘set_memory_block_size_order’; did you mean ‘memory_block_size_bytes’? [-Werror=implicit-function-declaration]
    set_memory_block_size_order(order – 1);
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~
    memory_block_size_bytes
    cc1: some warnings being treated as errors
    scripts/Makefile.build:333: recipe for target ‘arch/x86/kernel/apic/x2apic_uv_x.o’ failed
    make[3]: *** [arch/x86/kernel/apic/x2apic_uv_x.o] Error 1
    scripts/Makefile.build:607: recipe for target ‘arch/x86/kernel/apic’ failed
    make[2]: *** [arch/x86/kernel/apic] Error 2
    scripts/Makefile.build:607: recipe for target ‘arch/x86/kernel’ failed
    make[1]: *** [arch/x86/kernel] Error 2
    Makefile:1087: recipe for target ‘arch/x86’ failed
    make: *** [arch/x86] Error 2

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: