Merge branch 'for-linus' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching

Pull livepatching updates from Jiri Kosina:

 - shadow variables support, allowing livepatches to associate new
   "shadow" fields to existing data structures, from Joe Lawrence

 - pre/post patch callbacks API, allowing livepatch writers to register
   callbacks to be called before and after patch application, from Joe
   Lawrence

* 'for-linus' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching:
  livepatch: __klp_disable_patch() should never be called for disabled patches
  livepatch: Correctly call klp_post_unpatch_callback() in error paths
  livepatch: add transition notices
  livepatch: move transition "complete" notice into klp_complete_transition()
  livepatch: add (un)patch callbacks
  livepatch: Small shadow variable documentation fixes
  livepatch: __klp_shadow_get_or_alloc() is local to shadow.c
  livepatch: introduce shadow variable API
This commit is contained in:
Linus Torvalds 2017-11-15 10:21:58 -08:00
commit 0ef76878cf
17 changed files with 2162 additions and 21 deletions

View File

@ -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

View File

@ -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/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
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 <obj, id> 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 <obj, id> pair
* klp_shadow_alloc() - allocate and add a new shadow variable
- search hashtable for <obj, id> pair
- if exists
- WARN and return NULL
- if <obj, id> doesn't already exist
- allocate a new shadow variable
- copy data into the new shadow variable
- add <obj, id> to the global hashtable
* klp_shadow_get_or_alloc() - get existing or alloc a new shadow variable
- search hashtable for <obj, id> pair
- if exists
- return existing shadow variable
- if <obj, id> doesn't already exist
- allocate a new shadow variable
- copy data into the new shadow variable
- add <obj, id> pair to the global hashtable
* klp_shadow_free() - detach and free a <obj, id> shadow variable
- find and remove a <obj, id> 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 <obj, id> 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 <obj, id> 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".

View File

@ -87,10 +87,35 @@ struct klp_func {
bool transition;
};
struct klp_object;
/**
* struct klp_callbacks - pre/post live-(un)patch callback structure
* @pre_patch: executed before code patching
* @post_patch: executed after code patching
* @pre_unpatch: executed before code unpatching
* @post_unpatch: executed after code unpatching
* @post_unpatch_enabled: flag indicating if post-unpatch callback
* should run
*
* All callbacks are optional. Only the pre-patch callback, if provided,
* will be unconditionally executed. If the parent klp_object fails to
* patch for any reason, including a non-zero error status returned from
* the pre-patch callback, no further callbacks will be executed.
*/
struct klp_callbacks {
int (*pre_patch)(struct klp_object *obj);
void (*post_patch)(struct klp_object *obj);
void (*pre_unpatch)(struct klp_object *obj);
void (*post_unpatch)(struct klp_object *obj);
bool post_unpatch_enabled;
};
/**
* struct klp_object - kernel object structure for live patching
* @name: module name (or NULL for vmlinux)
* @funcs: function entries for functions to be patched in the object
* @callbacks: functions to be executed pre/post (un)patching
* @kobj: kobject for sysfs resources
* @mod: kernel module associated with the patched object
* (NULL for vmlinux)
@ -100,6 +125,7 @@ struct klp_object {
/* external */
const char *name;
struct klp_func *funcs;
struct klp_callbacks callbacks;
/* internal */
struct kobject kobj;
@ -164,6 +190,14 @@ static inline bool klp_have_reliable_stack(void)
IS_ENABLED(CONFIG_HAVE_RELIABLE_STACKTRACE);
}
void *klp_shadow_get(void *obj, unsigned long id);
void *klp_shadow_alloc(void *obj, unsigned long id, void *data,
size_t size, gfp_t gfp_flags);
void *klp_shadow_get_or_alloc(void *obj, unsigned long id, void *data,
size_t size, gfp_t gfp_flags);
void klp_shadow_free(void *obj, unsigned long id);
void klp_shadow_free_all(unsigned long id);
#else /* !CONFIG_LIVEPATCH */
static inline int klp_module_coming(struct module *mod) { return 0; }

View File

@ -1,3 +1,3 @@
obj-$(CONFIG_LIVEPATCH) += livepatch.o
livepatch-objs := core.o patch.o transition.o
livepatch-objs := core.o patch.o shadow.o transition.o

View File

