[FIX PATCH v1] powerpc/pseries: Fix reference count leak during CPU unplug

Michael Ellerman mpe at ellerman.id.au
Thu Mar 9 21:52:27 AEDT 2017


Bharata B Rao <bharata at linux.vnet.ibm.com> writes:

> The following warning is seen when a CPU is hot unplugged on a PowerKVM
> guest:
>
> refcount_t: underflow; use-after-free.
...
>
> Fix this by ensuring that of_node_put() is called only from the
> error path in dlpar_cpu_remove_by_index(). In the normal path,
> of_node_put() happens as part of dlpar_detach_node().

Don't we have the same bug in mobility.c ?

static int delete_dt_node(__be32 phandle)
{
	struct device_node *dn;

	dn = of_find_node_by_phandle(be32_to_cpu(phandle));
	if (!dn)
		return -ENOENT;

	dlpar_detach_node(dn);
	of_node_put(dn);
	return 0;
}

> diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c
> index 7bc0e91..c5ed510 100644
> --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c
> +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c
> @@ -619,7 +619,8 @@ static int dlpar_cpu_remove_by_index(u32 drc_index)
>  	}
>  
>  	rc = dlpar_cpu_remove(dn, drc_index);
> -	of_node_put(dn);
> +	if (rc)
> +		of_node_put(dn);
>  	return rc;
>  }
>  
> @@ -856,9 +857,12 @@ static ssize_t dlpar_cpu_release(const char *buf, size_t count)
>  	}
>  
>  	rc = dlpar_cpu_remove(dn, drc_index);
> -	of_node_put(dn);
> -
> -	return rc ? rc : count;
> +	if (rc) {
> +		of_node_put(dn);
> +		return rc;
> +	} else {
> +		return count;
> +	}
>  }


Which makes me think that the bug is actually that dlpar_detach_node()
should not be dropping the last reference on behalf of its caller. It
means that the caller has a pointer to a freed dn, which is wrong.

eg. A caller could do:

  dlpar_detach_node(dn);
  printk("detached %s\n", dn->name);

But dn will have been freed by the time the printk happens.


The problem is we call dlpar_detach_node() recursively, and for the
child nodes it *does* need to drop the last reference, because that's
the whole point. So it needs some refactoring.

I also notice there's no error checking on the removal of the child
nodes, which is pretty dubious:

	child = of_get_next_child(dn, NULL);
	while (child) {
		dlpar_detach_node(child);
		child = of_get_next_child(dn, child);
	}


cheers


More information about the Linuxppc-dev mailing list