]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
of: overlay: check prevents multiple fragments touching same property
authorFrank Rowand <frank.rowand@sony.com>
Fri, 5 Oct 2018 03:36:18 +0000 (20:36 -0700)
committerFrank Rowand <frank.rowand@sony.com>
Fri, 9 Nov 2018 06:12:03 +0000 (22:12 -0800)
Add test case of two fragments updating the same property.  After
adding the test case, the system hangs at end of boot, after
after slub stack dumps from kfree() in crypto modprobe code.

Multiple overlay fragments adding, modifying, or deleting the same
property is not supported.  Add check to detect the attempt and fail
the overlay apply.

Before this patch, the first fragment error would terminate
processing.  Allow fragment checking to proceed and report all
of the fragment errors before terminating the overlay apply. This
is not a hot path, thus not a performance issue (the error is not
transient and requires fixing the overlay before attempting to
apply it again).

After applying this patch, the devicetree unittest messages will
include:

   OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/rpm_avail

   ...

   ### dt-test ### end of unittest - 212 passed, 0 failed

The check to detect two fragments updating the same property is
folded into the patch that created the test case to maintain
bisectability.

Tested-by: Alan Tull <atull@kernel.org>
Signed-off-by: Frank Rowand <frank.rowand@sony.com>
drivers/of/overlay.c
drivers/of/unittest-data/Makefile
drivers/of/unittest-data/overlay_bad_add_dup_prop.dts [new file with mode: 0644]
drivers/of/unittest-data/overlay_base.dts
drivers/of/unittest.c

index 8af8115bd36e51a109182b647c0428436163c6f5..184cc2c4a9317415435c5dda0574d15f0b9caf0c 100644 (file)
@@ -508,52 +508,96 @@ static int build_changeset_symbols_node(struct overlay_changeset *ovcs,
        return 0;
 }
 
+static int find_dup_cset_node_entry(struct overlay_changeset *ovcs,
+               struct of_changeset_entry *ce_1)
+{
+       struct of_changeset_entry *ce_2;
+       char *fn_1, *fn_2;
+       int node_path_match;
+
+       if (ce_1->action != OF_RECONFIG_ATTACH_NODE &&
+           ce_1->action != OF_RECONFIG_DETACH_NODE)
+               return 0;
+
+       ce_2 = ce_1;
+       list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) {
+               if ((ce_2->action != OF_RECONFIG_ATTACH_NODE &&
+                    ce_2->action != OF_RECONFIG_DETACH_NODE) ||
+                   of_node_cmp(ce_1->np->full_name, ce_2->np->full_name))
+                       continue;
+
+               fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np);
+               fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np);
+               node_path_match = !strcmp(fn_1, fn_2);
+               kfree(fn_1);
+               kfree(fn_2);
+               if (node_path_match) {
+                       pr_err("ERROR: multiple fragments add and/or delete node %pOF\n",
+                              ce_1->np);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int find_dup_cset_prop(struct overlay_changeset *ovcs,
+               struct of_changeset_entry *ce_1)
+{
+       struct of_changeset_entry *ce_2;
+       char *fn_1, *fn_2;
+       int node_path_match;
+
+       if (ce_1->action != OF_RECONFIG_ADD_PROPERTY &&
+           ce_1->action != OF_RECONFIG_REMOVE_PROPERTY &&
+           ce_1->action != OF_RECONFIG_UPDATE_PROPERTY)
+               return 0;
+
+       ce_2 = ce_1;
+       list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) {
+               if ((ce_2->action != OF_RECONFIG_ADD_PROPERTY &&
+                    ce_2->action != OF_RECONFIG_REMOVE_PROPERTY &&
+                    ce_2->action != OF_RECONFIG_UPDATE_PROPERTY) ||
+                   of_node_cmp(ce_1->np->full_name, ce_2->np->full_name))
+                       continue;
+
+               fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np);
+               fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np);
+               node_path_match = !strcmp(fn_1, fn_2);
+               kfree(fn_1);
+               kfree(fn_2);
+               if (node_path_match &&
+                   !of_prop_cmp(ce_1->prop->name, ce_2->prop->name)) {
+                       pr_err("ERROR: multiple fragments add, update, and/or delete property %pOF/%s\n",
+                              ce_1->np, ce_1->prop->name);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 /**
- * check_changeset_dup_add_node() - changeset validation: duplicate add node
+ * changeset_dup_entry_check() - check for duplicate entries
  * @ovcs:      Overlay changeset
  *
- * Check changeset @ovcs->cset for multiple add node entries for the same
- * node.
+ * Check changeset @ovcs->cset for multiple {add or delete} node entries for
+ * the same node or duplicate {add, delete, or update} properties entries
+ * for the same property.
  *
- * Returns 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
- * invalid overlay in @ovcs->fragments[].
+ * Returns 0 on success, or -EINVAL if duplicate changeset entry found.
  */
-static int check_changeset_dup_add_node(struct overlay_changeset *ovcs)
+static int changeset_dup_entry_check(struct overlay_changeset *ovcs)
 {
-       struct of_changeset_entry *ce_1, *ce_2;
-       char *fn_1, *fn_2;
-       int name_match;
+       struct of_changeset_entry *ce_1;
+       int dup_entry = 0;
 
        list_for_each_entry(ce_1, &ovcs->cset.entries, node) {
-
-               if (ce_1->action == OF_RECONFIG_ATTACH_NODE ||
-                   ce_1->action == OF_RECONFIG_DETACH_NODE) {
-
-                       ce_2 = ce_1;
-                       list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) {
-                               if (ce_2->action == OF_RECONFIG_ATTACH_NODE ||
-                                   ce_2->action == OF_RECONFIG_DETACH_NODE) {
-                                       /* inexpensive name compare */
-                                       if (!of_node_cmp(ce_1->np->full_name,
-                                           ce_2->np->full_name)) {
-                                               /* expensive full path name compare */
-                                               fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np);
-                                               fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np);
-                                               name_match = !strcmp(fn_1, fn_2);
-                                               kfree(fn_1);
-                                               kfree(fn_2);
-                                               if (name_match) {
-                                                       pr_err("ERROR: multiple overlay fragments add and/or delete node %pOF\n",
-                                                              ce_1->np);
-                                                       return -EINVAL;
-                                               }
-                                       }
-                               }
-                       }
-               }
+               dup_entry |= find_dup_cset_node_entry(ovcs, ce_1);
+               dup_entry |= find_dup_cset_prop(ovcs, ce_1);
        }
 
