]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
of: Fix double free in of_parse_phandle_with_args_map
authorChristian A. Ehrhardt <lk@c--e.de>
Fri, 29 Dec 2023 10:54:11 +0000 (11:54 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 25 Jan 2024 22:52:46 +0000 (14:52 -0800)
[ Upstream commit 4dde83569832f9377362e50f7748463340c5db6b ]

In of_parse_phandle_with_args_map() the inner loop that
iterates through the map entries calls of_node_put(new)
to free the reference acquired by the previous iteration
of the inner loop. This assumes that the value of "new" is
NULL on the first iteration of the inner loop.

Make sure that this is true in all iterations of the outer
loop by setting "new" to NULL after its value is assigned to "cur".

Extend the unittest to detect the double free and add an additional
test case that actually triggers this path.

Fixes: bd6f2fd5a1 ("of: Support parsing phandle argument lists through a nexus node")
Cc: Stephen Boyd <stephen.boyd@linaro.org>
Signed-off-by: "Christian A. Ehrhardt" <lk@c--e.de>
Link: https://lore.kernel.org/r/20231229105411.1603434-1-lk@c--e.de
Signed-off-by: Rob Herring <robh@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/of/base.c
drivers/of/unittest-data/tests-phandle.dtsi
drivers/of/unittest.c

index 54719f8156ed1081d55b5e63accbcbd143b1222b..bc5abe650c5c2339458224a1ec01ce0b92201cec 100644 (file)
@@ -1669,6 +1669,7 @@ int of_parse_phandle_with_args_map(const struct device_node *np,
                out_args->np = new;
                of_node_put(cur);
                cur = new;
+               new = NULL;
        }
 put:
        of_node_put(cur);
index 6b33be4c4416ce7bd72d5639a5cfe4b03a79f082..aa0d7027ffa68942889d9cfc0f845b8a469dd19c 100644 (file)
                                phandle-map-pass-thru = <0x0 0xf0>;
                        };
 
+                       provider5: provider5 {
+                               #phandle-cells = <2>;
+                               phandle-map = <2 7 &provider4 2 3>;
+                               phandle-map-mask = <0xff 0xf>;
+                               phandle-map-pass-thru = <0x0 0xf0>;
+                       };
+
                        consumer-a {
                                phandle-list =  <&provider1 1>,
                                                <&provider2 2 0>,
@@ -64,7 +71,8 @@
                                                <&provider4 4 0x100>,
                                                <&provider4 0 0x61>,
                                                <&provider0>,
-                                               <&provider4 19 0x20>;
+                                               <&provider4 19 0x20>,
+                                               <&provider5 2 7>;
                                phandle-list-bad-phandle = <12345678 0 0>;
                                phandle-list-bad-args = <&provider2 1 0>,
                                                        <&provider4 0>;
index 073a3f44c40495329a0c4c23b7d2585e36af8363..f3a136d750939dd3e0c04481997df671e542cbfd 100644 (file)
@@ -448,6 +448,9 @@ static void __init of_unittest_parse_phandle_with_args(void)
 
                unittest(passed, "index %i - data error on node %pOF rc=%i\n",
                         i, args.np, rc);
+
+               if (rc == 0)
+                       of_node_put(args.np);
        }
 
        /* Check for missing list property */
@@ -537,8 +540,9 @@ static void __init of_unittest_parse_phandle_with_args(void)
 
 static void __init of_unittest_parse_phandle_with_args_map(void)
 {
-       struct device_node *np, *p0, *p1, *p2, *p3;
+       struct device_node *np, *p[6] = {};
        struct of_phandle_args args;
+       unsigned int prefs[6];
        int i, rc;
 
        np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-b");
@@ -547,34 +551,24 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
                return;
        }
 
