linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] of: phandle_cache, fix refcounts, remove stale entry
@ 2018-12-14  6:42 frowand.list
  2018-12-14  6:42 ` [PATCH 1/2] of: of_node_get()/of_node_put() nodes held in phandle cache frowand.list
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: frowand.list @ 2018-12-14  6:42 UTC (permalink / raw)
  To: robh+dt, Michael Bringmann, linuxppc-dev
  Cc: Michael Ellerman, Tyrel Datwyler, Thomas Falcon, Juliet Kim,
	devicetree, linux-kernel

From: Frank Rowand <frank.rowand@sony.com>

Non-overlay dynamic devicetree node removal may leave the node in
the phandle cache.  Subsequent calls to of_find_node_by_phandle()
will incorrectly find the stale entry.  This bug exposed the foloowing
phandle cache refcount bug.

The refcount of phandle_cache entries is not incremented while in
the cache, allowing use after free error after kfree() of the
cached entry.

Frank Rowand (2):
  of: of_node_get()/of_node_put() nodes held in phandle cache
  of: __of_detach_node() - remove node from phandle cache

 drivers/of/base.c       | 99 ++++++++++++++++++++++++++++++++++++-------------
 drivers/of/dynamic.c    |  3 ++
 drivers/of/of_private.h |  4 ++
 3 files changed, 81 insertions(+), 25 deletions(-)

-- 
Frank Rowand <frank.rowand@sony.com>


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/2] of: of_node_get()/of_node_put() nodes held in phandle cache
  2018-12-14  6:42 [PATCH 0/2] of: phandle_cache, fix refcounts, remove stale entry frowand.list
@ 2018-12-14  6:42 ` frowand.list
  2018-12-14 17:15   ` Rob Herring
  2018-12-14  6:42 ` [PATCH 2/2] of: __of_detach_node() - remove node from " frowand.list
  2018-12-14  6:46 ` [PATCH 0/2] of: phandle_cache, fix refcounts, remove stale entry Frank Rowand
  2 siblings, 1 reply; 10+ messages in thread
From: frowand.list @ 2018-12-14  6:42 UTC (permalink / raw)
  To: robh+dt, Michael Bringmann, linuxppc-dev
  Cc: Michael Ellerman, Tyrel Datwyler, Thomas Falcon, Juliet Kim,
	devicetree, linux-kernel

From: Frank Rowand <frank.rowand@sony.com>

The phandle cache contains struct device_node pointers.  The refcount
of the pointers was not incremented while in the cache, allowing use
after free error after kfree() of the node.  Add the proper increment
and decrement of the use count.

Fixes: 0b3ce78e90fc ("of: cache phandle nodes to reduce cost of of_find_node_by_phandle()")

Signed-off-by: Frank Rowand <frank.rowand@sony.com>
---

do not "cc: stable", unless the following commits are also in stable:

  commit e54192b48da7 ("of: fix phandle cache creation for DTs with no phandles")
  commit b9952b5218ad ("of: overlay: update phandle cache on overlay apply and remove")
  commit 0b3ce78e90fc ("of: cache phandle nodes to reduce cost of of_find_node_by_phandle()")

 drivers/of/base.c | 70 ++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 46 insertions(+), 24 deletions(-)

diff --git a/drivers/of/base.c b/drivers/of/base.c
index 09692c9b32a7..d599367cb92a 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -116,9 +116,6 @@ int __weak of_node_to_nid(struct device_node *np)
 }
 #endif
 
-static struct device_node **phandle_cache;
-static u32 phandle_cache_mask;
-
 /*
  * Assumptions behind phandle_cache implementation:
  *   - phandle property values are in a contiguous range of 1..n
@@ -127,6 +124,44 @@ int __weak of_node_to_nid(struct device_node *np)
  *   - the phandle lookup overhead reduction provided by the cache
  *     will likely be less
  */
+
+static struct device_node **phandle_cache;
+static u32 phandle_cache_mask;
+
+/*
+ * Caller must hold devtree_lock.
+ */
+void __of_free_phandle_cache(void)
+{
+	u32 cache_entries = phandle_cache_mask + 1;
+	u32 k;
+
+	if (!phandle_cache)
+		return;
+
+	for (k = 0; k < cache_entries; k++)
+		of_node_put(phandle_cache[k]);
+
+	kfree(phandle_cache);
+	phandle_cache = NULL;
+}
+
+int of_free_phandle_cache(void)
+{
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&devtree_lock, flags);
+
+	__of_free_phandle_cache();
+
+	raw_spin_unlock_irqrestore(&devtree_lock, flags);
+
+	return 0;
+}
+#if !defined(CONFIG_MODULES)
+late_initcall_sync(of_free_phandle_cache);
+#endif
+
 void of_populate_phandle_cache(void)
 {
 	unsigned long flags;
@@ -136,8 +171,7 @@ void of_populate_phandle_cache(void)
 
 	raw_spin_lock_irqsave(&devtree_lock, flags);
 
-	kfree(phandle_cache);
-	phandle_cache = NULL;
+	__of_free_phandle_cache();
 
 	for_each_of_allnodes(np)
 		if (np->phandle && np->phandle != OF_PHANDLE_ILLEGAL)
@@ -155,30 +189,15 @@ void of_populate_phandle_cache(void)
 		goto out;
 
 	for_each_of_allnodes(np)
-		if (np->phandle && np->phandle != OF_PHANDLE_ILLEGAL)
+		if (np->phandle && np->phandle != OF_PHANDLE_ILLEGAL) {
+			of_node_get(np);
 			phandle_cache[np->phandle & phandle_cache_mask] = np;
+		}
 
 out:
 	raw_spin_unlock_irqrestore(&devtree_lock, flags);
 }
 
-int of_free_phandle_cache(void)
-{
-	unsigned long flags;
-
-	raw_spin_lock_irqsave(&devtree_lock, flags);
-
-	kfree(phandle_cache);
-	phandle_cache = NULL;
-
-	raw_spin_unlock_irqrestore(&devtree_lock, flags);
-
-	return 0;
-}
-#if !defined(CONFIG_MODULES)
-late_initcall_sync(of_free_phandle_cache);
-#endif
-
 void __init of_core_init(void)
 {
 	struct device_node *np;
@@ -1195,8 +1214,11 @@ struct device_node *of_find_node_by_phandle(phandle handle)
 	if (!np) {
 		for_each_of_allnodes(np)
 			if (np->phandle == handle) {
-				if (phandle_cache)
+				if (phandle_cache) {
+					/* will put when removed from cache */
+					of_node_get(np);
 					phandle_cache[masked_handle] = np;
+				}
 				break;
 			}
 	}
-- 
Frank Rowand <frank.rowand@sony.com>


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/2] of: __of_detach_node() - remove node from phandle cache
  2018-12-14  6:42 [PATCH 0/2] of: phandle_cache, fix refcounts, remove stale entry frowand.list
  2018-12-14  6:42 ` [PATCH 1/2] of: of_node_get()/of_node_put() nodes held in phandle cache frowand.list
