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 | } |