diff mbox series

[v3,13/18] of: overlay: check prevents multiple fragments touching same property

Message ID 1539563070-12969-14-git-send-email-frowand.list@gmail.com (mailing list archive)
State Superseded, archived
Headers show
Series of: overlay: validation checks, subsequent fixes | expand

Commit Message

Frank Rowand Oct. 15, 2018, 12:24 a.m. UTC
From: Frank Rowand <frank.rowand@sony.com>

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.

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

   OF: overlay: ERROR: multiple overlay 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.

Signed-off-by: Frank Rowand <frank.rowand@sony.com>
---

Changes since v2:
  - Use continue to reduce indentation in find_dup_cset_node_entry()
    and find_dup_cset_prop()

 drivers/of/overlay.c                               | 118 ++++++++++++++-------
 drivers/of/unittest-data/Makefile                  |   1 +
 .../of/unittest-data/overlay_bad_add_dup_prop.dts  |  24 +++++
 drivers/of/unittest-data/overlay_base.dts          |   1 +
 drivers/of/unittest.c                              |   5 +
 5 files changed, 112 insertions(+), 37 deletions(-)
 create mode 100644 drivers/of/unittest-data/overlay_bad_add_dup_prop.dts

Comments

Joe Perches Oct. 15, 2018, 1:06 a.m. UTC | #1
On Sun, 2018-10-14 at 17:24 -0700, frowand.list@gmail.com wrote:
> From: Frank Rowand <frank.rowand@sony.com>
> 
> 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.
[]
> -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);

I think this is worse performance than before.

This now walks all entries when before it would
return -EINVAL directly when it found a match.
Frank Rowand Oct. 15, 2018, 1:52 a.m. UTC | #2
On 10/14/18 18:06, Joe Perches wrote:
> On Sun, 2018-10-14 at 17:24 -0700, frowand.list@gmail.com wrote:
>> From: Frank Rowand <frank.rowand@sony.com>
>>
>> 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.
> []
>> -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);
> 
> I think this is worse performance than before.
> 
> This now walks all entries when before it would
> return -EINVAL directly when it found a match.

Yes, it is worse performance, but that is OK.

This is a check that is done when a devicetree overlay is applied.
If an error occurs then that means that the overlay was incorrectly
specified.  The file drivers/of/unittest-data/overlay_bad_add_dup_prop.dts
in this patch provides an example of how a bad overlay can be created.

Once an error was detected, the check could return immediately, or it
could continue to give a complete list of detected errors.  I chose to
give the complete list of detected errors.

-Frank
Joe Perches Oct. 15, 2018, 1:55 a.m. UTC | #3
On Sun, 2018-10-14 at 18:52 -0700, Frank Rowand wrote:
> On 10/14/18 18:06, Joe Perches wrote:
> > On Sun, 2018-10-14 at 17:24 -0700, frowand.list@gmail.com wrote:
> > > From: Frank Rowand <frank.rowand@sony.com>
> > > 
> > > 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.
[]
> > I think this is worse performance than before.
> > 
> > This now walks all entries when before it would
> > return -EINVAL directly when it found a match.
> 
> Yes, it is worse performance, but that is OK.
> 
> This is a check that is done when a devicetree overlay is applied.
> If an error occurs then that means that the overlay was incorrectly
> specified.  The file drivers/of/unittest-data/overlay_bad_add_dup_prop.dts
> in this patch provides an example of how a bad overlay can be created.
> 
> Once an error was detected, the check could return immediately, or it
> could continue to give a complete list of detected errors.  I chose to
> give the complete list of detected errors.

