Reader small image

You're reading from  Linux Kernel Programming - Second Edition

Product typeBook
Published inFeb 2024
PublisherPackt
ISBN-139781803232225
Edition2nd Edition
Tools
Right arrow
Author (1)
Kaiwan N. Billimoria
Kaiwan N. Billimoria
author image
Kaiwan N. Billimoria

Kaiwan N. Billimoria taught himself BASIC programming on his dad's IBM PC back in 1983. He was programming in C and Assembly on DOS until he discovered the joys of Unix, and by around 1997, Linux! Kaiwan has worked on many aspects of the Linux system programming stack, including Bash scripting, system programming in C, kernel internals, device drivers, and embedded Linux work. He has actively worked on several commercial/FOSS projects. His contributions include drivers to the mainline Linux OS and many smaller projects hosted on GitHub. His Linux passion feeds well into his passion for teaching these topics to engineers, which he has done for well over two decades now. He's also the author of Hands-On System Programming with Linux, Linux Kernel Programming (and its Part 2 book) and Linux Kernel Debugging. It doesn't hurt that he is a recreational ultrarunner too.
Read more about Kaiwan N. Billimoria

Right arrow

Writing Your First Kernel Module – Part 2

This chapter is the second half of our coverage regarding the Loadable Kernel Module (LKM) framework and how to write kernel modules using it. To get the most out of it, I expect you to complete the previous chapter and try out the code and questions/exercises there before tackling this one.

In this chapter, we will continue from the point where we left off in the previous one. Here, we cover making use of a “better” Makefile for LKMs, cross-compiling a kernel module for the ARM platform (as a typical example), what module stacking is and how to do it, and how to set up and use module parameters. Along the way, among several other things, you will learn about the kernel API/ABI stability (or rather, the lack thereof!), the key differences between writing user-space and kernel code, auto-loading a kernel module at system boot, and security concerns and how they can be addressed. We end with information on the kernel...

Technical requirements

The technical requirements – the software packages required – for this chapter are identical to what was shown in the Technical requirements section in Chapter 4, Writing Your First Kernel Module – Part 1; please refer to it. As always, you can find the source code for this chapter in this book’s GitHub repository. Clone it with the following:

git clone https://github.com/PacktPublishing/Linux-Kernel-Programming_2E 

The code displayed in the book is often just a relevant snippet. Follow along with the full source code from the repository.

A “better” Makefile template for your kernel modules

The preceding chapter introduced you to the Makefile used to generate the kernel module from the source code, to install and clean it up. However, as we briefly mentioned there, I will now introduce what is, in my opinion, a superior, so-called “better” Makefile, and explain how it’s better.

Ultimately, we all must write better and more secure code – both user- and kernel-space. The good news is that there are several tools to help improve your code’s robustness and security posture, static and dynamic analyzers being among them (as several have already been mentioned in Online Chapter, Kernel Workspace Setup, I won’t repeat them here).

I have devised a simple yet useful Makefile “template” of sorts for kernel modules that includes several targets that help you run these tools. These targets allow you to perform valuable checks and analysis very easily...

Cross-compiling a kernel module

In Chapter 3, Building the 6.x Linux Kernel from Source – Part 2, in the Kernel build for the Raspberry Pi section, we showed you how we can cross-compile the Linux kernel for a “foreign” target architecture (such as ARM[64], PowerPC, MIPS, and so on). Essentially, the same can be done for a kernel module as well; you can easily cross-compile a kernel module by setting up a cross-toolchain as well as the “special” ARCH and CROSS_COMPILE environment variables appropriately.

For example, let’s imagine we are working on an embedded Linux product; the target device on which our code will run has an AArch64 (ARM-64) CPU. Why not take an actual example: let’s cross-compile our ch5/lkm_template kernel module for the Raspberry Pi 4 Single-Board Computer (SBC)!

This is interesting. You will find that although it appears simple and straightforward, we will end up taking four iterations before we succeed...

Gathering minimal system information

At times, especially when writing a module that’s meant to be portable across various architectures (CPUs), we need to conditionally perform work based on the actual processor family we’re running upon. The kernel provides a few macros and ways to figure this out; here, we build a simple demo module (ch5/min_sysinfo/min_sysinfo.c) that, though still quite simplistic, shows a few ways to “detect” some system details (such as the CPU family, bit-width, and endian-ness). In the following code snippet, we show only the relevant function:

