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 static LIST_HEAD(dummy_list); 100 static DEFINE_MUTEX(dummy_list_mutex); 101 102 struct dummy { 103 struct list_head list; 104 unsigned long jiffies_expire; 105 }; 106 107 static __used 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 if (!leak) { 122 kfree(d); 123 return NULL; 124 } 125 126 pr_info("%s: dummy @ %p, expires @ %lx\n", 127 __func__, d, d->jiffies_expire); 128 129 return d; 130 } 131 132 static __used noinline void dummy_free(struct dummy *d) 133 { 134 pr_info("%s: dummy @ %p, expired = %lx\n", 135 __func__, d, d->jiffies_expire); 136 137 kfree(d); 138 } 139 140 static __used noinline bool dummy_check(struct dummy *d, 141 unsigned long jiffies) 142 { 143 return time_after(jiffies, d->jiffies_expire); 144 } 145 146 /* 147 * alloc_work_func: allocates new dummy structures, allocates additional 148 * memory, aptly named "leak", but doesn't keep 149 * permanent record of it. 150 */ 151 152 static void alloc_work_func(struct work_struct *work); 153 static DECLARE_DELAYED_WORK(alloc_dwork, alloc_work_func); 154 155 static void alloc_work_func(struct work_struct *work) 156 { 157 struct dummy *d; 158 159 d = dummy_alloc(); 160 if (!d) 161 return; 162 163 mutex_lock(&dummy_list_mutex); 164 list_add(&d->list, &dummy_list); 165 mutex_unlock(&dummy_list_mutex); 166 167 schedule_delayed_work(&alloc_dwork, 168 msecs_to_jiffies(1000 * ALLOC_PERIOD)); 169 } 170 171 /* 172 * cleanup_work_func: frees dummy structures. Without knownledge of 173 * "leak", it leaks the additional memory that 174 * alloc_work_func created. 175 */ 176 177 static void cleanup_work_func(struct work_struct *work); 178 static DECLARE_DELAYED_WORK(cleanup_dwork, cleanup_work_func); 179 180 static void cleanup_work_func(struct work_struct *work) 181 { 182 struct dummy *d, *tmp; 183 unsigned long j; 184 185 j = jiffies; 186 pr_info("%s: jiffies = %lx\n", __func__, j); 187 188 mutex_lock(&dummy_list_mutex); 189 list_for_each_entry_safe(d, tmp, &dummy_list, list) { 190 191 /* Kick out and free any expired dummies */ 192 if (dummy_check(d, j)) { 193 list_del(&d->list); 194 dummy_free(d); 195 } 196 } 197 mutex_unlock(&dummy_list_mutex); 198 199 schedule_delayed_work(&cleanup_dwork, 200 msecs_to_jiffies(1000 * CLEANUP_PERIOD)); 201 } 202 203 static int livepatch_shadow_mod_init(void) 204 { 205 schedule_delayed_work(&alloc_dwork, 206 msecs_to_jiffies(1000 * ALLOC_PERIOD)); 207 schedule_delayed_work(&cleanup_dwork, 208 msecs_to_jiffies(1000 * CLEANUP_PERIOD)); 209 210 return 0; 211 } 212 213 static void livepatch_shadow_mod_exit(void) 214 { 215 struct dummy *d, *tmp; 216 217 /* Wait for any dummies at work */ 218 cancel_delayed_work_sync(&alloc_dwork); 219 cancel_delayed_work_sync(&cleanup_dwork); 220 221 /* Cleanup residual dummies */ 222 list_for_each_entry_safe(d, tmp, &dummy_list, list) { 223 list_del(&d->list); 224 dummy_free(d); 225 } 226 } 227 228 module_init(livepatch_shadow_mod_init); 229 module_exit(livepatch_shadow_mod_exit); 230