Linux Kernel Module Licensing, Kernel Taint, and DKMS Explained
Free Linux Kernel Programming Course — LKMs Part 2, Section 4
Intermediate
~20 min
Linux 6.x
Free
What You Will Learn
- What the MODULE_LICENSE macro does and why every kernel module must include it
- Which license strings are accepted and what GPL compatibility means for your module
- What kernel taint is, what causes it, and why it matters for bug reporting and support
- How to read and interpret the kernel taint flags from a live system
- The real-world implications of loading a proprietary kernel module
- What DKMS (Dynamic Kernel Module Support) is and how to package your module with it
- How DKMS solves the kernel upgrade problem for out-of-tree modules
- Best practices for licensing your own kernel modules in a free Linux kernel development context
Prerequisites
This tutorial assumes you have read the earlier parts of this series and can:
- Write, compile, load, and unload a basic Linux kernel module
- Understand
insmod,rmmod,modprobe, andmodinfo - Work comfortably with the Linux command line
What is MODULE_LICENSE and Why Is It Mandatory?
Every Linux kernel module must declare its license using the MODULE_LICENSE() macro. This is not optional — a module without a license declaration is treated as proprietary by default, which immediately taints the kernel when loaded. Understanding why this matters requires a brief look at how the Linux kernel is licensed.
The Linux kernel itself is released under the GNU General Public License version 2 (GPLv2). This means any code that is considered a derivative work of the kernel must also be GPLv2 licensed. Kernel modules — especially those that use kernel-internal functions and data structures — are generally considered derivative works.
The MODULE_LICENSE() macro serves several purposes in the free Linux kernel programming ecosystem:
- It tells the kernel loader whether the module is GPL-compatible, which determines which exported symbols the module can access
- It is embedded in the
.kofile and shown bymodinfo - It sets a taint flag on the kernel when a non-GPL module is loaded
- It is used by the kernel’s license compliance tooling
#include <linux/module.h>
/* This is how you declare the license in your module */
MODULE_LICENSE("GPL");
/* Other mandatory/recommended module metadata */
MODULE_AUTHOR("Your Name <your@email.com>");
MODULE_DESCRIPTION("A GPL-licensed kernel module - EmbeddedPathashala Free Course");
MODULE_VERSION("1.0.0");
Accepted License Strings
| License String | Meaning | GPL Symbols Accessible? |
|---|---|---|
"GPL" |
GNU General Public License v2 (or later) | Yes ✔ |
"GPL v2" |
GNU General Public License v2 only | Yes ✔ |
"GPL and additional rights" |
GPL with additional permissions granted | Yes ✔ |
"Dual MIT/GPL" |
Dual-licensed under MIT and GPL | Yes ✔ |
"Dual BSD/GPL" |
Dual-licensed under BSD and GPL | Yes ✔ |
"Dual MPL/GPL" |
Dual-licensed under Mozilla and GPL | Yes ✔ |
"Proprietary" |
Closed-source, non-GPL module | No ✘ |
EXPORT_SYMBOL_GPL(). Since most core kernel subsystem functions (I2C, SPI, USB, regmap, and more) use EXPORT_SYMBOL_GPL(), a proprietary module has very limited access to kernel infrastructure.
How the Kernel Enforces License Checking
When a module is loaded, the kernel’s module loader reads the MODULE_LICENSE string embedded in the .ko file. It then sets an internal flag on the module structure indicating whether it is GPL-compatible. When the module later tries to call a GPL-only exported symbol, the linker (at module load time, not compile time) checks this flag. If the module is not GPL-compatible and the symbol requires GPL, the load is refused.
| Module’s LICENSE | Symbol Type Used | Result at insmod |
|---|---|---|
"GPL" |
EXPORT_SYMBOL_GPL() |
✔ Load succeeds |
"GPL" |
EXPORT_SYMBOL() |
✔ Load succeeds |
"Proprietary" |
EXPORT_SYMBOL_GPL() |
✘ Load refused — unknown symbol |
"Proprietary" |
EXPORT_SYMBOL() |
⚠ Load succeeds but taints kernel |
What is Kernel Taint and Why Does It Matter?
Kernel taint is a mechanism where the kernel marks itself as potentially unreliable when certain conditions are met. Once tainted, the kernel logs a warning and the taint state is visible in /proc/sys/kernel/tainted. This is critically important in the Linux kernel programming and free Linux device driver development world because:
- Kernel developers and distribution support teams may refuse to investigate bug reports from a tainted kernel
- A tainted kernel means you have loaded something that the kernel community cannot audit or support
- Crash dumps and oops messages from a tainted kernel are harder to interpret
- Automated bug reporting tools like ABRT and Apport may suppress reports from tainted kernels
Reading the Taint Status
# Check if the kernel is currently tainted
cat /proc/sys/kernel/tainted
# 0 means clean (no taint)
# Any non-zero number means tainted
# A more human-readable view
cat /proc/sys/kernel/tainted
# 4096
# Decode what 4096 means
# Each bit in the tainted value represents a specific taint reason
# 4096 = bit 12 = unsigned module loaded
# Modern kernels also show taint info in the kernel log on oops
# Look for lines like: "Tainted: P W OE" in dmesg output
Kernel Taint Flags — Full Reference
| Bit | Value | Letter | Cause |
|---|---|---|---|
| 0 | 1 | P | Proprietary module loaded |
| 1 | 2 | F | Module was force-loaded (insmod –force) |
| 2 | 4 | S | SMP kernel on hardware not certified for SMP |
| 3 | 8 | R | Module was force-unloaded |
| 4 | 16 | M | Machine check exception (hardware error) occurred |
| 5 | 32 | B | Page-release function found a bad page reference |
| 6 | 64 | U | User-space application requested taint (for testing) |
| 7 | 128 | D | Kernel died recently — OOPS or BUG() |
| 8 | 256 | A | ACPI table overridden by user |
| 9 | 512 | W | Kernel issued a WARN() |
| 10 | 1024 | C | Staging driver loaded |
| 11 | 2048 | I | Workaround applied for a firmware or platform bug |
| 12 | 4096 | E | Unsigned module loaded (on a system with sig enforcement) |
| 13 | 8192 | L | Soft lockup occurred |
| 14 | 16384 | K | Kernel live patched |
| 15 | 32768 | X | Auxiliary taint (distro-specific) |
| 16 | 65536 | T | Kernel built with randstruct plugin |
Decoding a Taint Value Programmatically
# Example: tainted value is 4097
# 4097 = 4096 + 1 = bit 12 (E) + bit 0 (P)
# Meaning: proprietary module loaded AND unsigned module loaded
# Quick decode in bash
taint=$(cat /proc/sys/kernel/tainted)
echo "Taint value: $taint"
# Python one-liner to decode all bits
python3 -c "
taint_flags = {
0:'P - Proprietary module',
1:'F - Force-loaded module',
2:'S - SMP on non-certified HW',
3:'R - Force-unloaded module',
4:'M - Machine check error',
5:'B - Bad page reference',
6:'U - User-requested taint',
7:'D - Kernel OOPS/BUG',
8:'A - ACPI override',
9:'W - WARN() issued',
10:'C - Staging driver',
11:'I - Firmware workaround',
12:'E - Unsigned module',
13:'L - Soft lockup',
14:'K - Live patched',
15:'X - Distro taint',
16:'T - randstruct built',
}
val = int(open('/proc/sys/kernel/tainted').read().strip())
print(f'Taint: {val}')
if val == 0:
print(' Kernel is clean')
else:
for bit, desc in taint_flags.items():
if val & (1 << bit):
print(f' bit {bit}: {desc}')
"
Proprietary Kernel Modules — Real-World Implications
Some hardware vendors ship their Linux drivers as proprietary binary-only kernel modules. The most widely known examples are the NVIDIA GPU driver and some WiFi chip firmware loaders. Understanding how these work and what their limitations are is important for anyone doing embedded Linux work.
How a Proprietary Module Works
A proprietary (closed-source) binary kernel module is a .ko file that the vendor distributes without source code. It is compiled by the vendor for specific kernel versions. Loading it works the same way as any other module — with insmod or modprobe — but several consequences follow:
- The kernel immediately marks itself as tainted with the P flag
- The module cannot call
EXPORT_SYMBOL_GPL()functions - Kernel developers will typically not help debug crashes that occur on a tainted kernel
- The module must be recompiled (by the vendor) for every new kernel version — the vendor must release a new binary for each kernel update
- If the vendor stops updating the driver, users are stuck on old kernel versions
The Dual-Licensing Workaround
Some vendors use a “shim” or “wrapper” approach to partially work around the GPL restriction. They write a thin GPL-licensed wrapper module that calls GPL-only symbols, and then calls into a separate proprietary blob through a GPL-compatible interface. This is a legally and ethically grey area — the kernel community generally does not approve of this pattern, but it exists in practice.
DKMS — Dynamic Kernel Module Support
DKMS (Dynamic Kernel Module Support) is a framework that automatically rebuilds out-of-tree kernel modules every time a new kernel is installed. It was originally developed by Dell and is now part of most major Linux distributions. For anyone writing and deploying real Linux kernel modules — in embedded systems, desktop hardware support, or enterprise environments — DKMS is an essential tool.
The Problem DKMS Solves
| Without DKMS | With DKMS |
|---|---|
|
|
How DKMS Works Internally
DKMS stores your module’s source code in /usr/src/<module_name>-<version>/. It registers a package hook with the package manager (apt, dnf, etc.). Whenever a new kernel package is installed, the hook triggers DKMS to build the module against the new kernel’s headers and install the resulting .ko file into the right location. Everything happens automatically in the background.
Packaging Your Module with DKMS — Step by Step
# Step 1: Install DKMS
sudo apt install dkms
# Step 2: Create the module source directory under /usr/src/
# Convention: /usr/src/-/
sudo mkdir -p /usr/src/my_lkm-1.0/
# Step 3: Copy your source files there
sudo cp my_lkm.c /usr/src/my_lkm-1.0/
sudo cp Makefile /usr/src/my_lkm-1.0/
# Step 4: Create the DKMS configuration file — dkms.conf
# This tells DKMS everything it needs to know about building your module
sudo nano /usr/src/my_lkm-1.0/dkms.conf
# Contents of dkms.conf
PACKAGE_NAME="my_lkm"
PACKAGE_VERSION="1.0"
# Source directory name
BUILT_MODULE_NAME[0]="my_lkm"
BUILT_MODULE_LOCATION[0]="."
# Where to install the built .ko
DEST_MODULE_LOCATION[0]="/updates/dkms"
# Auto-install when a new kernel is installed
AUTOINSTALL="yes"
# Clean command before building
MAKE[0]="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}-${PACKAGE_VERSION}/build"
# Clean command
CLEAN="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}-${PACKAGE_VERSION}/build clean"
# Step 5: Add the module to DKMS
sudo dkms add -m my_lkm -v 1.0
# Creating symlink /var/lib/dkms/my_lkm/1.0/source ->
# /usr/src/my_lkm-1.0
# DKMS: add completed.
# Step 6: Build the module for the currently running kernel
sudo dkms build -m my_lkm -v 1.0
# Kernel preparation unnecessary for this kernel.
# Building module:
# Cleaning build area...
# make -j4 KERNELRELEASE=6.8.0-51-generic...
# Signing module...
# Cleaning build area...
# DKMS: build completed.
# Step 7: Install the built module
sudo dkms install -m my_lkm -v 1.0
# Installing module my_lkm into /lib/modules/6.8.0-51-generic/updates/dkms/
# depmod...
# DKMS: install completed.
# Verify installation
dkms status
# my_lkm/1.0, 6.8.0-51-generic, x86_64: installed
DKMS Useful Commands Reference
| Command | What It Does |
|---|---|
dkms add -m name -v ver |
Register module source with DKMS |
dkms build -m name -v ver |
Compile the module for the current kernel |
dkms install -m name -v ver |
Install compiled module to /lib/modules/ |
dkms remove -m name -v ver --all |
Remove module from DKMS tracking |
dkms status |
Show all DKMS-managed modules and their status |
dkms build -m name -v ver -k 6.11.0 |
Build specifically for kernel version 6.11.0 |
dkms autoinstall |
Rebuild all DKMS modules for the current kernel |
my-lkm-dkms package that copies the source to /usr/src/, adds the module to DKMS, and runs dkms build + dkms install in the package post-install script. This gives users a seamless experience where the driver just works and keeps working across kernel updates.
DKMS with Kernel Module Signing
On systems with Secure Boot enabled, DKMS-built modules may need to be signed before they can be loaded. DKMS has built-in support for this through its dkms.conf signing configuration:
# In dkms.conf, add signing configuration:
SIGN_TOOL="/usr/lib/linux-kbuild-$(uname -r | cut -d. -f1,2)/scripts/sign-file"
SIGNING_KEY="/var/lib/dkms/mok.key"
SIGNING_X509="/var/lib/dkms/mok.crt"
# DKMS can generate a MOK key automatically
# On Ubuntu with Secure Boot, this is handled when you first add a DKMS module
# You will see a prompt to set an enrollment password during dkms install
Licensing Best Practices for Kernel Module Developers
- Always use
"GPL"for open-source modules: If you are writing a module for a free software project, embedded product with open-source intent, or for learning purposes in a free Linux kernel development course, useMODULE_LICENSE("GPL"). It gives you full access to all kernel subsystems and avoids taint. - Use SPDX identifiers in source files: Modern kernel coding practice is to add an SPDX license identifier at the top of every source file. This is machine-readable and used by compliance tools.
- Document the license in your README: Make it clear in your project’s README what license the module uses and what obligations users have.
- Use DKMS for any module intended for deployment: If your module will be installed on end-user systems, package it with DKMS from day one. The cost of adding DKMS support early is low; retrofitting it later when users are hitting kernel upgrade breakage is painful.
- Check your taint status after testing: After loading your module during development, always check
/proc/sys/kernel/tainted. A clean (zero) value confirms your module is GPL-licensed and properly signed.
/* Example: correct file header with SPDX identifier */
// SPDX-License-Identifier: GPL-2.0-only
/*
* my_sensor_driver.c - Driver for My Sensor Chip
*
* Copyright (C) 2025 Your Name <your@email.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Your Name <your@email.com>");
MODULE_DESCRIPTION("Driver for My Sensor Chip");
MODULE_VERSION("1.0.0");
Common Mistakes with Module Licensing and DKMS
- Omitting MODULE_LICENSE entirely: Without
MODULE_LICENSE(), the kernel defaults to treating the module as proprietary. It loads, but taints the kernel with the P flag and blocks access to GPL symbols. Always include it. - Using
"GPL"without actually complying with GPL: DeclaringMODULE_LICENSE("GPL")while keeping the source closed is a GPL violation. If you declare GPL, you must distribute the source. The MODULE_LICENSE macro is not just a technical flag — it is a legal statement. - Wrong path in DKMS Makefile: The Makefile used with DKMS receives a
KERNELRELEASEvariable from the build system. Make sure your Makefile handles the standard kbuild two-pass invocation correctly — the same Makefile you use for manual builds should work with DKMS without modification if it follows the standardobj-mpattern. - Not running
dkms autoinstallafter kernel changes: If you manually install a new kernel (not through the package manager), DKMS hooks may not fire. Runsudo dkms autoinstallmanually to rebuild all registered modules for the new kernel. - Confusing DKMS versions: DKMS identifies modules by name+version. If you update your module source but do not change the version string in
dkms.conf, DKMS will not know to rebuild. Always increment the version when making changes.
Key Takeaways
MODULE_LICENSE()is mandatory in every kernel module. Use"GPL"for open-source modules. Any non-GPL string taints the kernel and restricts access to GPL-only exported symbols.- Kernel taint is a numeric bitmask in
/proc/sys/kernel/tainted. Each bit has a specific meaning. A clean kernel has value 0. Taint from a proprietary module sets the P flag (bit 0). - Loading a proprietary kernel module taints the kernel, blocks GPL-symbol access, and means kernel developers and distribution support will not help debug crashes.
- DKMS solves the kernel module rebuild problem by automatically recompiling your module whenever a new kernel is installed.
- Package your module with DKMS for any deployment scenario where users or systems will receive kernel updates.
- Add SPDX identifiers to source files for machine-readable license compliance tracking.
Frequently Asked Questions
MODULE_LICENSE("GPL"), you are making a legal statement that the module is licensed under GPL. GPL requires that you make the source code available to anyone who receives the binary. Using "GPL" while keeping the source private is a GPL license violation. If you intend to keep the source closed, use "Proprietary" and accept the consequences — tainted kernel and no access to GPL-only symbols./proc/sys/kernel/tainted. The proprietary module taint (P), unsigned module taint (E), and OOPS taint (D) flags persist until reboot. This is intentional — the kernel wants you to be aware that something happened that may have compromised kernel integrity.sudo apt install dkms. The main limitation on memory-constrained embedded systems is that compiling kernel modules requires significant RAM and storage. For very resource-constrained targets (under 256MB RAM), cross-compiling on a build host and installing pre-built modules is more practical than running DKMS on the target itself..ko. If the build fails for some reason (e.g., a kernel API change broke your module), apt will log the error but continue installing the new kernel. The old kernel’s module version remains available. Check sudo dkms status after major kernel upgrades to verify everything built successfully./usr/src/module-version/) controls what code gets compiled into kernel modules. On a well-administered system, only root should be able to write to these directories. On Secure Boot systems, DKMS-built modules still need to be signed with a trusted key before they can be loaded. The combination of file system permissions and module signing makes DKMS reasonably secure in practice."GPL" means the module is licensed under GPL version 2 or any later version. "GPL v2" means strictly version 2 only — the module cannot be used under GPL version 3 or later. For practical purposes in the kernel context, both give full access to all GPL-exported symbols. The Linux kernel itself is GPLv2-only, so most kernel modules use either "GPL" or "GPL v2". The distinction matters mainly when you care about compatibility with GPL v3 code in other contexts.Authoritative References
- Linux Kernel Documentation: Tainted Kernels (kernel.org)
- Linux Kernel License Rules (kernel.org)
- GNU General Public License v2 (gnu.org)
Free Linux Kernel Module Development — Keep Going
EmbeddedPathashala’s free Linux kernel programming course covers everything from LKM basics to advanced drivers — all original content, no cost, updated for Linux 6.x.
View Full Course Index Subscribe on YouTube