@ 2018-12-14  6:42 ` frowand.list
  2018-12-14 17:20   ` Rob Herring
  2018-12-14  6:46 ` [PATCH 0/2] of: phandle_cache, fix refcounts, remove stale entry Frank Rowand
  2 siblings, 1 reply; 10+ messages in thread
From: frowand.list @ 2018-12-14  6:42 UTC (permalink / raw)
  To: robh+dt, Michael Bringmann, linuxppc-dev
  Cc: Michael Ellerman, Tyrel Datwyler, Thomas Falcon, Juliet Kim,
	devicetree, linux-kernel

From: Frank Rowand <frank.rowand@sony.com>

Non-overlay dynamic devicetree node removal may leave the node in
the phandle cache.  Subsequent calls to of_find_node_by_phandle()
will incorrectly find the stale entry.  Remove the node from the
cache.

Add paranoia checks in of_find_node_by_phandle() as a second level
of defense (do not return cached node if detached, do not add node
to cache if detached).

Reported-by: Michael Bringmann <mwb@linux.vnet.ibm.com>
Signed-off-by: Frank Rowand <frank.rowand@sony.com>
---
 drivers/of/base.c       | 29 ++++++++++++++++++++++++++++-
 drivers/of/dynamic.c    |  3 +++
 drivers/of/of_private.h |  4 ++++
 3 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/drivers/of/base.c b/drivers/of/base.c
index d599367cb92a..34a5125713c8 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -162,6 +162,27 @@ int of_free_phandle_cache(void)
 late_initcall_sync(of_free_phandle_cache);
 #endif
 
+/*
+ * Caller must hold devtree_lock.
+ */
+void __of_free_phandle_cache_entry(phandle handle)
+{
+	phandle masked_handle;
+
+	if (!handle)
+		return;
+
+	masked_handle = handle & phandle_cache_mask;
+
+	if (phandle_cache) {
+		if (phandle_cache[masked_handle] &&
+		    handle == phandle_cache[masked_handle]->phandle) {
+			of_node_put(phandle_cache[masked_handle]);
+			phandle_cache[masked_handle] = NULL;
+		}
+	}
+}
+
 void of_populate_phandle_cache(void)
 {
 	unsigned long flags;
@@ -1209,11 +1230,17 @@ struct device_node *of_find_node_by_phandle(phandle handle)
 		if (phandle_cache[masked_handle] &&
 		    handle == phandle_cache[masked_handle]->phandle)
 			np = phandle_cache[masked_handle];
+		if (np && of_node_check_flag(np, OF_DETACHED)) {
+			of_node_put(np);
+			phandle_cache[masked_handle] = NULL;
+			np = NULL;
+		}
 	}
 
 	if (!np) {
 		for_each_of_allnodes(np)
-			if (np->phandle == handle) {
+			if (np->phandle == handle &&
+			    !of_node_check_flag(np, OF_DETACHED)) {
 				if (phandle_cache) {
 					/* will put when removed from cache */
 					of_node_get(np);
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index f4f8ed9b5454..ecea92f68c87 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -268,6 +268,9 @@ void __of_detach_node(struct device_node *np)
 	}
 
 	of_node_set_flag(np, OF_DETACHED);
+
+	/* race with of_find_node_by_phandle() prevented by devtree_lock */
+	__of_free_phandle_cache_entry(np->phandle);
 }
 
 /**
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index 5d1567025358..24786818e32e 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -84,6 +84,10 @@ static inline void __of_detach_node_sysfs(struct device_node *np) {}
 int of_resolve_phandles(struct device_node *tree);
 #endif
 
+#if defined(CONFIG_OF_DYNAMIC)
+void __of_free_phandle_cache_entry(phandle handle);
+#endif
+
 #if defined(CONFIG_OF_OVERLAY)
 void of_overlay_mutex_lock(void);
 void of_overlay_mutex_unlock(void);
-- 
Frank Rowand <frank.rowand@sony.com>


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/2] of: phandle_cache, fix refcounts, remove stale entry
  2018-12-14  6:42 [PATCH 0/2] of: phandle_cache, fix refcounts, remove stale entry frowand.list
  2018-12-14  6:42 ` [PATCH 1/2] of: of_node_get()/of_node_put() nodes held in phandle cache frowand.list
  2018-12-14  6:42 ` [PATCH 2/2] of: __of_detach_node() - remove node from " frowand.list
@ 2018-12-14  6:46 ` Frank Rowand
  2 siblings, 0 replies; 10+ messages in thread
