Skip to content

Kernel Live Patching (KLP)

Applying security fixes and bug fixes to a running kernel without rebooting

What KLP does

KLP redirects calls from the original (buggy) function to a replacement (patched) function at runtime, using ftrace's function entry hook:

Before patch:           After patch:
  caller                  caller
    │                       │
    call orig_func          call orig_func
                              │ (function entry hook)
                              └─► NEW: jump to patched_func
                                        └─► executes patch

The original function's first bytes are never modified. The ftrace hook at function entry branches to the replacement.

Architecture

/* include/linux/livepatch.h */

/* Describes one patched function */
struct klp_func {
    const char      *old_name;      /* name of function to patch */
    void            *new_func;      /* replacement function */
    unsigned long    old_sympos;    /* which occurrence (for duplicates) */
    unsigned long    old_addr;      /* resolved at patch time */
    struct kobject   kobj;
    struct list_head node;
    struct list_head stack_node;     /* position in klp_ops->func_stack */
    bool             nop;           /* no-op patch (for rollback testing) */
    bool             patched;       /* currently active */
    bool             transition;    /* in consistency transition */
};

/* A set of functions that form one complete patch */
struct klp_patch {
    struct module   *mod;           /* the live patch module */
    struct klp_object *objs;        /* array of objects (modules/vmlinux) */
    bool             enabled;
    bool             forced;        /* forced (skipped consistency check) */
    struct work_struct free_work;
    struct completion finish;
    struct kobject   kobj;
    struct list_head list;
};

/* Functions grouped by the kernel module (or vmlinux) they patch */
struct klp_object {
    const char      *name;          /* NULL for vmlinux */
    struct klp_func *funcs;         /* array of functions to patch */
    struct module   *mod;           /* resolved target module */
    struct kobject   kobj;
    struct list_head node;
    bool             dynamic;       /* for dynamically added funcs */
};

Writing a live patch module

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/livepatch.h>

/* The replacement function */
static int patched_vfs_read(struct file *file, char __user *buf,
                             size_t count, loff_t *pos)
{
    /* New implementation with the bug fixed */
    if (!file || !file->f_op)
        return -EBADF;   /* was: NULL deref here */

    /* Re-implement the fixed logic entirely — KLP has no klp_call_orig() */
    return orig_vfs_read_impl(file, buf, count, pos);
}

/* Patch descriptor */
static struct klp_func funcs[] = {
    {
        .old_name = "vfs_read",
        .new_func = patched_vfs_read,
    },
    {}  /* sentinel */
};

static struct klp_object objs[] = {
    {
        .name  = NULL,  /* NULL = patch vmlinux */
        .funcs = funcs,
    },
    {}  /* sentinel */
};

static struct klp_patch patch = {
    .mod  = THIS_MODULE,
    .objs = objs,
};

static int __init livepatch_init(void)
{
    return klp_enable_patch(&patch);
}

static void __exit livepatch_exit(void)
{
    /* KLP modules are sticky by default — can only unload after disabling */
}

module_init(livepatch_init);
module_exit(livepatch_exit);
MODULE_INFO(livepatch, "Y");
MODULE_LICENSE("GPL");

Calling the original function

The KLP API does not provide a klp_call_orig() helper. A live patch replacement must either fully re-implement the function or use klp_shadow_* to carry extra state. If you need access to the original logic, copy it into the patch module and apply only the targeted fix.

ftrace-based redirection

KLP uses ftrace's FTRACE_OPS_FL_SAVE_REGS to redirect function calls:

/* kernel/livepatch/patch.c */
static void notrace klp_ftrace_handler(unsigned long ip,
                                         unsigned long parent_ip,
                                         struct ftrace_ops *fops,
                                         struct ftrace_regs *fregs)
{
    struct klp_ops *ops;
    struct klp_func *func;
    int patch_state;

    ops = container_of(fops, struct klp_ops, fops);

    /* Find the active patch for this function */
    func = list_first_or_null_rcu(&ops->func_stack, struct klp_func, stack_node);
    if (!func)
        return;

