linux/samples/livepatch/livepatch-callbacks-demo.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
   4 */
   5
   6/*
   7 * livepatch-callbacks-demo.c - (un)patching callbacks livepatch demo
   8 *
   9 *
  10 * Purpose
  11 * -------
  12 *
  13 * Demonstration of registering livepatch (un)patching callbacks.
  14 *
  15 *
  16 * Usage
  17 * -----
  18 *
  19 * Step 1 - load the simple module
  20 *
  21 *   insmod samples/livepatch/livepatch-callbacks-mod.ko
  22 *
  23 *
  24 * Step 2 - load the demonstration livepatch (with callbacks)
  25 *
  26 *   insmod samples/livepatch/livepatch-callbacks-demo.ko
  27 *
  28 *
  29 * Step 3 - cleanup
  30 *
  31 *   echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
  32 *   rmmod livepatch_callbacks_demo
  33 *   rmmod livepatch_callbacks_mod
  34 *
  35 * Watch dmesg output to see livepatch enablement, callback execution
  36 * and patching operations for both vmlinux and module targets.
  37 *
  38 * NOTE: swap the insmod order of livepatch-callbacks-mod.ko and
  39 *       livepatch-callbacks-demo.ko to observe what happens when a
  40 *       target module is loaded after a livepatch with callbacks.
  41 *
  42 * NOTE: 'pre_patch_ret' is a module parameter that sets the pre-patch
  43 *       callback return status.  Try setting up a non-zero status
  44 *       such as -19 (-ENODEV):
  45 *
  46 *       # Load demo livepatch, vmlinux is patched
  47 *       insmod samples/livepatch/livepatch-callbacks-demo.ko
  48 *
  49 *       # Setup next pre-patch callback to return -ENODEV
  50 *       echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret
  51 *
  52 *       # Module loader refuses to load the target module
  53 *       insmod samples/livepatch/livepatch-callbacks-mod.ko
  54 *       insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device
  55 *
  56 * NOTE: There is a second target module,
  57 *       livepatch-callbacks-busymod.ko, available for experimenting
  58 *       with livepatch (un)patch callbacks.  This module contains
  59 *       a 'sleep_secs' parameter that parks the module on one of the
  60 *       functions that the livepatch demo module wants to patch.
  61 *       Modifying this value and tweaking the order of module loads can
  62 *       effectively demonstrate stalled patch transitions:
  63 *
  64 *       # Load a target module, let it park on 'busymod_work_func' for
  65 *       # thirty seconds
  66 *       insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=30
  67 *
  68 *       # Meanwhile load the livepatch
  69 *       insmod samples/livepatch/livepatch-callbacks-demo.ko
  70 *
  71 *       # ... then load and unload another target module while the
  72 *       # transition is in progress
  73 *       insmod samples/livepatch/livepatch-callbacks-mod.ko
  74 *       rmmod samples/livepatch/livepatch-callbacks-mod.ko
  75 *
  76 *       # Finally cleanup
  77 *       echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
  78 *       rmmod samples/livepatch/livepatch-callbacks-demo.ko
  79 */
  80
  81#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  82
  83#include <linux/module.h>
  84#include <linux/kernel.h>
  85#include <linux/livepatch.h>
  86
  87static int pre_patch_ret;
  88module_param(pre_patch_ret, int, 0644);
  89MODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (default=0)");
  90
  91static const char *const module_state[] = {
  92        [MODULE_STATE_LIVE]     = "[MODULE_STATE_LIVE] Normal state",
  93        [MODULE_STATE_COMING]   = "[MODULE_STATE_COMING] Full formed, running module_init",
  94        [MODULE_STATE_GOING]    = "[MODULE_STATE_GOING] Going away",
  95        [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up",
  96};
  97
  98static void callback_info(const char *callback, struct klp_object *obj)
  99{
 100        if (obj->mod)
 101                pr_info("%s: %s -> %s\n", callback, obj->mod->name,
 102                        module_state[obj->mod->state]);
 103        else
 104                pr_info("%s: vmlinux\n", callback);
 105}
 106
 107/* Executed on object patching (ie, patch enablement) */
 108static int pre_patch_callback(struct klp_object *obj)
 109{
 110        callback_info(__func__, obj);
 111        return pre_patch_ret;
 112}
 113
 114/* Executed on object unpatching (ie, patch disablement) */
 115static void post_patch_callback(struct klp_object *obj)
 116{
 117        callback_info(__func__, obj);
 118}
 119
 120/* Executed on object unpatching (ie, patch disablement) */
 121static void pre_unpatch_callback(struct klp_object *obj)
 122{
 123        callback_info(__func__, obj);
 124}
 125
 126/* Executed on object unpatching (ie, patch disablement) */
 127static void post_unpatch_callback(struct klp_object *obj)
 128{
 129        callback_info(__func__, obj);
 130}
 131
 132static void patched_work_func(struct work_struct *work)
 133{
 134        pr_info("%s\n", __func__);
 135}
 136
 137static struct klp_func no_funcs[] = {
 138        { }
 139};
 140
 141static struct klp_func busymod_funcs[] = {
 142        {
 143                .old_name = "busymod_work_func",
 144                .new_func = patched_work_func,
 145        }, { }
 146};
 147
 148static struct klp_object objs[] = {
 149        {
 150                .name = NULL,   /* vmlinux */
 151                .funcs = no_funcs,
 152                .callbacks = {
 153                        .pre_patch = pre_patch_callback,
 154                        .post_patch = post_patch_callback,
 155                        .pre_unpatch = pre_unpatch_callback,
 156                        .post_unpatch = post_unpatch_callback,
 157                },
 158        },      {
 159                .name = "livepatch_callbacks_mod",
 160                .funcs = no_funcs,
 161                .callbacks = {
 162                        .pre_patch = pre_patch_callback,
 163                        .post_patch = post_patch_callback,
 164                        .pre_unpatch = pre_unpatch_callback,
 165                        .post_unpatch = post_unpatch_callback,
 166                },
 167        },      {
 168                .name = "livepatch_callbacks_busymod",
 169                .funcs = busymod_funcs,
 170                .callbacks = {
 171                        .pre_patch = pre_patch_callback,
 172                        .post_patch = post_patch_callback,
 173                        .pre_unpatch = pre_unpatch_callback,
 174                        .post_unpatch = post_unpatch_callback,
 175                },
 176        }, { }
 177};
 178
 179static struct klp_patch patch = {
 180        .mod = THIS_MODULE,
 181        .objs = objs,
 182};
 183
 184static int livepatch_callbacks_demo_init(void)
 185{
 186        return klp_enable_patch(&patch);
 187}
 188
 189static void livepatch_callbacks_demo_exit(void)
 190{
 191}
 192
 193module_init(livepatch_callbacks_demo_init);
 194module_exit(livepatch_callbacks_demo_exit);
 195MODULE_LICENSE("GPL");
 196MODULE_INFO(livepatch, "Y");
 197