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