Applying Security Patches to the Raspberry Pi 5 Kernel

Applying Security Patches to the Raspberry Pi 5 Kernel

The Linux kernel receives hundreds of commits daily, many addressing security vulnerabilities. When running a Raspberry Pi 5 with a custom or older kernel, you may need to apply security patches before the Raspberry Pi Foundation releases an official update.

This guide covers the complete workflow: tracking CVEs, locating upstream fixes, backporting patches to the Pi kernel tree, and verifying the fix.

Understanding the Patch Ecosystem

The Kernel Release Model

Linux follows a structured release model that affects how patches flow:

Mainline (Linus)     →  6.8, 6.9, 6.10 ...
    ↓
Stable (Greg K-H)    →  6.6.15, 6.6.16, 6.6.17 ...
    ↓
LTS (Long Term)      →  6.1.x, 6.6.x (maintained 2-6 years)
    ↓
Vendor Trees         →  Raspberry Pi, Android, Distros

Mainline receives new features and fixes first. Stable kernels get critical fixes backported, tagged with Cc: stable@vger.kernel.org. LTS kernels receive extended maintenance. Vendor trees (like the Pi kernel) cherry-pick from stable/mainline.

The Raspberry Pi Foundation tracks the 6.6.y LTS branch, but there's always a lag between upstream fixes and Pi kernel updates—sometimes days, sometimes weeks for less critical issues.

Why Manual Patching Matters

Consider this timeline for a hypothetical CVE:

  • Day 0: Vulnerability disclosed, fix merged to mainline
  • Day 1-3: Fix backported to stable kernels (6.6.x, 6.1.x)
  • Day 7-14: Raspberry Pi Foundation merges to rpi-6.6.y
  • Day 14-30: Your distro (Raspberry Pi OS) releases updated package

If you're running a production Pi 5 exposed to untrusted networks, waiting 2-4 weeks for an official update isn't acceptable. Manual patching closes this window.

Tracking Security Vulnerabilities

Kernel CVE Database

The Linux kernel maintains its own CVE database with detailed analysis:

# Clone the kernel CVE database
git clone https://git.kernel.org/pub/scm/linux/security/vulns.git linux-cve-db
cd linux-cve-db

# List recent CVEs
ls -lt cve/published/ | head -20

# Search for specific subsystem CVEs
grep -r "net/bluetooth" cve/published/

Each CVE entry contains:

  • Affected kernel versions
  • Fix commit hash
  • Severity assessment
  • Affected subsystems

Monitoring Channels

Set up monitoring for security announcements:

Mailing Lists:

  • linux-kernel@vger.kernel.org - All kernel development
  • linux-cve-announce@vger.kernel.org - CVE announcements only
  • oss-security@lists.openwall.com - Cross-project security

RSS Feeds:

# Kernel stable releases (includes security fixes)
https://www.kernel.org/feeds/kdist.xml

# NVD CVE feed for Linux kernel
https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-recent.json.gz

Automated Monitoring Script:

#!/bin/bash
# check-kernel-cves.sh - Monitor for new kernel CVEs

CVE_DB="$HOME/linux-cve-db"
LAST_CHECK="$HOME/.last-cve-check"

cd "$CVE_DB" && git pull -q

if [ -f "$LAST_CHECK" ]; then
    NEW_CVES=$(find cve/published -newer "$LAST_CHECK" -name "CVE-*.json")
    if [ -n "$NEW_CVES" ]; then
        echo "New kernel CVEs since last check:"
        for cve in $NEW_CVES; do
            jq -r '"\(.cveId): \(.containers.cna.title)"' "$cve"
        done
    fi
fi

touch "$LAST_CHECK"

Identifying Relevant CVEs

Not every kernel CVE affects your Pi 5. Filter by:

Architecture: ARM64-specific vulnerabilities

grep -l "arm64\|aarch64" cve/published/CVE-2024-*.json

Enabled Subsystems: Check your kernel config

# On the Pi, extract running config
zcat /proc/config.gz > running.config

# Check if vulnerable subsystem is enabled
grep CONFIG_BLUETOOTH running.config
# CONFIG_BLUETOOTH=m  → Affected
# CONFIG_BLUETOOTH is not set → Not affected

Attack Surface: Consider your exposure

  • Network-facing services → Prioritize net/ipv4, net/ipv6, bluetooth
  • USB peripherals → Prioritize drivers/usb
  • Local users only → Lower priority for local privilege escalation

Locating Upstream Fixes

Finding the Fix Commit

Once you identify a CVE, locate the fixing commit:

Method 1: CVE Database

# The CVE JSON contains the fix commit
jq -r '.containers.cna.references[] | select(.tags[]? == "patch") | .url' \
    cve/published/CVE-2024-XXXX.json

Method 2: Stable Kernel Changelog

# Search stable kernel commits
cd ~/rpi-linux
git log --oneline --grep="CVE-2024-XXXX" origin/linux-6.6.y