@ -54,11 +54,6 @@ static bool klp_is_module(struct klp_object *obj)
return obj->name;
}
static bool klp_is_object_loaded(struct klp_object *obj)
{
return !obj->name || obj->mod;
}
/* sets obj->mod if object is not vmlinux and module is found */
static void klp_find_object_module(struct klp_object *obj)
{
@ -285,6 +280,11 @@ static int klp_write_object_relocations(struct module *pmod,
static int __klp_disable_patch(struct klp_patch *patch)
{
struct klp_object *obj;
if (WARN_ON(!patch->enabled))
return -EINVAL;
if (klp_transition_patch)
return -EBUSY;
@ -295,6 +295,10 @@ static int __klp_disable_patch(struct klp_patch *patch)
klp_init_transition(patch, KLP_UNPATCHED);
klp_for_each_object(patch, obj)
if (obj->patched)
klp_pre_unpatch_callback(obj);
/*
* Enforce the order of the func->transition writes in
* klp_init_transition() and the TIF_PATCH_PENDING writes in
@ -388,13 +392,18 @@ static int __klp_enable_patch(struct klp_patch *patch)
if (!klp_is_object_loaded(obj))
continue;
ret = klp_pre_patch_callback(obj);
if (ret) {
pr_warn("pre-patch callback failed for object '%s'\n",
klp_is_module(obj) ? obj->name : "vmlinux");
goto err;
}
ret = klp_patch_object(obj);
if (ret) {
pr_warn("failed to enable patch '%s'\n",
patch->mod->name);
klp_cancel_transition();
return ret;
pr_warn("failed to patch object '%s'\n",
klp_is_module(obj) ? obj->name : "vmlinux");
goto err;
}
}
@ -403,6 +412,11 @@ static int __klp_enable_patch(struct klp_patch *patch)
patch->enabled = true;
return 0;
err:
pr_warn("failed to enable patch '%s'\n", patch->mod->name);
klp_cancel_transition();
return ret;
}
/**
@ -854,9 +868,15 @@ static void klp_cleanup_module_patches_limited(struct module *mod,
* is in transition.
*/
if (patch->enabled || patch == klp_transition_patch) {
if (patch != klp_transition_patch)
klp_pre_unpatch_callback(obj);
pr_notice("reverting patch '%s' on unloading module '%s'\n",
patch->mod->name, obj->mod->name);
klp_unpatch_object(obj);
klp_post_unpatch_callback(obj);
}
klp_free_object_loaded(obj);
@ -906,13 +926,25 @@ int klp_module_coming(struct module *mod)
pr_notice("applying patch '%s' to loading module '%s'\n",
patch->mod->name, obj->mod->name);
ret = klp_pre_patch_callback(obj);
if (ret) {
pr_warn("pre-patch callback failed for object '%s'\n",
obj->name);
goto err;
}
ret = klp_patch_object(obj);
if (ret) {
pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
patch->mod->name, obj->mod->name, ret);
klp_post_unpatch_callback(obj);
goto err;
}
if (patch != klp_transition_patch)
klp_post_patch_callback(obj);
break;
}
}

View File

@ -2,6 +2,46 @@
#ifndef _LIVEPATCH_CORE_H
#define _LIVEPATCH_CORE_H
#include <linux/livepatch.h>
extern struct mutex klp_mutex;
static inline bool klp_is_object_loaded(struct klp_object *obj)
{
return !obj->name || obj->mod;
}
static inline int klp_pre_patch_callback(struct klp_object *obj)
{
int ret = 0;
if (obj->callbacks.pre_patch)
ret = (*obj->callbacks.pre_patch)(obj);
obj->callbacks.post_unpatch_enabled = !ret;
return ret;
}
static inline void klp_post_patch_callback(struct klp_object *obj)
{
if (obj->callbacks.post_patch)
(*obj->callbacks.post_patch)(obj);
}
static inline void klp_pre_unpatch_callback(struct klp_object *obj)
{
if (obj->callbacks.pre_unpatch)
(*obj->callbacks.pre_unpatch)(obj);
}
static inline void klp_post_unpatch_callback(struct klp_object *obj)
{
if (obj->callbacks.post_unpatch_enabled &&
obj->callbacks.post_unpatch)
(*obj->callbacks.post_unpatch)(obj);
obj->callbacks.post_unpatch_enabled = false;
}
#endif /* _LIVEPATCH_CORE_H */

View File

@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/bug.h>
#include <linux/printk.h>
#include "core.h"
#include "patch.h"
#include "transition.h"

277
kernel/livepatch/shadow.c Normal file
View File

