Skip to content

Collection of Linux Kernel Modules and PoC to discover, learn and practice Linux Kernel Development

License

Notifications You must be signed in to change notification settings

tpiekarski/lkm-sandbox

Repository files navigation

Linux Kernel Module (LKM) Sandbox

Build Status

Modules / Building / Make / Testing / PoC / Rehearsal / Notes / Disclaimer / License / Books / Links

The Linux Kernel Module (LKM) Sandbox is a collection of different modules to learn, discover and experiment with the development of Linux Kernel Modules. The purpose of this repository is also to practice development within the Linux Kernel and study miscellaneous concepts before moving on to submit the very first patch to the kernel.

Most of the modules illustrate one concept and show how to use the Kernel API. A few of the modules combine more than one concept to present how concepts are working together. For example the module lkm_device is accessing a character device and storing its major number in /proc. Or the module lkm_mem is exposing memory/swap information by files in /proc as well.

I hope it can be valuable for other developers which try to approach the Linux Kernel.

No. Module Source Description
1 LKM DebugFS lkm_debugfs.c Module showing how to use the debugging filesystem debugfs
2 LKM Device lkm_device.c Module showing how to operate with character devices and storing device information in /proc
3 LKM Device Numbers lkm_device_numbers.c Illustrating statically and dynamically allocated device numbers
3 LKM Memory lkm_mem.c Module exposing memory and swap information to /proc
4 LKM Memory-based Device lkm_mev.c Driver for a memory-based character device, based to some degree on scull, developed in the book Linux Device Drivers, Chapter 3
5 LKM Parameters lkm_parameters.c Module for passing parameters from user- to kernelspace
6 LKM Pretty Printk lkm_pp.c Module for testing integration of pretty-printk
7 LKM Proc lkm_proc.c Module accessing /proc filesystem using sequential I/O
8 LKM Process lkm_process.c Accessing and printing current process information
9 LKM Sandbox lkm_sandbox.c Sandbox module for different experiments
10 LKM Skeleton lkm_skeleton.c Skeleton module for faster scaffolding of new modules

When cloning for the first time, please clone also submodules with --recurse-submodules to get pretty-printk as well.

git clone --recurse-submodules git@github.com:tpiekarski/lkm-sandbox.git
make clean && make

Screenshots of make

To run all available tests, including basic loading/unloading and all additional concept tests..

make test

Testing by loading, unloading and outputting Kernel Ring Buffer (sudo will ask for root permissions).

make test-module name=<module-name>

Additional tests for the sandbox device including loading module, gathering major device number from /proc, creating device and comparing the final message either run the Makefile target with make test-device or run the following commands.

For creating character device the major number is needed and can be obtained by catting the file /proc/lkm_device_major. This major number is written to the Kernel Ring Buffer as well. It is possible to provide this major number using the module parameter param_major_num and load this module like sudo insmod lkm_device.ko param_major_num=241 (At this moment this static allocation seems not to work reliable. The registration of the sandbox device fails sometimes. It was tried with a major of -16 when registration fails and the operation is not permitted like that - the reason is unknown.)

sudo insmod lkm_device.ko
dmesg | grep "Registered sandbox device"
sudo mknod /dev/lkm_device c $(cat /proc/lkm_device_major) 0
test -c /dev/lkm_device && cat /dev/lkm_device || echo "Device /dev/lkm_device" not found."
sudo rmmod lkm_device

Additional tests for sandbox access to /proc including loading module, testing if file inside /proc exists and outputting that file. Either run the Makefile target test-proc with make test-proc or the following few commands:

sudo insmod lkm_proc.ko
test -f /proc/lkm_proc && cat /proc/lkm_proc || echo "File /proc/lkm_proc not found."
sudo rmmod lkm_proc

