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