net_shaper_commit() overrides nodes which may be concurrently read
under RCU. This is not a huge deal since the entries only contain
config, worst case user will see inconsistent config params. But
we should try to avoid this obvious RCU violation. Try to allocate
a new node. Since commit() can't fail fall back to overriding.
Full fix is probably not worth the complexity, struct net_shaper
is around 80B, and the allocation is with GFP_KERNEL.
Fixes: 93954b40f6a4 ("net-shapers: implement NL set and delete operations")
Signed-off-by: Jakub Kicinski <[email protected]>
---
net/shaper/shaper.c | 28 +++++++++++++++++++++++-----
1 file changed, 23 insertions(+), 5 deletions(-)
diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c
index 692e38df42cb..a2e9adca9afc 100644
--- a/net/shaper/shaper.c
+++ b/net/shaper/shaper.c
@@ -406,18 +406,36 @@ static void net_shaper_commit(struct net_shaper_binding
*binding,
int i;
for (i = 0; i < nr_shapers; ++i) {
+ struct net_shaper *new;
+
index = net_shaper_handle_to_index(&shapers[i].handle);
cur = xa_load(&hierarchy->shapers, index);
if (WARN_ON_ONCE(!cur))
continue;
- /* Successful update: drop the tentative mark
- * and update the hierarchy container.
+ /* If the shaper is already visible try to allocate a new
+ * entry so that we don't cause torn reads under RCU.
+ * This tiny GFP_KERNEL alloc should never fail, really,
+ * but if it does just override, the torn read is acceptable.
*/
- *cur = shapers[i];
- smp_wmb();
- xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID);
+ new = NULL;
+ if (xa_get_mark(&hierarchy->shapers, index, NET_SHAPER_VALID))
+ new = kmalloc_obj(*new);
+
+ if (new) {
+ *new = shapers[i];
+ smp_wmb();
+ /* Replacing an entry, xa_store() can't fail */
+ WARN_ON(xa_err(xa_store(&hierarchy->shapers, index,
+ new, GFP_KERNEL)));
+ kfree_rcu(cur, rcu);
+ } else {
+ *cur = shapers[i];
+ smp_wmb();
+ xa_set_mark(&hierarchy->shapers, index,
+ NET_SHAPER_VALID);
+ }
}
}
--
2.54.0