Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
0004  */
0005 
0006 /*
0007  * livepatch-shadow-mod.c - Shadow variables, buggy module demo
0008  *
0009  * Purpose
0010  * -------
0011  *
0012  * As a demonstration of livepatch shadow variable API, this module
0013  * introduces memory leak behavior that livepatch modules
0014  * livepatch-shadow-fix1.ko and livepatch-shadow-fix2.ko correct and
0015  * enhance.
0016  *
0017  * WARNING - even though the livepatch-shadow-fix modules patch the
0018  * memory leak, please load these modules at your own risk -- some
0019  * amount of memory may leaked before the bug is patched.
0020  *
0021  *
0022  * Usage
0023  * -----
0024  *
0025  * Step 1 - Load the buggy demonstration module:
0026  *
0027  *   insmod samples/livepatch/livepatch-shadow-mod.ko
0028  *
0029  * Watch dmesg output for a few moments to see new dummy being allocated
0030  * and a periodic cleanup check.  (Note: a small amount of memory is
0031  * being leaked.)
0032  *
0033  *
0034  * Step 2 - Load livepatch fix1:
0035  *
0036  *   insmod samples/livepatch/livepatch-shadow-fix1.ko
0037  *
0038  * Continue watching dmesg and note that now livepatch_fix1_dummy_free()
0039  * and livepatch_fix1_dummy_alloc() are logging messages about leaked
0040  * memory and eventually leaks prevented.
0041  *
0042  *
0043  * Step 3 - Load livepatch fix2 (on top of fix1):
0044  *
0045  *   insmod samples/livepatch/livepatch-shadow-fix2.ko
0046  *
0047  * This module extends functionality through shadow variables, as a new
0048  * "check" counter is added to the dummy structure.  Periodic dmesg
0049  * messages will log these as dummies are cleaned up.
0050  *
0051  *
0052  * Step 4 - Cleanup
0053  *
0054  * Unwind the demonstration by disabling the livepatch fix modules, then
0055  * removing them and the demo module:
0056  *
0057  *   echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix2/enabled
0058  *   echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix1/enabled
0059  *   rmmod livepatch-shadow-fix2
0060  *   rmmod livepatch-shadow-fix1
0061  *   rmmod livepatch-shadow-mod
0062  */
0063 
0064 
0065 #include <linux/kernel.h>
0066 #include <linux/module.h>
0067 #include <linux/sched.h>
0068 #include <linux/slab.h>
0069 #include <linux/stat.h>
0070 #include <linux/workqueue.h>
0071 
0072 MODULE_LICENSE("GPL");
0073 MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
0074 MODULE_DESCRIPTION("Buggy module for shadow variable demo");
0075 
0076 /* Allocate new dummies every second */
0077 #define ALLOC_PERIOD    1
0078 /* Check for expired dummies after a few new ones have been allocated */
0079 #define CLEANUP_PERIOD  (3 * ALLOC_PERIOD)
0080 /* Dummies expire after a few cleanup instances */
0081 #define EXPIRE_PERIOD   (4 * CLEANUP_PERIOD)
0082 
0083 /*
0084  * Keep a list of all the dummies so we can clean up any residual ones
0085  * on module exit
0086  */
0087 static LIST_HEAD(dummy_list);
0088 static DEFINE_MUTEX(dummy_list_mutex);
0089 
0090 struct dummy {
0091     struct list_head list;
0092     unsigned long jiffies_expire;
0093 };
0094 
0095 static __used noinline struct dummy *dummy_alloc(void)
0096 {
0097     struct dummy *d;
0098     int *leak;
0099 
0100     d = kzalloc(sizeof(*d), GFP_KERNEL);
0101     if (!d)
0102         return NULL;
0103 
0104     d->jiffies_expire = jiffies +
0105         msecs_to_jiffies(1000 * EXPIRE_PERIOD);
0106 
0107     /* Oops, forgot to save leak! */
0108     leak = kzalloc(sizeof(*leak), GFP_KERNEL);
0109     if (!leak) {
0110         kfree(d);
0111         return NULL;
0112     }
0113 
0114     pr_info("%s: dummy @ %p, expires @ %lx\n",
0115         __func__, d, d->jiffies_expire);
0116 
0117     return d;
0118 }
0119 
0120 static __used noinline void dummy_free(struct dummy *d)
0121 {
0122     pr_info("%s: dummy @ %p, expired = %lx\n",
0123         __func__, d, d->jiffies_expire);
0124 
0125     kfree(d);
0126 }
0127 
0128 static __used noinline bool dummy_check(struct dummy *d,
0129                        unsigned long jiffies)
0130 {
0131     return time_after(jiffies, d->jiffies_expire);
0132 }
0133 
0134 /*
0135  * alloc_work_func: allocates new dummy structures, allocates additional
0136  *                  memory, aptly named "leak", but doesn't keep
0137  *                  permanent record of it.
0138  */
0139 
0140 static void alloc_work_func(struct work_struct *work);
0141 static DECLARE_DELAYED_WORK(alloc_dwork, alloc_work_func);
0142 
0143 static void alloc_work_func(struct work_struct *work)
0144 {
0145     struct dummy *d;
0146 
0147     d = dummy_alloc();
0148     if (!d)
0149         return;
0150 
0151     mutex_lock(&dummy_list_mutex);
0152     list_add(&d->list, &dummy_list);
0153     mutex_unlock(&dummy_list_mutex);
0154 
0155     schedule_delayed_work(&alloc_dwork,
0156         msecs_to_jiffies(1000 * ALLOC_PERIOD));
0157 }
0158 
0159 /*
0160  * cleanup_work_func: frees dummy structures.  Without knownledge of
0161  *                    "leak", it leaks the additional memory that
0162  *                    alloc_work_func created.
0163  */
0164 
0165 static void cleanup_work_func(struct work_struct *work);
0166 static DECLARE_DELAYED_WORK(cleanup_dwork, cleanup_work_func);
0167 
0168 static void cleanup_work_func(struct work_struct *work)
0169 {
0170     struct dummy *d, *tmp;
0171     unsigned long j;
0172 
0173     j = jiffies;
0174     pr_info("%s: jiffies = %lx\n", __func__, j);
0175 
0176     mutex_lock(&dummy_list_mutex);
0177     list_for_each_entry_safe(d, tmp, &dummy_list, list) {
0178 
0179         /* Kick out and free any expired dummies */
0180         if (dummy_check(d, j)) {
0181             list_del(&d->list);
0182             dummy_free(d);
0183         }
0184     }
0185     mutex_unlock(&dummy_list_mutex);
0186 
0187     schedule_delayed_work(&cleanup_dwork,
0188         msecs_to_jiffies(1000 * CLEANUP_PERIOD));
0189 }
0190 
0191 static int livepatch_shadow_mod_init(void)
0192 {
0193     schedule_delayed_work(&alloc_dwork,
0194         msecs_to_jiffies(1000 * ALLOC_PERIOD));
0195     schedule_delayed_work(&cleanup_dwork,
0196         msecs_to_jiffies(1000 * CLEANUP_PERIOD));
0197 
0198     return 0;
0199 }
0200 
0201 static void livepatch_shadow_mod_exit(void)
0202 {
0203     struct dummy *d, *tmp;
0204 
0205     /* Wait for any dummies at work */
0206     cancel_delayed_work_sync(&alloc_dwork);
0207     cancel_delayed_work_sync(&cleanup_dwork);
0208 
0209     /* Cleanup residual dummies */
0210     list_for_each_entry_safe(d, tmp, &dummy_list, list) {
0211         list_del(&d->list);
0212         dummy_free(d);
0213     }
0214 }
0215 
0216 module_init(livepatch_shadow_mod_init);
0217 module_exit(livepatch_shadow_mod_exit);