From: Frank Rowand @ 2018-12-14  6:46 UTC (permalink / raw)
  To: robh+dt, Michael Bringmann, linuxppc-dev
  Cc: Michael Ellerman, Tyrel Datwyler, Thomas Falcon, Juliet Kim,
	devicetree, linux-kernel

Hi Michael Bringmann,

On 12/13/18 10:42 PM, frowand.list@gmail.com wrote:
> From: Frank Rowand <frank.rowand@sony.com>
> 
> Non-overlay dynamic devicetree node removal may leave the node in
> the phandle cache.  Subsequent calls to of_find_node_by_phandle()
> will incorrectly find the stale entry.  This bug exposed the foloowing
> phandle cache refcount bug.
> 
> The refcount of phandle_cache entries is not incremented while in
> the cache, allowing use after free error after kfree() of the
> cached entry.
> 
> Frank Rowand (2):
>   of: of_node_get()/of_node_put() nodes held in phandle cache
>   of: __of_detach_node() - remove node from phandle cache
> 
>  drivers/of/base.c       | 99 ++++++++++++++++++++++++++++++++++++-------------
>  drivers/of/dynamic.c    |  3 ++
>  drivers/of/of_private.h |  4 ++
>  3 files changed, 81 insertions(+), 25 deletions(-)
> 

Can you please test that these patches fix the problem that you
reported in:

    [PATCH v03] powerpc/mobility: Fix node detach/rename problem