-       return 0;
+       return dup_entry ? -EINVAL : 0;
 }
 
 /**
@@ -611,7 +655,7 @@ static int build_changeset(struct overlay_changeset *ovcs)
                }
        }
 
-       return check_changeset_dup_add_node(ovcs);
+       return changeset_dup_entry_check(ovcs);
 }
 
 /*
index 166dbdbfd1c55be1d9ebd4119dbc4142ce6005d3..9b680706582716a9b9d2275f3e6d8f3261abdd81 100644 (file)
@@ -18,6 +18,7 @@ obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \
                            overlay_13.dtb.o \
                            overlay_15.dtb.o \
                            overlay_bad_add_dup_node.dtb.o \
+                           overlay_bad_add_dup_prop.dtb.o \
                            overlay_bad_phandle.dtb.o \
                            overlay_bad_symbol.dtb.o \
                            overlay_base.dtb.o
diff --git a/drivers/of/unittest-data/overlay_bad_add_dup_prop.dts b/drivers/of/unittest-data/overlay_bad_add_dup_prop.dts
new file mode 100644 (file)
index 0000000..c190da5
--- /dev/null
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+/*
+ * &electric_1/motor-1 and &spin_ctrl_1 are the same node:
+ *   /testcase-data-2/substation@100/motor-1
+ *
+ * Thus the property "rpm_avail" in each fragment will
+ * result in an attempt to update the same property twice.
+ * This will result in an error and the overlay apply
+ * will fail.
+ */
+
+&electric_1 {
+
+       motor-1 {
+               rpm_avail = < 100 >;
+       };
+};
+
+&spin_ctrl_1 {
+               rpm_avail = < 100 200 >;
+};
index 820b79ca378a932683bc6c62e3121f8dd22b61e9..99ab9d12d00b538acba74bc62a57756d4d7205db 100644 (file)
@@ -30,6 +30,7 @@ hvac_1: hvac-medium-1 {
                        spin_ctrl_1: motor-1 {
                                compatible = "ot,ferris-wheel-motor";
                                spin = "clockwise";
+                               rpm_avail = < 50 >;
                        };
 
                        spin_ctrl_2: motor-8 {
index f82edf829f434bb7a93732fbd85ced271c02acc2..f0139d1e8b63b30214155e5eca386f3fb651db17 100644 (file)
@@ -2162,6 +2162,7 @@ OVERLAY_INFO_EXTERN(overlay_12);
 OVERLAY_INFO_EXTERN(overlay_13);
 OVERLAY_INFO_EXTERN(overlay_15);
 OVERLAY_INFO_EXTERN(overlay_bad_add_dup_node);
+OVERLAY_INFO_EXTERN(overlay_bad_add_dup_prop);
 OVERLAY_INFO_EXTERN(overlay_bad_phandle);
 OVERLAY_INFO_EXTERN(overlay_bad_symbol);
 
@@ -2185,6 +2186,7 @@ static struct overlay_info overlays[] = {
        OVERLAY_INFO(overlay_13, 0),
        OVERLAY_INFO(overlay_15, 0),
        OVERLAY_INFO(overlay_bad_add_dup_node, -EINVAL),
+       OVERLAY_INFO(overlay_bad_add_dup_prop, -EINVAL),
        OVERLAY_INFO(overlay_bad_phandle, -EINVAL),
        OVERLAY_INFO(overlay_bad_symbol, -EINVAL),
        {}
@@ -2435,6 +2437,9 @@ static __init void of_unittest_overlay_high_level(void)
        unittest(overlay_data_apply("overlay_bad_add_dup_node", NULL),
                 "Adding overlay 'overlay_bad_add_dup_node' failed\n");
 
+       unittest(overlay_data_apply("overlay_bad_add_dup_prop", NULL),
+                "Adding overlay 'overlay_bad_add_dup_prop' failed\n");
+
        unittest(overlay_data_apply("overlay_bad_phandle", NULL),
                 "Adding overlay 'overlay_bad_phandle' failed\n");