Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
0004  */
0005 
0006 /*
0007  * livepatch-callbacks-demo.c - (un)patching callbacks livepatch demo
0008  *
0009  *
0010  * Purpose
0011  * -------
0012  *
0013  * Demonstration of registering livepatch (un)patching callbacks.
0014  *
0015  *
0016  * Usage
0017  * -----
0018  *
0019  * Step 1 - load the simple module
0020  *
0021  *   insmod samples/livepatch/livepatch-callbacks-mod.ko
0022  *
0023  *
0024  * Step 2 - load the demonstration livepatch (with callbacks)
0025  *
0026  *   insmod samples/livepatch/livepatch-callbacks-demo.ko
0027  *
0028  *
0029  * Step 3 - cleanup
0030  *
0031  *   echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
0032  *   rmmod livepatch_callbacks_demo
0033  *   rmmod livepatch_callbacks_mod
0034  *
0035  * Watch dmesg output to see livepatch enablement, callback execution
0036  * and patching operations for both vmlinux and module targets.
0037  *
0038  * NOTE: swap the insmod order of livepatch-callbacks-mod.ko and
0039  *       livepatch-callbacks-demo.ko to observe what happens when a
0040  *       target module is loaded after a livepatch with callbacks.
0041  *
0042  * NOTE: 'pre_patch_ret' is a module parameter that sets the pre-patch
0043  *       callback return status.  Try setting up a non-zero status
0044  *       such as -19 (-ENODEV):
0045  *
0046  *       # Load demo livepatch, vmlinux is patched
0047  *       insmod samples/livepatch/livepatch-callbacks-demo.ko
0048  *
0049  *       # Setup next pre-patch callback to return -ENODEV
0050  *       echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret
0051  *
0052  *       # Module loader refuses to load the target module
0053  *       insmod samples/livepatch/livepatch-callbacks-mod.ko
0054  *       insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device
0055  *
0056  * NOTE: There is a second target module,
0057  *       livepatch-callbacks-busymod.ko, available for experimenting
0058  *       with livepatch (un)patch callbacks.  This module contains
0059  *       a 'sleep_secs' parameter that parks the module on one of the
0060  *       functions that the livepatch demo module wants to patch.
0061  *       Modifying this value and tweaking the order of module loads can
0062  *       effectively demonstrate stalled patch transitions:
0063  *
0064  *       # Load a target module, let it park on 'busymod_work_func' for
0065  *       # thirty seconds
0066  *       insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=30
0067  *
0068  *       # Meanwhile load the livepatch
0069  *       insmod samples/livepatch/livepatch-callbacks-demo.ko
0070  *
0071  *       # ... then load and unload another target module while the
0072  *       # transition is in progress
0073  *       insmod samples/livepatch/livepatch-callbacks-mod.ko
0074  *       rmmod samples/livepatch/livepatch-callbacks-mod.ko
0075  *
0076  *       # Finally cleanup
0077  *       echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
0078  *       rmmod samples/livepatch/livepatch-callbacks-demo.ko
0079  */
0080 
0081 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0082 
0083 #include <linux/module.h>
0084 #include <linux/kernel.h>
0085 #include <linux/livepatch.h>
0086 
0087 static int pre_patch_ret;
0088 module_param(pre_patch_ret, int, 0644);
0089 MODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (default=0)");
0090 
0091 static const char *const module_state[] = {
0092     [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state",
0093     [MODULE_STATE_COMING]   = "[MODULE_STATE_COMING] Full formed, running module_init",
0094     [MODULE_STATE_GOING]    = "[MODULE_STATE_GOING] Going away",
0095     [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up",
0096 };
0097 
0098 static void callback_info(const char *callback, struct klp_object *obj)
0099 {
0100     if (obj->mod)
0101         pr_info("%s: %s -> %s\n", callback, obj->mod->name,
0102             module_state[obj->mod->state]);
0103     else
0104         pr_info("%s: vmlinux\n", callback);
0105 }
0106 
0107 /* Executed on object patching (ie, patch enablement) */
0108 static int pre_patch_callback(struct klp_object *obj)
0109 {
0110     callback_info(__func__, obj);
0111     return pre_patch_ret;
0112 }
0113 
0114 /* Executed on object unpatching (ie, patch disablement) */
0115 static void post_patch_callback(struct klp_object *obj)
0116 {
0117     callback_info(__func__, obj);
0118 }
0119 
0120 /* Executed on object unpatching (ie, patch disablement) */
0121 static void pre_unpatch_callback(struct klp_object *obj)
0122 {
0123     callback_info(__func__, obj);
0124 }
0125 
0126 /* Executed on object unpatching (ie, patch disablement) */
0127 static void post_unpatch_callback(struct klp_object *obj)
0128 {
0129     callback_info(__func__, obj);
0130 }
0131 
0132 static void patched_work_func(struct work_struct *work)
0133 {
0134     pr_info("%s\n", __func__);
0135 }
0136 
0137 static struct klp_func no_funcs[] = {
0138     { }
0139 };
0140 
0141 static struct klp_func busymod_funcs[] = {
0142     {
0143         .old_name = "busymod_work_func",
0144         .new_func = patched_work_func,
0145     }, { }
0146 };
0147 
0148 static struct klp_object objs[] = {
0149     {
0150         .name = NULL,   /* vmlinux */
0151         .funcs = no_funcs,
0152         .callbacks = {
0153             .pre_patch = pre_patch_callback,
0154             .post_patch = post_patch_callback,
0155             .pre_unpatch = pre_unpatch_callback,
0156             .post_unpatch = post_unpatch_callback,
0157         },
0158     },  {
0159         .name = "livepatch_callbacks_mod",
0160         .funcs = no_funcs,
0161         .callbacks = {
0162             .pre_patch = pre_patch_callback,
0163             .post_patch = post_patch_callback,
0164             .pre_unpatch = pre_unpatch_callback,
0165             .post_unpatch = post_unpatch_callback,
0166         },
0167     },  {
0168         .name = "livepatch_callbacks_busymod",
0169         .funcs = busymod_funcs,
0170         .callbacks = {
0171             .pre_patch = pre_patch_callback,
0172             .post_patch = post_patch_callback,
0173             .pre_unpatch = pre_unpatch_callback,
0174             .post_unpatch = post_unpatch_callback,
0175         },
0176     }, { }
0177 };
0178 
0179 static struct klp_patch patch = {
0180     .mod = THIS_MODULE,
0181     .objs = objs,
0182 };
0183 
0184 static int livepatch_callbacks_demo_init(void)
0185 {
0186     return klp_enable_patch(&patch);
0187 }
0188 
0189 static void livepatch_callbacks_demo_exit(void)
0190 {
0191 }
0192 
0193 module_init(livepatch_callbacks_demo_init);
0194 module_exit(livepatch_callbacks_demo_exit);
0195 MODULE_LICENSE("GPL");
0196 MODULE_INFO(livepatch, "Y");