Thanks,

Frank

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] of: of_node_get()/of_node_put() nodes held in phandle cache
  2018-12-14  6:42 ` [PATCH 1/2] of: of_node_get()/of_node_put() nodes held in phandle cache frowand.list
@ 2018-12-14 17:15   ` Rob Herring
  2018-12-14 22:47     ` Frank Rowand
  0 siblings, 1 reply; 10+ messages in thread
From: Rob Herring @ 2018-12-14 17:15 UTC (permalink / raw)
  To: Frank Rowand
  Cc: mwb, linuxppc-dev, Michael Ellerman, Tyrel Datwyler, tlfalcon,
	minkim, devicetree, linux-kernel

On Fri, Dec 14, 2018 at 12:43 AM <frowand.list@gmail.com> wrote:
>
> From: Frank Rowand <frank.rowand@sony.com>
>
> The phandle cache contains struct device_node pointers.  The refcount
> of the pointers was not incremented while in the cache, allowing use
> after free error after kfree() of the node.  Add the proper increment
> and decrement of the use count.

Since we pre-populate the cache at boot, all the nodes will have a ref
count and will never be freed unless we happen to repopulate the whole
cache. That doesn't seem ideal. The node pointer is not "in use" just
because it is in the cache.

Rob

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] of: __of_detach_node() - remove node from phandle cache
  2018-12-14  6:42 ` [PATCH 2/2] of: __of_detach_node() - remove node from " frowand.list
@ 2018-12-14 17:20   ` Rob Herring
  2018-12-14 21:56     ` Michael Bringmann
  0 siblings, 1 reply; 10+ messages in thread
From: Rob Herring @ 2018-12-14 17:20 UTC (permalink / raw)
  To: Frank Rowand
  Cc: mwb, linuxppc-dev, Michael Ellerman, Tyrel Datwyler, tlfalcon,
	minkim, devicetree, linux-kernel

