From 439e7271dc2b63de379e37971dc2f64d71e24f8a Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Thu, 31 Aug 2017 16:37:41 -0400 Subject: livepatch: introduce shadow variable API Add exported API for livepatch modules: klp_shadow_get() klp_shadow_alloc() klp_shadow_get_or_alloc() klp_shadow_free() klp_shadow_free_all() that implement "shadow" variables, which allow callers to associate new shadow fields to existing data structures. This is intended to be used by livepatch modules seeking to emulate additions to data structure definitions. See Documentation/livepatch/shadow-vars.txt for a summary of the new shadow variable API, including a few common use cases. See samples/livepatch/livepatch-shadow-* for example modules that demonstrate shadow variables. [jkosina@suse.cz: fix __klp_shadow_get_or_alloc() comment as spotted by Josh] Signed-off-by: Joe Lawrence Acked-by: Josh Poimboeuf Acked-by: Miroslav Benes Signed-off-by: Jiri Kosina --- Documentation/livepatch/shadow-vars.txt | 192 ++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 Documentation/livepatch/shadow-vars.txt (limited to 'Documentation') diff --git a/Documentation/livepatch/shadow-vars.txt b/Documentation/livepatch/shadow-vars.txt new file mode 100644 index 000000000000..e3ffc4301bfa --- /dev/null +++ b/Documentation/livepatch/shadow-vars.txt @@ -0,0 +1,192 @@ +================ +Shadow Variables +================ + +Shadow variables are a simple way for livepatch modules to associate +additional "shadow" data with existing data structures. Shadow data is +allocated separately from parent data structures, which are left +unmodified. The shadow variable API described in this document is used +to allocate/attach and detach/release shadow variables to their parents. + +The implementation introduces a global, in-kernel hashtable that +associates pointers to parent objects and a numeric identifier of the +shadow data. The numeric identifier is a simple enumeration that may be +used to describe shadow variable version, class or type, etc. More +specifically, the parent pointer serves as the hashtable key while the +numeric id subsequently filters hashtable queries. Multiple shadow +variables may attach to the same parent object, but their numeric +identifier distinguishes between them. + + +1. Brief API summary +==================== + +(See the full API usage docbook notes in livepatch/shadow.c.) + +A hashtable references all shadow variables. These references are +stored and retrieved through a pair. + +* The klp_shadow variable data structure encapsulates both tracking +meta-data and shadow-data: + - meta-data + - obj - pointer to parent object + - id - data identifier + - data[] - storage for shadow data + +It is important to note that the klp_shadow_alloc() and +klp_shadow_get_or_alloc() calls, described below, store a *copy* of the +data that the functions are provided. Callers should provide whatever +mutual exclusion is required of the shadow data. + +* klp_shadow_get() - retrieve a shadow variable data pointer + - search hashtable for pair + +* klp_shadow_alloc() - allocate and add a new shadow variable + - search hashtable for pair + - if exists + - WARN and return NULL + - if doesn't already exist + - allocate a new shadow variable + - copy data into the new shadow variable + - add to the global hashtable + +* klp_shadow_get_or_alloc() - get existing or alloc a new shadow variable + - search hashtable for pair + - if exists + - return existing shadow variable + - if doesn't already exist + - allocate a new shadow variable + - copy data into the new shadow variable + - add pair to the global hashtable + +* klp_shadow_free() - detach and free a shadow variable + - find and remove a reference from global hashtable + - if found, free shadow variable + +* klp_shadow_free_all() - detach and free all <*, id> shadow variables + - find and remove any <*, id> references from global hashtable + - if found, free shadow variable + + +2. Use cases +============ + +(See the example shadow variable livepatch modules in samples/livepatch/ +for full working demonstrations.) + +For the following use-case examples, consider commit 1d147bfa6429 +("mac80211: fix AP powersave TX vs. wakeup race"), which added a +spinlock to net/mac80211/sta_info.h :: struct sta_info. Each use-case +example can be considered a stand-alone livepatch implementation of this +fix. + + +Matching parent's lifecycle +--------------------------- + +If parent data structures are frequently created and destroyed, it may +be easiest to align their shadow variables lifetimes to the same +allocation and release functions. In this case, the parent data +structure is typically allocated, initialized, then registered in some +manner. Shadow variable allocation and setup can then be considered +part of the parent's initialization and should be completed before the +parent "goes live" (ie, any shadow variable get-API requests are made +for this pair.) + +For commit 1d147bfa6429, when a parent sta_info structure is allocated, +allocate a shadow copy of the ps_lock pointer, then initialize it: + +#define PS_LOCK 1 +struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, + const u8 *addr, gfp_t gfp) +{ + struct sta_info *sta; + spinlock_t *ps_lock; + + /* Parent structure is created */ + sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp); + + /* Attach a corresponding shadow variable, then initialize it */ + ps_lock = klp_shadow_alloc(sta, PS_LOCK, NULL, sizeof(ps_lock), gfp); + if (!ps_lock) + goto shadow_fail; + spin_lock_init(ps_lock); + ... + +When requiring a ps_lock, query the shadow variable API to retrieve one +for a specific struct sta_info: + +void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) +{ + spinlock_t *ps_lock; + + /* sync with ieee80211_tx_h_unicast_ps_buf */ + ps_lock = klp_shadow_get(sta, PS_LOCK); + if (ps_lock) + spin_lock(ps_lock); + ... + +When the parent sta_info structure is freed, first free the shadow +variable: + +void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) +{ + klp_shadow_free(sta, PS_LOCK); + kfree(sta); + ... + + +In-flight parent objects +------------------------ + +Sometimes it may not be convenient or possible to allocate shadow +variables alongside their parent objects. Or a livepatch fix may +require shadow varibles to only a subset of parent object instances. In +these cases, the klp_shadow_get_or_alloc() call can be used to attach +shadow variables to parents already in-flight. + +For commit 1d147bfa6429, a good spot to allocate a shadow spinlock is +inside ieee80211_sta_ps_deliver_wakeup(): + +#define PS_LOCK 1 +void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) +{ + DEFINE_SPINLOCK(ps_lock_fallback); + spinlock_t *ps_lock; + + /* sync with ieee80211_tx_h_unicast_ps_buf */ + ps_lock = klp_shadow_get_or_alloc(sta, PS_LOCK, + &ps_lock_fallback, sizeof(ps_lock_fallback), + GFP_ATOMIC); + if (ps_lock) + spin_lock(ps_lock); + ... + +This usage will create a shadow variable, only if needed, otherwise it +will use one that was already created for this pair. + +Like the previous use-case, the shadow spinlock needs to be cleaned up. +A shadow variable can be freed just before its parent object is freed, +or even when the shadow variable itself is no longer required. + + +Other use-cases +--------------- + +Shadow variables can also be used as a flag indicating that a data +structure was allocated by new, livepatched code. In this case, it +doesn't matter what data value the shadow variable holds, its existence +suggests how to handle the parent object. + + +3. References +============= + +* https://github.com/dynup/kpatch +The livepatch implementation is based on the kpatch version of shadow +variables. + +* http://files.mkgnu.net/files/dynamos/doc/papers/dynamos_eurosys_07.pdf +Dynamic and Adaptive Updates of Non-Quiescent Subsystems in Commodity +Operating System Kernels (Kritis Makris, Kyung Dong Ryu 2007) presented +a datatype update technique called "shadow data structures". -- cgit From 19205da6a0da701787d42ad754edd1ffb514c956 Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Mon, 25 Sep 2017 17:41:59 +0200 Subject: livepatch: Small shadow variable documentation fixes The description of the basic operations was a bit inconsistent and based on older version of the patchset. Also the size of the spinlock structure should be allocated instead of the pointer. Signed-off-by: Petr Mladek Acked-by: Joe Lawrence Signed-off-by: Jiri Kosina --- Documentation/livepatch/shadow-vars.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/livepatch/shadow-vars.txt b/Documentation/livepatch/shadow-vars.txt index e3ffc4301bfa..89c66634d600 100644 --- a/Documentation/livepatch/shadow-vars.txt +++ b/Documentation/livepatch/shadow-vars.txt @@ -6,7 +6,7 @@ Shadow variables are a simple way for livepatch modules to associate additional "shadow" data with existing data structures. Shadow data is allocated separately from parent data structures, which are left unmodified. The shadow variable API described in this document is used -to allocate/attach and detach/release shadow variables to their parents. +to allocate/add and remove/free shadow variables to/from their parents. The implementation introduces a global, in-kernel hashtable that associates pointers to parent objects and a numeric identifier of the @@ -107,7 +107,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp); /* Attach a corresponding shadow variable, then initialize it */ - ps_lock = klp_shadow_alloc(sta, PS_LOCK, NULL, sizeof(ps_lock), gfp); + ps_lock = klp_shadow_alloc(sta, PS_LOCK, NULL, sizeof(*ps_lock), gfp); if (!ps_lock) goto shadow_fail; spin_lock_init(ps_lock); -- cgit From 93862e385ded7c60351e09fcd2a541d273650905 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Fri, 13 Oct 2017 15:08:41 -0400 Subject: livepatch: add (un)patch callbacks Provide livepatch modules a klp_object (un)patching notification mechanism. Pre and post-(un)patch callbacks allow livepatch modules to setup or synchronize changes that would be difficult to support in only patched-or-unpatched code contexts. Callbacks can be registered for target module or vmlinux klp_objects, but each implementation is klp_object specific. - Pre-(un)patch callbacks run before any (un)patching transition starts. - Post-(un)patch callbacks run once an object has been (un)patched and the klp_patch fully transitioned to its target state. Example use cases include modification of global data and registration of newly available services/handlers. See Documentation/livepatch/callbacks.txt for details and samples/livepatch/ for examples. Signed-off-by: Joe Lawrence Acked-by: Josh Poimboeuf Acked-by: Miroslav Benes Signed-off-by: Jiri Kosina --- Documentation/livepatch/callbacks.txt | 605 ++++++++++++++++++++++++++++++++++ 1 file changed, 605 insertions(+) create mode 100644 Documentation/livepatch/callbacks.txt (limited to 'Documentation') diff --git a/Documentation/livepatch/callbacks.txt b/Documentation/livepatch/callbacks.txt new file mode 100644 index 000000000000..c9776f48e458 --- /dev/null +++ b/Documentation/livepatch/callbacks.txt @@ -0,0 +1,605 @@ +====================== +(Un)patching Callbacks +====================== + +Livepatch (un)patch-callbacks provide a mechanism for livepatch modules +to execute callback functions when a kernel object is (un)patched. They +can be considered a "power feature" that extends livepatching abilities +to 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 conjunction +with memory barriers and kernel synchronization primitives, like +mutexes/spinlocks, or even stop_machine(), to avoid concurrency issues. + +Callbacks differ from existing kernel facilities: + + - Module init/exit code doesn't run when disabling and re-enabling a + patch. + + - A module notifier can't stop a to-be-patched module from loading. + +Callbacks are part of the klp_object structure and their implementation +is specific to that klp_object. Other livepatch objects may or may not +be patched, irrespective of the target klp_object's current state. + +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 active + across all tasks + + * Pre-unpatch - before a klp_object is unpatched (ie, patched code is + active), used to clean up post-patch callback + resources + + * Post-unpatch - after a klp_object has been patched, all code has + been restored and no tasks are running patched code, + used to cleanup pre-patch callback resources + +Each callback is optional, omitting one does not preclude specifying any +other. However, the livepatching core executes the handlers in +symmetry: pre-patch callbacks have a post-unpatch counterpart and +post-patch callbacks have a pre-unpatch counterpart. An unpatch +callback will only be executed if its corresponding patch callback was +executed. Typical use cases pair a patch handler that acquires and +configures resources with an unpatch handler tears down and releases +those same resources. + +A callback is only executed if its host klp_object is loaded. For +in-kernel vmlinux targets, this means that callbacks will always execute +when a livepatch is enabled/disabled. For patch target kernel modules, +callbacks will only execute if the target module is loaded. When a +module target is (un)loaded, its callbacks will execute only if the +livepatch module is enabled. + +The pre-patch callback, if specified, is expected to return a status +code (0 for success, -ERRNO on error). An error status code indicates +to the livepatching core that patching of the current klp_object is not +safe and to stop the current patching request. (When no pre-patch +callback is provided, the transition is assumed to be safe.) If a +pre-patch callback returns failure, the kernel's module loader will: + + - Refuse to load a livepatch, if the livepatch is loaded after + targeted code. + + or: + + - Refuse to load a module, if the livepatch was already successfully + loaded. + +No post-patch, pre-unpatch, or post-unpatch callbacks will be executed +for a given klp_object if the object failed to patch, due to a failed +pre_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 callbacks +will only occur if their corresponding post-patch callback executed). + +If the object did successfully patch, but the patch transition never +started for some reason (e.g., if another object failed to patch), +only the post-unpatch callback will be called. + + +Example Use-cases +================= + +Update global data +------------------ + +A pre-patch callback can be useful to update a global variable. For +example, 75ff39ccc1bd ("tcp: make challenge acks less predictable") +changes a global sysctl, as well as patches the tcp_send_challenge_ack() +function. + +In this case, if we're being super paranoid, it might make sense to +patch the data *after* patching is complete with a post-patch callback, +so that tcp_send_challenge_ack() could first be changed to read +sysctl_tcp_challenge_ack_limit with READ_ONCE. + + +Support __init and probe function patches +----------------------------------------- + +Although __init and probe functions are not directly livepatch-able, it +may be possible to implement similar updates via pre/post-patch +callbacks. + +48900cb6af42 ("virtio-net: drop NETIF_F_FRAGLIST") change the way that +virtnet_probe() initialized its driver's net_device features. A +pre/post-patch callback could iterate over all such devices, making a +similar change to their hw_features value. (Client functions of the +value may need to be updated accordingly.) + + +Test cases +========== + +What follows is not an exhaustive test suite of every possible livepatch +pre/post-(un)patch combination, but a selection that demonstrates a few +important concepts. Each test case uses the kernel modules located in +the samples/livepatch/ and assumes that no livepatches are loaded at the +beginning of the test. + + +Test 1 +------ + +Test a combination of loading a kernel module and a livepatch that +patches a function in the first module. (Un)load the target module +before the livepatch module: + +- load target module +- load livepatch +- disable livepatch +- unload target module +- unload livepatch + +First load a target module: + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 34.475708] livepatch_callbacks_mod: livepatch_callbacks_mod_init + +On livepatch enable, before the livepatch transition starts, pre-patch +callbacks are executed for vmlinux and livepatch_callbacks_mod (those +klp_objects currently loaded). After klp_objects are patched according +to the klp_patch, their post-patch callbacks run and the transition +completes: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 36.503719] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 36.504213] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 36.504238] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 36.504721] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 36.505849] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 37.727133] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 37.727232] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 37.727860] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 37.728792] livepatch: 'livepatch_callbacks_demo': patching complete + +Similarly, on livepatch disable, pre-patch callbacks run before the +unpatching transition starts. klp_objects are reverted, post-patch +callbacks execute and the transition completes: + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 38.510209] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 38.510234] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 38.510982] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 38.512209] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 39.711132] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 39.711210] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 39.711779] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 39.712735] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + % rmmod samples/livepatch/livepatch-callbacks-mod.ko + [ 42.534183] livepatch_callbacks_mod: livepatch_callbacks_mod_exit + + +Test 2 +------ + +This test is similar to the previous test, but (un)load the livepatch +module before the target kernel module. This tests the livepatch core's +module_coming handler: + +- load livepatch +- load target module +- disable livepatch +- unload livepatch +- unload target module + + +On livepatch enable, only pre/post-patch callbacks are executed for +currently loaded klp_objects, in this case, vmlinux: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 44.553328] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 44.553997] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 44.554049] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 44.554845] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 45.727128] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 45.727212] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 45.727961] livepatch: 'livepatch_callbacks_demo': patching complete + +When a targeted module is subsequently loaded, only its pre/post-patch +callbacks are executed: + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 46.560845] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' + [ 46.561988] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 46.563452] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 46.565495] livepatch_callbacks_mod: livepatch_callbacks_mod_init + +On livepatch disable, all currently loaded klp_objects' (vmlinux and +livepatch_callbacks_mod) pre/post-unpatch callbacks are executed: + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 48.568885] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 48.568910] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 48.569441] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 48.570502] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 49.759091] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 49.759171] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 49.759742] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 49.760690] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + % rmmod samples/livepatch/livepatch-callbacks-mod.ko + [ 52.592283] livepatch_callbacks_mod: livepatch_callbacks_mod_exit + + +Test 3 +------ + +Test loading the livepatch after a targeted kernel module, then unload +the kernel module before disabling the livepatch. This tests the +livepatch core's module_going handler: + +- load target module +- load livepatch +- unload target module +- disable livepatch +- unload livepatch + +First load a target module, then the livepatch: + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 54.607948] livepatch_callbacks_mod: livepatch_callbacks_mod_init + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 56.613919] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 56.614411] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 56.614436] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 56.614818] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 56.615656] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 57.759070] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 57.759147] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 57.759621] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 57.760307] livepatch: 'livepatch_callbacks_demo': patching complete + +When a target module is unloaded, the livepatch is only reverted from +that klp_object (livepatch_callbacks_mod). As such, only its pre and +post-unpatch callbacks are executed when this occurs: + + % rmmod samples/livepatch/livepatch-callbacks-mod.ko + [ 58.623409] livepatch_callbacks_mod: livepatch_callbacks_mod_exit + [ 58.623903] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away + [ 58.624658] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod' + [ 58.625305] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away + +When the livepatch is disabled, pre and post-unpatch callbacks are run +for the remaining klp_object, vmlinux: + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 60.638420] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 60.638444] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 60.638996] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 61.727088] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 61.727165] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 61.727985] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + + +Test 4 +------ + +This test is similar to the previous test, however the livepatch is +loaded first. This tests the livepatch core's module_coming and +module_going handlers: + +- load livepatch +- load target module +- unload target module +- disable livepatch +- unload livepatch + +First load the livepatch: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 64.661552] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 64.662147] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 64.662175] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 64.662850] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 65.695056] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 65.695147] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 65.695561] livepatch: 'livepatch_callbacks_demo': patching complete + +When a targeted kernel module is subsequently loaded, only its +pre/post-patch callbacks are executed: + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 66.669196] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' + [ 66.669882] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 66.670744] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 66.672873] livepatch_callbacks_mod: livepatch_callbacks_mod_init + +When the target module is unloaded, the livepatch is only reverted from +the livepatch_callbacks_mod klp_object. As such, only pre and +post-unpatch callbacks are executed when this occurs: + + % rmmod samples/livepatch/livepatch-callbacks-mod.ko + [ 68.680065] livepatch_callbacks_mod: livepatch_callbacks_mod_exit + [ 68.680688] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away + [ 68.681452] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod' + [ 68.682094] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 70.689225] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 70.689256] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 70.689882] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 71.711080] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 71.711481] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 71.711988] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + + +Test 5 +------ + +A simple test of loading a livepatch without one of its patch target +klp_objects ever loaded (livepatch_callbacks_mod): + +- load livepatch +- disable livepatch +- unload livepatch + +Load the livepatch: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 74.711081] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 74.711595] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 74.711639] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 74.712272] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 75.743137] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 75.743219] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 75.743867] livepatch: 'livepatch_callbacks_demo': patching complete + +As expected, only pre/post-(un)patch handlers are executed for vmlinux: + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 76.716254] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 76.716278] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 76.716666] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 77.727089] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 77.727194] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 77.727907] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + + +Test 6 +------ + +Test a scenario where a vmlinux pre-patch callback returns a non-zero +status (ie, failure): + +- load target module +- load livepatch -ENODEV +- unload target module + +First load a target module: + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 80.740520] livepatch_callbacks_mod: livepatch_callbacks_mod_init + +Load the livepatch module, setting its 'pre_patch_ret' value to -19 +(-ENODEV). When its vmlinux pre-patch callback executed, this status +code will propagate back to the module-loading subsystem. The result is +that the insmod command refuses to load the livepatch module: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko pre_patch_ret=-19 + [ 82.747326] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 82.747743] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 82.747767] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 82.748237] livepatch: pre-patch callback failed for object 'vmlinux' + [ 82.748637] livepatch: failed to enable patch 'livepatch_callbacks_demo' + [ 82.749059] livepatch: 'livepatch_callbacks_demo': canceling transition, going to unpatch + [ 82.749060] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 82.749868] livepatch: 'livepatch_callbacks_demo': unpatching complete + [ 82.765809] insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-demo.ko: No such device + + % rmmod samples/livepatch/livepatch-callbacks-mod.ko + [ 84.774238] livepatch_callbacks_mod: livepatch_callbacks_mod_exit + + +Test 7 +------ + +Similar to the previous test, setup a livepatch such that its vmlinux +pre-patch callback returns success. However, when a targeted kernel +module is later loaded, have the livepatch return a failing status code: + +- load livepatch +- setup -ENODEV +- load target module +- disable livepatch +- unload livepatch + +Load the livepatch, notice vmlinux pre-patch callback succeeds: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 86.787845] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 86.788325] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 86.788427] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 86.788821] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 87.711069] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 87.711143] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 87.711886] livepatch: 'livepatch_callbacks_demo': patching complete + +Set a trap so subsequent pre-patch callbacks to this livepatch will +return -ENODEV: + + % echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret + +The livepatch pre-patch callback for subsequently loaded target modules +will return failure, so the module loader refuses to load the kernel +module. Notice that no post-patch or pre/post-unpatch callbacks are +executed for this klp_object: + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 90.796976] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' + [ 90.797834] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 90.798900] livepatch: pre-patch callback failed for object 'livepatch_callbacks_mod' + [ 90.799652] livepatch: patch 'livepatch_callbacks_demo' failed for module 'livepatch_callbacks_mod', refusing to load module 'livepatch_callbacks_mod' + [ 90.819737] insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device + +However, pre/post-unpatch callbacks run for the vmlinux klp_object: + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 92.823547] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 92.823573] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 92.824331] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 93.727128] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 93.727327] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 93.727861] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + + +Test 8 +------ + +Test loading multiple targeted kernel modules. This test-case is +mainly for comparing with the next test-case. + +- load busy target module (0s sleep), +- load livepatch +- load target module +- unload target module +- disable livepatch +- unload livepatch +- unload busy target module + + +Load a target "busy" kernel module which kicks off a worker function +that immediately exits: + + % insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=0 + [ 96.910107] livepatch_callbacks_busymod: livepatch_callbacks_mod_init + [ 96.910600] livepatch_callbacks_busymod: busymod_work_func, sleeping 0 seconds ... + [ 96.913024] livepatch_callbacks_busymod: busymod_work_func exit + +Proceed with loading the livepatch and another ordinary target module, +notice that the post-patch callbacks are executed and the transition +completes quickly: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 98.917892] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 98.918426] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 98.918453] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 98.918955] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state + [ 98.923835] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 99.743104] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 99.743156] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 99.743679] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state + [ 99.744616] livepatch: 'livepatch_callbacks_demo': patching complete + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 100.930955] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' + [ 100.931668] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 100.932645] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 100.934125] livepatch_callbacks_mod: livepatch_callbacks_mod_init + + % rmmod samples/livepatch/livepatch-callbacks-mod.ko + [ 102.942805] livepatch_callbacks_mod: livepatch_callbacks_mod_exit + [ 102.943640] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away + [ 102.944585] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod' + [ 102.945455] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 104.953815] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 104.953838] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 104.954431] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state + [ 104.955426] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 106.719073] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 106.722633] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 106.723282] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state + [ 106.724279] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + % rmmod samples/livepatch/livepatch-callbacks-busymod.ko + [ 108.975660] livepatch_callbacks_busymod: livepatch_callbacks_mod_exit + + +Test 9 +------ + +A similar test as the previous one, but force the "busy" kernel module +to do longer work. + +The livepatching core will refuse to patch a task that is currently +executing a to-be-patched function -- the consistency model stalls the +current patch transition until this safety-check is met. Test a +scenario where one of a livepatch's target klp_objects sits on such a +function for a long time. Meanwhile, load and unload other target +kernel modules while the livepatch transition is in progress. + +- load busy target module (30s sleep) +- load livepatch +- load target module +- unload target module +- disable livepatch +- unload livepatch +- unload busy target module + + +Load the "busy" kernel module, this time make it do 30 seconds worth of +work: + + % insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=30 + [ 110.993362] livepatch_callbacks_busymod: livepatch_callbacks_mod_init + [ 110.994059] livepatch_callbacks_busymod: busymod_work_func, sleeping 30 seconds ... + +Meanwhile, the livepatch is loaded. Notice that the patch transition +does not complete as the targeted "busy" module is sitting on a +to-be-patched function: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 113.000309] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 113.000764] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 113.000791] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 113.001289] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state + [ 113.005208] livepatch: 'livepatch_callbacks_demo': starting patching transition + +Load a second target module (this one is an ordinary idle kernel +module). Note that *no* post-patch callbacks will be executed while the +livepatch is still in transition: + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 115.012740] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' + [ 115.013406] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 115.015315] livepatch_callbacks_mod: livepatch_callbacks_mod_init + +Request an unload of the simple kernel module. The patch is still +transitioning, so its pre-unpatch callbacks are skipped: + + % rmmod samples/livepatch/livepatch-callbacks-mod.ko + [ 117.022626] livepatch_callbacks_mod: livepatch_callbacks_mod_exit + [ 117.023376] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod' + [ 117.024533] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away + +Finally the livepatch is disabled. Since none of the patch's +klp_object's post-patch callbacks executed, the remaining klp_object's +pre-unpatch callbacks are skipped: + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 119.035408] livepatch: 'livepatch_callbacks_demo': reversing transition from patching to unpatching + [ 119.035485] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 119.711166] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 119.714179] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 119.714653] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state + [ 119.715437] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + % rmmod samples/livepatch/livepatch-callbacks-busymod.ko + [ 141.279111] livepatch_callbacks_busymod: busymod_work_func exit + [ 141.279760] livepatch_callbacks_busymod: livepatch_callbacks_mod_exit -- cgit