Path: blob/master/samples/livepatch/livepatch-shadow-mod.c
26278 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Copyright (C) 2017 Joe Lawrence <[email protected]>3*/45/*6* livepatch-shadow-mod.c - Shadow variables, buggy module demo7*8* Purpose9* -------10*11* As a demonstration of livepatch shadow variable API, this module12* introduces memory leak behavior that livepatch modules13* livepatch-shadow-fix1.ko and livepatch-shadow-fix2.ko correct and14* enhance.15*16* WARNING - even though the livepatch-shadow-fix modules patch the17* memory leak, please load these modules at your own risk -- some18* amount of memory may leaked before the bug is patched.19*20*21* Usage22* -----23*24* Step 1 - Load the buggy demonstration module:25*26* insmod samples/livepatch/livepatch-shadow-mod.ko27*28* Watch dmesg output for a few moments to see new dummy being allocated29* and a periodic cleanup check. (Note: a small amount of memory is30* being leaked.)31*32*33* Step 2 - Load livepatch fix1:34*35* insmod samples/livepatch/livepatch-shadow-fix1.ko36*37* Continue watching dmesg and note that now livepatch_fix1_dummy_free()38* and livepatch_fix1_dummy_alloc() are logging messages about leaked39* memory and eventually leaks prevented.40*41*42* Step 3 - Load livepatch fix2 (on top of fix1):43*44* insmod samples/livepatch/livepatch-shadow-fix2.ko45*46* This module extends functionality through shadow variables, as a new47* "check" counter is added to the dummy structure. Periodic dmesg48* messages will log these as dummies are cleaned up.49*50*51* Step 4 - Cleanup52*53* Unwind the demonstration by disabling the livepatch fix modules, then54* removing them and the demo module:55*56* echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix2/enabled57* echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix1/enabled58* rmmod livepatch-shadow-fix259* rmmod livepatch-shadow-fix160* rmmod livepatch-shadow-mod61*/626364#include <linux/kernel.h>65#include <linux/module.h>66#include <linux/sched.h>67#include <linux/slab.h>68#include <linux/stat.h>69#include <linux/workqueue.h>7071MODULE_LICENSE("GPL");72MODULE_AUTHOR("Joe Lawrence <[email protected]>");73MODULE_DESCRIPTION("Buggy module for shadow variable demo");7475/* Allocate new dummies every second */76#define ALLOC_PERIOD 177/* Check for expired dummies after a few new ones have been allocated */78#define CLEANUP_PERIOD (3 * ALLOC_PERIOD)79/* Dummies expire after a few cleanup instances */80#define EXPIRE_PERIOD (4 * CLEANUP_PERIOD)8182/*83* Keep a list of all the dummies so we can clean up any residual ones84* on module exit85*/86static LIST_HEAD(dummy_list);87static DEFINE_MUTEX(dummy_list_mutex);8889struct dummy {90struct list_head list;91unsigned long jiffies_expire;92};9394static __used noinline struct dummy *dummy_alloc(void)95{96struct dummy *d;97int *leak;9899d = kzalloc(sizeof(*d), GFP_KERNEL);100if (!d)101return NULL;102103d->jiffies_expire = jiffies + secs_to_jiffies(EXPIRE_PERIOD);104105/* Oops, forgot to save leak! */106leak = kzalloc(sizeof(*leak), GFP_KERNEL);107if (!leak) {108kfree(d);109return NULL;110}111112pr_info("%s: dummy @ %p, expires @ %lx\n",113__func__, d, d->jiffies_expire);114115return d;116}117118static __used noinline void dummy_free(struct dummy *d)119{120pr_info("%s: dummy @ %p, expired = %lx\n",121__func__, d, d->jiffies_expire);122123kfree(d);124}125126static __used noinline bool dummy_check(struct dummy *d,127unsigned long jiffies)128{129return time_after(jiffies, d->jiffies_expire);130}131132/*133* alloc_work_func: allocates new dummy structures, allocates additional134* memory, aptly named "leak", but doesn't keep135* permanent record of it.136*/137138static void alloc_work_func(struct work_struct *work);139static DECLARE_DELAYED_WORK(alloc_dwork, alloc_work_func);140141static void alloc_work_func(struct work_struct *work)142{143struct dummy *d;144145d = dummy_alloc();146if (!d)147return;148149mutex_lock(&dummy_list_mutex);150list_add(&d->list, &dummy_list);151mutex_unlock(&dummy_list_mutex);152153schedule_delayed_work(&alloc_dwork, secs_to_jiffies(ALLOC_PERIOD));154}155156/*157* cleanup_work_func: frees dummy structures. Without knownledge of158* "leak", it leaks the additional memory that159* alloc_work_func created.160*/161162static void cleanup_work_func(struct work_struct *work);163static DECLARE_DELAYED_WORK(cleanup_dwork, cleanup_work_func);164165static void cleanup_work_func(struct work_struct *work)166{167struct dummy *d, *tmp;168unsigned long j;169170j = jiffies;171pr_info("%s: jiffies = %lx\n", __func__, j);172173mutex_lock(&dummy_list_mutex);174list_for_each_entry_safe(d, tmp, &dummy_list, list) {175176/* Kick out and free any expired dummies */177if (dummy_check(d, j)) {178list_del(&d->list);179dummy_free(d);180}181}182mutex_unlock(&dummy_list_mutex);183184schedule_delayed_work(&cleanup_dwork, secs_to_jiffies(CLEANUP_PERIOD));185}186187static int livepatch_shadow_mod_init(void)188{189schedule_delayed_work(&alloc_dwork, secs_to_jiffies(ALLOC_PERIOD));190schedule_delayed_work(&cleanup_dwork, secs_to_jiffies(CLEANUP_PERIOD));191192return 0;193}194195static void livepatch_shadow_mod_exit(void)196{197struct dummy *d, *tmp;198199/* Wait for any dummies at work */200cancel_delayed_work_sync(&alloc_dwork);201cancel_delayed_work_sync(&cleanup_dwork);202203/* Cleanup residual dummies */204list_for_each_entry_safe(d, tmp, &dummy_list, list) {205list_del(&d->list);206dummy_free(d);207}208}209210module_init(livepatch_shadow_mod_init);211module_exit(livepatch_shadow_mod_exit);212213214