On Fri, Dec 14, 2018 at 12:43 AM <frowand.list@gmail.com> wrote:
>
> From: Frank Rowand <frank.rowand@sony.com>
>
> Non-overlay dynamic devicetree node removal may leave the node in
> the phandle cache.  Subsequent calls to of_find_node_by_phandle()
> will incorrectly find the stale entry.  Remove the node from the
> cache.
>
> Add paranoia checks in of_find_node_by_phandle() as a second level
> of defense (do not return cached node if detached, do not add node
> to cache if detached).
>
> Reported-by: Michael Bringmann <mwb@linux.vnet.ibm.com>
> Signed-off-by: Frank Rowand <frank.rowand@sony.com>
> ---
>  drivers/of/base.c       | 29 ++++++++++++++++++++++++++++-
>  drivers/of/dynamic.c    |  3 +++
>  drivers/of/of_private.h |  4 ++++
>  3 files changed, 35 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/of/base.c b/drivers/of/base.c
> index d599367cb92a..34a5125713c8 100644
> --- a/drivers/of/base.c
> +++ b/drivers/of/base.c
> @@ -162,6 +162,27 @@ int of_free_phandle_cache(void)
>  late_initcall_sync(of_free_phandle_cache);
>  #endif
>
> +/*
> + * Caller must hold devtree_lock.
> + */
> +void __of_free_phandle_cache_entry(phandle handle)
> +{
> +       phandle masked_handle;
> +
> +       if (!handle)
> +               return;
> +
> +       masked_handle = handle & phandle_cache_mask;
> +
> +       if (phandle_cache) {
> +               if (phandle_cache[masked_handle] &&
> +                   handle == phandle_cache[masked_handle]->phandle) {
> +                       of_node_put(phandle_cache[masked_handle]);
> +                       phandle_cache[masked_handle] = NULL;
> +               }
> +       }
> +}
> +
>  void of_populate_phandle_cache(void)
>  {
>         unsigned long flags;
> @@ -1209,11 +1230,17 @@ struct device_node *of_find_node_by_phandle(phandle handle)
>                 if (phandle_cache[masked_handle] &&
>                     handle == phandle_cache[masked_handle]->phandle)
>                         np = phandle_cache[masked_handle];
> +               if (np && of_node_check_flag(np, OF_DETACHED)) {
> +                       of_node_put(np);
> +                       phandle_cache[masked_handle] = NULL;

This should never happen, right? Any time we set OF_DETACHED, the
entry should get removed from the cache. I think we want a WARN here
in case we're in an unexpected state.

Rob

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] of: __of_detach_node() - remove node from phandle cache
  2018-12-14 17:20   ` Rob Herring
@ 2018-12-14 21:56     ` Michael Bringmann
  2018-12-14 22:38       ` Frank Rowand
  0 siblings, 1 reply; 10+ messages in thread
From: Michael Bringmann @ 2018-12-14 21:56 UTC (permalink / raw)
  To: Rob Herring, Frank Rowand
  Cc: linuxppc-dev, Michael Ellerman, Tyrel Datwyler, tlfalcon, minkim,
	devicetree, linux-kernel

On 12/14/2018 11:20 AM, Rob Herring wrote:
> On Fri, Dec 14, 2018 at 12:43 AM <frowand.list@gmail.com> wrote:
>>
>> From: Frank Rowand <frank.rowand@sony.com>
>>
>> Non-overlay dynamic devicetree node removal may leave the node in
>> the phandle cache.  Subsequent calls to of_find_node_by_phandle()
>> will incorrectly find the stale entry.  Remove the node from the
>> cache.
>>
>> Add paranoia checks in of_find_node_by_phandle() as a second level
>> of defense (do not return cached node if detached, do not add node
>> to cache if detached).
>>
>> Reported-by: Michael Bringmann <mwb@linux.vnet.ibm.com>
>> Signed-off-by: Frank Rowand <frank.rowand@sony.com>
>> ---
>>  drivers/of/base.c       | 29 ++++++++++++++++++++++++++++-
>>  drivers/of/dynamic.c    |  3 +++
>>  drivers/of/of_private.h |  4 ++++
>>  3 files changed, 35 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/of/base.c b/drivers/of/base.c
>> index d599367cb92a..34a5125713c8 100644
>> --- a/drivers/of/base.c
>> +++ b/drivers/of/base.c
>> @@ -162,6 +162,27 @@ int of_free_phandle_cache(void)
>>  late_initcall_sync(of_free_phandle_cache);
>>  #endif
>>
>> +/*
>> + * Caller must hold devtree_lock.
>> + */
>> +void __of_free_phandle_cache_entry(phandle handle)
>> +{
>> +       phandle masked_handle;
>> +
>> +       if (!handle)
>> +               return;
>> +
>> +       masked_handle = handle & phandle_cache_mask;
>> +
>> +       if (phandle_cache) {
>> +               if (phandle_cache[masked_handle] &&
>> +                   handle == phandle_cache[masked_handle]->phandle) {
>> +                       of_node_put(phandle_cache[masked_handle]);
>> +                       phandle_cache[masked_handle] = NULL;
>> +               }
>> +       }
>> +}
>> +
>>  void of_populate_phandle_cache(void)
>>  {
>>         unsigned long flags;
>> @@ -1209,11 +1230,17 @@ struct device_node *of_find_node_by_phandle(phandle handle)
>>                 if (phandle_cache[masked_handle] &&
>>                     handle == phandle_cache[masked_handle]->phandle)
>>                         np = phandle_cache[masked_handle];
>> +               if (np && of_node_check_flag(np, OF_DETACHED)) {
>> +                       of_node_put(np);
>> +                       phandle_cache[masked_handle] = NULL;
> 
> This should never happen, right? Any time we set OF_DETACHED, the
> entry should get removed from the cache. I think we want a WARN here
> in case we're in an unexpected state.

We don't actually remove the pointer from the phandle cache when we set
OF_DETACHED in drivers/of/dynamic.c:__of_detach_node.  The phandle cache
is currently static within drivers/of/base.c.  There are a couple of
calls to of_populate_phandle_cache / of_free_phandle_cache within
drivers/of/overlay.c, but these are not involved in the device tree
updates that occur during LPAR migration.  A WARN here would only make
sense, if we also arrange to clear the handle.

> 
> Rob

Michael

> 
> 

-- 
Michael W. Bringmann
Linux I/O, Networking and Security Development
IBM Corporation
Tie-Line  363-5196
External: (512) 286-5196
Cell:       (512) 466-0650
mwb@linux.vnet.ibm.com


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] of: __of_detach_node() - remove node from phandle cache
  2018-12-14 21:56     ` Michael Bringmann
@ 2018-12-14 22:38       ` Frank Rowand
  0 siblings, 0 replies; 10+ messages in thread
From: Frank Rowand @ 2018-12-14 22:38 UTC (permalink / raw)
  To: Michael Bringmann, Rob Herring
  Cc: linuxppc-dev, Michael Ellerman, Tyrel Datwyler, tlfalcon, minkim,
	devicetree, linux-kernel, Frank Rowand

On 12/14/18 1:56 PM, Michael Bringmann wrote:
> On 12/14/2018 11:20 AM, Rob Herring wrote:
>> On Fri, Dec 14, 2018 at 12:43 AM <frowand.list@gmail.com> wrote:
>>>
>>> From: Frank Rowand <frank.rowand@sony.com>
>>>
>>> Non-overlay dynamic devicetree node removal may leave the node in
>>> the phandle cache.  Subsequent calls to of_find_node_by_phandle()
>>> will incorrectly find the stale entry.  Remove the node from the
>>> cache.
>>>
>>> Add paranoia checks in of_find_node_by_phandle() as a second level
>>> of defense (do not return cached node if detached, do not add node
>>> to cache if detached).
>>>
>>> Reported-by: Michael Bringmann <mwb@linux.vnet.ibm.com>
>>> Signed-off-by: Frank Rowand <frank.rowand@sony.com>
>>> ---
>>>  drivers/of/base.c       | 29 ++++++++++++++++++++++++++++-
>>>  drivers/of/dynamic.c    |  3 +++
>>>  drivers/of/of_private.h |  4 ++++
>>>  3 files changed, 35 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/of/base.c b/drivers/of/base.c
>>> index d599367cb92a..34a5125713c8 100644
>>> --- a/drivers/of/base.c
>>> +++ b/drivers/of/base.c
>>> @@ -162,6 +162,27 @@ int of_free_phandle_cache(void)
>>>  late_initcall_sync(of_free_phandle_cache);
>>>  #endif
>>>
>>> +/*
>>> + * Caller must hold devtree_lock.
>>> + */
>>> +void __of_free_phandle_cache_entry(phandle handle)
>>> +{
>>> +       phandle masked_handle;
>>> +
>>> +       if (!handle)
>>> +               return;
>>> +
>>> +       masked_handle = handle & phandle_cache_mask;
>>> +
>>> +       if (phandle_cache) {
>>> +               if (phandle_cache[masked_handle] &&
>>> +                   handle == phandle_cache[masked_handle]->phandle) {
>>> +                       of_node_put(phandle_cache[masked_handle]);
>>> +                       phandle_cache[masked_handle] = NULL;
>>> +               }
>>> +       }
>>> +}
>>> +
>>>  void of_populate_phandle_cache(void)
>>>  {
>>>         unsigned long flags;
>>> @@ -1209,11 +1230,17 @@ struct device_node *of_find_node_by_phandle(phandle handle)
>>>                 if (phandle_cache[masked_handle] &&
>>>                     handle == phandle_cache[masked_handle]->phandle)
>>>                         np = phandle_cache[masked_handle];
>>> +               if (np && of_node_check_flag(np, OF_DETACHED)) {
>>> +                       of_node_put(np);
>>> +                       phandle_cache[masked_handle] = NULL;
>>
>> This should never happen, right? Any time we set OF_DETACHED, the
>> entry should get removed from the cache. I think we want a WARN here
>> in case we're in an unexpected state.

Correct, this should never happen.  I will add the WARN.


> We don't actually remove the pointer from the phandle cache when we set
> OF_DETACHED in drivers/of/dynamic.c:__of_detach_node.  The phandle cache
> is currently static within drivers/of/base.c.  There are a couple of
> calls to of_populate_phandle_cache / of_free_phandle_cache within
> drivers/of/overlay.c, but these are not involved in the device tree
> updates that occur during LPAR migration.  A WARN here would only make
> sense, if we also arrange to clear the handle.

Rob's reply did not include the full patch 2/2.  The full patch 2/2 also
adds a call to __of_free_phandle_cache_entry() in __of_detach_node().

-Frank

> 
>>
>> Rob
> 
> Michael
> 
>>
>>
> 


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] of: of_node_get()/of_node_put() nodes held in phandle cache
  2018-12-14 17:15   ` Rob Herring
@ 2018-12-14 22:47     ` Frank Rowand
  2018-12-14 23:04       ` Frank Rowand
  0 siblings, 1 reply; 10+ messages in thread
From: Frank Rowand @ 2018-12-14 22:47 UTC (permalink / raw)
  To: Rob Herring
  Cc: mwb, linuxppc-dev, Michael Ellerman, Tyrel Datwyler, tlfalcon,
	minkim, devicetree, linux-kernel, Frank Rowand

On 12/14/18 9:15 AM, Rob Herring wrote:
> On Fri, Dec 14, 2018 at 12:43 AM <frowand.list@gmail.com> wrote:
>>
>> From: Frank Rowand <frank.rowand@sony.com>
>>
>> The phandle cache contains struct device_node pointers.  The refcount
>> of the pointers was not incremented while in the cache, allowing use
>> after free error after kfree() of the node.  Add the proper increment
>> and decrement of the use count.
> 
> Since we pre-populate the cache at boot, all the nodes will have a ref
> count and will never be freed unless we happen to repopulate the whole
> cache. That doesn't seem ideal. The node pointer is not "in use" just
> because it is in the cache.
> 
> Rob
> 

This patch also adds of_node_put() so that the refcount will go to zero
when the node is removed as part of an overlay remove, if the node was
added by an overlay.

Patch 2/2 adds the free cache entry call to __of_detach_node(), so the
refcount will go to zero when the node is removed for dynamic use cases
other than overlays.  (For overlays, all nodes are instead removed from
the cache before __of_detach_node() is called.)

-Frank

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] of: of_node_get()/of_node_put() nodes held in phandle cache
  2018-12-14 22:47     ` Frank Rowand
@ 2018-12-14 23:04       ` Frank Rowand
  0 siblings, 0 replies; 10+ messages in thread
