Coccinelle: The Semantic Patch Master



This is Blog 1 of a 3-part series on Static Analysis Tools for Linux Device Driver Development.

Blog 1 (This): Coccinelle — Semantic Patching & Code Transformation

Blog 2: Sparse — Type System Guardian

Blog 3: Smatch — Deep Path-Sensitive Analysis

Each blog is a standalone 101 guide you can follow in sequence or jump to whichever tool you need.

Why Static Analysis Matters for Driver Developers?

Picture this: You’ve spent hours writing a new device driver for Linux, compiled it successfully, and deployed it to production. Everything seems fine until the system crashes mysteriously, and you discover a null pointer dereference that could have been caught before the code ever ran. This is where static analysis tools become invaluable guardians of code quality.

For Linux device drivers, static analysis is particularly crucial because:

  • Device drivers run in kernel space, where bugs can crash the entire system rather than just a single application
  • Driver code interacts directly with hardware, making errors potentially dangerous
  • Kernel bugs are harder to debug than user-space application bugs
  • Security vulnerabilities in drivers can compromise the entire system

Static analysis helps you catch issues like type mismatches, memory leaks, null pointer dereferences, and incorrect API usage before your code runs. This proactive approach saves countless hours of debugging and prevents critical bugs from reaching production systems.

 
Coccinelle: The Semantic Patch Master

What Makes Coccinelle Special?

Coccinelle is a sophisticated pattern matching and transformation tool designed specifically for C code. Its name comes from the French word for “ladybug,” and like its namesake, it helps keep your code garden healthy by identifying and fixing problematic patterns.

Unlike simple text-based search and replace tools, Coccinelle understands the semantic structure of your code. This means it can recognize patterns regardless of formatting differences, making it incredibly powerful for large-scale code transformations.

Think of Coccinelle as a smart find-and-replace that understands C at the AST (Abstract Syntax Tree) level, not just text. This is what makes it fundamentally different from tools like sed or grep.

Real-World Use Cases

Coccinelle excels at:

  • API migration: When a kernel API changes, you can write a semantic patch to automatically update all driver code using the old API
  • Bug pattern detection: Identify common mistakes like forgetting to check return values or incorrect locking sequences
  • Code modernization: Update deprecated coding patterns across the entire kernel tree
  • Driver porting: When porting a driver to a newer kernel, Coccinelle can help identify all call sites that need updating
  • Consistency enforcement: Ensure all allocation calls in your driver follow the same null-check pattern

How Coccinelle Fits Into Your Daily Workflow

As a driver developer, here is where Coccinelle slots in:

  • Before submitting patches upstream: Run Coccinelle with the standard kernel scripts to catch known bad patterns
  • During kernel version upgrades: Use semantic patches to mass-update deprecated API usage
  • Writing your own Cocci scripts: Encode your team’s coding rules as semantic patches and enforce them automatically
  • Code review automation: Run Cocci in CI/CD to flag issues before human reviewers see the code.
 
Installing Coccinelle

 

On Ubuntu/Debian Systems

sudo apt install coccinelle

 

From Source (Latest Features)

git clone https://github.com/coccinelle/coccinelle.git

cd coccinelle

make && sudo make install

 

After installation, verify it works:

spatch –version

 

Using Coccinelle with Your Driver Code

 

Basic Integration with the Kernel Build System

Coccinelle integrates seamlessly with the kernel build system. Navigate to your kernel source directory and run:

 

Basic Check

make C=1 CHECK=”scripts/coccicheck” M=drivers/path/to/your/driver/

 

Comprehensive Check

make C=2 CHECK=”scripts/coccicheck” M=drivers/path/to/your/driver/

 

The C=1 flag performs basic analysis, while C=2 enables deeper, more thorough checking. The script will apply all available semantic patches and report any matches or potential issues.

 

Understanding the Output

When Coccinelle finds a match, it prints the file, line, and a diff showing what the semantic patch would change. Example output:

 

drivers/mydriver/mydriver.c:87:1-8: WARNING: NULL check before some freeing functions is

  not needed.

–  if (buf)

–      kfree(buf);

+  kfree(buf);

 

This tells you that kfree() is null-safe, and the null check before it is redundant clutter. Coccinelle found this automatically using the null_check_before_free.cocci script from the kernel tree.

 

Understanding Semantic Patches (.cocci files)

The SmPL Language

Coccinelle uses its own language called SmPL (Semantic Patch Language). A .cocci file defines a transformation rule: a pattern to match and optionally what to replace it with. The syntax is similar to a unified diff with metavariables for generalization.

 

Example 1: Detecting Missing Null Checks After kmalloc

This is the most common issue in early driver development — forgetting to check the return of kmalloc:

 

@@