    /* Check consistency: is this task in a safe state? */
    patch_state = current->patch_state;
    if (patch_state == KLP_UNDEFINED)
        patch_state = current->patch_state = KLP_UNPATCHED;

    if (func->transition) {
        if (patch_state == KLP_UNPATCHED)
            return;  /* task not yet transitioned */
    }

    /* Redirect: change IP to point to patched function */
    klp_arch_set_pc(fregs, (unsigned long)func->new_func);
}

The consistency model

KLP can't simply redirect calls immediately — a task might be in the middle of executing the old function. The consistency model ensures all tasks have transitioned before the patch is considered active:

State: KLP_UNPATCHED → (patch applied) → KLP_PATCHED

For each task:
  1. When task returns to userspace (schedule point), mark as KLP_PATCHED
  2. Tasks already in kernel get KLP_PATCHED at their next schedule
  3. Until all tasks are KLP_PATCHED, the patch is in "transition" state

During transition:
  - New task entries: use new (patched) function
  - Tasks already running in old function: continue until they return
  - Patch isn't "complete" until all tasks are transitioned
# Check transition status
cat /sys/kernel/livepatch/mypatch/transition
# 1 = still transitioning (some tasks not yet patched)
# 0 = complete

# Force completion (skips consistency check — may be unsafe!)
echo 1 > /sys/kernel/livepatch/mypatch/force

Sleepy tasks

A task blocked in D state (uninterruptible sleep) inside the old function will delay the transition. The kernel uses klp_send_signals() to wake such tasks and a periodic workqueue to re-check transition completion:

# If transition is stuck: find who's blocking it
cat /sys/kernel/livepatch/mypatch/transition
# 1

# Find tasks in the old function's call stack
ps aux | grep ' D '  # uninterruptible sleep tasks
cat /proc/<pid>/stack  # check if in the old function

Shadow variables

Shadow variables attach extra data to existing kernel objects without modifying their structure — useful when a patch needs to add state:

/* Add a "magic" field to struct file without changing the struct */
#define KLP_SHADOW_MAGIC 0xdeadbeef

struct my_shadow_data {
    u32 magic;
    int new_state;
};

/* On first use: allocate and attach shadow data */
struct my_shadow_data *data;
data = klp_shadow_get_or_alloc(file,  /* attached to this object */
                                 KLP_SHADOW_MAGIC,
                                 sizeof(*data), GFP_KERNEL,
                                 shadow_init_fn, NULL);
data->new_state = 42;

/* On subsequent uses: retrieve */
data = klp_shadow_get(file, KLP_SHADOW_MAGIC);

/* On cleanup (e.g., file close): free */
klp_shadow_free(file, KLP_SHADOW_MAGIC, NULL);

Shadow data is stored in a global hash table keyed by (object pointer, ID).

Building a live patch

# Kernel build tree required
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules

# Or: use kpatch-build (automated live patch creation)
kpatch-build -t vmlinux fix.patch

# Install
insmod mypatch.ko
lsmod | grep livepatch

# Verify active
cat /sys/kernel/livepatch/mypatch/enabled
# 1

Observing live patches

# All active patches
for p in /sys/kernel/livepatch/*/; do
    echo "Patch: $(basename $p)"
    echo "  enabled: $(cat $p/enabled)"
    echo "  transition: $(cat $p/transition)"
done

# Which functions are patched
cat /sys/kernel/livepatch/*/objs/*/funcs/*/patched
cat /sys/kernel/livepatch/*/objs/*/funcs/*/old_addr

# Kernel taint from live patch
cat /proc/sys/kernel/tainted
# bit 15 set (TAINT_LIVEPATCH: KLP patch applied)

# Verify with dmesg
dmesg | grep livepatch
# livepatch: enabling patch 'mypatch'
# livepatch: 'mypatch': starting patching transition
# livepatch: 'mypatch': patching complete

Further reading

  • kexec — loading and booting a new kernel
  • Kernel Modules — KLP uses the module system
  • Tracing: ftrace — ftrace function hooks used by KLP
  • kernel/livepatch/ in the kernel tree — KLP implementation
  • Documentation/livepatch/ in the kernel tree