For additional tests for passing parameters to module lkm_parameters run the Makefile target test-parameter with make test-parameters. This will load/unload the module and compare the parameters number and message passed when loading the module with the values read in the /sys filesystem (/sys/module/lkm_parameters/parameters/*). Or run the following commands.

sudo insmod lkm_parameters.ko number=33 message=\"Some message...\"
cat /sys/module/lkm_parameters/parameters/number
cat /sys/module/lkm_parameters/parameters/message
sudo rmmod lkm_parameters

While triaging, debugging and working with bugs and issues it can come in handy to experiment with some code and write a PoC to prove some statements or to answer a question. In the following are a collection of such PoC which follow up on a lead to prove statements, ideas and questions I recently came across.

File Description Motivation
comparing-iopl-ioperm.c Comparing I/O Permissions granted by iopl and ioperm Bug 205317 - iopl(2) - privilege level is set per-process or per-thread?
permissions-revisited.c How are I/O Permissions granted when using clone, fork, execve or pthread? Bug 205317 - iopl(2) - privilege level is set per-process or per-thread?

For a better understanding of concepts in the kernelspace it is necessary to review and rehearse fundamental basics of C and the standard library. Next to being able to improve understanding it is possible to compare approaches. Most of those basics are low-level, starting with file I/O and can be looked through as a companion source. It is never bad to rehearse things, but sometimes a little embarrassing to admit having to rehearse such things :)

File Concept
clone.c Cloning processes with clone()
execve.c Executing another process with execve()
fork.c Creating child process with fork()
io_ports.c Low-level port-mapped I/O operations
read.c Reading of files in vanilla C
simple_circular_buffer.c Simple and straight forward circular buffer
write.c Writing/Appending to files in vanilla C

To build those files just run make clean && make in ./rehearsals/ and all executables will be placed in the build directory.

"A Linux kernel module is a piece of compiled binary code that is inserted directly into the Linux kernel, running at ring 0, the lowest and least protected ring of execution in the x86–64 processor."

"Traditional application development paradigms can be largely discarded. Other than loading and unloading of your module, you’ll be writing code that responds to system events rather than operates in a sequential pattern."

"With kernel development, you’re writing APIs, not applications themselves."

This repository will ask you for root permission, because certain operations like loading/unloading modules and accessing files in the Linux/GNU System depends on root privileges. The Makefile will state beforehand for what these permissions will be used.

You can review all this operations by searching this repository for sudo and be sure that this won't be misused in any way. I am aware of that this can be a security issue, but I am trying to make this process as much transparent as possible. But be also aware that these modules are coming without any warranty. Kernel panics and data loss can happen, please use them preferably inside a Virtual Machine.

Use of sudo

In the following is a table with all locations where sudo is used (except the README.md).

grep -n -r "sudo" *
File:Line Use of sudo
Makefile:118 $(call test_file_exists,$(number_file),"-r", "sudo")
Makefile:119 $(eval number_file_content = sudo cat $(number_file))
Makefile:122 $(eval message_file_content = `sudo cat $(message_file)
Makefile:123 $(call test_file_exists,$(message_file),"-r", "sudo")
Makefile:126 @sudo rmmod $(module_filename)
Makefile:140 @sudo mknod $(device_filename) c cat $(proc_filename) 0
Makefile:143 @sudo rm $(device_filename)
Makefile:144 @sudo rmmod $(module_filename)
Makefile:162 @sudo rmmod $(module_filename)
Makefile:175 @sudo mknod $(device_file) c $(major) 0
Makefile:176 @echo "Testing" | sudo tee $(device_file)
Makefile:178 @sudo rm -fv $(device_file)
Makefile:179 @sudo rmmod $(module)
Makefile:190 @sudo insmod $(module).ko number=$(number) message="$(message)"
Makefile:193 @sudo rmmod $(module)
Makefile:207 @sudo rmmod ${module}
Makefile:219 @sudo insmod $(module).ko
Makefile:222 @sudo rmmod $(module)
tests.mk:31 @lsmod | awk '{print $$1}' | grep -qE "^$(1)$$" && (sudo rmmod $(1) &amp;&amp; sudo insmod $(1).ko) || sudo insmod $(1).ko
tests.mk:75 @sudo dmesg --clear
tests.mk:78 @sudo rmmod $(1)

LKM Sandbox is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version.

LKM Sandbox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with LKM Sandbox. If not, see https://www.gnu.org/licenses/.