// ch5/min_sysinfo/min_sysinfo.c
[ ... ]
void llkd_sysinfo(void)
{
    char msg[128];
    memset(msg, 0, 128);
    my_snprintf_lkp(msg, 47, "%s(): minimal Platform Info:\nCPU: ", __func__);
    /* Strictly speaking, all this #if...#endif is considered ugly and should be
     * isolated as far as is possible */
#ifdef CONFIG_X86
#if(BITS_PER_LONG == 32)
    strncat(msg, "...

Licensing kernel modules

As is well known, the Linux kernel code base itself is licensed under the GNU GPL v2 (aka GPL-2.0; GPL stands for General Public License), and as far as most people are concerned will remain that way. As briefly mentioned before, in Chapter 4, Writing Your First Kernel Module – Part 1, licensing your kernel code is required and important. Let’s split up this short discussion on licensing into two portions: one, as it applies to inline kernel code (or the mainline kernel), and two, as it applies to writing third-party out-of-tree kernel modules (as many of us do).

Licensing of inline kernel code

We begin with the first, that is, inline kernel code. A key point with regard to licensing here: if your intention is to directly use Linux kernel code and/or contribute your code upstream into the mainline kernel (we cover more on this in an upcoming section), you must release the code under the same license that the Linux kernel is released under...

Emulating “library-like” features for kernel modules

One of the major differences between user-mode and kernel-mode programming is the complete absence of the familiar “library” concept in the latter. Libraries are essentially a collection or archive of APIs, conveniently allowing developers to meet these important goals: do not reinvent the wheel, software reuse, modularity, portability and the like. But within the Linux kernel, libraries – in the traditional sense of the word – just do not exist. Having said that, the lib/ folder within the kernel source tree contains library-like routines, several of which get built into the kernel image and are thus available to kernel/module developers at runtime.

The good news is that, broadly speaking, there are two techniques by which you can achieve “library-like” functionality in kernel space for your kernel modules:

  • The first technique is by explicitly “linking...

Passing parameters to a kernel module

A common debugging technique is to instrument your code; that is, insert prints at appropriate points such that you can follow the path the code takes. Within a kernel module, of course, we would use the versatile printk (and friends) functions for this purpose. So, let’s say we do something like the following (pseudo-code):

#define pr_fmt(fmt) "%s:%s():%d: " fmt, KBUILD_MODNAME, __func__, __LINE__
[ ... ]
func_x() { 
    pr_debug("At 1\n");
    [...]
    while (<cond>) {
        pr_debug("At 2: j=0x%x\n", j); 
        [...] 
 }
 [...]
}

Okay, great. But, hey, we don’t want the debug prints to appear in a production (or release) version. That’s precisely why we’re using pr_debug(): it emits a printk only when the DEBUG symbol is defined! Indeed, but what if, interestingly, our customer is an engineering customer and wants to dynamically turn on or turn off these debug prints...

Floating point not allowed in the kernel

Years ago, a young and relatively inexperienced lad working on a temperature sensor device driver (me) had an amusing experience (though it wasn’t quite so amusing at the time). Attempting to express a temperature value in millidegrees Celsius as a “regular” temperature value in degrees Celsius correct to three decimal places, he did something like the following:

int temperature;
double temperature_fp;
 [... processing …] 
temperature_fp = temperature / 1000.0;
printk(KERN_INFO "temperature is %.3f degrees C\n", temperature_fp);

It all went downhill from there!

The venerable LDD (Linux Device Drivers, by Corbet, Rubini, and Kroah-Hartman) book pointed out my error – floating-point (FP) arithmetic is not allowed in kernel space! It’s a conscious design decision; saving processor (FP) state, turning on the FP unit, working on it, then turning off and restoring the FP state is just...

Auto-loading modules on system boot

Until now, we have written simple “out-of-tree” kernel modules that reside in their own private directories and have to be manually loaded up, typically via the insmod or modprobe(8) utilities. In most real-world projects and products, you will require your out-of-tree kernel module(s) to be auto-loaded at boot. This section covers how you can achieve this.

Consider we have a kernel module named foo.ko. We assume we have access to its source code and Makefile. In order to have it auto-load on system boot, you need to first install the kernel module to a known location on the system. To do so, we expect that the Makefile for the module contains an install target, typically:

install:
        make -C $(KDIR) M=$(PWD) modules_install

This is not something new; we have been placing the install target within the Makefiles of our demo kernel modules. Further, as it writes into a root-owned directory, we can always modify the...

Kernel modules and security – an overview

An ironic reality is that enormous efforts spent on improving user-space security considerations have resulted in a large payoff over recent years. A malicious user performing a Buffer Overflow (BoF) attack was relatively straightforward a couple of decades back, but today it is really hard to pull off well. Why? Because there are many layers of beefed-up security mechanisms to prevent many of these attack classes.

To quickly name a few countermeasures: compiler protections (-fstack-protector[...], -Wformat-security, -D_FORTIFY_SOURCE=3), partial/full RELRO, better sanity and security checker tools (checksec.sh, address sanitizers, PaxTest, static analysis tools, and so on), secure libraries, hardware-level protection mechanisms (NX, SMEP, SMAP, and so on), [K]ASLR, better testing (fuzzing), and so on. Whew.

The irony is that kernel-space attacks have become increasingly common over the last several years! It has...

Coding style guidelines for kernel developers

Many large projects specify their own set of coding guidelines; so does the Linux kernel community. Adhering to the Linux kernel coding style guidelines is a really good idea when writing kernel and/or kernel module code. You can see the style guidelines officially documented here: https://www.kernel.org/doc/html/latest/process/coding-style.html (please do read them!).

Furthermore, as part of the (quite exhaustive) code-submission checklist(s) for developers like you wanting to upstream your code, you are expected to run your patch through a Perl script that checks your code for congruence with the Linux kernel coding style: scripts/checkpatch.pl.

By default, this script only runs on a well-formatted git patch. It’s possible to run it against standalone C code (as in your out-of-tree kernel module code), as follows (as our “better” Makefile indeed does):

<kernel-src>/scripts/checkpatch.pl --no-tree...

Contributing to the mainline kernel

In this book, we typically perform kernel development outside the kernel source tree, via the LKM framework. What if you are writing code within the kernel tree, with the explicit goal of upstreaming your code to the mainline kernel? This is a laudable goal indeed – the whole basis of open source stems from the community’s willingness to put in work and contribute upstream to the project.

Getting started with contributing to the kernel

The most frequently asked question, of course, is how do I get started? To help you precisely with this, a long and detailed answer lies within the official kernel documentation here: HOWTO do Linux kernel development: https://www.kernel.org/doc/html/latest/process/howto.html#howto-do-linux-kernel-development.

As a matter of fact, you can generate the full Linux kernel documentation (via the make pdfdocs command, in the root of the kernel source tree); once successful, you will find this...

Summary

In this chapter, the second one on writing kernel modules via the LKM framework, we covered several (remaining) areas pertaining to this important topic: among them, using a “better” Makefile for your kernel module, tips on configuring a debug kernel (it’s very important!), cross-compiling a kernel module, gathering some minimal platform information from within a kernel module, and even a bit on the licensing of kernel modules. We also looked at emulating library-like features with two different approaches (first – the typically preferred one, which is the linking approach, and second, the module-stacking approach), using module parameters, avoiding floating-point arithmetic, the auto-loading of your kernel modules at boot, and so on. Security concerns, especially regarding modules and how they can be addressed, are important and have been covered. Finally, we wrapped up this chapter by covering kernel coding style guidelines, and how you can get...

Questions

As we conclude, here is a list of questions for you to test your knowledge regarding this chapter’s material: https://github.com/PacktPublishing/Linux-Kernel-Programming_2E/blob/main/questions/ch5_qs_assignments.txt. You will find some of the questions answered in the book’s GitHub repo: https://github.com/PacktPublishing/Linux-Kernel-Programming_2E/tree/main/solutions_to_assgn.

Further reading

To aid you in delving deeper into the subject with useful materials, we provide a rather detailed list of online references and links (and at times even books) in a Further reading markdown document – organized by chapter – in this book’s GitHub repository. The Further reading document is available here: https://github.com/PacktPublishing/Linux-Kernel-Programming_2E/blob/main/Further_Reading.md.

Learn more on Discord

To join the Discord community for this book – where you can share feedback, ask questions to the author, and learn about new releases – follow the QR code below:

https://packt.link/SecNet

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Linux Kernel Programming - Second Edition
Published in: Feb 2024Publisher: PacktISBN-13: 9781803232225
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Kaiwan N. Billimoria

Kaiwan N. Billimoria taught himself BASIC programming on his dad's IBM PC back in 1983. He was programming in C and Assembly on DOS until he discovered the joys of Unix, and by around 1997, Linux! Kaiwan has worked on many aspects of the Linux system programming stack, including Bash scripting, system programming in C, kernel internals, device drivers, and embedded Linux work. He has actively worked on several commercial/FOSS projects. His contributions include drivers to the mainline Linux OS and many smaller projects hosted on GitHub. His Linux passion feeds well into his passion for teaching these topics to engineers, which he has done for well over two decades now. He's also the author of Hands-On System Programming with Linux, Linux Kernel Programming (and its Part 2 book) and Linux Kernel Debugging. It doesn't hurt that he is a recreational ultrarunner too.
Read more about Kaiwan N. Billimoria