Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0d36496b authored by Prateek Sood's avatar Prateek Sood Committed by Greg Kroah-Hartman
Browse files

BACKPORT: FROMLIST: 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 __vunmap() relies on nr_pages of
struct vm_struct. vmap() does not update nr_pages.

Bug: 166705104
Link: https://lore.kernel.org/patchwork/patch/1292695/


Change-Id: I8d9610d89f2936edc01b977ceba1853b41d5c249
Signed-off-by: default avatarPrateek Sood <prsood@codeaurora.org>
parent 77c45270
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -139,10 +139,12 @@ int assign_fw(struct firmware *fw, struct device *device,
void fw_free_paged_buf(struct fw_priv *fw_priv);
int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed);
int fw_map_paged_buf(struct fw_priv *fw_priv);
bool fw_is_paged_buf(struct fw_priv *fw_priv);
#else
static inline void fw_free_paged_buf(struct fw_priv *fw_priv) {}
static inline int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed) { return -ENXIO; }
static inline int fw_map_paged_buf(struct fw_priv *fw_priv) { return -ENXIO; }
static inline bool fw_is_paged_buf(struct fw_priv *fw_priv) { return false; }
#endif

#endif /* __FIRMWARE_LOADER_H */
+11 −6
Original line number Diff line number Diff line
@@ -252,9 +252,11 @@ static void __free_fw_priv(struct kref *ref)
	list_del(&fw_priv->list);
	spin_unlock(&fwc->lock);

	if (fw_is_paged_buf(fw_priv))
		fw_free_paged_buf(fw_priv); /* free leftover pages */
	if (!fw_priv->allocated_size)
	else if (!fw_priv->allocated_size)
		vfree(fw_priv->data);

	kfree_const(fw_priv->fw_name);
	kfree(fw_priv);
}
@@ -268,6 +270,11 @@ static void free_fw_priv(struct fw_priv *fw_priv)
}

#ifdef CONFIG_FW_LOADER_PAGED_BUF
bool fw_is_paged_buf(struct fw_priv *fw_priv)
{
	return fw_priv->is_paged_buf;
}

void fw_free_paged_buf(struct fw_priv *fw_priv)
{
	int i;
@@ -275,6 +282,8 @@ void fw_free_paged_buf(struct fw_priv *fw_priv)
	if (!fw_priv->pages)
		return;

	vunmap(fw_priv->data);

	for (i = 0; i < fw_priv->nr_pages; i++)
		__free_page(fw_priv->pages[i]);
	kvfree(fw_priv->pages);
@@ -328,10 +337,6 @@ int fw_map_paged_buf(struct fw_priv *fw_priv)
	if (!fw_priv->data)
		return -ENOMEM;

	/* page table is no longer needed after mapping, let's free */
	kvfree(fw_priv->pages);
	fw_priv->pages = NULL;

	return 0;
}
#endif