Commit 1352f09b authored by Geert Uytterhoeven's avatar Geert Uytterhoeven Committed by Rob Herring

of: overlay: Fix memory leak in of_overlay_apply() error path

If of_resolve_phandles() fails, free_overlay_changeset() is called in
the error path.  However, that function returns early if the list hasn't
been initialized yet, before freeing the object.

Explicitly calling kfree() instead would solve that issue. However, that
complicates matter, by having to consider which of two different methods
to use to dispose of the same object.

Hence make free_overlay_changeset() consider initialization state of the
different parts of the object, making it always safe to call (once!) to
dispose of a (partially) initialized overlay_changeset:
  - Only destroy the changeset if the list was initialized,
  - Make init_overlay_changeset() store the ID in ovcs->id on success,
    to avoid calling idr_remove() with an error value or an already
    released ID.
Reported-by: default avatarColin King <colin.king@canonical.com>
Fixes: f948d6d8 ("of: overlay: avoid race condition between applying multiple overlays")
Signed-off-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: default avatarFrank Rowand <frank.rowand@sony.com>
Signed-off-by: default avatarRob Herring <robh@kernel.org>
parent 3bcca2c2
...@@ -522,7 +522,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs, ...@@ -522,7 +522,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
struct device_node *node, *overlay_node; struct device_node *node, *overlay_node;
struct fragment *fragment; struct fragment *fragment;
struct fragment *fragments; struct fragment *fragments;
int cnt, ret; int cnt, id, ret;
/* /*
* Warn for some issues. Can not return -EINVAL for these until * Warn for some issues. Can not return -EINVAL for these until
...@@ -543,9 +543,9 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs, ...@@ -543,9 +543,9 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
of_changeset_init(&ovcs->cset); of_changeset_init(&ovcs->cset);
ovcs->id = idr_alloc(&ovcs_idr, ovcs, 1, 0, GFP_KERNEL); id = idr_alloc(&ovcs_idr, ovcs, 1, 0, GFP_KERNEL);
if (ovcs->id <= 0) if (id <= 0)
return ovcs->id; return id;
cnt = 0; cnt = 0;
...@@ -611,6 +611,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs, ...@@ -611,6 +611,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
goto err_free_fragments; goto err_free_fragments;
} }
ovcs->id = id;
ovcs->count = cnt; ovcs->count = cnt;
ovcs->fragments = fragments; ovcs->fragments = fragments;
...@@ -619,7 +620,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs, ...@@ -619,7 +620,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
err_free_fragments: err_free_fragments:
kfree(fragments); kfree(fragments);
err_free_idr: err_free_idr:
idr_remove(&ovcs_idr, ovcs->id); idr_remove(&ovcs_idr, id);
pr_err("%s() failed, ret = %d\n", __func__, ret); pr_err("%s() failed, ret = %d\n", __func__, ret);
...@@ -630,8 +631,7 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs) ...@@ -630,8 +631,7 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
{ {
int i; int i;
if (!ovcs->cset.entries.next) if (ovcs->cset.entries.next)
return;
of_changeset_destroy(&ovcs->cset); of_changeset_destroy(&ovcs->cset);
if (ovcs->id) if (ovcs->id)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment