18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci/*
78c2ecf20Sopenharmony_ci * livepatch-shadow-mod.c - Shadow variables, buggy module demo
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Purpose
108c2ecf20Sopenharmony_ci * -------
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * As a demonstration of livepatch shadow variable API, this module
138c2ecf20Sopenharmony_ci * introduces memory leak behavior that livepatch modules
148c2ecf20Sopenharmony_ci * livepatch-shadow-fix1.ko and livepatch-shadow-fix2.ko correct and
158c2ecf20Sopenharmony_ci * enhance.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * WARNING - even though the livepatch-shadow-fix modules patch the
188c2ecf20Sopenharmony_ci * memory leak, please load these modules at your own risk -- some
198c2ecf20Sopenharmony_ci * amount of memory may leaked before the bug is patched.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * Usage
238c2ecf20Sopenharmony_ci * -----
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * Step 1 - Load the buggy demonstration module:
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci *   insmod samples/livepatch/livepatch-shadow-mod.ko
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * Watch dmesg output for a few moments to see new dummy being allocated
308c2ecf20Sopenharmony_ci * and a periodic cleanup check.  (Note: a small amount of memory is
318c2ecf20Sopenharmony_ci * being leaked.)
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * Step 2 - Load livepatch fix1:
358c2ecf20Sopenharmony_ci *
368c2ecf20Sopenharmony_ci *   insmod samples/livepatch/livepatch-shadow-fix1.ko
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * Continue watching dmesg and note that now livepatch_fix1_dummy_free()
398c2ecf20Sopenharmony_ci * and livepatch_fix1_dummy_alloc() are logging messages about leaked
408c2ecf20Sopenharmony_ci * memory and eventually leaks prevented.
418c2ecf20Sopenharmony_ci *
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci * Step 3 - Load livepatch fix2 (on top of fix1):
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci *   insmod samples/livepatch/livepatch-shadow-fix2.ko
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * This module extends functionality through shadow variables, as a new
488c2ecf20Sopenharmony_ci * "check" counter is added to the dummy structure.  Periodic dmesg
498c2ecf20Sopenharmony_ci * messages will log these as dummies are cleaned up.
508c2ecf20Sopenharmony_ci *
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci * Step 4 - Cleanup
538c2ecf20Sopenharmony_ci *
548c2ecf20Sopenharmony_ci * Unwind the demonstration by disabling the livepatch fix modules, then
558c2ecf20Sopenharmony_ci * removing them and the demo module:
568c2ecf20Sopenharmony_ci *
578c2ecf20Sopenharmony_ci *   echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix2/enabled
588c2ecf20Sopenharmony_ci *   echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix1/enabled
598c2ecf20Sopenharmony_ci *   rmmod livepatch-shadow-fix2
608c2ecf20Sopenharmony_ci *   rmmod livepatch-shadow-fix1
618c2ecf20Sopenharmony_ci *   rmmod livepatch-shadow-mod
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#include <linux/kernel.h>
668c2ecf20Sopenharmony_ci#include <linux/module.h>
678c2ecf20Sopenharmony_ci#include <linux/sched.h>
688c2ecf20Sopenharmony_ci#include <linux/slab.h>
698c2ecf20Sopenharmony_ci#include <linux/stat.h>
708c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
738c2ecf20Sopenharmony_ciMODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
748c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Buggy module for shadow variable demo");
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* Allocate new dummies every second */
778c2ecf20Sopenharmony_ci#define ALLOC_PERIOD	1
788c2ecf20Sopenharmony_ci/* Check for expired dummies after a few new ones have been allocated */
798c2ecf20Sopenharmony_ci#define CLEANUP_PERIOD	(3 * ALLOC_PERIOD)
808c2ecf20Sopenharmony_ci/* Dummies expire after a few cleanup instances */
818c2ecf20Sopenharmony_ci#define EXPIRE_PERIOD	(4 * CLEANUP_PERIOD)
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/*
848c2ecf20Sopenharmony_ci * Keep a list of all the dummies so we can clean up any residual ones
858c2ecf20Sopenharmony_ci * on module exit
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_cistatic LIST_HEAD(dummy_list);
888c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(dummy_list_mutex);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistruct dummy {
918c2ecf20Sopenharmony_ci	struct list_head list;
928c2ecf20Sopenharmony_ci	unsigned long jiffies_expire;
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic __used noinline struct dummy *dummy_alloc(void)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct dummy *d;
988c2ecf20Sopenharmony_ci	int *leak;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	d = kzalloc(sizeof(*d), GFP_KERNEL);
1018c2ecf20Sopenharmony_ci	if (!d)
1028c2ecf20Sopenharmony_ci		return NULL;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	d->jiffies_expire = jiffies +
1058c2ecf20Sopenharmony_ci		msecs_to_jiffies(1000 * EXPIRE_PERIOD);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* Oops, forgot to save leak! */
1088c2ecf20Sopenharmony_ci	leak = kzalloc(sizeof(*leak), GFP_KERNEL);
1098c2ecf20Sopenharmony_ci	if (!leak) {
1108c2ecf20Sopenharmony_ci		kfree(d);
1118c2ecf20Sopenharmony_ci		return NULL;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	pr_info("%s: dummy @ %p, expires @ %lx\n",
1158c2ecf20Sopenharmony_ci		__func__, d, d->jiffies_expire);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return d;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic __used noinline void dummy_free(struct dummy *d)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	pr_info("%s: dummy @ %p, expired = %lx\n",
1238c2ecf20Sopenharmony_ci		__func__, d, d->jiffies_expire);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	kfree(d);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic __used noinline bool dummy_check(struct dummy *d,
1298c2ecf20Sopenharmony_ci					   unsigned long jiffies)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	return time_after(jiffies, d->jiffies_expire);
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci/*
1358c2ecf20Sopenharmony_ci * alloc_work_func: allocates new dummy structures, allocates additional
1368c2ecf20Sopenharmony_ci *                  memory, aptly named "leak", but doesn't keep
1378c2ecf20Sopenharmony_ci *                  permanent record of it.
1388c2ecf20Sopenharmony_ci */
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic void alloc_work_func(struct work_struct *work);
1418c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(alloc_dwork, alloc_work_func);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void alloc_work_func(struct work_struct *work)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct dummy *d;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	d = dummy_alloc();
1488c2ecf20Sopenharmony_ci	if (!d)
1498c2ecf20Sopenharmony_ci		return;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	mutex_lock(&dummy_list_mutex);
1528c2ecf20Sopenharmony_ci	list_add(&d->list, &dummy_list);
1538c2ecf20Sopenharmony_ci	mutex_unlock(&dummy_list_mutex);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	schedule_delayed_work(&alloc_dwork,
1568c2ecf20Sopenharmony_ci		msecs_to_jiffies(1000 * ALLOC_PERIOD));
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/*
1608c2ecf20Sopenharmony_ci * cleanup_work_func: frees dummy structures.  Without knownledge of
1618c2ecf20Sopenharmony_ci *                    "leak", it leaks the additional memory that
1628c2ecf20Sopenharmony_ci *                    alloc_work_func created.
1638c2ecf20Sopenharmony_ci */
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic void cleanup_work_func(struct work_struct *work);
1668c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(cleanup_dwork, cleanup_work_func);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic void cleanup_work_func(struct work_struct *work)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct dummy *d, *tmp;
1718c2ecf20Sopenharmony_ci	unsigned long j;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	j = jiffies;
1748c2ecf20Sopenharmony_ci	pr_info("%s: jiffies = %lx\n", __func__, j);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	mutex_lock(&dummy_list_mutex);
1778c2ecf20Sopenharmony_ci	list_for_each_entry_safe(d, tmp, &dummy_list, list) {
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci		/* Kick out and free any expired dummies */
1808c2ecf20Sopenharmony_ci		if (dummy_check(d, j)) {
1818c2ecf20Sopenharmony_ci			list_del(&d->list);
1828c2ecf20Sopenharmony_ci			dummy_free(d);
1838c2ecf20Sopenharmony_ci		}
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci	mutex_unlock(&dummy_list_mutex);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	schedule_delayed_work(&cleanup_dwork,
1888c2ecf20Sopenharmony_ci		msecs_to_jiffies(1000 * CLEANUP_PERIOD));
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int livepatch_shadow_mod_init(void)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	schedule_delayed_work(&alloc_dwork,
1948c2ecf20Sopenharmony_ci		msecs_to_jiffies(1000 * ALLOC_PERIOD));
1958c2ecf20Sopenharmony_ci	schedule_delayed_work(&cleanup_dwork,
1968c2ecf20Sopenharmony_ci		msecs_to_jiffies(1000 * CLEANUP_PERIOD));
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	return 0;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic void livepatch_shadow_mod_exit(void)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct dummy *d, *tmp;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/* Wait for any dummies at work */
2068c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&alloc_dwork);
2078c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&cleanup_dwork);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* Cleanup residual dummies */
2108c2ecf20Sopenharmony_ci	list_for_each_entry_safe(d, tmp, &dummy_list, list) {
2118c2ecf20Sopenharmony_ci		list_del(&d->list);
2128c2ecf20Sopenharmony_ci		dummy_free(d);
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cimodule_init(livepatch_shadow_mod_init);
2178c2ecf20Sopenharmony_cimodule_exit(livepatch_shadow_mod_exit);
218