[Skiboot] [PATCH] core/device.c: Fix dt_find_compatible_node

Andrew Donnellan andrew.donnellan at au1.ibm.com
Wed Feb 7 17:56:39 AEDT 2018


On 07/02/18 13:56, Alistair Popple wrote:
> dt_find_compatible_node() and dt_find_compatible_node_on_chip() are used to
> find device nodes under a parent/root node with a given compatible
> property.
> 
> dt_next(root, prev) is used to walk the child nodes of the given parent and
> takes two arguments - root contains the parent node to walk whilst prev
> contains the previous child to search from so that it can be used as an
> iterator over all children nodes.
> 
> The first iteration of dt_find_compatible_node(root, prev) calls
> dt_next(root, root) which is not a well defined operation as prev is
> assumed to be child of the root node. The result is that when a node
> contains no children it will start returning the parent nodes siblings
> until it hits the top of the tree at which point a NULL derefence is
> attempted when looking for the root nodes parent.
> 
> Dereferencing NULL can result in undesirable data exceptions during system
> boot and untimely non-hilarious system crashes. dt_next() should not be
> called with prev == root. Instead we add a check to dt_next() such that
> passing prev = NULL will cause it to start iterating from the first child
> node (if any).
> 
> Also add a unit test for this case to run-device.c.
> 
> Signed-off-by: Alistair Popple <alistair at popple.id.au>

This is both less buggy and clearer.

Reviewed-by: Andrew Donnellan <andrew.donnellan at au1.ibm.com>

> ---
>   core/device.c          | 16 ++++++++++------
>   core/test/run-device.c |  1 +
>   2 files changed, 11 insertions(+), 6 deletions(-)
> 
> diff --git a/core/device.c b/core/device.c
> index fc1db568..194eaef3 100644
> --- a/core/device.c
> +++ b/core/device.c
> @@ -608,6 +608,12 @@ struct dt_node *dt_first(const struct dt_node *root)
>   struct dt_node *dt_next(const struct dt_node *root,
>   			const struct dt_node *prev)
>   {
> +	if (!prev)
> +		prev = dt_first(root);
> +
> +	if (!prev)
> +		return NULL;
> +
>   	/* Children? */
>   	if (!list_empty(&prev->children))
>   		return dt_first(prev);
> @@ -719,10 +725,9 @@ struct dt_node *dt_find_compatible_node(struct dt_node *root,
>   					struct dt_node *prev,
>   					const char *compat)
>   {
> -	struct dt_node *node;
> +	struct dt_node *node = prev;
>   
> -	node = prev ? dt_next(root, prev) : root;
> -	for (; node; node = dt_next(root, node))
> +	while ((node = dt_next(root, node)))
>   		if (dt_node_is_compatible(node, compat))
>   			return node;
>   	return NULL;
> @@ -964,10 +969,9 @@ struct dt_node *dt_find_compatible_node_on_chip(struct dt_node *root,
>   						const char *compat,
>   						uint32_t chip_id)
>   {
> -	struct dt_node *node;
> +	struct dt_node *node = prev;
>   
> -	node = prev ? dt_next(root, prev) : root;
> -	for (; node; node = dt_next(root, node)) {
> +	while ((node = dt_next(root, node))) {
>   		u32 cid = __dt_get_chip_id(node);
>   		if (cid == chip_id &&
>   		    dt_node_is_compatible(node, compat))
> diff --git a/core/test/run-device.c b/core/test/run-device.c
> index 326be3ff..6d3842e6 100644
> --- a/core/test/run-device.c
> +++ b/core/test/run-device.c
> @@ -327,6 +327,7 @@ int main(void)
>   	gc2 = dt_new(c1, "node-without-compatible");
>   	assert(__dt_find_property(gc2, "compatible") == NULL);
>   	assert(!dt_node_is_compatible(gc2, "any-property"));
> +	assert(dt_find_compatible_node(gc2, NULL, "node-without-compatible") == NULL);
>   
>   	assert(dt_find_compatible_node(root, NULL, "generic-fake-bus") == c2);
>   	assert(dt_find_compatible_node(root, c2, "generic-fake-bus") == NULL);
> 

-- 
Andrew Donnellan              OzLabs, ADL Canberra
andrew.donnellan at au1.ibm.com  IBM Australia Limited



More information about the Skiboot mailing list