Method 3: Commit Message Search Security fixes often reference the CVE or describe the vulnerability:

git log --oneline --all --grep="use-after-free" --grep="net/bluetooth" --all-match

Method 4: LKML Archives Search the mailing list archives at https://lore.kernel.org/lkml/ for the CVE ID or vulnerability description.

Anatomy of a Security Fix

Let's examine a real security fix structure:

commit abc123def456...
Author: Security Researcher <researcher@example.com>
Date:   Mon Jan 15 10:30:00 2024 +0000

    net: fix use-after-free in sk_buff handling

    When processing fragmented packets, the sk_buff can be freed
    while still referenced by the fragment queue. This leads to
    a use-after-free that can be triggered by a malicious packet.

    Fix by taking an additional reference before queueing.

    Reported-by: Security Team <security@example.com>
    Fixes: def789abc123 ("net: add fragment queue support")
    Cc: stable@vger.kernel.org
    Signed-off-by: Security Researcher <researcher@example.com>
    Signed-off-by: Network Maintainer <maintainer@kernel.org>

Key elements:

  • Fixes: tag points to the commit that introduced the bug
  • Cc: stable indicates the fix should be backported
  • Signed-off-by chain shows review process

Extracting the Patch

# Get the patch for a specific commit
git format-patch -1 abc123def456 --stdout > fix-cve-2024-xxxx.patch

# Or export a range of related commits
git format-patch abc123^..def456 -o patches/

Examine the patch before applying:

# View patch statistics
diffstat fix-cve-2024-xxxx.patch

# Check which files are modified
grep "^diff --git" fix-cve-2024-xxxx.patch

Backporting to the Raspberry Pi Kernel

Setting Up the Workspace

# Clone Pi kernel if not already done
git clone --depth=1 --branch rpi-6.6.y \
    https://github.com/raspberrypi/linux.git rpi-linux
cd rpi-linux

# Add mainline as a remote for fetching fixes
git remote add mainline https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
git remote add stable https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

# Fetch stable branch metadata (not full history)
git fetch stable linux-6.6.y --depth=100

Clean Cherry-Pick (Ideal Case)

When the patch applies cleanly:

# Create a branch for your security fixes
git checkout -b security-patches-$(date +%Y%m%d)

# Cherry-pick the fix commit from stable
git cherry-pick -x abc123def456

# The -x flag adds "cherry picked from commit..." to the message

If successful, the patch is now in your tree. Skip to the verification section.

Handling Conflicts

Real-world backporting often involves conflicts due to code drift between mainline and the Pi tree.

git cherry-pick -x abc123def456
# CONFLICT (content): Merge conflict in net/core/skbuff.c
# error: could not apply abc123...

Resolving Conflicts:

# View the conflict
git diff

# Open conflicted file
nano net/core/skbuff.c

You'll see conflict markers:

<<<<<<< HEAD
    // Pi kernel version of the code
    skb = alloc_skb(size, GFP_KERNEL);
    if (!skb)
        return -ENOMEM;
=======
    // Mainline version with the fix
    skb = alloc_skb(size, GFP_KERNEL);
    if (!skb)
        return -ENOMEM;
    skb_get(skb);  // <- This is the security fix
>>>>>>> abc123... net: fix use-after-free

Resolution Strategy:

  1. Understand the fix: What does skb_get(skb) do? It increments the reference count.

  2. Understand the context: Why does the Pi kernel differ? Maybe function signatures changed, or the Pi has additional error handling.

  3. Apply the fix logic, not literal code: The fix adds a reference count increment. Apply that concept to the Pi kernel's code structure.

    // Resolved: Pi kernel code with the fix applied
    skb = alloc_skb(size, GFP_KERNEL);
    if (!skb)
        return -ENOMEM;
    skb_get(skb);  // Security fix: prevent use-after-free
# Mark resolved
git add net/core/skbuff.c

# Continue cherry-pick
git cherry-pick --continue

Multi-Commit Fixes

Some vulnerabilities require multiple commits. Apply them in order:

# List the fix series
git log --oneline abc123^..def789

# Apply the series
git cherry-pick -x abc123^..def789

# Or apply individually for complex conflicts
for commit in abc123 bcd234 cde345 def456; do
    git cherry-pick -x $commit || {
        echo "Conflict in $commit - resolve and run: git cherry-pick --continue"
        break
    }
done

Using Quilt for Patch Management

For managing multiple patches over time, quilt provides better tracking:

# Install quilt
sudo apt install quilt

# Initialize quilt in kernel tree
cd rpi-linux
mkdir -p patches
echo "patches" > .pc/.quilt_patches

# Import a patch
quilt import ../fix-cve-2024-xxxx.patch
quilt push

# If conflicts, edit and refresh
quilt push -f
# ... resolve conflicts ...
quilt refresh

# List applied patches
quilt series

