(Un)patching Callbacks¶
Livepatch (un)patch-callbacks provide a mechanism for livepatch modulesto execute callback functions when a kernel object is (un)patched. Theycan be considered apower feature thatextends livepatching abilitiesto include:
Safe updates to global data
“Patches” to init and probe functions
Patching otherwise unpatchable code (i.e. assembly)
In most cases, (un)patch callbacks will need to be used in conjunctionwith memory barriers and kernel synchronization primitives, likemutexes/spinlocks, or evenstop_machine(), to avoid concurrency issues.
1. Motivation¶
Callbacks differ from existing kernel facilities:
Module init/exit code doesn’t run when disabling and re-enabling apatch.
A module notifier can’t stop a to-be-patched module from loading.
Callbacks are part of the klp_object structure and their implementationis specific to that klp_object. Other livepatch objects may or may notbe patched, irrespective of the target klp_object’s current state.
2. Callback types¶
Callbacks can be registered for the following livepatch actions:
- Pre-patch
before a klp_object is patched
- Post-patch
after a klp_object has been patched and is activeacross all tasks
- Pre-unpatch
before a klp_object is unpatched (ie, patched code isactive), used to clean up post-patch callbackresources
- Post-unpatch
after a klp_object has been patched, all code hasbeen restored and no tasks are running patched code,used to cleanup pre-patch callback resources
3. How it works¶
Each callback is optional, omitting one does not preclude specifying anyother. However, the livepatching core executes the handlers insymmetry: pre-patch callbacks have a post-unpatch counterpart andpost-patch callbacks have a pre-unpatch counterpart. An unpatchcallback will only be executed if its corresponding patch callback wasexecuted. Typical use cases pair a patch handler that acquires andconfigures resources with an unpatch handler tears down and releasesthose same resources.
A callback is only executed if its host klp_object is loaded. Forin-kernel vmlinux targets, this means that callbacks will always executewhen a livepatch is enabled/disabled. For patch target kernel modules,callbacks will only execute if the target module is loaded. When amodule target is (un)loaded, its callbacks will execute only if thelivepatch module is enabled.
The pre-patch callback, if specified, is expected to return a statuscode (0 for success, -ERRNO on error). An error status code indicatesto the livepatching core that patching of the current klp_object is notsafe and to stop the current patching request. (When no pre-patchcallback is provided, the transition is assumed to be safe.) If apre-patch callback returns failure, the kernel’s module loader will:
Refuse to load a livepatch, if the livepatch is loaded aftertargeted code.
or:
Refuse to load a module, if the livepatch was already successfullyloaded.
No post-patch, pre-unpatch, or post-unpatch callbacks will be executedfor a given klp_object if the object failed to patch, due to a failedpre_patch callback or for any other reason.
If a patch transition is reversed, no pre-unpatch handlers will be run(this follows the previously mentioned symmetry -- pre-unpatch callbackswill only occur if their corresponding post-patch callback executed).
If the object did successfully patch, but the patch transition neverstarted for some reason (e.g., if another object failed to patch),only the post-unpatch callback will be called.
4. Use cases¶
Sample livepatch modules demonstrating the callback API can be found insamples/livepatch/ directory. These samples were modified for use inkselftests and can be found in the lib/livepatch directory.
Global data update¶
A pre-patch callback can be useful to update a global variable. Forexample,commit 75ff39ccc1bd (“tcp: make challenge acks less predictable”)changes a global sysctl, as well as patches thetcp_send_challenge_ack()function.
In this case, if we’re being super paranoid, it might make sense topatch the dataafter patching is complete with a post-patch callback,so thattcp_send_challenge_ack() could first be changed to readsysctl_tcp_challenge_ack_limit with READ_ONCE.
__init and probe function patches support¶
Although __init and probe functions are not directly livepatch-able, itmay be possible to implement similar updates via pre/post-patchcallbacks.
Thecommit 48900cb6af42 (“virtio-net: drop NETIF_F_FRAGLIST”) change the way thatvirtnet_probe() initialized its driver’s net_device features. Apre/post-patch callback could iterate over all such devices, making asimilar change to their hw_features value. (Client functions of thevalue may need to be updated accordingly.)