Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

of: overlay: check prevents multiple fragments touching same property

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>

+113 -38
+82 -38
drivers/of/overlay.c
··· 508 508 return 0; 509 509 } 510 510 511 - /** 512 - * check_changeset_dup_add_node() - changeset validation: duplicate add node 513 - * @ovcs: Overlay changeset 514 - * 515 - * Check changeset @ovcs->cset for multiple add node entries for the same 516 - * node. 517 - * 518 - * Returns 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if 519 - * invalid overlay in @ovcs->fragments[]. 520 - */ 521 - static int check_changeset_dup_add_node(struct overlay_changeset *ovcs) 511 + static int find_dup_cset_node_entry(struct overlay_changeset *ovcs, 512 + struct of_changeset_entry *ce_1) 522 513 { 523 - struct of_changeset_entry *ce_1, *ce_2; 514 + struct of_changeset_entry *ce_2; 524 515 char *fn_1, *fn_2; 525 - int name_match; 516 + int node_path_match; 526 517 527 - list_for_each_entry(ce_1, &ovcs->cset.entries, node) { 518 + if (ce_1->action != OF_RECONFIG_ATTACH_NODE && 519 + ce_1->action != OF_RECONFIG_DETACH_NODE) 520 + return 0; 528 521 529 - if (ce_1->action == OF_RECONFIG_ATTACH_NODE || 530 - ce_1->action == OF_RECONFIG_DETACH_NODE) { 522 + ce_2 = ce_1; 523 + list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) { 524 + if ((ce_2->action != OF_RECONFIG_ATTACH_NODE && 525 + ce_2->action != OF_RECONFIG_DETACH_NODE) || 526 + of_node_cmp(ce_1->np->full_name, ce_2->np->full_name)) 527 + continue; 531 528 532 - ce_2 = ce_1; 533 - list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) { 534 - if (ce_2->action == OF_RECONFIG_ATTACH_NODE || 535 - ce_2->action == OF_RECONFIG_DETACH_NODE) { 536 - /* inexpensive name compare */ 537 - if (!of_node_cmp(ce_1->np->full_name, 538 - ce_2->np->full_name)) { 539 - /* expensive full path name compare */ 540 - fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np); 541 - fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np); 542 - name_match = !strcmp(fn_1, fn_2); 543 - kfree(fn_1); 544 - kfree(fn_2); 545 - if (name_match) { 546 - pr_err("ERROR: multiple overlay fragments add and/or delete node %pOF\n", 547 - ce_1->np); 548 - return -EINVAL; 549 - } 550 - } 551 - } 552 - } 529 + fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np); 530 + fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np); 531 + node_path_match = !strcmp(fn_1, fn_2); 532 + kfree(fn_1); 533 + kfree(fn_2); 534 + if (node_path_match) { 535 + pr_err("ERROR: multiple fragments add and/or delete node %pOF\n", 536 + ce_1->np); 537 + return -EINVAL; 553 538 } 554 539 } 555 540 556 541 return 0; 542 + } 543 + 544 + static int find_dup_cset_prop(struct overlay_changeset *ovcs, 545 + struct of_changeset_entry *ce_1) 546 + { 547 + struct of_changeset_entry *ce_2; 548 + char *fn_1, *fn_2; 549 + int node_path_match; 550 + 551 + if (ce_1->action != OF_RECONFIG_ADD_PROPERTY && 552 + ce_1->action != OF_RECONFIG_REMOVE_PROPERTY && 553 + ce_1->action != OF_RECONFIG_UPDATE_PROPERTY) 554 + return 0; 555 + 556 + ce_2 = ce_1; 557 + list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) { 558 + if ((ce_2->action != OF_RECONFIG_ADD_PROPERTY && 559 + ce_2->action != OF_RECONFIG_REMOVE_PROPERTY && 560 + ce_2->action != OF_RECONFIG_UPDATE_PROPERTY) || 561 + of_node_cmp(ce_1->np->full_name, ce_2->np->full_name)) 562 + continue; 563 + 564 + fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np); 565 + fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np); 566 + node_path_match = !strcmp(fn_1, fn_2); 567 + kfree(fn_1); 568 + kfree(fn_2); 569 + if (node_path_match && 570 + !of_prop_cmp(ce_1->prop->name, ce_2->prop->name)) { 571 + pr_err("ERROR: multiple fragments add, update, and/or delete property %pOF/%s\n", 572 + ce_1->np, ce_1->prop->name); 573 + return -EINVAL; 574 + } 575 + } 576 + 577 + return 0; 578 + } 579 + 580 + /** 581 + * changeset_dup_entry_check() - check for duplicate entries 582 + * @ovcs: Overlay changeset 583 + * 584 + * Check changeset @ovcs->cset for multiple {add or delete} node entries for 585 + * the same node or duplicate {add, delete, or update} properties entries 586 + * for the same property. 587 + * 588 + * Returns 0 on success, or -EINVAL if duplicate changeset entry found. 589 + */ 590 + static int changeset_dup_entry_check(struct overlay_changeset *ovcs) 591 + { 592 + struct of_changeset_entry *ce_1; 593 + int dup_entry = 0; 594 + 595 + list_for_each_entry(ce_1, &ovcs->cset.entries, node) { 596 + dup_entry |= find_dup_cset_node_entry(ovcs, ce_1); 597 + dup_entry |= find_dup_cset_prop(ovcs, ce_1); 598 + } 599 + 600 + return dup_entry ? -EINVAL : 0; 557 601 } 558 602 559 603 /** ··· 655 611 } 656 612 } 657 613 658 - return check_changeset_dup_add_node(ovcs); 614 + return changeset_dup_entry_check(ovcs); 659 615 } 660 616 661 617 /*
+1
drivers/of/unittest-data/Makefile
··· 18 18 overlay_13.dtb.o \ 19 19 overlay_15.dtb.o \ 20 20 overlay_bad_add_dup_node.dtb.o \ 21 + overlay_bad_add_dup_prop.dtb.o \ 21 22 overlay_bad_phandle.dtb.o \ 22 23 overlay_bad_symbol.dtb.o \ 23 24 overlay_base.dtb.o
+24
drivers/of/unittest-data/overlay_bad_add_dup_prop.dts
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /dts-v1/; 3 + /plugin/; 4 + 5 + /* 6 + * &electric_1/motor-1 and &spin_ctrl_1 are the same node: 7 + * /testcase-data-2/substation@100/motor-1 8 + * 9 + * Thus the property "rpm_avail" in each fragment will 10 + * result in an attempt to update the same property twice. 11 + * This will result in an error and the overlay apply 12 + * will fail. 13 + */ 14 + 15 + &electric_1 { 16 + 17 + motor-1 { 18 + rpm_avail = < 100 >; 19 + }; 20 + }; 21 + 22 + &spin_ctrl_1 { 23 + rpm_avail = < 100 200 >; 24 + };
+1
drivers/of/unittest-data/overlay_base.dts
··· 30 30 spin_ctrl_1: motor-1 { 31 31 compatible = "ot,ferris-wheel-motor"; 32 32 spin = "clockwise"; 33 + rpm_avail = < 50 >; 33 34 }; 34 35 35 36 spin_ctrl_2: motor-8 {
+5
drivers/of/unittest.c
··· 2162 2162 OVERLAY_INFO_EXTERN(overlay_13); 2163 2163 OVERLAY_INFO_EXTERN(overlay_15); 2164 2164 OVERLAY_INFO_EXTERN(overlay_bad_add_dup_node); 2165 + OVERLAY_INFO_EXTERN(overlay_bad_add_dup_prop); 2165 2166 OVERLAY_INFO_EXTERN(overlay_bad_phandle); 2166 2167 OVERLAY_INFO_EXTERN(overlay_bad_symbol); 2167 2168 ··· 2186 2185 OVERLAY_INFO(overlay_13, 0), 2187 2186 OVERLAY_INFO(overlay_15, 0), 2188 2187 OVERLAY_INFO(overlay_bad_add_dup_node, -EINVAL), 2188 + OVERLAY_INFO(overlay_bad_add_dup_prop, -EINVAL), 2189 2189 OVERLAY_INFO(overlay_bad_phandle, -EINVAL), 2190 2190 OVERLAY_INFO(overlay_bad_symbol, -EINVAL), 2191 2191 {} ··· 2436 2434 2437 2435 unittest(overlay_data_apply("overlay_bad_add_dup_node", NULL), 2438 2436 "Adding overlay 'overlay_bad_add_dup_node' failed\n"); 2437 + 2438 + unittest(overlay_data_apply("overlay_bad_add_dup_prop", NULL), 2439 + "Adding overlay 'overlay_bad_add_dup_prop' failed\n"); 2439 2440 2440 2441 unittest(overlay_data_apply("overlay_bad_phandle", NULL), 2441 2442 "Adding overlay 'overlay_bad_phandle' failed\n");