File Index
Files and directories
Sign in
FILE

livepatch-shadow-mod.c

File preview with syntax highlighting.

linux-6.19.14/samples/livepatch/livepatch-shadow-mod.c C
  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-shadow-mod.c - Shadow variables, buggy module demo
  8 *
  9 * Purpose
 10 * -------
 11 *
 12 * As a demonstration of livepatch shadow variable API, this module
 13 * introduces memory leak behavior that livepatch modules
 14 * livepatch-shadow-fix1.ko and livepatch-shadow-fix2.ko correct and
 15 * enhance.
 16 *
 17 * WARNING - even though the livepatch-shadow-fix modules patch the
 18 * memory leak, please load these modules at your own risk -- some
 19 * amount of memory may leaked before the bug is patched.
 20 *
 21 *
 22 * Usage
 23 * -----
 24 *
 25 * Step 1 - Load the buggy demonstration module:
 26 *
 27 *   insmod samples/livepatch/livepatch-shadow-mod.ko
 28 *
 29 * Watch dmesg output for a few moments to see new dummy being allocated
 30 * and a periodic cleanup check.  (Note: a small amount of memory is
 31 * being leaked.)
 32 *
 33 *
 34 * Step 2 - Load livepatch fix1:
 35 *
 36 *   insmod samples/livepatch/livepatch-shadow-fix1.ko
 37 *
 38 * Continue watching dmesg and note that now livepatch_fix1_dummy_free()
 39 * and livepatch_fix1_dummy_alloc() are logging messages about leaked
 40 * memory and eventually leaks prevented.
 41 *
 42 *
 43 * Step 3 - Load livepatch fix2 (on top of fix1):
 44 *
 45 *   insmod samples/livepatch/livepatch-shadow-fix2.ko
 46 *
 47 * This module extends functionality through shadow variables, as a new
 48 * "check" counter is added to the dummy structure.  Periodic dmesg
 49 * messages will log these as dummies are cleaned up.
 50 *
 51 *
 52 * Step 4 - Cleanup
 53 *
 54 * Unwind the demonstration by disabling the livepatch fix modules, then
 55 * removing them and the demo module:
 56 *
 57 *   echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix2/enabled
 58 *   echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix1/enabled
 59 *   rmmod livepatch-shadow-fix2
 60 *   rmmod livepatch-shadow-fix1
 61 *   rmmod livepatch-shadow-mod
 62 */
 63
 64
 65#include <linux/kernel.h>
 66#include <linux/module.h>
 67#include <linux/sched.h>
 68#include <linux/slab.h>
 69#include <linux/stat.h>
 70#include <linux/workqueue.h>
 71
 72MODULE_LICENSE("GPL");
 73MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
 74MODULE_DESCRIPTION("Buggy module for shadow variable demo");
 75
 76/* Allocate new dummies every second */
 77#define ALLOC_PERIOD	1
 78/* Check for expired dummies after a few new ones have been allocated */
 79#define CLEANUP_PERIOD	(3 * ALLOC_PERIOD)
 80/* Dummies expire after a few cleanup instances */
 81#define EXPIRE_PERIOD	(4 * CLEANUP_PERIOD)
 82
 83/*
 84 * Keep a list of all the dummies so we can clean up any residual ones
 85 * on module exit
 86 */
 87static LIST_HEAD(dummy_list);
 88static DEFINE_MUTEX(dummy_list_mutex);
 89
 90struct dummy {
 91	struct list_head list;
 92	unsigned long jiffies_expire;
 93};
 94
 95static __used noinline struct dummy *dummy_alloc(void)
 96{
 97	struct dummy *d;
 98	int *leak;
 99
100	d = kzalloc(sizeof(*d), GFP_KERNEL);
101	if (!d)
102		return NULL;
103
104	d->jiffies_expire = jiffies + secs_to_jiffies(EXPIRE_PERIOD);
105
106	/* Oops, forgot to save leak! */
107	leak = kzalloc(sizeof(*leak), GFP_KERNEL);
108	if (!leak) {
109		kfree(d);
110		return NULL;
111	}
112
113	pr_info("%s: dummy @ %p, expires @ %lx\n",
114		__func__, d, d->jiffies_expire);
115
116	return d;
117}
118
119static __used noinline void dummy_free(struct dummy *d)
120{
121	pr_info("%s: dummy @ %p, expired = %lx\n",
122		__func__, d, d->jiffies_expire);
123
124	kfree(d);
125}
126
127static __used noinline bool dummy_check(struct dummy *d,
128					   unsigned long jiffies)
129{
130	return time_after(jiffies, d->jiffies_expire);
131}
132
133/*
134 * alloc_work_func: allocates new dummy structures, allocates additional
135 *                  memory, aptly named "leak", but doesn't keep
136 *                  permanent record of it.
137 */
138
139static void alloc_work_func(struct work_struct *work);
140static DECLARE_DELAYED_WORK(alloc_dwork, alloc_work_func);
141
142static void alloc_work_func(struct work_struct *work)
143{
144	struct dummy *d;
145
146	d = dummy_alloc();
147	if (!d)
148		return;
149
150	mutex_lock(&dummy_list_mutex);
151	list_add(&d->list, &dummy_list);
152	mutex_unlock(&dummy_list_mutex);
153
154	schedule_delayed_work(&alloc_dwork, secs_to_jiffies(ALLOC_PERIOD));
155}
156
157/*
158 * cleanup_work_func: frees dummy structures.  Without knownledge of
159 *                    "leak", it leaks the additional memory that
160 *                    alloc_work_func created.
161 */
162
163static void cleanup_work_func(struct work_struct *work);
164static DECLARE_DELAYED_WORK(cleanup_dwork, cleanup_work_func);
165
166static void cleanup_work_func(struct work_struct *work)
167{
168	struct dummy *d, *tmp;
169	unsigned long j;
170
171	j = jiffies;
172	pr_info("%s: jiffies = %lx\n", __func__, j);
173
174	mutex_lock(&dummy_list_mutex);
175	list_for_each_entry_safe(d, tmp, &dummy_list, list) {
176
177		/* Kick out and free any expired dummies */
178		if (dummy_check(d, j)) {
179			list_del(&d->list);
180			dummy_free(d);
181		}
182	}
183	mutex_unlock(&dummy_list_mutex);
184
185	schedule_delayed_work(&cleanup_dwork, secs_to_jiffies(CLEANUP_PERIOD));
186}
187
188static int livepatch_shadow_mod_init(void)
189{
190	schedule_delayed_work(&alloc_dwork, secs_to_jiffies(ALLOC_PERIOD));
191	schedule_delayed_work(&cleanup_dwork, secs_to_jiffies(CLEANUP_PERIOD));
192
193	return 0;
194}
195
196static void livepatch_shadow_mod_exit(void)
197{
198	struct dummy *d, *tmp;
199
200	/* Wait for any dummies at work */
201	cancel_delayed_work_sync(&alloc_dwork);
202	cancel_delayed_work_sync(&cleanup_dwork);
203
204	/* Cleanup residual dummies */
205	list_for_each_entry_safe(d, tmp, &dummy_list, list) {
206		list_del(&d->list);
207		dummy_free(d);
208	}
209}
210
211module_init(livepatch_shadow_mod_init);
212module_exit(livepatch_shadow_mod_exit);
~/ Read-only file preview.