@ -0,0 +1,277 @@
/*
* shadow.c - Shadow Variables
*
* Copyright (C) 2014 Josh Poimboeuf <jpoimboe@redhat.com>
* Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
* Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/**
* DOC: Shadow variable API concurrency notes:
*
* The shadow variable API provides a simple relationship between an
* <obj, id> pair and a pointer value. It is the responsibility of the
* caller to provide any mutual exclusion required of the shadow data.
*
* Once a shadow variable is attached to its parent object via the
* klp_shadow_*alloc() API calls, it is considered live: any subsequent
* call to klp_shadow_get() may then return the shadow variable's data
* pointer. Callers of klp_shadow_*alloc() should prepare shadow data
* accordingly.
*
* The klp_shadow_*alloc() API calls may allocate memory for new shadow
* variable structures. Their implementation does not call kmalloc
* inside any spinlocks, but API callers should pass GFP flags according
* to their specific needs.
*
* The klp_shadow_hash is an RCU-enabled hashtable and is safe against
* concurrent klp_shadow_free() and klp_shadow_get() operations.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/hashtable.h>
#include <linux/slab.h>
#include <linux/livepatch.h>
static DEFINE_HASHTABLE(klp_shadow_hash, 12);
/*
* klp_shadow_lock provides exclusive access to the klp_shadow_hash and
* the shadow variables it references.
*/
static DEFINE_SPINLOCK(klp_shadow_lock);
/**
* struct klp_shadow - shadow variable structure
* @node: klp_shadow_hash hash table node
* @rcu_head: RCU is used to safely free this structure
* @obj: pointer to parent object
* @id: data identifier
* @data: data area
*/
struct klp_shadow {
struct hlist_node node;
struct rcu_head rcu_head;
void *obj;
unsigned long id;
char data[];
};
/**
* klp_shadow_match() - verify a shadow variable matches given <obj, id>
* @shadow: shadow variable to match
* @obj: pointer to parent object
* @id: data identifier
*
* Return: true if the shadow variable matches.
*/
static inline bool klp_shadow_match(struct klp_shadow *shadow, void *obj,
unsigned long id)
{
return shadow->obj == obj && shadow->id == id;
}
/**
* klp_shadow_get() - retrieve a shadow variable data pointer
* @obj: pointer to parent object
* @id: data identifier
*
* Return: the shadow variable data element, NULL on failure.
*/
void *klp_shadow_get(void *obj, unsigned long id)
{
struct klp_shadow *shadow;
rcu_read_lock();
hash_for_each_possible_rcu(klp_shadow_hash, shadow, node,
(unsigned long)obj) {
if (klp_shadow_match(shadow, obj, id)) {
rcu_read_unlock();
return shadow->data;
}
}
rcu_read_unlock();
return NULL;
}
EXPORT_SYMBOL_GPL(klp_shadow_get);
static void *__klp_shadow_get_or_alloc(void *obj, unsigned long id, void *data,
size_t size, gfp_t gfp_flags, bool warn_on_exist)
{
struct klp_shadow *new_shadow;
void *shadow_data;
unsigned long flags;
/* Check if the shadow variable already exists */
shadow_data = klp_shadow_get(obj, id);
if (shadow_data)
goto exists;
/* Allocate a new shadow variable for use inside the lock below */
new_shadow = kzalloc(size + sizeof(*new_shadow), gfp_flags);
if (!new_shadow)
return NULL;
new_shadow->obj = obj;
new_shadow->id = id;
/* Initialize the shadow variable if data provided */
if (data)
memcpy(new_shadow->data, data, size);
/* Look for <obj, id> again under the lock */
spin_lock_irqsave(&klp_shadow_lock, flags);
shadow_data = klp_shadow_get(obj, id);
if (unlikely(shadow_data)) {
/*
* Shadow variable was found, throw away speculative
* allocation.
*/
spin_unlock_irqrestore(&klp_shadow_lock, flags);
kfree(new_shadow);
goto exists;
}
/* No <obj, id> found, so attach the newly allocated one */
hash_add_rcu(klp_shadow_hash, &new_shadow->node,
(unsigned long)new_shadow->obj);
spin_unlock_irqrestore(&klp_shadow_lock, flags);
return new_shadow->data;
exists:
if (warn_on_exist) {
WARN(1, "Duplicate shadow variable <%p, %lx>\n", obj, id);
return NULL;
}
return shadow_data;
}
/**
* klp_shadow_alloc() - allocate and add a new shadow variable
* @obj: pointer to parent object
* @id: data identifier
* @data: pointer to data to attach to parent
* @size: size of attached data
* @gfp_flags: GFP mask for allocation
*
* Allocates @size bytes for new shadow variable data using @gfp_flags
* and copies @size bytes from @data into the new shadow variable's own
* data space. If @data is NULL, @size bytes are still allocated, but
* no copy is performed. The new shadow variable is then added to the
* global hashtable.
*
* If an existing <obj, id> shadow variable can be found, this routine
* will issue a WARN, exit early and return NULL.
*
* Return: the shadow variable data element, NULL on duplicate or
* failure.
*/
void *klp_shadow_alloc(void *obj, unsigned long id, void *data,
size_t size, gfp_t gfp_flags)
{
return __klp_shadow_get_or_alloc(obj, id, data, size, gfp_flags, true);
}
EXPORT_SYMBOL_GPL(klp_shadow_alloc);
/**
* klp_shadow_get_or_alloc() - get existing or allocate a new shadow variable
* @obj: pointer to parent object
* @id: data identifier
* @data: pointer to data to attach to parent
* @size: size of attached data
* @gfp_flags: GFP mask for allocation
*
* Returns a pointer to existing shadow data if an <obj, id> shadow
* variable is already present. Otherwise, it creates a new shadow
* variable like klp_shadow_alloc().
*
* This function guarantees that only one shadow variable exists with
* the given @id for the given @obj. It also guarantees that the shadow
* variable will be initialized by the given @data only when it did not
* exist before.
*
* Return: the shadow variable data element, NULL on failure.
*/
void *klp_shadow_get_or_alloc(void *obj, unsigned long id, void *data,
size_t size, gfp_t gfp_flags)
{
return __klp_shadow_get_or_alloc(obj, id, data, size, gfp_flags, false);
}
EXPORT_SYMBOL_GPL(klp_shadow_get_or_alloc);
/**
* klp_shadow_free() - detach and free a <obj, id> shadow variable
* @obj: pointer to parent object
* @id: data identifier
*
* This function releases the memory for this <obj, id> shadow variable
* instance, callers should stop referencing it accordingly.
*/
void klp_shadow_free(void *obj, unsigned long id)
{
struct klp_shadow *shadow;
unsigned long flags;
spin_lock_irqsave(&klp_shadow_lock, flags);
/* Delete <obj, id> from hash */
hash_for_each_possible(klp_shadow_hash, shadow, node,
(unsigned long)obj) {
if (klp_shadow_match(shadow, obj, id)) {
hash_del_rcu(&shadow->node);
kfree_rcu(shadow, rcu_head);
break;
}
}
spin_unlock_irqrestore(&klp_shadow_lock, flags);
}
EXPORT_SYMBOL_GPL(klp_shadow_free);
/**
* klp_shadow_free_all() - detach and free all <*, id> shadow variables
* @id: data identifier
*
* This function releases the memory for all <*, id> shadow variable
* instances, callers should stop referencing them accordingly.
*/
void klp_shadow_free_all(unsigned long id)
{
struct klp_shadow *shadow;
unsigned long flags;
int i;
spin_lock_irqsave(&klp_shadow_lock, flags);
/* Delete all <*, id> from hash */
hash_for_each(klp_shadow_hash, i, shadow, node) {
if (klp_shadow_match(shadow, shadow->obj, id)) {
hash_del_rcu(&shadow->node);
kfree_rcu(shadow, rcu_head);
}
}
spin_unlock_irqrestore(&klp_shadow_lock, flags);
}
EXPORT_SYMBOL_GPL(klp_shadow_free_all);

