Skip to content

Commit

Permalink
Merge tag 'kfree_validate_v7-for-4.20' of git://git.kernel.org/pub/sc…
Browse files Browse the repository at this point in the history
…m/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: Rob Herring <[email protected]>
  • Loading branch information
robherring committed Nov 9, 2018
2 parents 1ae367a + eeb07c5 commit f8274f1
Show file tree
Hide file tree
Showing 10 changed files with 432 additions and 97 deletions.
2 changes: 2 additions & 0 deletions arch/powerpc/platforms/pseries/dlpar.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ int dlpar_detach_node(struct device_node *dn)
if (rc)
return rc;

of_node_put(dn);

return 0;
}

Expand Down
59 changes: 47 additions & 12 deletions drivers/of/dynamic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion drivers/of/kobj.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);
}
Loading

0 comments on commit f8274f1

Please sign in to comment.