Skip to content

Commit 3b2c77d

Browse files
pmladekJiri Kosina
authored andcommitted
livepatch: Allow to call a custom callback when freeing shadow variables
We might need to do some actions before the shadow variable is freed. For example, we might need to remove it from a list or free some data that it points to. This is already possible now. The user can get the shadow variable by klp_shadow_get(), do the necessary actions, and then call klp_shadow_free(). This patch allows to do it a more elegant way. The user could implement the needed actions in a callback that is passed to klp_shadow_free() as a parameter. The callback usually does reverse operations to the constructor callback that can be called by klp_shadow_*alloc(). It is especially useful for klp_shadow_free_all(). There we need to do these extra actions for each found shadow variable with the given ID. Note that the memory used by the shadow variable itself is still released later by rcu callback. It is needed to protect internal structures that keep all shadow variables. But the destructor is called immediately. The shadow variable must not be access anyway after klp_shadow_free() is called. The user is responsible to protect this any suitable way. Be aware that the destructor is called under klp_shadow_lock. It is the same as for the contructor in klp_shadow_alloc(). Signed-off-by: Petr Mladek <[email protected]> Acked-by: Josh Poimboeuf <[email protected]> Acked-by: Miroslav Benes <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent e91c251 commit 3b2c77d

File tree

5 files changed

+59
-34
lines changed

5 files changed

+59
-34
lines changed

Documentation/livepatch/shadow-vars.txt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,15 @@ to do actions that can be done only once when a new variable is allocated.
6565

6666
* klp_shadow_free() - detach and free a <obj, id> shadow variable
6767
- 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
6971

7072
* klp_shadow_free_all() - detach and free all <*, id> shadow variables
7173
- 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
7377

7478

7579
2. Use cases
@@ -136,7 +140,7 @@ variable:
136140

