Live Patching

On this page

When the function entry point is switched, the old stack frames currently executing do not jump into the new implementation. The core of livepatch is compatibility between old and new logic during the transition, not merely redirecting the entry point.

It applies to fixes that have been specifically designed and verified; it is not a general-purpose online version upgrader. Patches must still match the target build and configuration.

Review ObjectiveReading Location
When task switching is allowedFunction Entry, Task Consistency, and Transition
How state continues to be compatibleCumulative Patches, State Lifecycle
How to stop and verifyDisable/Unload, PREEMPT_RT Boundaries

Function Entry and Task Consistency

Livepatch requires migrating tasks to a consistent code version Prepare new patch and redirect entry points Parse target functions, install controlled redirections 1 Tasks reach safe switching points Reliable stack checks, user-space boundaries, and other mechanisms 2 Maintain version consistency during transition Tasks may temporarily be in different patch states 3 All conversions completed Then handle the lifecycle of corresponding old patches and states 4 Force is not waiting for a faster safe mode; skipping consistency checks may break correctness and unloading conditions.

Common implementations rely on dynamic ftrace to redirect at the function entry point, with the livepatch handler selecting the appropriate version based on the task's patch state, among other factors. Stack frames currently executing an old function will not automatically jump into the middle of the new implementation just because the entry point was changed.

The kernel uses a consistency model to determine when tasks can be switched. Reliable stack checks can identify whether a task is still within an affected call chain, and returning to user space or other safe boundaries also help advance the state; kernel threads that remain for a long time or paths lacking reliable stack information may hinder completion. Livepatch

It is incorrect to generalize the implementation into three fictional generic enum modes: IDLE, FORCE, and IMMEDIATE. In reality, one should understand the interface states such as enabled, transition, and force, along with the specific kernel model.

Before Transition Completion, Old and New Versions May Coexist

Transition StateWhat the Patch Must Be Compatible With
Task A has switched to the new versionNew logic accessing shared state
Task B is still in the old call chainOld layout and old lock ordering may still be used
All related tasks complete the transitionOnly then can subsequent stages be handled according to completion conditions

Shared data formats and cross-function invariants must cover the coexistence phase, not just be designed for the final state.

If a task long blocks the transition, analyze its stack and wait reasons. Auxiliary wake-ups or signals do not equate to being able to force the safe evacuation of any currently executing function. force bypasses corresponding safety guarantees and affects subsequent unloading capabilities; it should not be treated as a simple timeout retry button.

Why A and B Temporarily Choose Different Versions at the Same Entry

Assume the patch updates both update and lookup. Task A has the old update in its stack while sleeping, while Task B's reliable sleep stack does not contain these two functions. Task A temporarily retains the old patch state, while Task B can switch first; subsequent calls to redirection still need to select based on each task's state. Otherwise, Task A might suddenly call the new lookup within an old call chain, breaking the conventions maintained by both.

Transition chooses per task; old stack frames must finish Task A: old update on stack Keep v0; later lookup uses v0 Task B: reliable unaffected stack May use v1; lookup follows v1 A can join v1 after leaving the old chain and passing a safe boundary Redirection does not rewrite active frames; tasks can still share mutable data.

The model below demonstrates only reliable sleep stack checks and return-to-user-space boundaries. It assumes affected fully lists the functions that need to be replaced together, and does not simulate ftrace, preemption, stack unwinding, or kernel state bits.

from dataclasses import dataclass

@dataclass
class Task:
    version: int
    stack: tuple
    sleeping: bool = True
    reliable: bool = True

    def try_stack_switch(self, affected, target):
        if not self.sleeping or not self.reliable:
            return False
        if any(function in affected for function in self.stack):
            return False
        self.version = target
        return True

    def return_to_user(self, target):
        self.stack = ()
        self.version = target  # This method represents having reached the user-space return boundary

    def implementation(self, name):
        return f"{name}_v{self.version}"

affected = {"update", "lookup"}
a = Task(0, ("request", "update"))
b = Task(0, ("unrelated_wait",))
c = Task(0, (), reliable=False)
assert not a.try_stack_switch(affected, 1)
assert b.try_stack_switch(affected, 1)
assert not c.try_stack_switch(affected, 1)
assert a.implementation("lookup") == "lookup_v0"
assert b.implementation("lookup") == "lookup_v1"
assert not all(t.version == 1 for t in (a, b, c))
a.stack = ("request",)  # Old update has returned; other stack frames in this example are unaffected
assert a.try_stack_switch(affected, 1)
c.return_to_user(1)
assert all(t.version == 1 for t in (a, b, c))
assert not Task(0, (), sleeping=False).try_stack_switch(affected, 1)

Although Task C's stack list is empty, "unreliable unwinding didn't see the target function" does not equal "reliably proven that the target function does not exist." Therefore, the model refuses to switch based solely on that result and waits for another known safe boundary. The actual protocol must also prevent races between the check and the task continuing execution; user space cannot read /proc/PID/stack once and then arbitrarily rewrite the task's patch state. Livepatch Consistency Model

These per-task rules do not automatically solve shared data compatibility across tasks. Patch authors must also check whether the old logic of A and the new logic of B satisfy lock ordering, object interpretation, and call contracts when running simultaneously. Transition completion is a necessary state indicator and cannot replace these semantic conditions.

Keep one task inside the old function while enabling the patch. Existing frames retain their version, and reverse transitions and unloading have their own waiting conditions.

