Path: blob/master/samples/livepatch/livepatch-callbacks-demo.c
26278 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Copyright (C) 2017 Joe Lawrence <[email protected]>3*/45/*6* livepatch-callbacks-demo.c - (un)patching callbacks livepatch demo7*8*9* Purpose10* -------11*12* Demonstration of registering livepatch (un)patching callbacks.13*14*15* Usage16* -----17*18* Step 1 - load the simple module19*20* insmod samples/livepatch/livepatch-callbacks-mod.ko21*22*23* Step 2 - load the demonstration livepatch (with callbacks)24*25* insmod samples/livepatch/livepatch-callbacks-demo.ko26*27*28* Step 3 - cleanup29*30* echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled31* rmmod livepatch_callbacks_demo32* rmmod livepatch_callbacks_mod33*34* Watch dmesg output to see livepatch enablement, callback execution35* and patching operations for both vmlinux and module targets.36*37* NOTE: swap the insmod order of livepatch-callbacks-mod.ko and38* livepatch-callbacks-demo.ko to observe what happens when a39* target module is loaded after a livepatch with callbacks.40*41* NOTE: 'pre_patch_ret' is a module parameter that sets the pre-patch42* callback return status. Try setting up a non-zero status43* such as -19 (-ENODEV):44*45* # Load demo livepatch, vmlinux is patched46* insmod samples/livepatch/livepatch-callbacks-demo.ko47*48* # Setup next pre-patch callback to return -ENODEV49* echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret50*51* # Module loader refuses to load the target module52* insmod samples/livepatch/livepatch-callbacks-mod.ko53* insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device54*55* NOTE: There is a second target module,56* livepatch-callbacks-busymod.ko, available for experimenting57* with livepatch (un)patch callbacks. This module contains58* a 'sleep_secs' parameter that parks the module on one of the59* functions that the livepatch demo module wants to patch.60* Modifying this value and tweaking the order of module loads can61* effectively demonstrate stalled patch transitions:62*63* # Load a target module, let it park on 'busymod_work_func' for64* # thirty seconds65* insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=3066*67* # Meanwhile load the livepatch68* insmod samples/livepatch/livepatch-callbacks-demo.ko69*70* # ... then load and unload another target module while the71* # transition is in progress72* insmod samples/livepatch/livepatch-callbacks-mod.ko73* rmmod samples/livepatch/livepatch-callbacks-mod.ko74*75* # Finally cleanup76* echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled77* rmmod samples/livepatch/livepatch-callbacks-demo.ko78*/7980#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt8182#include <linux/module.h>83#include <linux/kernel.h>84#include <linux/livepatch.h>8586static int pre_patch_ret;87module_param(pre_patch_ret, int, 0644);88MODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (default=0)");8990static const char *const module_state[] = {91[MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state",92[MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init",93[MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away",94[MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up",95};9697static void callback_info(const char *callback, struct klp_object *obj)98{99if (obj->mod)100pr_info("%s: %s -> %s\n", callback, obj->mod->name,101module_state[obj->mod->state]);102else103pr_info("%s: vmlinux\n", callback);104}105106/* Executed on object patching (ie, patch enablement) */107static int pre_patch_callback(struct klp_object *obj)108{109callback_info(__func__, obj);110return pre_patch_ret;111}112113/* Executed on object unpatching (ie, patch disablement) */114static void post_patch_callback(struct klp_object *obj)115{116callback_info(__func__, obj);117}118119/* Executed on object unpatching (ie, patch disablement) */120static void pre_unpatch_callback(struct klp_object *obj)121{122callback_info(__func__, obj);123}124125/* Executed on object unpatching (ie, patch disablement) */126static void post_unpatch_callback(struct klp_object *obj)127{128callback_info(__func__, obj);129}130131static void patched_work_func(struct work_struct *work)132{133pr_info("%s\n", __func__);134}135136static struct klp_func no_funcs[] = {137{ }138};139140static struct klp_func busymod_funcs[] = {141{142.old_name = "busymod_work_func",143.new_func = patched_work_func,144}, { }145};146147static struct klp_object objs[] = {148{149.name = NULL, /* vmlinux */150.funcs = no_funcs,151.callbacks = {152.pre_patch = pre_patch_callback,153.post_patch = post_patch_callback,154.pre_unpatch = pre_unpatch_callback,155.post_unpatch = post_unpatch_callback,156},157}, {158.name = "livepatch_callbacks_mod",159.funcs = no_funcs,160.callbacks = {161.pre_patch = pre_patch_callback,162.post_patch = post_patch_callback,163.pre_unpatch = pre_unpatch_callback,164.post_unpatch = post_unpatch_callback,165},166}, {167.name = "livepatch_callbacks_busymod",168.funcs = busymod_funcs,169.callbacks = {170.pre_patch = pre_patch_callback,171.post_patch = post_patch_callback,172.pre_unpatch = pre_unpatch_callback,173.post_unpatch = post_unpatch_callback,174},175}, { }176};177178static struct klp_patch patch = {179.mod = THIS_MODULE,180.objs = objs,181};182183static int livepatch_callbacks_demo_init(void)184{185return klp_enable_patch(&patch);186}187188static void livepatch_callbacks_demo_exit(void)189{190}191192module_init(livepatch_callbacks_demo_init);193module_exit(livepatch_callbacks_demo_exit);194MODULE_DESCRIPTION("Live patching demo for (un)patching callbacks");195MODULE_LICENSE("GPL");196MODULE_INFO(livepatch, "Y");197198199