Swell.  Please describe that in the commit message.
Frank Rowand Oct. 15, 2018, 3:21 a.m. UTC | #4
On 10/14/18 18:55, Joe Perches wrote:
> On Sun, 2018-10-14 at 18:52 -0700, Frank Rowand wrote:
>> On 10/14/18 18:06, Joe Perches wrote:
>>> On Sun, 2018-10-14 at 17:24 -0700, frowand.list@gmail.com wrote:
>>>> From: Frank Rowand <frank.rowand@sony.com>
>>>>
>>>> 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.
> []
>>> I think this is worse performance than before.
>>>
>>> This now walks all entries when before it would
>>> return -EINVAL directly when it found a match.
>>
>> Yes, it is worse performance, but that is OK.
>>
>> This is a check that is done when a devicetree overlay is applied.
>> If an error occurs then that means that the overlay was incorrectly
>> specified.  The file drivers/of/unittest-data/overlay_bad_add_dup_prop.dts
>> in this patch provides an example of how a bad overlay can be created.
>>
>> Once an error was detected, the check could return immediately, or it
>> could continue to give a complete list of detected errors.  I chose to
>> give the complete list of detected errors.
> 
> Swell.  Please describe that in the commit message.

If a version 4 of the series is created I will update the commit
message.  As a stand alone item I do not think it is worth a
new version.

-Frank
Frank Rowand Oct. 15, 2018, 7:30 p.m. UTC | #5
On 10/14/18 20:21, Frank Rowand wrote:
> On 10/14/18 18:55, Joe Perches wrote:
>> On Sun, 2018-10-14 at 18:52 -0700, Frank Rowand wrote:
>>> On 10/14/18 18:06, Joe Perches wrote:
>>>> On Sun, 2018-10-14 at 17:24 -0700, frowand.list@gmail.com wrote:
>>>>> From: Frank Rowand <frank.rowand@sony.com>
>>>>>
>>>>> 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.
>> []
>>>> I think this is worse performance than before.
>>>>
>>>> This now walks all entries when before it would
>>>> return -EINVAL directly when it found a match.
>>>
>>> Yes, it is worse performance, but that is OK.
>>>
>>> This is a check that is done when a devicetree overlay is applied.
>>> If an error occurs then that means that the overlay was incorrectly
>>> specified.  The file drivers/of/unittest-data/overlay_bad_add_dup_prop.dts
>>> in this patch provides an example of how a bad overlay can be created.
>>>
>>> Once an error was detected, the check could return immediately, or it
>>> could continue to give a complete list of detected errors.  I chose to
>>> give the complete list of detected errors.
>>
>> Swell.  Please describe that in the commit message.
> 
> If a version 4 of the series is created I will update the commit
> message.  As a stand alone item I do not think it is worth a
> new version.

And there will be a version 4, so I will update the commit message.

-Frank
diff mbox series

Patch

diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index b0a0dafb6a13..908bcc62019b 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -514,52 +514,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 overlay 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 overlay 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;
 }
 
 /**
@@ -617,7 +661,7 @@  static int build_changeset(struct overlay_changeset *ovcs)
 		}
 	}
 
-	return check_changeset_dup_add_node(ovcs);
+	return changeset_dup_entry_check(ovcs);
 }
 
 /*
diff --git a/drivers/of/unittest-data/Makefile b/drivers/of/unittest-data/Makefile
index 166dbdbfd1c5..9b6807065827 100644
--- a/drivers/of/unittest-data/Makefile
+++ b/drivers/of/unittest-data/Makefile
@@ -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
index 000000000000..c190da54f175
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_bad_add_dup_prop.dts
@@ -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 >;
+};
diff --git a/drivers/of/unittest-data/overlay_base.dts b/drivers/of/unittest-data/overlay_base.dts
index 820b79ca378a..99ab9d12d00b 100644
--- a/drivers/of/unittest-data/overlay_base.dts
+++ b/drivers/of/unittest-data/overlay_base.dts
@@ -30,6 +30,7 @@ 
 			spin_ctrl_1: motor-1 {
 				compatible = "ot,ferris-wheel-motor";
 				spin = "clockwise";
+				rpm_avail = < 50 >;
 			};
 
 			spin_ctrl_2: motor-8 {
diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c
index 471b8eb6e842..efd9c947f192 100644
--- a/drivers/of/unittest.c
+++ b/drivers/of/unittest.c
@@ -2148,6 +2148,7 @@  struct overlay_info {
 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);
 
@@ -2171,6 +2172,7 @@  struct overlay_info {
 	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),
 	{}
@@ -2418,6 +2420,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");