Skip to content

Commit f41deee

Browse files
kkdwvdAlexei Starovoitov
authored andcommitted
bpf: Delay freeing fields in local storage
Currently, when use_kmalloc_nolock is false, the freeing of fields for a local storage selem is done eagerly before waiting for the RCU or RCU tasks trace grace period to elapse. This opens up a window where the program which has access to the selem can recreate the fields after the freeing of fields is done eagerly, causing memory leaks when the element is finally freed and returned to the kernel. Make a few changes to address this. First, delay the freeing of fields until after the grace periods have expired using a __bpf_selem_free_rcu wrapper which is eventually invoked after transitioning through the necessary number of grace period waits. Replace usage of the kfree_rcu with call_rcu to be able to take a custom callback. Finally, care needs to be taken to extend the rcu barriers for all cases, and not just when use_kmalloc_nolock is true, as RCU and RCU tasks trace callbacks can be in flight for either case and access the smap field, which is used to obtain the BTF record to walk over special fields in the map value. While we're at it, drop migrate_disable() from bpf_selem_free_rcu, since migration should be disabled for RCU callbacks already. Fixes: 9bac675 ("bpf: Postpone bpf_obj_free_fields to the rcu callback") Reviewed-by: Amery Hung <ameryhung@gmail.com> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Link: https://lore.kernel.org/r/20260227224806.646888-4-memxor@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent ae51772 commit f41deee

1 file changed

Lines changed: 21 additions & 19 deletions

File tree

kernel/bpf/bpf_local_storage.c

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -164,24 +164,36 @@ static void bpf_local_storage_free(struct bpf_local_storage *local_storage,
164164
bpf_local_storage_free_trace_rcu);
165165
}
166166

167-
/* rcu tasks trace callback for use_kmalloc_nolock == false */
168-
static void __bpf_selem_free_trace_rcu(struct rcu_head *rcu)
167+
/* rcu callback for use_kmalloc_nolock == false */
168+
static void __bpf_selem_free_rcu(struct rcu_head *rcu)
169169
{
170170
struct bpf_local_storage_elem *selem;
171+
struct bpf_local_storage_map *smap;
171172

172173
selem = container_of(rcu, struct bpf_local_storage_elem, rcu);
174+
/* bpf_selem_unlink_nofail may have already cleared smap and freed fields. */
175+
smap = rcu_dereference_check(SDATA(selem)->smap, 1);
176+
177+
if (smap)
178+
bpf_obj_free_fields(smap->map.record, SDATA(selem)->data);
179+
kfree(selem);
180+
}
181+
182+
/* rcu tasks trace callback for use_kmalloc_nolock == false */
183+
static void __bpf_selem_free_trace_rcu(struct rcu_head *rcu)
184+
{
173185
if (rcu_trace_implies_rcu_gp())
174-
kfree(selem);
186+
__bpf_selem_free_rcu(rcu);
175187
else
176-
kfree_rcu(selem, rcu);
188+
call_rcu(rcu, __bpf_selem_free_rcu);
177189
}
178190

179191
/* Handle use_kmalloc_nolock == false */
180192
static void __bpf_selem_free(struct bpf_local_storage_elem *selem,
181193
bool vanilla_rcu)
182194
{
183195
if (vanilla_rcu)
184-
kfree_rcu(selem, rcu);
196+
call_rcu(&selem->rcu, __bpf_selem_free_rcu);
185197
else
186198
call_rcu_tasks_trace(&selem->rcu, __bpf_selem_free_trace_rcu);
187199
}
@@ -195,11 +207,8 @@ static void bpf_selem_free_rcu(struct rcu_head *rcu)
195207
/* The bpf_local_storage_map_free will wait for rcu_barrier */
196208
smap = rcu_dereference_check(SDATA(selem)->smap, 1);
197209

198-
if (smap) {
199-
migrate_disable();
210+
if (smap)
200211
bpf_obj_free_fields(smap->map.record, SDATA(selem)->data);
201-
migrate_enable();
202-
}
203212
kfree_nolock(selem);
204213
}
205214

@@ -214,18 +223,12 @@ static void bpf_selem_free_trace_rcu(struct rcu_head *rcu)
214223
void bpf_selem_free(struct bpf_local_storage_elem *selem,
215224
bool reuse_now)
216225
{
217-
struct bpf_local_storage_map *smap;
218-
219-
smap = rcu_dereference_check(SDATA(selem)->smap, bpf_rcu_lock_held());
220-
221226
if (!selem->use_kmalloc_nolock) {
222227
/*
223228
* No uptr will be unpin even when reuse_now == false since uptr
224229
* is only supported in task local storage, where
225230
* smap->use_kmalloc_nolock == true.
226231
*/
227-
if (smap)
228-
bpf_obj_free_fields(smap->map.record, SDATA(selem)->data);
229232
__bpf_selem_free(selem, reuse_now);
230233
return;
231234
}
@@ -958,10 +961,9 @@ void bpf_local_storage_map_free(struct bpf_map *map,
958961
*/
959962
synchronize_rcu();
960963

961-
if (smap->use_kmalloc_nolock) {
962-
rcu_barrier_tasks_trace();
963-
rcu_barrier();
964-
}
964+
/* smap remains in use regardless of kmalloc_nolock, so wait unconditionally. */
965+
rcu_barrier_tasks_trace();
966+
rcu_barrier();
965967
kvfree(smap->buckets);
966968
bpf_map_area_free(smap);
967969
}

0 commit comments

Comments
 (0)