162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/*
762306a36Sopenharmony_ci * livepatch-shadow-mod.c - Shadow variables, buggy module demo
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Purpose
1062306a36Sopenharmony_ci * -------
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * As a demonstration of livepatch shadow variable API, this module
1362306a36Sopenharmony_ci * introduces memory leak behavior that livepatch modules
1462306a36Sopenharmony_ci * livepatch-shadow-fix1.ko and livepatch-shadow-fix2.ko correct and
1562306a36Sopenharmony_ci * enhance.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * WARNING - even though the livepatch-shadow-fix modules patch the
1862306a36Sopenharmony_ci * memory leak, please load these modules at your own risk -- some
1962306a36Sopenharmony_ci * amount of memory may leaked before the bug is patched.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * Usage
2362306a36Sopenharmony_ci * -----
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * Step 1 - Load the buggy demonstration module:
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci *   insmod samples/livepatch/livepatch-shadow-mod.ko
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * Watch dmesg output for a few moments to see new dummy being allocated
3062306a36Sopenharmony_ci * and a periodic cleanup check.  (Note: a small amount of memory is
3162306a36Sopenharmony_ci * being leaked.)
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * Step 2 - Load livepatch fix1:
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci *   insmod samples/livepatch/livepatch-shadow-fix1.ko
3762306a36Sopenharmony_ci *
3862306a36Sopenharmony_ci * Continue watching dmesg and note that now livepatch_fix1_dummy_free()
3962306a36Sopenharmony_ci * and livepatch_fix1_dummy_alloc() are logging messages about leaked
4062306a36Sopenharmony_ci * memory and eventually leaks prevented.
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * Step 3 - Load livepatch fix2 (on top of fix1):
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci *   insmod samples/livepatch/livepatch-shadow-fix2.ko
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci * This module extends functionality through shadow variables, as a new
4862306a36Sopenharmony_ci * "check" counter is added to the dummy structure.  Periodic dmesg
4962306a36Sopenharmony_ci * messages will log these as dummies are cleaned up.
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci *
5262306a36Sopenharmony_ci * Step 4 - Cleanup
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * Unwind the demonstration by disabling the livepatch fix modules, then
5562306a36Sopenharmony_ci * removing them and the demo module:
5662306a36Sopenharmony_ci *
5762306a36Sopenharmony_ci *   echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix2/enabled
5862306a36Sopenharmony_ci *   echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix1/enabled
5962306a36Sopenharmony_ci *   rmmod livepatch-shadow-fix2
6062306a36Sopenharmony_ci *   rmmod livepatch-shadow-fix1
6162306a36Sopenharmony_ci *   rmmod livepatch-shadow-mod
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#include <linux/kernel.h>
6662306a36Sopenharmony_ci#include <linux/module.h>
6762306a36Sopenharmony_ci#include <linux/sched.h>
6862306a36Sopenharmony_ci#include <linux/slab.h>
6962306a36Sopenharmony_ci#include <linux/stat.h>
7062306a36Sopenharmony_ci#include <linux/workqueue.h>
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
7362306a36Sopenharmony_ciMODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
7462306a36Sopenharmony_ciMODULE_DESCRIPTION("Buggy module for shadow variable demo");
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/* Allocate new dummies every second */
7762306a36Sopenharmony_ci#define ALLOC_PERIOD	1
7862306a36Sopenharmony_ci/* Check for expired dummies after a few new ones have been allocated */
7962306a36Sopenharmony_ci#define CLEANUP_PERIOD	(3 * ALLOC_PERIOD)
8062306a36Sopenharmony_ci/* Dummies expire after a few cleanup instances */
8162306a36Sopenharmony_ci#define EXPIRE_PERIOD	(4 * CLEANUP_PERIOD)
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci * Keep a list of all the dummies so we can clean up any residual ones
8562306a36Sopenharmony_ci * on module exit
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_cistatic LIST_HEAD(dummy_list);
8862306a36Sopenharmony_cistatic DEFINE_MUTEX(dummy_list_mutex);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistruct dummy {
9162306a36Sopenharmony_ci	struct list_head list;
9262306a36Sopenharmony_ci	unsigned long jiffies_expire;
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic __used noinline struct dummy *dummy_alloc(void)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct dummy *d;
9862306a36Sopenharmony_ci	int *leak;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	d = kzalloc(sizeof(*d), GFP_KERNEL);
10162306a36Sopenharmony_ci	if (!d)
10262306a36Sopenharmony_ci		return NULL;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	d->jiffies_expire = jiffies +
10562306a36Sopenharmony_ci		msecs_to_jiffies(1000 * EXPIRE_PERIOD);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* Oops, forgot to save leak! */
10862306a36Sopenharmony_ci	leak = kzalloc(sizeof(*leak), GFP_KERNEL);
10962306a36Sopenharmony_ci	if (!leak) {
11062306a36Sopenharmony_ci		kfree(d);
11162306a36Sopenharmony_ci		return NULL;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	pr_info("%s: dummy @ %p, expires @ %lx\n",
11562306a36Sopenharmony_ci		__func__, d, d->jiffies_expire);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return d;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic __used noinline void dummy_free(struct dummy *d)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	pr_info("%s: dummy @ %p, expired = %lx\n",
12362306a36Sopenharmony_ci		__func__, d, d->jiffies_expire);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	kfree(d);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic __used noinline bool dummy_check(struct dummy *d,
12962306a36Sopenharmony_ci					   unsigned long jiffies)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	return time_after(jiffies, d->jiffies_expire);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/*
13562306a36Sopenharmony_ci * alloc_work_func: allocates new dummy structures, allocates additional
13662306a36Sopenharmony_ci *                  memory, aptly named "leak", but doesn't keep
13762306a36Sopenharmony_ci *                  permanent record of it.
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic void alloc_work_func(struct work_struct *work);
14162306a36Sopenharmony_cistatic DECLARE_DELAYED_WORK(alloc_dwork, alloc_work_func);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void alloc_work_func(struct work_struct *work)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct dummy *d;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	d = dummy_alloc();
14862306a36Sopenharmony_ci	if (!d)
14962306a36Sopenharmony_ci		return;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	mutex_lock(&dummy_list_mutex);
15262306a36Sopenharmony_ci	list_add(&d->list, &dummy_list);
15362306a36Sopenharmony_ci	mutex_unlock(&dummy_list_mutex);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	schedule_delayed_work(&alloc_dwork,
15662306a36Sopenharmony_ci		msecs_to_jiffies(1000 * ALLOC_PERIOD));
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/*
16062306a36Sopenharmony_ci * cleanup_work_func: frees dummy structures.  Without knownledge of
16162306a36Sopenharmony_ci *                    "leak", it leaks the additional memory that
16262306a36Sopenharmony_ci *                    alloc_work_func created.
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic void cleanup_work_func(struct work_struct *work);
16662306a36Sopenharmony_cistatic DECLARE_DELAYED_WORK(cleanup_dwork, cleanup_work_func);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic void cleanup_work_func(struct work_struct *work)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct dummy *d, *tmp;
17162306a36Sopenharmony_ci	unsigned long j;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	j = jiffies;
17462306a36Sopenharmony_ci	pr_info("%s: jiffies = %lx\n", __func__, j);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	mutex_lock(&dummy_list_mutex);
17762306a36Sopenharmony_ci	list_for_each_entry_safe(d, tmp, &dummy_list, list) {
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		/* Kick out and free any expired dummies */
18062306a36Sopenharmony_ci		if (dummy_check(d, j)) {
18162306a36Sopenharmony_ci			list_del(&d->list);
18262306a36Sopenharmony_ci			dummy_free(d);
18362306a36Sopenharmony_ci		}
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci	mutex_unlock(&dummy_list_mutex);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	schedule_delayed_work(&cleanup_dwork,
18862306a36Sopenharmony_ci		msecs_to_jiffies(1000 * CLEANUP_PERIOD));
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic int livepatch_shadow_mod_init(void)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	schedule_delayed_work(&alloc_dwork,
19462306a36Sopenharmony_ci		msecs_to_jiffies(1000 * ALLOC_PERIOD));
19562306a36Sopenharmony_ci	schedule_delayed_work(&cleanup_dwork,
19662306a36Sopenharmony_ci		msecs_to_jiffies(1000 * CLEANUP_PERIOD));
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void livepatch_shadow_mod_exit(void)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct dummy *d, *tmp;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Wait for any dummies at work */
20662306a36Sopenharmony_ci	cancel_delayed_work_sync(&alloc_dwork);
20762306a36Sopenharmony_ci	cancel_delayed_work_sync(&cleanup_dwork);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* Cleanup residual dummies */
21062306a36Sopenharmony_ci	list_for_each_entry_safe(d, tmp, &dummy_list, list) {
21162306a36Sopenharmony_ci		list_del(&d->list);
21262306a36Sopenharmony_ci		dummy_free(d);
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cimodule_init(livepatch_shadow_mod_init);
21762306a36Sopenharmony_cimodule_exit(livepatch_shadow_mod_exit);
218