Skip to content

Commit 8dbea64

Browse files
frowandgregkh
authored andcommitted
of: __of_detach_node() - remove node from phandle cache
commit 5801169 upstream. 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). Fixes: 0b3ce78 ("of: cache phandle nodes to reduce cost of of_find_node_by_phandle()") Reported-by: Michael Bringmann <mwb@linux.vnet.ibm.com> Cc: stable@vger.kernel.org # v4.17+ Signed-off-by: Frank Rowand <frank.rowand@sony.com> Signed-off-by: Rob Herring <robh@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 1cb5a03 commit 8dbea64

3 files changed

Lines changed: 37 additions & 1 deletion

File tree

drivers/of/base.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,28 @@ int of_free_phandle_cache(void)
161161
late_initcall_sync(of_free_phandle_cache);
162162
#endif
163163

164+
/*
165+
* Caller must hold devtree_lock.
166+
*/
167+
void __of_free_phandle_cache_entry(phandle handle)
168+
{
169+
phandle masked_handle;
170+
struct device_node *np;
171+
172+
if (!handle)
173+
return;
174+
175+
masked_handle = handle & phandle_cache_mask;
176+
177+
if (phandle_cache) {
178+
np = phandle_cache[masked_handle];
179+
if (np && handle == np->phandle) {
180+
of_node_put(np);
181+
phandle_cache[masked_handle] = NULL;
182+
}
183+
}
184+
}
185+
164186
void of_populate_phandle_cache(void)
165187
{
166188
unsigned long flags;
@@ -1169,11 +1191,18 @@ struct device_node *of_find_node_by_phandle(phandle handle)
11691191
if (phandle_cache[masked_handle] &&
11701192
handle == phandle_cache[masked_handle]->phandle)
11711193
np = phandle_cache[masked_handle];
1194+
if (np && of_node_check_flag(np, OF_DETACHED)) {
1195+
WARN_ON(1); /* did not uncache np on node removal */
1196+
of_node_put(np);
1197+
phandle_cache[masked_handle] = NULL;
1198+
np = NULL;
1199+
}
11721200
}
11731201

11741202
if (!np) {
11751203
for_each_of_allnodes(np)
1176-
if (np->phandle == handle) {
1204+
if (np->phandle == handle &&
1205+
!of_node_check_flag(np, OF_DETACHED)) {
11771206
if (phandle_cache) {
11781207
/* will put when removed from cache */
11791208
of_node_get(np);

drivers/of/dynamic.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ void __of_detach_node(struct device_node *np)
268268
}
269269

270270
of_node_set_flag(np, OF_DETACHED);
271+
272+
/* race with of_find_node_by_phandle() prevented by devtree_lock */
273+
__of_free_phandle_cache_entry(np->phandle);
271274
}
272275

273276
/**

drivers/of/of_private.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ static inline void __of_detach_node_sysfs(struct device_node *np) {}
7676
int of_resolve_phandles(struct device_node *tree);
7777
#endif
7878

79+
#if defined(CONFIG_OF_DYNAMIC)
80+
void __of_free_phandle_cache_entry(phandle handle);
81+
#endif
82+
7983
#if defined(CONFIG_OF_OVERLAY)
8084
void of_overlay_mutex_lock(void);
8185
void of_overlay_mutex_unlock(void);

0 commit comments

Comments
 (0)