aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeert Uytterhoeven <geert+renesas@glider.be>2017-12-05 10:27:02 -0500
committerRob Herring <robh@kernel.org>2017-12-06 17:04:36 -0500
commit1352f09b4cc4f9dce386620b118401738bbf0d5f (patch)
tree698e66155eb0e01ddf6acfa8cce1e7433db71dff
parent3bcca2c271259ab2c3b539f0afa17d0043854c01 (diff)
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: Colin King <colin.king@canonical.com> Fixes: f948d6d8b792bb90 ("of: overlay: avoid race condition between applying multiple overlays") Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Frank Rowand <frank.rowand@sony.com> Signed-off-by: Rob Herring <robh@kernel.org>
-rw-r--r--drivers/of/overlay.c16
1 files changed, 8 insertions, 8 deletions
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 2b852a39581e..bb3f123ed259 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -522,7 +522,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
522 struct device_node *node, *overlay_node; 522 struct device_node *node, *overlay_node;
523 struct fragment *fragment; 523 struct fragment *fragment;
524 struct fragment *fragments; 524 struct fragment *fragments;
525 int cnt, ret; 525 int cnt, id, ret;
526 526
527 /* 527 /*
528 * Warn for some issues. Can not return -EINVAL for these until 528 * 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 543
544 of_changeset_init(&ovcs->cset); 544 of_changeset_init(&ovcs->cset);
545 545
546 ovcs->id = idr_alloc(&ovcs_idr, ovcs, 1, 0, GFP_KERNEL); 546 id = idr_alloc(&ovcs_idr, ovcs, 1, 0, GFP_KERNEL);
547 if (ovcs->id <= 0) 547 if (id <= 0)
548 return ovcs->id; 548 return id;
549 549
550 cnt = 0; 550 cnt = 0;
551 551
@@ -611,6 +611,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
611 goto err_free_fragments; 611 goto err_free_fragments;
612 } 612 }
613 613
614 ovcs->id = id;
614 ovcs->count = cnt; 615 ovcs->count = cnt;
615 ovcs->fragments = fragments; 616 ovcs->fragments = fragments;
616 617
@@ -619,7 +620,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
619err_free_fragments: 620err_free_fragments:
620 kfree(fragments); 621 kfree(fragments);
621err_free_idr: 622err_free_idr:
622 idr_remove(&ovcs_idr, ovcs->id); 623 idr_remove(&ovcs_idr, id);
623 624
624 pr_err("%s() failed, ret = %d\n", __func__, ret); 625 pr_err("%s() failed, ret = %d\n", __func__, ret);
625 626
@@ -630,9 +631,8 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
630{ 631{
631 int i; 632 int i;
632 633
633 if (!ovcs->cset.entries.next) 634 if (ovcs->cset.entries.next)
634 return; 635 of_changeset_destroy(&ovcs->cset);
635 of_changeset_destroy(&ovcs->cset);
636 636
637 if (ovcs->id) 637 if (ovcs->id)
638 idr_remove(&ovcs_idr, ovcs->id); 638 idr_remove(&ovcs_idr, ovcs->id);