expression E;

@@

E = kmalloc(…);

+ if (!E)

+     return -ENOMEM;

 

This semantic patch identifies kmalloc() calls and suggests adding null pointer checks. The @@ delimiters separate metavariable declarations from the pattern body. The + lines indicate what should be added.

 

Example 2: Replacing Deprecated API (devm_ migration)

When migrating a driver to use managed device resources, you need to replace kzalloc with devm_kzalloc and remove the corresponding kfree. Coccinelle can do this automatically:

 

@@

struct device *dev;

expression size, flags;

expression ptr;

@@

– ptr = kzalloc(size, flags);

+ ptr = devm_kzalloc(dev, size, flags);

  …

– kfree(ptr);

 

This is extremely powerful. It removes the kfree call, too, because devm_ managed allocations are freed automatically on device removal. A text-based tool could never do this safely.

 

Example 3: Detecting Unchecked Return Values from request_irq

Forgetting to check the return value of request_irq() is a classic driver bug. The interrupt handler is never actually registered, and the driver appears to work until an interrupt fires and nothing handles it:

 

@@

expression irq, handler, flags, name, dev;

@@

– request_irq(irq, handler, flags, name, dev);

+ int ret = request_irq(irq, handler, flags, name, dev);

+ if (ret) {

+     dev_err(dev, “Failed to request IRQ: %d\n”, ret);

+     return ret;

+ }

 

Example 4: Lock/Unlock Imbalance Detection

This is a Coccinelle pattern to detect spin_lock without a corresponding spin_unlock on all paths:

 

@@

expression lock;

@@

spin_lock(&lock);

… when != spin_unlock(&lock);

return …;

 

The ‘when != spin_unlock’ clause tells Coccinelle to match only code paths where spin_unlock does NOT appear between the lock and the return. This is exactly the kind of semantic reasoning that no text tool can do.

 

Example 5: Your Own Driver’s Custom Rule

Suppose your team has a rule: always use your driver’s wrapper my_alloc() instead of kmalloc() directly. You can enforce it:

 

@@

expression size;

@@

– kmalloc(size, GFP_KERNEL)

+ my_alloc(size)

 

Save this as enforce_my_alloc.cocci and run it in CI. Any developer who accidentally uses kmalloc directly will get a warning automatically.

 

Example 6: Redundant Error Handling After platform_get_irq()

This pattern detects unnecessary error handling after calling platform_get_irq().

The function already prints an error message internally, so adding another dev_err() is redundant.

 

@@

expression pdev, irq;

@@

irq = platform_get_irq(pdev, …);

if (irq < 0) {

dev_err(…);

}

 

Example 7: Unnecessary .owner Field in Platform Driver

This pattern identifies redundant usage of .owner = THIS_MODULE inside a platform driver structure.

The kernel automatically assigns the owner field, so explicitly setting it is not required.

 

@@

identifier drv;

@@

static struct platform_driver drv = {

.driver = {

.owner = THIS_MODULE,

},

};

 

Example 8: Redundant NULL Check on Iterator in List Traversal

This pattern detects unnecessary NULL checks on iterator variables used in list_for_each_entry_safe.

The iterator is guaranteed to be valid during traversal, so checking if (pos) is redundant.

 

@@

identifier pos, n, head, member;

@@

list_for_each_entry_safe(pos, n, head, member) {

if (pos && …) {

}

}

 

Running Custom Semantic Patches

You can run a specific .cocci file directly with spatch:

 

# Run a single cocci script against your driver

spatch –sp-file enforce_my_alloc.cocci drivers/myvendor/mydriver.c

# Run against the whole driver directory

spatch –sp-file enforce_my_alloc.cocci –dir drivers/myvendor/mydriver/

# Apply changes in-place (use with caution, commit first!)

spatch –sp-file enforce_my_alloc.cocci –in-place drivers/myvendor/mydriver.c

 

Exploring the Kernel’s Built-In Cocci Scripts

The kernel source tree ships with hundreds of ready-made semantic patches:

 

ls /path/to/linux/scripts/coccinelle/

 api/         – checks for correct API usage

 iterators/   – loop pattern checks

 locks/       – locking correctness

 memory-funcs/ – allocation and deallocation patterns

 misc/        – miscellaneous checks

 null/        – null pointer issues

 tests/       – test infrastructure

 

Start by exploring scripts/coccinelle/null/ and scripts/coccinelle/memory-funcs/. These are immediately useful for any driver developer and will teach you a lot about common pitfalls.

 

Case Studies: Real Problems Coccinelle Solves

 

Case Study 1: Massive API Migration in a Real Kernel

When the kernel’s DMA API changed from dma_alloc_coherent() to a newer pattern, thousands of driver files across the kernel tree needed updating. Coccinelle semantic patches helped automatically update all those calls. What would have taken weeks of manual work was completed in hours with high confidence.

This is the most famous use case for Coccinelle and why kernel maintainers love it. Entire subsystem API changes are now announced with an accompanying .cocci file so driver authors can run the transformation themselves.

 

Case Study 2: Your DMA Driver’s Error Path Consistency

In DMA driver development (which you’re familiar with), a common pattern is:

 

buf = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);

if (!buf)

    return -ENOMEM;

 

But sometimes developers forget the check, or they check with if (buf == NULL) instead of the canonical if (!buf). Coccinelle can enforce the exact pattern your team wants:

 

@@

expression dev, size, handle, flags;

expression ptr;

@@

ptr = dma_alloc_coherent(dev, size, &handle, flags);

– if (ptr == NULL)

+ if (!ptr)

 

Case Study 3: Enforcing devm_ Usage in New Drivers

Modern Linux drivers should use managed resources (devm_ prefix) to simplify error paths. Coccinelle can warn whenever a driver’s probe() function uses plain kzalloc instead of devm_kzalloc:

 

@@

identifier probe;

expression size, flags;

@@

probe(…) {

  …

– kzalloc(size, flags)

+ devm_kzalloc(dev, size, flags)

  …

}

 

Common Issues Coccinelle Catches in Driver Code

 

Issue 1: Null Check Before kfree

Problem: kfree() is already null-safe, so wrapping it in an if (ptr) check is unnecessary clutter.

Code with the issue:

 

if (priv->dma_buf)

    kfree(priv->dma_buf);

 

What Coccinelle reports:

 

WARNING: NULL check before kfree is not needed.

 

Issue 2: Using Obsolete API

Problem: Driver uses an old API like init_MUTEX() which was removed in favor of sema_init():

 

init_MUTEX(&dev->sem);  // Coccinelle: obsolete API

 

Issue 3: Missing Error Check on platform_get_resource

A very common driver bug: forgetting to check if platform_get_resource() returned NULL:

 

struct resource *res;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

// No null check!

base = ioremap(res->start, resource_size(res));  // Crash if res is NULL

 

Coccinelle has a script for exactly this pattern. The fix:

 

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

if (!res) {

    dev_err(&pdev->dev, “Failed to get memory resource\n”);

    return -EINVAL;

}

 

Integrating Coccinelle Into Your Workflow

Pre-Commit Hook

Automate Coccinelle checking with a Git pre-commit hook:

 

#!/bin/bash

# .git/hooks/pre-commit

DRIVER_PATH=”drivers/myvendor/mydriver”

KERNEL_DIR=”/path/to/linux”

echo “Running Coccinelle checks…”

cd $KERNEL_DIR

make C=1 CHECK=”scripts/coccicheck” M=$DRIVER_PATH 2>&1 | grep -i warning

if [ $? -ne 0 ]; then

    echo “Coccinelle checks failed! Fix warnings before committing.”

    exit 1

fi

echo “Coccinelle: all good!”

 

Performance Considerations

Coccinelle can be slower than Sparse, adding 50-100% to compilation time depending on the number of semantic patches. Here are strategies to manage this:

  • Run Sparse on every build (it’s fast) and Coccinelle only before patch submission
  • Use M=drivers/specific/path/ to limit analysis scope to your driver
  • Run full Coccinelle analysis overnight or in CI/CD pipelines
  • Focus on the specific cocci scripts most relevant to your driver type

 

Summary and What’s Next

Coccinelle is uniquely powerful for driver developers because it understands C at a semantic level. You can use it to:

  • Detect missing null checks, bad API usage, and error path bugs automatically
  • Write your own team-specific rules as .cocci scripts
  • Perform large-scale API migrations safely when upgrading kernels
  • Enforce consistent coding patterns across your entire driver tree
  • Integrate into CI/CD to catch issues before code review

 

Up Next: Blog 2 of 3
Sparse — The Type System Guardian
Learn how Sparse enforces kernel-specific pointer annotations like __user, __iomem, and __rcu,
catches endianness bugs, and prevents address space violations that the compiler silently ignores.
If you write any driver that touches hardware registers or user space, Sparse is non-negotiable.

 

Additional Resources
  • Coccinelle Official Website: https://coccinelle.gitlabpages.inria.fr/website/
  • Kernel Coccinelle Scripts: linux/scripts/coccinelle/
  • Linux Kernel Coding Style Guide: https://www.kernel.org/doc/html/latest/process/coding-style.html
  • Kernel Newbies Static Analysis: https://kernelnewbies.org/StaticAnalysis

 

Happy coding, and may your drivers always be bug-free!

100% LikesVS
0% Dislikes

Author