Commit f8274f14 authored by Rob Herring's avatar Rob Herring

Merge tag 'kfree_validate_v7-for-4.20' of...

Merge tag 'kfree_validate_v7-for-4.20' of git://git.kernel.org/pub/scm/linux/kernel/git/frowand/linux into dt/next

Pull overlay validation checks from Frank Rowand:

"Add checks to (1) overlay apply process and (2) memory freeing
triggered by overlay release.  The checks are intended to detect
possible memory leaks and invalid overlays.

The checks revealed bugs in existing code.  Fixed the bugs.

While fixing bugs, noted other issues, which are fixed in
separate patches."
Signed-off-by: default avatarRob Herring <robh@kernel.org>
parents 1ae367a2 eeb07c57
......@@ -270,6 +270,8 @@ int dlpar_detach_node(struct device_node *dn)
if (rc)
return rc;
of_node_put(dn);
return 0;
}
......
......@@ -205,15 +205,24 @@ static void __of_attach_node(struct device_node *np)
const __be32 *phandle;
int sz;
np->name = __of_get_property(np, "name", NULL) ? : "<NULL>";
np->type = __of_get_property(np, "device_type", NULL) ? : "<NULL>";
phandle = __of_get_property(np, "phandle", &sz);
if (!phandle)
phandle = __of_get_property(np, "linux,phandle", &sz);
if (IS_ENABLED(CONFIG_PPC_PSERIES) && !phandle)
phandle = __of_get_property(np, "ibm,phandle", &sz);
np->phandle = (phandle && (sz >= 4)) ? be32_to_cpup(phandle) : 0;
if (!of_node_check_flag(np, OF_OVERLAY)) {
np->name = __of_get_property(np, "name", NULL);
np->type = __of_get_property(np, "device_type", NULL);
if (!np->name)
np->name = "<NULL>";
if (!np->type)
np->type = "<NULL>";
phandle = __of_get_property(np, "phandle", &sz);
if (!phandle)
phandle = __of_get_property(np, "linux,phandle", &sz);
if (IS_ENABLED(CONFIG_PPC_PSERIES) && !phandle)
phandle = __of_get_property(np, "ibm,phandle", &sz);
if (phandle && (sz >= 4))
np->phandle = be32_to_cpup(phandle);
else
np->phandle = 0;
}
np->child = NULL;
np->sibling = np->parent->child;
......@@ -272,9 +281,6 @@ void __of_detach_node(struct device_node *np)
/**
* of_detach_node() - "Unplug" a node from the device tree.
*
* The caller must hold a reference to the node. The memory associated with
* the node is not freed until its refcount goes to zero.
*/
int of_detach_node(struct device_node *np)
{
......@@ -330,6 +336,25 @@ void of_node_release(struct kobject *kobj)
if (!of_node_check_flag(node, OF_DYNAMIC))
return;
if (of_node_check_flag(node, OF_OVERLAY)) {
if (!of_node_check_flag(node, OF_OVERLAY_FREE_CSET)) {
/* premature refcount of zero, do not free memory */
pr_err("ERROR: memory leak before free overlay changeset, %pOF\n",
node);
return;
}
/*
* If node->properties non-empty then properties were added
* to this node either by different overlay that has not
* yet been removed, or by a non-overlay mechanism.
*/
if (node->properties)
pr_err("ERROR: %s(), unexpected properties in %pOF\n",
__func__, node);
}
property_list_free(node->properties);
property_list_free(node->deadprops);
......@@ -434,6 +459,16 @@ struct device_node *__of_node_dup(const struct device_node *np,
static void __of_changeset_entry_destroy(struct of_changeset_entry *ce)
{
if (ce->action == OF_RECONFIG_ATTACH_NODE &&
of_node_check_flag(ce->np, OF_OVERLAY)) {
if (kref_read(&ce->np->kobj.kref) > 1) {
pr_err("ERROR: memory leak, expected refcount 1 instead of %d, of_node_get()/of_node_put() unbalanced - destroy cset entry: attach overlay node %pOF\n",
kref_read(&ce->np->kobj.kref), ce->np);
} else {
of_node_set_flag(ce->np, OF_OVERLAY_FREE_CSET);
}
}
of_node_put(ce->np);
list_del(&ce->node);
kfree(ce);
......
......@@ -133,6 +133,9 @@ int __of_attach_node_sysfs(struct device_node *np)
}
if (!name)
return -ENOMEM;
of_node_get(np);
rc = kobject_add(&np->kobj, parent, "%s", name);
kfree(name);
if (rc)
......@@ -159,6 +162,5 @@ void __of_detach_node_sysfs(struct device_node *np)
kobject_del(&np->kobj);
}
/* finally remove the kobj_init ref */
of_node_put(np);
}
This diff is collapsed.
......@@ -17,6 +17,8 @@ obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \
overlay_12.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
......
// 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 new node "controller" in each fragment will
* result in an attempt to add the same node twice.
* This will result in an error and the overlay apply
* will fail.
*/
&electric_1 {
motor-1 {
controller {
power_bus = < 0x1 0x2 >;
};
};
};
&spin_ctrl_1 {
controller {
power_bus_emergency = < 0x101 0x102 >;
};
};
// 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 >;
};
......@@ -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 {
......
......@@ -379,6 +379,7 @@ static void __init of_unittest_parse_phandle_with_args(void)
for (i = 0; i < 8; i++) {
bool passed = true;
memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args(np, "phandle-list",
"#phandle-cells", i, &args);
......@@ -432,6 +433,7 @@ static void __init of_unittest_parse_phandle_with_args(void)
}
/* Check for missing list property */
memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args(np, "phandle-list-missing",
"#phandle-cells", 0, &args);
unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc);
......@@ -440,6 +442,7 @@ static void __init of_unittest_parse_phandle_with_args(void)
unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc);
/* Check for missing cells property */
memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args(np, "phandle-list",
"#phandle-cells-missing", 0, &args);
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
......@@ -448,6 +451,7 @@ static void __init of_unittest_parse_phandle_with_args(void)
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
/* Check for bad phandle in list */
memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args(np, "phandle-list-bad-phandle",
"#phandle-cells", 0, &args);
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
......@@ -456,6 +460,7 @@ static void __init of_unittest_parse_phandle_with_args(void)
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
/* Check for incorrectly formed argument list */
memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args(np, "phandle-list-bad-args",
"#phandle-cells", 1, &args);
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
......@@ -506,6 +511,7 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
for (i = 0; i < 8; i++) {
bool passed = true;
memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args_map(np, "phandle-list",
"phandle", i, &args);
......@@ -563,21 +569,25 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
}
/* Check for missing list property */
memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args_map(np, "phandle-list-missing",
"phandle", 0, &args);
unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc);
/* Check for missing cells,map,mask property */
memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args_map(np, "phandle-list",
"phandle-missing", 0, &args);
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
/* Check for bad phandle in list */
memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args_map(np, "phandle-list-bad-phandle",
"phandle", 0, &args);
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
/* Check for incorrectly formed argument list */
memset(&args, 0, sizeof(args));
rc = of_parse_phandle_with_args_map(np, "phandle-list-bad-args",
"phandle", 1, &args);
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
......@@ -787,7 +797,7 @@ static void __init of_unittest_parse_interrupts(void)
for (i = 0; i < 4; i++) {
bool passed = true;
args.args_count = 0;
memset(&args, 0, sizeof(args));
rc = of_irq_parse_one(np, i, &args);
passed &= !rc;
......@@ -808,7 +818,7 @@ static void __init of_unittest_parse_interrupts(void)
for (i = 0; i < 4; i++) {
bool passed = true;
args.args_count = 0;
memset(&args, 0, sizeof(args));
rc = of_irq_parse_one(np, i, &args);
/* Test the values from tests-phandle.dtsi */
......@@ -864,6 +874,7 @@ static void __init of_unittest_parse_interrupts_extended(void)
for (i = 0; i < 7; i++) {
bool passed = true;
memset(&args, 0, sizeof(args));
rc = of_irq_parse_one(np, i, &args);
/* Test the values from tests-phandle.dtsi */
......@@ -1071,20 +1082,44 @@ static void __init of_unittest_platform_populate(void)
* of np into dup node (present in live tree) and
* updates parent of children of np to dup.
*
* @np: node already present in live tree
* @np: node whose properties are being added to the live tree
* @dup: node present in live tree to be updated
*/
static void update_node_properties(struct device_node *np,
struct device_node *dup)
{
struct property *prop;
struct property *save_next;
struct device_node *child;
for_each_property_of_node(np, prop)
of_add_property(dup, prop);
int ret;
for_each_child_of_node(np, child)
child->parent = dup;
/*
* "unittest internal error: unable to add testdata property"
*
* If this message reports a property in node '/__symbols__' then
* the respective unittest overlay contains a label that has the
* same name as a label in the live devicetree. The label will
* be in the live devicetree only if the devicetree source was
* compiled with the '-@' option. If you encounter this error,
* please consider renaming __all__ of the labels in the unittest
* overlay dts files with an odd prefix that is unlikely to be
* used in a real devicetree.
*/
/*
* open code for_each_property_of_node() because of_add_property()
* sets prop->next to NULL
*/
for (prop = np->properties; prop != NULL; prop = save_next) {
save_next = prop->next;
ret = of_add_property(dup, prop);
if (ret)
pr_err("unittest internal error: unable to add testdata property %pOF/%s",
np, prop->name);
}
}
/**
......@@ -1093,18 +1128,23 @@ static void update_node_properties(struct device_node *np,
*
* @np: Node to attach to live tree
*/
static int attach_node_and_children(struct device_node *np)
static void attach_node_and_children(struct device_node *np)
{
struct device_node *next, *dup, *child;
unsigned long flags;
const char *full_name;
full_name = kasprintf(GFP_KERNEL, "%pOF", np);
if (!strcmp(full_name, "/__local_fixups__") ||
!strcmp(full_name, "/__fixups__"))
return;
dup = of_find_node_by_path(full_name);
kfree(full_name);
if (dup) {
update_node_properties(np, dup);
return 0;
return;
}
child = np->child;
......@@ -1125,8 +1165,6 @@ static int attach_node_and_children(struct device_node *np)
attach_node_and_children(child);
child = next;
}
return 0;
}
/**
......@@ -1433,8 +1471,7 @@ static void of_unittest_destroy_tracked_overlays(void)
} while (defers > 0);
}
static int __init of_unittest_apply_overlay(int overlay_nr, int unittest_nr,
int *overlay_id)
static int __init of_unittest_apply_overlay(int overlay_nr, int *overlay_id)
{
const char *overlay_name;
......@@ -1467,7 +1504,7 @@ static int __init of_unittest_apply_overlay_check(int overlay_nr,
}
ovcs_id = 0;
ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, &ovcs_id);
ret = of_unittest_apply_overlay(overlay_nr, &ovcs_id);
if (ret != 0) {
/* of_unittest_apply_overlay already called unittest() */
return ret;
......@@ -1503,7 +1540,7 @@ static int __init of_unittest_apply_revert_overlay_check(int overlay_nr,
/* apply the overlay */
ovcs_id = 0;
ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, &ovcs_id);
ret = of_unittest_apply_overlay(overlay_nr, &ovcs_id);
if (ret != 0) {
/* of_unittest_apply_overlay already called unittest() */
return ret;
......@@ -2161,10 +2198,12 @@ OVERLAY_INFO_EXTERN(overlay_11);
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);
/* order of entries is hard-coded into users of overlays[] */
/* entries found by name */
static struct overlay_info overlays[] = {
OVERLAY_INFO(overlay_base, -9999),
OVERLAY_INFO(overlay, 0),
......@@ -2183,9 +2222,12 @@ static struct overlay_info overlays[] = {
OVERLAY_INFO(overlay_12, 0),
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),
{}
/* end marker */
{.dtb_begin = NULL, .dtb_end = NULL, .expected_result = 0, .name = NULL}
};
static struct device_node *overlay_base_root;
......@@ -2215,6 +2257,19 @@ void __init unittest_unflatten_overlay_base(void)
u32 data_size;
void *new_fdt;
u32 size;
int found = 0;
const char *overlay_name = "overlay_base";
for (info = overlays; info && info->name; info++) {
if (!strcmp(overlay_name, info->name)) {
found = 1;
break;
}
}
if (!found) {
pr_err("no overlay data for %s\n", overlay_name);
return;
}
info = &overlays[0];
......@@ -2262,11 +2317,10 @@ static int __init overlay_data_apply(const char *overlay_name, int *overlay_id)
{
struct overlay_info *info;
int found = 0;
int k;
int ret;
u32 size;
for (k = 0, info = overlays; info && info->name; info++, k++) {
for (info = overlays; info && info->name; info++) {
if (!strcmp(overlay_name, info->name)) {
found = 1;
break;
......@@ -2430,6 +2484,12 @@ static __init void of_unittest_overlay_high_level(void)
unittest(overlay_data_apply("overlay", NULL),
"Adding overlay 'overlay' failed\n");
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");
......
......@@ -138,11 +138,16 @@ extern struct device_node *of_aliases;
extern struct device_node *of_stdout;
extern raw_spinlock_t devtree_lock;
/* flag descriptions (need to be visible even when !CONFIG_OF) */
#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */
#define OF_DETACHED 2 /* node has been detached from the device tree */
#define OF_POPULATED 3 /* device already created for the node */
#define OF_POPULATED_BUS 4 /* of_platform_populate recursed to children of this node */
/*
* struct device_node flag descriptions
* (need to be visible even when !CONFIG_OF)
*/
#define OF_DYNAMIC 1 /* (and properties) allocated via kmalloc */
#define OF_DETACHED 2 /* detached from the device tree */
#define OF_POPULATED 3 /* device already created */
#define OF_POPULATED_BUS 4 /* platform bus created for children */
#define OF_OVERLAY 5 /* allocated for an overlay */
#define OF_OVERLAY_FREE_CSET 6 /* in overlay cset being freed */
#define OF_BAD_ADDR ((u64)-1)
......@@ -985,6 +990,12 @@ static inline int of_map_rid(struct device_node *np, u32 rid,
#define of_node_cmp(s1, s2) strcasecmp((s1), (s2))
#endif
static inline int of_prop_val_eq(struct property *p1, struct property *p2)
{
return p1->length == p2->length &&
!memcmp(p1->value, p2->value, (size_t)p1->length);
}
#if defined(CONFIG_OF) && defined(CONFIG_NUMA)
extern int of_node_to_nid(struct device_node *np);
#else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment