Linux Kernel Modules: Licensing, Taint & DKMS

Linux Kernel Module Licensing, Kernel Taint and DKMS Explained | Free Linux Kernel Programming Course

EmbeddedPathashalaFree Linux Kernel Programming Course › Kernel Module Licensing, Taint & DKMS

Linux Kernel Module Licensing, Kernel Taint, and DKMS Explained

Free Linux Kernel Programming Course — LKMs Part 2, Section 4

Level
Intermediate
Read Time
~20 min
Kernel
Linux 6.x
Cost
Free

Part of the Free Linux Kernel Programming Course at EmbeddedPathashala — updated for Linux 6.x

Topics Covered:
MODULE_LICENSE Macro GPL Compatibility Kernel Taint Flags Proprietary Kernel Modules DKMS Framework EXPORT_SYMBOL_GPL Free Kernel Programming Free Device Drivers Course

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, and modinfo
  • 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 .ko file and shown by modinfo
  • 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 ✘
Important: If you use any string other than the GPL-compatible ones listed above, the kernel treats your module as proprietary. This means you cannot call any function exported with 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.

Kernel Module License Check at Load Time
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

Linux Kernel Taint Flags (Linux 6.x)
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.

Recommendation for embedded projects: In commercial embedded Linux projects, try to use open-source drivers wherever possible. For hardware where only proprietary drivers exist, evaluate at the component selection stage whether the vendor actively maintains their Linux driver and keeps up with kernel version changes. A vendor who stops updating their proprietary driver can leave you unable to upgrade your kernel — a serious long-term security risk.

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 vs With DKMS — Kernel Upgrade Scenario
Without DKMS With DKMS
  1. Install kernel 6.8 — module works ✔
  2. apt upgrade installs kernel 6.11
  3. Reboot into 6.11
  4. modprobe my_module → ERROR: version mismatch ✘
  5. Manually rebuild and reinstall the module
  6. Repeat for every kernel update
  1. Register module source with DKMS once
  2. apt upgrade installs kernel 6.11
  3. DKMS hook fires automatically
  4. Module is rebuilt for 6.11 silently
  5. Reboot into 6.11 — module works ✔
  6. Never manually rebuild again ✔

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
Tip: When packaging your Linux kernel module as a Debian or RPM package for distribution, include DKMS integration. The standard pattern is to create a 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, use MODULE_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: Declaring MODULE_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 KERNELRELEASE variable 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 standard obj-m pattern.
  • Not running dkms autoinstall after kernel changes: If you manually install a new kernel (not through the package manager), DKMS hooks may not fire. Run sudo dkms autoinstall manually 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

Q1. Can I use MODULE_LICENSE(“GPL”) even if I do not plan to release the source code?
No. If you declare 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.
Q2. Does a tainted kernel cause any performance degradation?
No. The taint mechanism is purely informational — it sets bits in a kernel variable and prints a warning in the kernel log. It does not change how the kernel schedules tasks, allocates memory, or handles interrupts. The performance impact is zero. The consequences of taint are entirely about supportability and community debugging assistance, not runtime behavior.
Q3. Can I clear the kernel taint flag without rebooting?
Most taint flags cannot be cleared without a reboot. The exception is taint bit 6 (the user-requested taint flag used for testing), which can be cleared by writing to /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.
Q4. Does DKMS work on embedded systems and Raspberry Pi?
Yes, DKMS works on any Linux system that has the kernel headers installed and gcc available. On Raspberry Pi OS (which is Debian-based), you can install DKMS with 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.
Q5. What happens to DKMS modules when I do “apt dist-upgrade” on Ubuntu?
When apt installs a new kernel package, it triggers the DKMS hook automatically via dpkg triggers. DKMS builds your registered module against the new kernel’s headers and installs the new .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.
Q6. Is DKMS secure? Can it be used as an attack vector?
DKMS builds modules as root and installs them into the kernel module directory. This means that whoever controls the DKMS source directory (/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.
Q7. What is the difference between MODULE_LICENSE(“GPL”) and MODULE_LICENSE(“GPL v2”)?
"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

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

Leave a Reply

Your email address will not be published. Required fields are marked *