View File

@ -82,6 +82,10 @@ static void klp_complete_transition(void)
unsigned int cpu;
bool immediate_func = false;
pr_debug("'%s': completing %s transition\n",
klp_transition_patch->mod->name,
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
if (klp_target_state == KLP_UNPATCHED) {
/*
* All tasks have transitioned to KLP_UNPATCHED so we can now
@ -109,9 +113,6 @@ static void klp_complete_transition(void)
}
}
if (klp_target_state == KLP_UNPATCHED && !immediate_func)
module_put(klp_transition_patch->mod);
/* Prevent klp_ftrace_handler() from seeing KLP_UNDEFINED state */
if (klp_target_state == KLP_PATCHED)
klp_synchronize_transition();
@ -130,6 +131,27 @@ static void klp_complete_transition(void)
}
done:
klp_for_each_object(klp_transition_patch, obj) {
if (!klp_is_object_loaded(obj))
continue;
if (klp_target_state == KLP_PATCHED)
klp_post_patch_callback(obj);
else if (klp_target_state == KLP_UNPATCHED)
klp_post_unpatch_callback(obj);
}
pr_notice("'%s': %s complete\n", klp_transition_patch->mod->name,
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
/*
* See complementary comment in __klp_enable_patch() for why we
* keep the module reference for immediate patches.
*/
if (!klp_transition_patch->immediate && !immediate_func &&
klp_target_state == KLP_UNPATCHED) {
module_put(klp_transition_patch->mod);
}
klp_target_state = KLP_UNDEFINED;
klp_transition_patch = NULL;
}
@ -145,6 +167,9 @@ void klp_cancel_transition(void)
if (WARN_ON_ONCE(klp_target_state != KLP_PATCHED))
return;
pr_debug("'%s': canceling patching transition, going to unpatch\n",
klp_transition_patch->mod->name);
klp_target_state = KLP_UNPATCHED;
klp_complete_transition();
}
@ -408,9 +433,6 @@ void klp_try_complete_transition(void)
}
success:
pr_notice("'%s': %s complete\n", klp_transition_patch->mod->name,
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
/* we're done, now cleanup the data structures */
klp_complete_transition();
}
@ -426,7 +448,8 @@ void klp_start_transition(void)
WARN_ON_ONCE(klp_target_state == KLP_UNDEFINED);
pr_notice("'%s': %s...\n", klp_transition_patch->mod->name,
pr_notice("'%s': starting %s transition\n",
klp_transition_patch->mod->name,
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
/*
@ -482,6 +505,9 @@ void klp_init_transition(struct klp_patch *patch, int state)
*/
klp_target_state = state;
pr_debug("'%s': initializing %s transition\n", patch->mod->name,
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
/*
* If the patch can be applied or reverted immediately, skip the
* per-task transitions.
@ -547,6 +573,11 @@ void klp_reverse_transition(void)
unsigned int cpu;
struct task_struct *g, *task;
pr_debug("'%s': reversing transition from %s\n",
klp_transition_patch->mod->name,
klp_target_state == KLP_PATCHED ? "patching to unpatching" :
"unpatching to patching");
klp_transition_patch->enabled = !klp_transition_patch->enabled;
klp_target_state = !klp_target_state;

View File

@ -71,11 +71,10 @@ config SAMPLE_RPMSG_CLIENT
the rpmsg bus.
config SAMPLE_LIVEPATCH
tristate "Build live patching sample -- loadable modules only"
tristate "Build live patching samples -- loadable modules only"
depends on LIVEPATCH && m
help
Builds a sample live patch that replaces the procfs handler
for /proc/cmdline to print "this has been live patched".
Build sample live patch demonstrations.
config SAMPLE_CONFIGFS
tristate "Build configfs patching sample -- loadable modules only"

View File

@ -1 +1,7 @@
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-sample.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-mod.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix1.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix2.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-demo.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-mod.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-busymod.o

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* livepatch-callbacks-busymod.c - (un)patching callbacks demo support module
*
*
* Purpose
* -------
*
* Simple module to demonstrate livepatch (un)patching callbacks.
*
*
* Usage
* -----
*
* This module is not intended to be standalone. See the "Usage"
* section of livepatch-callbacks-mod.c.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
static int sleep_secs;
module_param(sleep_secs, int, 0644);
MODULE_PARM_DESC(sleep_secs, "sleep_secs (default=0)");
static void busymod_work_func(struct work_struct *work);
static DECLARE_DELAYED_WORK(work, busymod_work_func);
static void busymod_work_func(struct work_struct *work)
{
pr_info("%s, sleeping %d seconds ...\n", __func__, sleep_secs);
msleep(sleep_secs * 1000);
pr_info("%s exit\n", __func__);
}
static int livepatch_callbacks_mod_init(void)
{
pr_info("%s\n", __func__);
schedule_delayed_work(&work,
msecs_to_jiffies(1000 * 0));
return 0;
}
static void livepatch_callbacks_mod_exit(void)
{
cancel_delayed_work_sync(&work);
pr_info("%s\n", __func__);
}
module_init(livepatch_callbacks_mod_init);
module_exit(livepatch_callbacks_mod_exit);
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,234 @@
/*
* Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* livepatch-callbacks-demo.c - (un)patching callbacks livepatch demo
*
*
* Purpose
* -------
*
* Demonstration of registering livepatch (un)patching callbacks.
*
*
* Usage
* -----
*
* Step 1 - load the simple module
*
* insmod samples/livepatch/livepatch-callbacks-mod.ko
*
*
* Step 2 - load the demonstration livepatch (with callbacks)
*
* insmod samples/livepatch/livepatch-callbacks-demo.ko
*
*
* Step 3 - cleanup
*
* echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
* rmmod livepatch_callbacks_demo
* rmmod livepatch_callbacks_mod
*
* Watch dmesg output to see livepatch enablement, callback execution
* and patching operations for both vmlinux and module targets.
*
* NOTE: swap the insmod order of livepatch-callbacks-mod.ko and
* livepatch-callbacks-demo.ko to observe what happens when a
* target module is loaded after a livepatch with callbacks.
*
* NOTE: 'pre_patch_ret' is a module parameter that sets the pre-patch
* callback return status. Try setting up a non-zero status
* such as -19 (-ENODEV):
*
* # Load demo livepatch, vmlinux is patched
* insmod samples/livepatch/livepatch-callbacks-demo.ko
*
* # Setup next pre-patch callback to return -ENODEV
* echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret
*
* # Module loader refuses to load the target module
* insmod samples/livepatch/livepatch-callbacks-mod.ko
* insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device
*
* NOTE: There is a second target module,
* livepatch-callbacks-busymod.ko, available for experimenting
* with livepatch (un)patch callbacks. This module contains
* a 'sleep_secs' parameter that parks the module on one of the
* functions that the livepatch demo module wants to patch.
* Modifying this value and tweaking the order of module loads can
* effectively demonstrate stalled patch transitions:
*
* # Load a target module, let it park on 'busymod_work_func' for
* # thirty seconds
* insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=30
*
* # Meanwhile load the livepatch
* insmod samples/livepatch/livepatch-callbacks-demo.ko
*
* # ... then load and unload another target module while the
* # transition is in progress
* insmod samples/livepatch/livepatch-callbacks-mod.ko
* rmmod samples/livepatch/livepatch-callbacks-mod.ko
*
* # Finally cleanup
* echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
* rmmod samples/livepatch/livepatch-callbacks-demo.ko
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/livepatch.h>
static int pre_patch_ret;
module_param(pre_patch_ret, int, 0644);
MODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (default=0)");
static const char *const module_state[] = {
[MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state",
[MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init",
[MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away",
[MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up",
};
static void callback_info(const char *callback, struct klp_object *obj)
{
if (obj->mod)
pr_info("%s: %s -> %s\n", callback, obj->mod->name,
module_state[obj->mod->state]);
else
pr_info("%s: vmlinux\n", callback);
}
/* Executed on object patching (ie, patch enablement) */
static int pre_patch_callback(struct klp_object *obj)
{
callback_info(__func__, obj);
return pre_patch_ret;
}
/* Executed on object unpatching (ie, patch disablement) */
static void post_patch_callback(struct klp_object *obj)
{
callback_info(__func__, obj);
}
/* Executed on object unpatching (ie, patch disablement) */
static void pre_unpatch_callback(struct klp_object *obj)
{
callback_info(__func__, obj);
}
/* Executed on object unpatching (ie, patch disablement) */
static void post_unpatch_callback(struct klp_object *obj)
{
callback_info(__func__, obj);
}
static void patched_work_func(struct work_struct *work)
{
pr_info("%s\n", __func__);
}
static struct klp_func no_funcs[] = {
{ }
};
static struct klp_func busymod_funcs[] = {
{
.old_name = "busymod_work_func",
.new_func = patched_work_func,
}, { }
};
static struct klp_object objs[] = {
{
.name = NULL, /* vmlinux */
.funcs = no_funcs,
.callbacks = {
.pre_patch = pre_patch_callback,
.post_patch = post_patch_callback,
.pre_unpatch = pre_unpatch_callback,
.post_unpatch = post_unpatch_callback,
},
}, {
.name = "livepatch_callbacks_mod",
.funcs = no_funcs,
.callbacks = {
.pre_patch = pre_patch_callback,
.post_patch = post_patch_callback,
.pre_unpatch = pre_unpatch_callback,
.post_unpatch = post_unpatch_callback,
},
}, {
.name = "livepatch_callbacks_busymod",
.funcs = busymod_funcs,
.callbacks = {
.pre_patch = pre_patch_callback,
.post_patch = post_patch_callback,
.pre_unpatch = pre_unpatch_callback,
.post_unpatch = post_unpatch_callback,
},
}, { }
};
static struct klp_patch patch = {
.mod = THIS_MODULE,
.objs = objs,
};
static int livepatch_callbacks_demo_init(void)
{
int ret;
if (!klp_have_reliable_stack() && !patch.immediate) {
/*
* WARNING: Be very careful when using 'patch.immediate' in
* your patches. It's ok to use it for simple patches like
* this, but for more complex patches which change function
* semantics, locking semantics, or data structures, it may not
* be safe. Use of this option will also prevent removal of
* the patch.
*
* See Documentation/livepatch/livepatch.txt for more details.
*/
patch.immediate = true;
pr_notice("The consistency model isn't supported for your architecture. Bypassing safety mechanisms and applying the patch immediately.\n");
}
ret = klp_register_patch(&patch);
if (ret)
return ret;
ret = klp_enable_patch(&patch);
if (ret) {
WARN_ON(klp_unregister_patch(&patch));
return ret;
}
return 0;
}
static void livepatch_callbacks_demo_exit(void)
{
WARN_ON(klp_unregister_patch(&patch));
}
module_init(livepatch_callbacks_demo_init);
module_exit(livepatch_callbacks_demo_exit);
MODULE_LICENSE("GPL");
MODULE_INFO(livepatch, "Y");

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* livepatch-callbacks-mod.c - (un)patching callbacks demo support module
*
*
* Purpose
* -------
*
* Simple module to demonstrate livepatch (un)patching callbacks.
*
*
* Usage
* -----
*
* This module is not intended to be standalone. See the "Usage"
* section of livepatch-callbacks-demo.c.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
static int livepatch_callbacks_mod_init(void)
{
pr_info("%s\n", __func__);
return 0;
}
static void livepatch_callbacks_mod_exit(void)
{
pr_info("%s\n", __func__);
}
module_init(livepatch_callbacks_mod_init);
module_exit(livepatch_callbacks_mod_exit);
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,173 @@
/*
* Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* livepatch-shadow-fix1.c - Shadow variables, livepatch demo
*
* Purpose
* -------
*
* Fixes the memory leak introduced in livepatch-shadow-mod through the
* use of a shadow variable. This fix demonstrates the "extending" of
* short-lived data structures by patching its allocation and release
* functions.
*
*
* Usage
* -----
*
* This module is not intended to be standalone. See the "Usage"
* section of livepatch-shadow-mod.c.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/livepatch.h>
#include <linux/slab.h>
/* Shadow variable enums */
#define SV_LEAK 1
/* Allocate new dummies every second */
#define ALLOC_PERIOD 1
/* Check for expired dummies after a few new ones have been allocated */
#define CLEANUP_PERIOD (3 * ALLOC_PERIOD)
/* Dummies expire after a few cleanup instances */
#define EXPIRE_PERIOD (4 * CLEANUP_PERIOD)
struct dummy {
struct list_head list;
unsigned long jiffies_expire;
};
struct dummy *livepatch_fix1_dummy_alloc(void)
{
struct dummy *d;
void *leak;
d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d)
return NULL;
d->jiffies_expire = jiffies +
msecs_to_jiffies(1000 * EXPIRE_PERIOD);
/*
* Patch: save the extra memory location into a SV_LEAK shadow
* variable. A patched dummy_free routine can later fetch this
* pointer to handle resource release.
*/
leak = kzalloc(sizeof(int), GFP_KERNEL);
klp_shadow_alloc(d, SV_LEAK, &leak, sizeof(leak), GFP_KERNEL);
pr_info("%s: dummy @ %p, expires @ %lx\n",
__func__, d, d->jiffies_expire);
return d;
}
void livepatch_fix1_dummy_free(struct dummy *d)
{
void **shadow_leak, *leak;
/*
* Patch: fetch the saved SV_LEAK shadow variable, detach and
* free it. Note: handle cases where this shadow variable does
* not exist (ie, dummy structures allocated before this livepatch
* was loaded.)
*/
shadow_leak = klp_shadow_get(d, SV_LEAK);
if (shadow_leak) {
leak = *shadow_leak;
klp_shadow_free(d, SV_LEAK);
kfree(leak);
pr_info("%s: dummy @ %p, prevented leak @ %p\n",
__func__, d, leak);
} else {
pr_info("%s: dummy @ %p leaked!\n", __func__, d);
}
kfree(d);
}
static struct klp_func funcs[] = {
{
.old_name = "dummy_alloc",
.new_func = livepatch_fix1_dummy_alloc,
},
{
.old_name = "dummy_free",
.new_func = livepatch_fix1_dummy_free,
}, { }
};
static struct klp_object objs[] = {
{
.name = "livepatch_shadow_mod",
.funcs = funcs,
}, { }
};
static struct klp_patch patch = {
.mod = THIS_MODULE,
.objs = objs,
};
static int livepatch_shadow_fix1_init(void)
{
int ret;
if (!klp_have_reliable_stack() && !patch.immediate) {
/*
* WARNING: Be very careful when using 'patch.immediate' in
* your patches. It's ok to use it for simple patches like
* this, but for more complex patches which change function
* semantics, locking semantics, or data structures, it may not
* be safe. Use of this option will also prevent removal of
* the patch.
*
* See Documentation/livepatch/livepatch.txt for more details.
*/
patch.immediate = true;
pr_notice("The consistency model isn't supported for your architecture. Bypassing safety mechanisms and applying the patch immediately.\n");
}
ret = klp_register_patch(&patch);
if (ret)
return ret;
ret = klp_enable_patch(&patch);
if (ret) {
WARN_ON(klp_unregister_patch(&patch));
return ret;
}
return 0;
}
static void livepatch_shadow_fix1_exit(void)
{
/* Cleanup any existing SV_LEAK shadow variables */
klp_shadow_free_all(SV_LEAK);
WARN_ON(klp_unregister_patch(&patch));
}
module_init(livepatch_shadow_fix1_init);
module_exit(livepatch_shadow_fix1_exit);
MODULE_LICENSE("GPL");
MODULE_INFO(livepatch, "Y");

View File

@ -0,0 +1,168 @@
/*
* Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* livepatch-shadow-fix2.c - Shadow variables, livepatch demo
*
* Purpose
* -------
*
* Adds functionality to livepatch-shadow-mod's in-flight data
* structures through a shadow variable. The livepatch patches a
* routine that periodically inspects data structures, incrementing a
* per-data-structure counter, creating the counter if needed.
*
*
* Usage
* -----
*
* This module is not intended to be standalone. See the "Usage"
* section of livepatch-shadow-mod.c.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/livepatch.h>
#include <linux/slab.h>
/* Shadow variable enums */
#define SV_LEAK 1
#define SV_COUNTER 2
struct dummy {
struct list_head list;
unsigned long jiffies_expire;
};
bool livepatch_fix2_dummy_check(struct dummy *d, unsigned long jiffies)
{
int *shadow_count;
int count;
/*
* Patch: handle in-flight dummy structures, if they do not
* already have a SV_COUNTER shadow variable, then attach a
* new one.
*/
count = 0;
shadow_count = klp_shadow_get_or_alloc(d, SV_COUNTER,
&count, sizeof(count),
GFP_NOWAIT);
if (shadow_count)
*shadow_count += 1;
return time_after(jiffies, d->jiffies_expire);
}
void livepatch_fix2_dummy_free(struct dummy *d)
{
void **shadow_leak, *leak;
int *shadow_count;
/* Patch: copy the memory leak patch from the fix1 module. */
shadow_leak = klp_shadow_get(d, SV_LEAK);
if (shadow_leak) {
leak = *shadow_leak;
klp_shadow_free(d, SV_LEAK);
kfree(leak);
pr_info("%s: dummy @ %p, prevented leak @ %p\n",
__func__, d, leak);
} else {
pr_info("%s: dummy @ %p leaked!\n", __func__, d);
}
/*
* Patch: fetch the SV_COUNTER shadow variable and display
* the final count. Detach the shadow variable.
*/
shadow_count = klp_shadow_get(d, SV_COUNTER);
if (shadow_count) {
pr_info("%s: dummy @ %p, check counter = %d\n",
__func__, d, *shadow_count);
klp_shadow_free(d, SV_COUNTER);
}
kfree(d);
}
static struct klp_func funcs[] = {
{
.old_name = "dummy_check",
.new_func = livepatch_fix2_dummy_check,
},
{
.old_name = "dummy_free",
.new_func = livepatch_fix2_dummy_free,
}, { }
};
static struct klp_object objs[] = {
{
.name = "livepatch_shadow_mod",
.funcs = funcs,
}, { }
};
static struct klp_patch patch = {
.mod = THIS_MODULE,
.objs = objs,
};
static int livepatch_shadow_fix2_init(void)
{
int ret;
if (!klp_have_reliable_stack() && !patch.immediate) {
/*
* WARNING: Be very careful when using 'patch.immediate' in
* your patches. It's ok to use it for simple patches like
* this, but for more complex patches which change function
* semantics, locking semantics, or data structures, it may not
* be safe. Use of this option will also prevent removal of
* the patch.
*
* See Documentation/livepatch/livepatch.txt for more details.
*/
patch.immediate = true;
pr_notice("The consistency model isn't supported for your architecture. Bypassing safety mechanisms and applying the patch immediately.\n");
}
ret = klp_register_patch(&patch);
if (ret)
return ret;
ret = klp_enable_patch(&patch);
if (ret) {
WARN_ON(klp_unregister_patch(&patch));
return ret;
}
return 0;
}
static void livepatch_shadow_fix2_exit(void)
{
/* Cleanup any existing SV_COUNTER shadow variables */
klp_shadow_free_all(SV_COUNTER);
WARN_ON(klp_unregister_patch(&patch));
}
module_init(livepatch_shadow_fix2_init);
module_exit(livepatch_shadow_fix2_exit);
MODULE_LICENSE("GPL");
MODULE_INFO(livepatch, "Y");

View File

@ -0,0 +1,224 @@
/*
* Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* livepatch-shadow-mod.c - Shadow variables, buggy module demo
*
* Purpose
* -------
*
* As a demonstration of livepatch shadow variable API, this module
* introduces memory leak behavior that livepatch modules
* livepatch-shadow-fix1.ko and livepatch-shadow-fix2.ko correct and
* enhance.
*
* WARNING - even though the livepatch-shadow-fix modules patch the
* memory leak, please load these modules at your own risk -- some
* amount of memory may leaked before the bug is patched.
*
*
* Usage
* -----
*
* Step 1 - Load the buggy demonstration module:
*
* insmod samples/livepatch/livepatch-shadow-mod.ko
*
* Watch dmesg output for a few moments to see new dummy being allocated
* and a periodic cleanup check. (Note: a small amount of memory is
* being leaked.)
*
*
* Step 2 - Load livepatch fix1:
*
* insmod samples/livepatch/livepatch-shadow-fix1.ko
*
* Continue watching dmesg and note that now livepatch_fix1_dummy_free()
* and livepatch_fix1_dummy_alloc() are logging messages about leaked
* memory and eventually leaks prevented.
*
*
* Step 3 - Load livepatch fix2 (on top of fix1):
*
* insmod samples/livepatch/livepatch-shadow-fix2.ko
*
* This module extends functionality through shadow variables, as a new
* "check" counter is added to the dummy structure. Periodic dmesg
* messages will log these as dummies are cleaned up.
*
*
* Step 4 - Cleanup
*
* Unwind the demonstration by disabling the livepatch fix modules, then
* removing them and the demo module:
*
* echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix2/enabled
* echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix1/enabled
* rmmod livepatch-shadow-fix2
* rmmod livepatch-shadow-fix1
* rmmod livepatch-shadow-mod
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/workqueue.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
MODULE_DESCRIPTION("Buggy module for shadow variable demo");
/* Allocate new dummies every second */
#define ALLOC_PERIOD 1
/* Check for expired dummies after a few new ones have been allocated */
#define CLEANUP_PERIOD (3 * ALLOC_PERIOD)
/* Dummies expire after a few cleanup instances */
#define EXPIRE_PERIOD (4 * CLEANUP_PERIOD)
/*
* Keep a list of all the dummies so we can clean up any residual ones
* on module exit
*/
LIST_HEAD(dummy_list);
DEFINE_MUTEX(dummy_list_mutex);
struct dummy {
struct list_head list;
unsigned long jiffies_expire;
};
noinline struct dummy *dummy_alloc(void)
{
struct dummy *d;
void *leak;
d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d)
return NULL;
d->jiffies_expire = jiffies +
msecs_to_jiffies(1000 * EXPIRE_PERIOD);
/* Oops, forgot to save leak! */
leak = kzalloc(sizeof(int), GFP_KERNEL);
pr_info("%s: dummy @ %p, expires @ %lx\n",
__func__, d, d->jiffies_expire);
return d;
}
noinline void dummy_free(struct dummy *d)
{
pr_info("%s: dummy @ %p, expired = %lx\n",
__func__, d, d->jiffies_expire);
kfree(d);
}
noinline bool dummy_check(struct dummy *d, unsigned long jiffies)
{
return time_after(jiffies, d->jiffies_expire);
}
/*
* alloc_work_func: allocates new dummy structures, allocates additional
* memory, aptly named "leak", but doesn't keep
* permanent record of it.
*/
static void alloc_work_func(struct work_struct *work);
static DECLARE_DELAYED_WORK(alloc_dwork, alloc_work_func);
static void alloc_work_func(struct work_struct *work)
{
struct dummy *d;
d = dummy_alloc();
if (!d)
return;
mutex_lock(&dummy_list_mutex);
list_add(&d->list, &dummy_list);
mutex_unlock(&dummy_list_mutex);
schedule_delayed_work(&alloc_dwork,
msecs_to_jiffies(1000 * ALLOC_PERIOD));
}
/*
* cleanup_work_func: frees dummy structures. Without knownledge of
* "leak", it leaks the additional memory that
* alloc_work_func created.
*/
static void cleanup_work_func(struct work_struct *work);
static DECLARE_DELAYED_WORK(cleanup_dwork, cleanup_work_func);
static void cleanup_work_func(struct work_struct *work)
{
struct dummy *d, *tmp;
unsigned long j;
j = jiffies;
pr_info("%s: jiffies = %lx\n", __func__, j);
mutex_lock(&dummy_list_mutex);
list_for_each_entry_safe(d, tmp, &dummy_list, list) {
/* Kick out and free any expired dummies */
if (dummy_check(d, j)) {
list_del(&d->list);
dummy_free(d);
}
}
mutex_unlock(&dummy_list_mutex);
schedule_delayed_work(&cleanup_dwork,
msecs_to_jiffies(1000 * CLEANUP_PERIOD));
}
static int livepatch_shadow_mod_init(void)
{
schedule_delayed_work(&alloc_dwork,
msecs_to_jiffies(1000 * ALLOC_PERIOD));
schedule_delayed_work(&cleanup_dwork,
msecs_to_jiffies(1000 * CLEANUP_PERIOD));
return 0;
}
static void livepatch_shadow_mod_exit(void)
{
struct dummy *d, *tmp;
/* Wait for any dummies at work */
cancel_delayed_work_sync(&alloc_dwork);
cancel_delayed_work_sync(&cleanup_dwork);
/* Cleanup residual dummies */
list_for_each_entry_safe(d, tmp, &dummy_list, list) {
list_del(&d->list);
dummy_free(d);
}
}
module_init(livepatch_shadow_mod_init);
module_exit(livepatch_shadow_mod_exit);