Thread (4 messages) 4 messages, 3 authors, 4d ago

Re: [PATCH net] tipc: hold a reference to nodes found by link name

From: Chengfeng Ye <hidden>
Date: 2026-09-28 16:19:37
Also in: lkml, stable

On Mon, Sep 28, 2026 at 8:17 PM Tung Quang Nguyen
[off-list ref] wrote:
quoted
Subject: [PATCH net] tipc: hold a reference to nodes found by link name

tipc_node_find_by_name() returns a node after dropping its RCU read lock
without taking a reference. The LINK_SET, LINK_GET and LINK_RESET_STATS
handlers then lock and access the node, racing with timer-driven cleanup of a
down peer. Generic netlink serialization does not exclude the node timer.

The following interleaving can leave a handler using a freed node:

 CPU 0: find the node under RCU and release the node read lock
 CPU 1: tipc_node_timeout() clears the links and unlinks the down node
 CPU 1: drop the list and timer references, queuing tipc_node_free()
 CPU 0: leave the RCU read-side critical section
 CPU 1: complete the grace period and free the node
 CPU 0: acquire the node lock through the stale pointer

LINK_SET also uses the node's media address after releasing the node lock,
when passing queued packets to tipc_bearer_xmit().

KASAN reported:

 BUG: KASAN: slab-use-after-free in _raw_read_lock_bh+0x1d/0x40
 Write of size 4 at addr ffff888112723808 by task poc/87
 Call Trace:
  _raw_read_lock_bh+0x1d/0x40
  tipc_nl_node_set_link+0x30e/0x680
  genl_family_rcv_msg_doit+0x1e0/0x2c0
  genl_rcv_msg+0x419/0x6d0
  netlink_rcv_skb+0x11f/0x350
 Allocated by task 28:
  tipc_node_create+0x9c1/0x1fa0
  tipc_node_check_dest+0x121/0x11e0
  tipc_disc_rcv+0xdbf/0x1430
 Freed by task 87:
  kfree+0x149/0x330
  rcu_core+0x50a/0x1850
 Last potentially related work creation:
  __call_rcu_common.constprop.0+0x71/0xa10
  tipc_node_timeout+0xb1b/0xe70
Can you update your changelog with decoded stack trace ?
With decoded stack trace, It helps me understand how your reproducer triggers the issue.
The partial decoded stack trace is attached on the changelog on v2.
https://lore.kernel.org/netdev/179061216640.31693.424352671155791136@kernel.org/T/#t (local)
I will send you the full KASAN report as well as the reproduction
method in a separate private email.
quoted
Acquire a reference to the selected node with kref_get_unless_zero() before
leaving RCU, returning NULL if the node has already been released.
Release that reference on every caller exit after the last node access, including
transmission in LINK_SET. Keep the existing link lookup order and locking so
concurrent link removal still takes the existing error paths.

Fixes: 6a939f365bdb ("tipc: Auto removal of peer down node instance")
Cc: stable@vger.kernel.org
Signed-off-by: Chengfeng Ye <redacted>
---
net/tipc/node.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/net/tipc/node.c b/net/tipc/node.c index
bd91378b7540..2726bee3bb40 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -2424,6 +2424,8 @@ static struct tipc_node
*tipc_node_find_by_name(struct net *net,
              if (found_node)
                      break;
      }
+      if (found_node && !kref_get_unless_zero(&found_node->kref))
+              found_node = NULL;
quoted hunk ↗ jump to hunk
This checking is not optimal.
Try this:
diff --git a/net/tipc/node.c b/net/tipc/node.c
index bd91378b7540..3e61e9106dd7 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -2421,8 +2421,11 @@ static struct tipc_node *tipc_node_find_by_name(struct net *net,
                        }
                }
                tipc_node_read_unlock(n);
-               if (found_node)
+               if (found_node) {
+                       if (!kref_get_unless_zero(&found_node->kref))
+                               found_node = NULL;
                        break;
+               }
        }
        rcu_read_unlock();
quoted
      rcu_read_unlock();

      return found_node;
@@ -2507,6 +2509,7 @@ int tipc_nl_node_set_link(struct sk_buff *skb, struct
genl_info *info)
      tipc_node_read_unlock(node);
      tipc_bearer_xmit(net, bearer_id, &xmitq, &node-
quoted
links[bearer_id].maddr,
                       NULL);
+      tipc_node_put(node);
      return res;
}
@@ -2558,12 +2561,14 @@ int tipc_nl_node_get_link(struct sk_buff *skb,
struct genl_info *info)
              link = node->links[bearer_id].link;
              if (!link) {
                      tipc_node_read_unlock(node);
+                      tipc_node_put(node);
                      err = -EINVAL;
                      goto err_free;
              }

              err = __tipc_nl_add_link(net, &msg, link, 0);
              tipc_node_read_unlock(node);
+              tipc_node_put(node);
              if (err)
                      goto err_free;
      }
@@ -2634,11 +2639,13 @@ int tipc_nl_node_reset_link_stats(struct sk_buff
*skb, struct genl_info *info)
      if (!link) {
              spin_unlock_bh(&le->lock);
              tipc_node_read_unlock(node);
+              tipc_node_put(node);
              return -EINVAL;
      }
      tipc_link_reset_stats(link);
      spin_unlock_bh(&le->lock);
      tipc_node_read_unlock(node);
+      tipc_node_put(node);
      return 0;
}

--
2.43.0
The adjustment has been adapted in v2.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help