137141
void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
138142
{
139-
klp_shadow_free(sta, PS_LOCK);
143+
klp_shadow_free(sta, PS_LOCK, NULL);
140144
kfree(sta);
141145
...
142146

include/linux/livepatch.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ static inline bool klp_have_reliable_stack(void)
189189
typedef int (*klp_shadow_ctor_t)(void *obj,
190190
void *shadow_data,
191191
void *ctor_data);
192+
typedef void (*klp_shadow_dtor_t)(void *obj, void *shadow_data);
192193

193194
void *klp_shadow_get(void *obj, unsigned long id);
194195
void *klp_shadow_alloc(void *obj, unsigned long id,
@@ -197,8 +198,8 @@ void *klp_shadow_alloc(void *obj, unsigned long id,
197198
void *klp_shadow_get_or_alloc(void *obj, unsigned long id,
198199
size_t size, gfp_t gfp_flags,
199200
klp_shadow_ctor_t ctor, void *ctor_data);
200-
void klp_shadow_free(void *obj, unsigned long id);
201-
void klp_shadow_free_all(unsigned long id);
201+
void klp_shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor);
202+
void klp_shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor);
202203

203204
#else /* !CONFIG_LIVEPATCH */
204205

kernel/livepatch/shadow.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -243,15 +243,26 @@ void *klp_shadow_get_or_alloc(void *obj, unsigned long id,
243243
}
244244
EXPORT_SYMBOL_GPL(klp_shadow_get_or_alloc);
245245

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+
246255
/**
247256
* klp_shadow_free() - detach and free a <obj, id> shadow variable
248257
* @obj: pointer to parent object
249258
* @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)
250261
*
251262
* This function releases the memory for this <obj, id> shadow variable
252263
* instance, callers should stop referencing it accordingly.
253264
*/
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)
255266
{
256267
struct klp_shadow *shadow;
257268
unsigned long flags;
@@ -263,8 +274,7 @@ void klp_shadow_free(void *obj, unsigned long id)
263274
(unsigned long)obj) {
264275

265276
if (klp_shadow_match(shadow, obj, id)) {
266-
hash_del_rcu(&shadow->node);
267-
kfree_rcu(shadow, rcu_head);
277+
klp_shadow_free_struct(shadow, dtor);
268278
break;
269279
}
270280
}
@@ -276,11 +286,13 @@ EXPORT_SYMBOL_GPL(klp_shadow_free);
276286
/**
277287
* klp_shadow_free_all() - detach and free all <*, id> shadow variables
278288
* @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)
279291
*
280292
* This function releases the memory for all <*, id> shadow variable
281293
* instances, callers should stop referencing them accordingly.
282294
*/
283-
void klp_shadow_free_all(unsigned long id)
295+
void klp_shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor)
284296
{
285297
struct klp_shadow *shadow;
286298
unsigned long flags;
@@ -290,10 +302,8 @@ void klp_shadow_free_all(unsigned long id)
290302

291303
/* Delete all <*, id> from hash */
292304
hash_for_each(klp_shadow_hash, i, shadow, node) {
293-
if (klp_shadow_match(shadow, shadow->obj, id)) {
294-
hash_del_rcu(&shadow->node);
295-
kfree_rcu(shadow, rcu_head);
296-
}
305+
if (klp_shadow_match(shadow, shadow->obj, id))
306+
klp_shadow_free_struct(shadow, dtor);
297307
}
298308

299309
spin_unlock_irqrestore(&klp_shadow_lock, flags);

samples/livepatch/livepatch-shadow-fix1.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,19 @@ struct dummy *livepatch_fix1_dummy_alloc(void)
9898
return d;
9999
}
100100

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+
101111
void livepatch_fix1_dummy_free(struct dummy *d)
102112
{
103-
void **shadow_leak, *leak;
113+
void **shadow_leak;
104114

105115
/*
106116
* Patch: fetch the saved SV_LEAK shadow variable, detach and
@@ -109,15 +119,10 @@ void livepatch_fix1_dummy_free(struct dummy *d)
109119
* was loaded.)
110120
*/
111121
shadow_leak = klp_shadow_get(d, SV_LEAK);
112-
if (shadow_leak) {
113-
leak = *shadow_leak;
114-
klp_shadow_free(d, SV_LEAK);
115-
kfree(leak);
116-
pr_info("%s: dummy @ %p, prevented leak @ %p\n",
117-
__func__, d, leak);
118-
} else {
122+
if (shadow_leak)
123+
klp_shadow_free(d, SV_LEAK, livepatch_fix1_dummy_leak_dtor);
124+
else
119125
pr_info("%s: dummy @ %p leaked!\n", __func__, d);
120-
}
121126

122127
kfree(d);
123128
}
@@ -163,7 +168,7 @@ static int livepatch_shadow_fix1_init(void)
163168
static void livepatch_shadow_fix1_exit(void)
164169
{
165170
/* 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);
167172

168173
WARN_ON(klp_unregister_patch(&patch));
169174
}

samples/livepatch/livepatch-shadow-fix2.c

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,22 +68,27 @@ bool livepatch_fix2_dummy_check(struct dummy *d, unsigned long jiffies)
6868
return time_after(jiffies, d->jiffies_expire);
6969
}
7070

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+
7181
void livepatch_fix2_dummy_free(struct dummy *d)
7282
{
73-
void **shadow_leak, *leak;
83+
void **shadow_leak;
7484
int *shadow_count;
7585

7686
/* Patch: copy the memory leak patch from the fix1 module. */
7787
shadow_leak = klp_shadow_get(d, SV_LEAK);
78-
if (shadow_leak) {
79-
leak = *shadow_leak;
80-
klp_shadow_free(d, SV_LEAK);
81-
kfree(leak);
82-
pr_info("%s: dummy @ %p, prevented leak @ %p\n",
83-
__func__, d, leak);
84-
} else {
88+
if (shadow_leak)
89+
klp_shadow_free(d, SV_LEAK, livepatch_fix2_dummy_leak_dtor);
90+
else
8591
pr_info("%s: dummy @ %p leaked!\n", __func__, d);
86-
}
8792

8893
/*
8994
* Patch: fetch the SV_COUNTER shadow variable and display
@@ -93,7 +98,7 @@ void livepatch_fix2_dummy_free(struct dummy *d)
9398
if (shadow_count) {
9499
pr_info("%s: dummy @ %p, check counter = %d\n",
95100
__func__, d, *shadow_count);
96-
klp_shadow_free(d, SV_COUNTER);
101+
klp_shadow_free(d, SV_COUNTER, NULL);
97102
}
98103

99104
kfree(d);
@@ -140,7 +145,7 @@ static int livepatch_shadow_fix2_init(void)
140145
static void livepatch_shadow_fix2_exit(void)
141146
{
142147
/* Cleanup any existing SV_COUNTER shadow variables */
143-
klp_shadow_free_all(SV_COUNTER);
148+
klp_shadow_free_all(SV_COUNTER, NULL);
144149

145150
WARN_ON(klp_unregister_patch(&patch));
146151
}

0 commit comments

Comments
 (0)