Building and Testing the Patched Kernel

Compile with the Fix

export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-

# Use your existing config
make bcm2712_defconfig

# Build
make -j$(nproc) Image.gz modules dtbs

Verify the Fix is Included

# Check the patch is in the git log
git log --oneline -5
# Should show your cherry-picked commit

# Verify the fixed code is in the binary (if symbols available)
# Recompile with CONFIG_KALLSYMS=y
grep "skb_get" System.map

Testing the Fix

Static Analysis:

# Run sparse (semantic parser)
make C=2 net/core/skbuff.o

# Run coccinelle checks if available
make coccicheck MODE=report M=net/core/

Runtime Testing:

Deploy to a test Pi 5 first:

# Copy to test Pi
scp arch/arm64/boot/Image.gz pi@test-pi:/tmp/
ssh pi@test-pi "sudo cp /tmp/Image.gz /boot/firmware/kernel8.img && sudo reboot"

Trigger the Vulnerability (Safely):

For network vulnerabilities, use a fuzzer or PoC in a controlled environment:

# Example: Test with network stress
sudo apt install netperf
netperf -H test-pi -t TCP_STREAM -l 60

# Monitor for kernel warnings
ssh pi@test-pi "dmesg -w" &

Verify the Fix:

# Check for KASAN reports (if enabled)
ssh pi@test-pi "dmesg | grep -i kasan"

# Check for general warnings
ssh pi@test-pi "dmesg | grep -iE 'warning|bug|error|oops'"

Documenting Your Patches

Maintain a record of applied security patches:

# Create a patches directory in your tree
mkdir -p Documentation/security-patches

# Document each patch
cat > Documentation/security-patches/CVE-2024-XXXX.md << 'EOF'
# CVE-2024-XXXX: Use-after-free in network stack

## Summary
- **CVE ID**: CVE-2024-XXXX
- **Severity**: High (CVSS 7.8)
- **Affected**: net/core/skbuff.c
- **Fix Commit**: abc123def456 (mainline)
- **Applied**: 2024-01-20
- **Tested**: Yes (netperf stress test)

## Description
A use-after-free vulnerability in sk_buff handling allows
remote attackers to cause denial of service or potentially
execute arbitrary code via crafted network packets.

## Verification
- Kernel builds successfully
- No KASAN reports under stress testing
- Network performance unchanged
EOF

Automating Security Updates

Patch Tracking Script

#!/bin/bash
# track-security-fixes.sh

PI_KERNEL="$HOME/rpi-linux"
STABLE_BRANCH="linux-6.6.y"

cd "$PI_KERNEL"

# Fetch latest stable
git fetch stable $STABLE_BRANCH

# Find security-relevant commits not in Pi tree
git log --oneline stable/$STABLE_BRANCH ^HEAD --grep="Cc: stable" | \
while read hash msg; do
    # Check if already applied
    if ! git log --oneline HEAD | grep -q "${msg:0:60}"; then
        echo "Missing: $hash $msg"
    fi
done

CI Integration

Add to your CI pipeline (GitHub Actions example):

name: Security Patch Check

on:
  schedule:
    - cron: '0 6 * * *'  # Daily at 6 AM

jobs:
  check-patches:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Check for missing security fixes
        run: |
          git remote add stable https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
          git fetch stable linux-6.6.y --depth=50

          MISSING=$(git log --oneline stable/linux-6.6.y ^HEAD --grep="CVE-" | wc -l)
          if [ "$MISSING" -gt 0 ]; then
            echo "::warning::$MISSING security commits missing from this tree"
            git log --oneline stable/linux-6.6.y ^HEAD --grep="CVE-"
          fi

Best Practices

Do's

  • Always test on non-production hardware first - A bad patch can brick your Pi
  • Keep your patch branch separate - Don't pollute the upstream tracking branch
  • Document everything - Future you will thank present you
  • Subscribe to security lists - Early warning is critical
  • Verify the fix source - Only apply patches from kernel.org or trusted maintainers

Don'ts

  • Don't blindly apply patches - Understand what they fix and why
  • Don't skip testing - Even "obvious" fixes can have unexpected interactions
  • Don't ignore conflicts - A misresolved conflict can introduce new vulnerabilities
  • Don't forget to update - One patch doesn't make you secure forever

Conclusion

Applying upstream security patches to your Raspberry Pi 5 kernel is a critical skill for maintaining a secure system. The process requires:

  1. Monitoring CVE announcements and stable kernel releases
  2. Identifying which vulnerabilities affect your configuration
  3. Locating and extracting fix commits from mainline/stable
  4. Carefully backporting patches, resolving conflicts when necessary
  5. Thorough testing before production deployment

Combined with custom kernel building, you can maintain a Pi 5 that's both optimized for your workload and protected against known vulnerabilities—without waiting for upstream distribution updates.

Stay vigilant, patch promptly, and always verify your fixes.

← Back to Blog