From: Frank Rowand @ 2018-12-14 23:04 UTC (permalink / raw)
  To: Rob Herring
  Cc: mwb, linuxppc-dev, Michael Ellerman, Tyrel Datwyler, tlfalcon,
	minkim, devicetree, linux-kernel

On 12/14/18 2:47 PM, Frank Rowand wrote:
> On 12/14/18 9:15 AM, Rob Herring wrote:
>> On Fri, Dec 14, 2018 at 12:43 AM <frowand.list@gmail.com> wrote:
>>>
>>> From: Frank Rowand <frank.rowand@sony.com>
>>>
>>> The phandle cache contains struct device_node pointers.  The refcount
>>> of the pointers was not incremented while in the cache, allowing use
>>> after free error after kfree() of the node.  Add the proper increment
>>> and decrement of the use count.
>>
>> Since we pre-populate the cache at boot, all the nodes will have a ref
>> count and will never be freed unless we happen to repopulate the whole

>> cache. That doesn't seem ideal. The node pointer is not "in use" just
>> because it is in the cache.

I forgot to reply to this sentence.

The node pointers are "in use" because of_find_node_by_phandle() will
use the pointers to access the phandle field.  This is a use after
free bug if the node has been kfree()'ed.


>>
>> Rob
>>
> 
> This patch also adds of_node_put() so that the refcount will go to zero
> when the node is removed as part of an overlay remove, if the node was
> added by an overlay.
> 
> Patch 2/2 adds the free cache entry call to __of_detach_node(), so the
> refcount will go to zero when the node is removed for dynamic use cases
> other than overlays.  (For overlays, all nodes are instead removed from
> the cache before __of_detach_node() is called.)
> 
> -Frank
> 


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2018-12-14 23:05 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-14  6:42 [PATCH 0/2] of: phandle_cache, fix refcounts, remove stale entry frowand.list
2018-12-14  6:42 ` [PATCH 1/2] of: of_node_get()/of_node_put() nodes held in phandle cache frowand.list
2018-12-14 17:15   ` Rob Herring
2018-12-14 22:47     ` Frank Rowand
2018-12-14 23:04       ` Frank Rowand
2018-12-14  6:42 ` [PATCH 2/2] of: __of_detach_node() - remove node from " frowand.list
2018-12-14 17:20   ` Rob Herring
2018-12-14 21:56     ` Michael Bringmann
2018-12-14 22:38       ` Frank Rowand
2018-12-14  6:46 ` [PATCH 0/2] of: phandle_cache, fix refcounts, remove stale entry Frank Rowand

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).