diff options
| -rw-r--r-- | Documentation/livepatch/shadow-vars.txt | 10 | ||||
| -rw-r--r-- | include/linux/livepatch.h | 5 | ||||
| -rw-r--r-- | kernel/livepatch/shadow.c | 26 | ||||
| -rw-r--r-- | samples/livepatch/livepatch-shadow-fix1.c | 25 | ||||
| -rw-r--r-- | samples/livepatch/livepatch-shadow-fix2.c | 27 |
5 files changed, 59 insertions, 34 deletions
diff --git a/Documentation/livepatch/shadow-vars.txt b/Documentation/livepatch/shadow-vars.txt index 9c7ae191641c..ecc09a7be5dd 100644 --- a/Documentation/livepatch/shadow-vars.txt +++ b/Documentation/livepatch/shadow-vars.txt | |||
| @@ -65,11 +65,15 @@ to do actions that can be done only once when a new variable is allocated. | |||
| 65 | 65 | ||
| 66 | * klp_shadow_free() - detach and free a <obj, id> shadow variable | 66 | * klp_shadow_free() - detach and free a <obj, id> shadow variable |
| 67 | - find and remove a <obj, id> reference from global hashtable | 67 | - find and remove a <obj, id> reference from global hashtable |
| 68 | - if found, free shadow variable | 68 | - if found |
| 69 | - call destructor function if defined | ||
| 70 | - free shadow variable | ||
| 69 | 71 | ||
| 70 | * klp_shadow_free_all() - detach and free all <*, id> shadow variables | 72 | * klp_shadow_free_all() - detach and free all <*, id> shadow variables |
| 71 | - find and remove any <*, id> references from global hashtable | 73 | - find and remove any <*, id> references from global hashtable |
| 72 | - if found, free shadow variable | 74 | - if found |
| 75 | - call destructor function if defined | ||
| 76 | - free shadow variable | ||
| 73 | 77 | ||
| 74 | 78 | ||
| 75 | 2. Use cases | 79 | 2. Use cases |
| @@ -136,7 +140,7 @@ variable: | |||
| 136 | 140 | ||
| 137 | void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) | 141 | void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) |
| 138 | { | 142 | { |
| 139 | klp_shadow_free(sta, PS_LOCK); | 143 | klp_shadow_free(sta, PS_LOCK, NULL); |
| 140 | kfree(sta); | 144 | kfree(sta); |
| 141 | ... | 145 | ... |
| 142 | 146 | ||
diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index 7e084321b146..aec44b1d9582 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h | |||
| @@ -189,6 +189,7 @@ static inline bool klp_have_reliable_stack(void) | |||
| 189 | typedef int (*klp_shadow_ctor_t)(void *obj, | 189 | typedef int (*klp_shadow_ctor_t)(void *obj, |
| 190 | void *shadow_data, | 190 | void *shadow_data, |
| 191 | void *ctor_data); | 191 | void *ctor_data); |
| 192 | typedef void (*klp_shadow_dtor_t)(void *obj, void *shadow_data); | ||
| 192 | 193 | ||
| 193 | void *klp_shadow_get(void *obj, unsigned long id); | 194 | void *klp_shadow_get(void *obj, unsigned long id); |
| 194 | void *klp_shadow_alloc(void *obj, unsigned long id, | 195 | void *klp_shadow_alloc(void *obj, unsigned long id, |
| @@ -197,8 +198,8 @@ void *klp_shadow_alloc(void *obj, unsigned long id, | |||
| 197 | void *klp_shadow_get_or_alloc(void *obj, unsigned long id, | 198 | void *klp_shadow_get_or_alloc(void *obj, unsigned long id, |
| 198 | size_t size, gfp_t gfp_flags, | 199 | size_t size, gfp_t gfp_flags, |
| 199 | klp_shadow_ctor_t ctor, void *ctor_data); | 200 | klp_shadow_ctor_t ctor, void *ctor_data); |
| 200 | void klp_shadow_free(void *obj, unsigned long id); | 201 | void klp_shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor); |
| 201 | void klp_shadow_free_all(unsigned long id); | 202 | void klp_shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor); |
| 202 | 203 | ||
| 203 | #else /* !CONFIG_LIVEPATCH */ | 204 | #else /* !CONFIG_LIVEPATCH */ |
| 204 | 205 | ||
diff --git a/kernel/livepatch/shadow.c b/kernel/livepatch/shadow.c index b10a0bbb7f84..83958c814439 100644 --- a/kernel/livepatch/shadow.c +++ b/kernel/livepatch/shadow.c | |||
| @@ -243,15 +243,26 @@ void *klp_shadow_get_or_alloc(void *obj, unsigned long id, | |||
| 243 | } | 243 | } |
| 244 | EXPORT_SYMBOL_GPL(klp_shadow_get_or_alloc); | 244 | EXPORT_SYMBOL_GPL(klp_shadow_get_or_alloc); |
| 245 | 245 | ||
| 246 | static void klp_shadow_free_struct(struct klp_shadow *shadow, | ||
| 247 | klp_shadow_dtor_t dtor) | ||
| 248 | { | ||
| 249 | hash_del_rcu(&shadow->node); | ||
| 250 | if (dtor) | ||
| 251 | dtor(shadow->obj, shadow->data); | ||
| 252 | kfree_rcu(shadow, rcu_head); | ||
| 253 | } | ||
| 254 | |||
| 246 | /** | 255 | /** |
| 247 | * klp_shadow_free() - detach and free a <obj, id> shadow variable | 256 | * klp_shadow_free() - detach and free a <obj, id> shadow variable |
| 248 | * @obj: pointer to parent object | 257 | * @obj: pointer to parent object |
| 249 | * @id: data identifier | 258 | * @id: data identifier |
| 259 | * @dtor: custom callback that can be used to unregister the variable | ||
| 260 | * and/or free data that the shadow variable points to (optional) | ||
| 250 | * | 261 | * |
| 251 | * This function releases the memory for this <obj, id> shadow variable | 262 | * This function releases the memory for this <obj, id> shadow variable |
| 252 | * instance, callers should stop referencing it accordingly. | 263 | * instance, callers should stop referencing it accordingly. |
| 253 | */ | 264 | */ |
| 254 | void klp_shadow_free(void *obj, unsigned long id) | 265 | void klp_shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor) |
| 255 | { | 266 | { |
| 256 | struct klp_shadow *shadow; | 267 | struct klp_shadow *shadow; |
| 257 | unsigned long flags; | 268 | unsigned long flags; |
| @@ -263,8 +274,7 @@ void klp_shadow_free(void *obj, unsigned long id) | |||
| 263 | (unsigned long)obj) { | 274 | (unsigned long)obj) { |
| 264 | 275 | ||
| 265 | if (klp_shadow_match(shadow, obj, id)) { | 276 | if (klp_shadow_match(shadow, obj, id)) { |
| 266 | hash_del_rcu(&shadow->node); | 277 | klp_shadow_free_struct(shadow, dtor); |
| 267 | kfree_rcu(shadow, rcu_head); | ||
| 268 | break; | 278 | break; |
| 269 | } | 279 | } |
| 270 | } | 280 | } |
| @@ -276,11 +286,13 @@ EXPORT_SYMBOL_GPL(klp_shadow_free); | |||
| 276 | /** | 286 | /** |
| 277 | * klp_shadow_free_all() - detach and free all <*, id> shadow variables | 287 | * klp_shadow_free_all() - detach and free all <*, id> shadow variables |
| 278 | * @id: data identifier | 288 | * @id: data identifier |
| 289 | * @dtor: custom callback that can be used to unregister the variable | ||
| 290 | * and/or free data that the shadow variable points to (optional) | ||
| 279 | * | 291 | * |
| 280 | * This function releases the memory for all <*, id> shadow variable | 292 | * This function releases the memory for all <*, id> shadow variable |
| 281 | * instances, callers should stop referencing them accordingly. | 293 | * instances, callers should stop referencing them accordingly. |
| 282 | */ | 294 | */ |
| 283 | void klp_shadow_free_all(unsigned long id) | 295 | void klp_shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor) |
| 284 | { | 296 | { |
| 285 | struct klp_shadow *shadow; | 297 | struct klp_shadow *shadow; |
| 286 | unsigned long flags; | 298 | unsigned long flags; |
| @@ -290,10 +302,8 @@ void klp_shadow_free_all(unsigned long id) | |||
| 290 | 302 | ||
| 291 | /* Delete all <*, id> from hash */ | 303 | /* Delete all <*, id> from hash */ |
| 292 | hash_for_each(klp_shadow_hash, i, shadow, node) { | 304 | hash_for_each(klp_shadow_hash, i, shadow, node) { |
| 293 | if (klp_shadow_match(shadow, shadow->obj, id)) { | 305 | if (klp_shadow_match(shadow, shadow->obj, id)) |
| 294 | hash_del_rcu(&shadow->node); | 306 | klp_shadow_free_struct(shadow, dtor); |
| 295 | kfree_rcu(shadow, rcu_head); | ||
| 296 | } | ||
| 297 | } | 307 | } |
| 298 | 308 | ||
| 299 | spin_unlock_irqrestore(&klp_shadow_lock, flags); | 309 | spin_unlock_irqrestore(&klp_shadow_lock, flags); |
diff --git a/samples/livepatch/livepatch-shadow-fix1.c b/samples/livepatch/livepatch-shadow-fix1.c index 04151c7f2631..49b13553eaae 100644 --- a/samples/livepatch/livepatch-shadow-fix1.c +++ b/samples/livepatch/livepatch-shadow-fix1.c | |||
| @@ -98,9 +98,19 @@ struct dummy *livepatch_fix1_dummy_alloc(void) | |||
| 98 | return d; | 98 | return d; |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | static void livepatch_fix1_dummy_leak_dtor(void *obj, void *shadow_data) | ||
| 102 | { | ||
| 103 | void *d = obj; | ||
| 104 | void **shadow_leak = shadow_data; | ||
| 105 | |||
| 106 | kfree(*shadow_leak); | ||
| 107 | pr_info("%s: dummy @ %p, prevented leak @ %p\n", | ||
| 108 | __func__, d, *shadow_leak); | ||
| 109 | } | ||
| 110 | |||
| 101 | void livepatch_fix1_dummy_free(struct dummy *d) | 111 | void livepatch_fix1_dummy_free(struct dummy *d) |
| 102 | { | 112 | { |
| 103 | void **shadow_leak, *leak; | 113 | void **shadow_leak; |
| 104 | 114 | ||
| 105 | /* | 115 | /* |
| 106 | * Patch: fetch the saved SV_LEAK shadow variable, detach and | 116 | * Patch: fetch the saved SV_LEAK shadow variable, detach and |
| @@ -109,15 +119,10 @@ void livepatch_fix1_dummy_free(struct dummy *d) | |||
| 109 | * was loaded.) | 119 | * was loaded.) |
| 110 | */ | 120 | */ |
| 111 | shadow_leak = klp_shadow_get(d, SV_LEAK); | 121 | shadow_leak = klp_shadow_get(d, SV_LEAK); |
| 112 | if (shadow_leak) { | 122 | if (shadow_leak) |
| 113 | leak = *shadow_leak; | 123 | klp_shadow_free(d, SV_LEAK, livepatch_fix1_dummy_leak_dtor); |
| 114 | klp_shadow_free(d, SV_LEAK); | 124 | else |
| 115 | kfree(leak); | ||
| 116 | pr_info("%s: dummy @ %p, prevented leak @ %p\n", | ||
| 117 | __func__, d, leak); | ||
| 118 | } else { | ||
| 119 | pr_info("%s: dummy @ %p leaked!\n", __func__, d); | 125 | pr_info("%s: dummy @ %p leaked!\n", __func__, d); |
| 120 | } | ||
| 121 | 126 | ||
| 122 | kfree(d); | 127 | kfree(d); |
| 123 | } | 128 | } |
| @@ -163,7 +168,7 @@ static int livepatch_shadow_fix1_init(void) | |||
| 163 | static void livepatch_shadow_fix1_exit(void) | 168 | static void livepatch_shadow_fix1_exit(void) |
| 164 | { | 169 | { |
| 165 | /* Cleanup any existing SV_LEAK shadow variables */ | 170 | /* Cleanup any existing SV_LEAK shadow variables */ |
| 166 | klp_shadow_free_all(SV_LEAK); | 171 | klp_shadow_free_all(SV_LEAK, livepatch_fix1_dummy_leak_dtor); |
| 167 | 172 | ||
| 168 | WARN_ON(klp_unregister_patch(&patch)); | 173 | WARN_ON(klp_unregister_patch(&patch)); |
| 169 | } | 174 | } |
diff --git a/samples/livepatch/livepatch-shadow-fix2.c b/samples/livepatch/livepatch-shadow-fix2.c index d6c62844dc15..b34c7bf83356 100644 --- a/samples/livepatch/livepatch-shadow-fix2.c +++ b/samples/livepatch/livepatch-shadow-fix2.c | |||
| @@ -68,22 +68,27 @@ bool livepatch_fix2_dummy_check(struct dummy *d, unsigned long jiffies) | |||
| 68 | return time_after(jiffies, d->jiffies_expire); | 68 | return time_after(jiffies, d->jiffies_expire); |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | static void livepatch_fix2_dummy_leak_dtor(void *obj, void *shadow_data) | ||
| 72 | { | ||
| 73 | void *d = obj; | ||
| 74 | void **shadow_leak = shadow_data; | ||
| 75 | |||
| 76 | kfree(*shadow_leak); | ||
| 77 | pr_info("%s: dummy @ %p, prevented leak @ %p\n", | ||
| 78 | __func__, d, *shadow_leak); | ||
| 79 | } | ||
| 80 | |||
| 71 | void livepatch_fix2_dummy_free(struct dummy *d) | 81 | void livepatch_fix2_dummy_free(struct dummy *d) |
| 72 | { | 82 | { |
| 73 | void **shadow_leak, *leak; | 83 | void **shadow_leak; |
| 74 | int *shadow_count; | 84 | int *shadow_count; |
| 75 | 85 | ||
| 76 | /* Patch: copy the memory leak patch from the fix1 module. */ | 86 | /* Patch: copy the memory leak patch from the fix1 module. */ |
| 77 | shadow_leak = klp_shadow_get(d, SV_LEAK); | 87 | shadow_leak = klp_shadow_get(d, SV_LEAK); |
| 78 | if (shadow_leak) { | 88 | if (shadow_leak) |
| 79 | leak = *shadow_leak; | 89 | klp_shadow_free(d, SV_LEAK, livepatch_fix2_dummy_leak_dtor); |
| 80 | klp_shadow_free(d, SV_LEAK); | 90 | else |
| 81 | kfree(leak); | ||
| 82 | pr_info("%s: dummy @ %p, prevented leak @ %p\n", | ||
| 83 | __func__, d, leak); | ||
| 84 | } else { | ||
| 85 | pr_info("%s: dummy @ %p leaked!\n", __func__, d); | 91 | pr_info("%s: dummy @ %p leaked!\n", __func__, d); |
| 86 | } | ||
| 87 | 92 | ||
| 88 | /* | 93 | /* |
| 89 | * Patch: fetch the SV_COUNTER shadow variable and display | 94 | * Patch: fetch the SV_COUNTER shadow variable and display |
| @@ -93,7 +98,7 @@ void livepatch_fix2_dummy_free(struct dummy *d) | |||
| 93 | if (shadow_count) { | 98 | if (shadow_count) { |
| 94 | pr_info("%s: dummy @ %p, check counter = %d\n", | 99 | pr_info("%s: dummy @ %p, check counter = %d\n", |
| 95 | __func__, d, *shadow_count); | 100 | __func__, d, *shadow_count); |
| 96 | klp_shadow_free(d, SV_COUNTER); | 101 | klp_shadow_free(d, SV_COUNTER, NULL); |
| 97 | } | 102 | } |
| 98 | 103 | ||
| 99 | kfree(d); | 104 | kfree(d); |
| @@ -140,7 +145,7 @@ static int livepatch_shadow_fix2_init(void) | |||
| 140 | static void livepatch_shadow_fix2_exit(void) | 145 | static void livepatch_shadow_fix2_exit(void) |
| 141 | { | 146 | { |
| 142 | /* Cleanup any existing SV_COUNTER shadow variables */ | 147 | /* Cleanup any existing SV_COUNTER shadow variables */ |
| 143 | klp_shadow_free_all(SV_COUNTER); | 148 | klp_shadow_free_all(SV_COUNTER, NULL); |
| 144 | 149 | ||
| 145 | WARN_ON(klp_unregister_patch(&patch)); | 150 | WARN_ON(klp_unregister_patch(&patch)); |
| 146 | } | 151 | } |