Preparing the visual
Does changing the target version move an existing stack frame?

Entry redirection does not rewrite active frames. Task versions can coexist during transition; disabling requires a reverse transition before patch-code unloading.

Cumulative Patches and Atomic Replace

Overlapping multiple independent patches increases the complexity of ordering and compatibility. Cumulative patches can include a set of existing fixes, using atomic replace to substitute the previous patch set during a consistent transition.

Here, atomic does not mean all CPUs rewrite all instructions at the exact same instant, nor does it provide arbitrary data rollback like a database transaction. It refers to the mechanism for replacing the patch set, which still requires completing task conversion and maintaining system state. Livepatch Documentation Index

What It Means If the New Cumulative Patch Does Not List g

Suppose the original kernel provides f and g; P1 fixes f, and P2 fixes g. Now use P3 with replace semantics to replace the old set, but P3 only contains the new implementation of f. After the transition completes, g will revert to the original implementation, rather than automatically retaining P2's fix.

Completed ConfigurationWhat f UsesWhat g Uses
P1 and P2 are activeP1's fP2's g
P3 contains only f, replacing the old setP3's fOriginal g
P3 contains both required f and g fixesP3's fP3's g
base = {"f": "base_f", "g": "base_g"}
p1 = {"f": "fix_f1"}
p2 = {"g": "fix_g1"}
p3 = {"f": "fix_f2"}
old_active = base | p1 | p2
replacement = base | p3
assert old_active["g"] == "fix_g1"
assert replacement == {"f": "fix_f2", "g": "base_g"}
p3_cumulative = {"f": "fix_f2", "g": "fix_g1"}
assert (base | p3_cumulative)["g"] == "fix_g1"
# Only compares the function set after completion; does not simulate transition or callbacks.
disabled_replacement = base.copy()
assert disabled_replacement != old_active

Therefore, cumulative patches must include all fixes that still need to be retained. Disabling the new patch does not automatically atomically restore the old patch set; when rolling back, you must prepare a replacement scheme compatible with the current state.

Furthermore, atomic replace primarily executes the relevant callbacks of the new cumulative patch; it does not execute cleanup for each replaced patch in the imagined order. The new patch needs to take over necessary shared state; when the old module exits, it cannot blindly release shadow variables that the new patch is still using. Atomic Replace and Cumulative Patches

Stateful Fixes Require Explicit Lifecycle Design

You cannot simply replace a structure layout currently in use with any new layout; old objects still exist according to the original layout. However, stating that "no stateful patches are possible" is also inaccurate: shadow variables, callbacks, and system-state tracking can support designed state changes.

Patches need to handle first enablement, replacing old patches, undoing transitions, disabling, and failure cleanup. The id/version in klp_state are used to track compatible states and do not automatically understand how business objects are converted. System State Changes

Even With Unchanged Field Widths, State Migration May Be Required

Suppose an old object's timeout=100 represents 100 ms, and the new code wants to change the same integer to be in microseconds. Even if the structure size, field offsets, and function signatures remain unchanged, the two sets of code interpret the same bit pattern differently:

Object Content and ReaderActual InterpretationDifference from Original 100 ms
Old object value 100, read by new code as μs0.1 msShortened by 1000x
New code writes 100000, read by old code as ms100 sExtended by 1000x

Simply waiting for all tasks to complete the code transition will not automatically change the 100 in existing objects to 100000. During the transition, the old field can be retained as the common authoritative representation, allowing the new code to convert at boundaries; to change the persistent representation, compatible version identifiers, object update protocols, and appropriate conversion timing are required. Additional state tools provide storage and tracking mechanisms but do not decide the conversion order or rollback semantics for the author. Livepatch System State Changes

When Replacing Functions, the Caller's Contract Must Also Hold

Function signatures and calling conventions must be compatible with retained callers. When a fix involves parameters, object lifecycles, or cross-module calls, the scope may be far larger than replacing a single function; it cannot be considered safe just because redirection is possible.

Disable and Unload Are Not the Same Step

Regular patches can request disablement and complete the reverse transition, and then unload the relevant module once conditions are met; the statement "livepatch modules can never be unloaded" is an overgeneralization. States such as still in use, transition not complete, or forced transition will restrict unloading, and actual interface conditions must be followed.

Read-only observation can start with the enabled and transition attributes in each patch directory under /sys/kernel/livepatch. The absence of this directory or entry may indicate that support is not enabled or the patch is not loaded; this cannot be used to infer whether the system has fixed a specific vulnerability; distributions may also fix vulnerabilities through regular kernel updates.

PREEMPT_RT Is a Separate Issue

PREEMPT_RT primarily reduces scheduling latency caused by non-preemptible sections, involving mechanisms such as threaded interrupts, sleepable locks, and priority inheritance. It is not a mode of livepatch, nor can all preempt-disable operations be generalized as replacements with rt_mutex. Real-Time Kernel Mechanisms

Modern mainline kernels provide PREEMPT_RT configuration capabilities; architecture support and driver constraints still need to be checked against the current target. Do not retain outdated time descriptions like "6.12 is close to merge completion," nor make unsourced promises that a specific minor version solves all livepatch/RT interactions.

Verifying hotpatching should cover the target workload, transition completion, disablement, and state compatibility paths. Successful builds or sysfs showing enabled do not replace functional regression testing; related methods can be found in Debugging and Testing.