-       p0 = of_find_node_by_path("/testcase-data/phandle-tests/provider0");
-       if (!p0) {
-               pr_err("missing testcase data\n");
-               return;
-       }
-
-       p1 = of_find_node_by_path("/testcase-data/phandle-tests/provider1");
-       if (!p1) {
-               pr_err("missing testcase data\n");
-               return;
-       }
-
-       p2 = of_find_node_by_path("/testcase-data/phandle-tests/provider2");
-       if (!p2) {
-               pr_err("missing testcase data\n");
-               return;
-       }
-
-       p3 = of_find_node_by_path("/testcase-data/phandle-tests/provider3");
-       if (!p3) {
-               pr_err("missing testcase data\n");
-               return;
+       p[0] = of_find_node_by_path("/testcase-data/phandle-tests/provider0");
+       p[1] = of_find_node_by_path("/testcase-data/phandle-tests/provider1");
+       p[2] = of_find_node_by_path("/testcase-data/phandle-tests/provider2");
+       p[3] = of_find_node_by_path("/testcase-data/phandle-tests/provider3");
+       p[4] = of_find_node_by_path("/testcase-data/phandle-tests/provider4");
+       p[5] = of_find_node_by_path("/testcase-data/phandle-tests/provider5");
+       for (i = 0; i < ARRAY_SIZE(p); ++i) {
+               if (!p[i]) {
+                       pr_err("missing testcase data\n");
+                       return;
+               }
+               prefs[i] = kref_read(&p[i]->kobj.kref);
        }
 
        rc = of_count_phandle_with_args(np, "phandle-list", "#phandle-cells");
-       unittest(rc == 7, "of_count_phandle_with_args() returned %i, expected 7\n", rc);
+       unittest(rc == 8, "of_count_phandle_with_args() returned %i, expected 7\n", rc);
 
-       for (i = 0; i < 8; i++) {
+       for (i = 0; i < 9; i++) {
                bool passed = true;
 
                memset(&args, 0, sizeof(args));
@@ -585,13 +579,13 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
                switch (i) {
                case 0:
                        passed &= !rc;
-                       passed &= (args.np == p1);
+                       passed &= (args.np == p[1]);
                        passed &= (args.args_count == 1);
                        passed &= (args.args[0] == 1);
                        break;
                case 1:
                        passed &= !rc;
-                       passed &= (args.np == p3);
+                       passed &= (args.np == p[3]);
                        passed &= (args.args_count == 3);
                        passed &= (args.args[0] == 2);
                        passed &= (args.args[1] == 5);
@@ -602,28 +596,36 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
                        break;
                case 3:
                        passed &= !rc;
-                       passed &= (args.np == p0);
+                       passed &= (args.np == p[0]);
                        passed &= (args.args_count == 0);
                        break;
                case 4:
                        passed &= !rc;
-                       passed &= (args.np == p1);
+                       passed &= (args.np == p[1]);
                        passed &= (args.args_count == 1);
                        passed &= (args.args[0] == 3);
                        break;
                case 5:
                        passed &= !rc;
-                       passed &= (args.np == p0);
+                       passed &= (args.np == p[0]);
                        passed &= (args.args_count == 0);
                        break;
                case 6:
                        passed &= !rc;
-                       passed &= (args.np == p2);
+                       passed &= (args.np == p[2]);
                        passed &= (args.args_count == 2);
                        passed &= (args.args[0] == 15);
                        passed &= (args.args[1] == 0x20);
                        break;
                case 7:
+                       passed &= !rc;
+                       passed &= (args.np == p[3]);
+                       passed &= (args.args_count == 3);
+                       passed &= (args.args[0] == 2);
+                       passed &= (args.args[1] == 5);
+                       passed &= (args.args[2] == 3);
+                       break;
+               case 8:
                        passed &= (rc == -ENOENT);
                        break;
                default:
@@ -632,6 +634,9 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
 
                unittest(passed, "index %i - data error on node %s rc=%i\n",
                         i, args.np->full_name, rc);
+
+               if (rc == 0)
+                       of_node_put(args.np);
        }
 
        /* Check for missing list property */
@@ -678,6 +683,13 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
                   "OF: /testcase-data/phandle-tests/consumer-b: #phandle-cells = 2 found -1");
 
        unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+
+       for (i = 0; i < ARRAY_SIZE(p); ++i) {
+               unittest(prefs[i] == kref_read(&p[i]->kobj.kref),
+                        "provider%d: expected:%d got:%d\n",
+                        i, prefs[i], kref_read(&p[i]->kobj.kref));
+               of_node_put(p[i]);
+       }
 }
 
 static void __init of_unittest_property_string(void)