Skip to content

Commit 4965b8c

Browse files
KAGA-KOKOgregkh
authored andcommitted
firmware_loader: fix memory leak for paged buffer
vfree() is being called on paged buffer allocated using alloc_page() and mapped using vmap(). Freeing of pages in vfree() relies on nr_pages of struct vm_struct. vmap() does not update nr_pages. It can lead to memory leaks. Fixes: ddaf29f ("firmware: Free temporary page table after vmapping") Signed-off-by: Prateek Sood <prsood@codeaurora.org> Reviewed-by: Takashi Iwai <tiwai@suse.de> Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/1597957070-27185-1-git-send-email-prsood@codeaurora.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent d012a71 commit 4965b8c

File tree

2 files changed

+13
-6
lines changed

2 files changed

+13
-6
lines changed

drivers/base/firmware_loader/firmware.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,12 @@ int assign_fw(struct firmware *fw, struct device *device, u32 opt_flags);
142142
void fw_free_paged_buf(struct fw_priv *fw_priv);
143143
int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed);
144144
int fw_map_paged_buf(struct fw_priv *fw_priv);
145+
bool fw_is_paged_buf(struct fw_priv *fw_priv);
145146
#else
146147
static inline void fw_free_paged_buf(struct fw_priv *fw_priv) {}
147148
static inline int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed) { return -ENXIO; }
148149
static inline int fw_map_paged_buf(struct fw_priv *fw_priv) { return -ENXIO; }
150+
static inline bool fw_is_paged_buf(struct fw_priv *fw_priv) { return false; }
149151
#endif
150152

151153
#endif /* __FIRMWARE_LOADER_H */

drivers/base/firmware_loader/main.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,11 @@ static void __free_fw_priv(struct kref *ref)
252252
list_del(&fw_priv->list);
253253
spin_unlock(&fwc->lock);
254254

255-
fw_free_paged_buf(fw_priv); /* free leftover pages */
256-
if (!fw_priv->allocated_size)
255+
if (fw_is_paged_buf(fw_priv))
256+
fw_free_paged_buf(fw_priv);
257+
else if (!fw_priv->allocated_size)
257258
vfree(fw_priv->data);
259+
258260
kfree_const(fw_priv->fw_name);
259261
kfree(fw_priv);
260262
}
@@ -268,13 +270,20 @@ static void free_fw_priv(struct fw_priv *fw_priv)
268270
}
269271

270272
#ifdef CONFIG_FW_LOADER_PAGED_BUF
273+
bool fw_is_paged_buf(struct fw_priv *fw_priv)
274+
{
275+
return fw_priv->is_paged_buf;
276+
}
277+
271278
void fw_free_paged_buf(struct fw_priv *fw_priv)
272279
{
273280
int i;
274281

275282
if (!fw_priv->pages)
276283
return;
277284

285+
vunmap(fw_priv->data);
286+
278287
for (i = 0; i < fw_priv->nr_pages; i++)
279288
__free_page(fw_priv->pages[i]);
280289
kvfree(fw_priv->pages);
@@ -328,10 +337,6 @@ int fw_map_paged_buf(struct fw_priv *fw_priv)
328337
if (!fw_priv->data)
329338
return -ENOMEM;
330339

331-
/* page table is no longer needed after mapping, let's free */
332-
kvfree(fw_priv->pages);
333-
fw_priv->pages = NULL;
334-
335340
return 0;
336341
}
337342
#endif

0 commit comments

Comments
 (0)