Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

net: qrtr: replace qrtr_tx_flow radix_tree with xarray to fix memory leak

__radix_tree_create() allocates and links intermediate nodes into the
tree one by one. If a subsequent allocation fails, the already-linked
nodes remain in the tree with no corresponding leaf entry. These orphaned
internal nodes are never reclaimed because radix_tree_for_each_slot()
only visits slots containing leaf values.

The radix_tree API is deprecated in favor of xarray. As suggested by
Matthew Wilcox, migrate qrtr_tx_flow from radix_tree to xarray instead
of fixing the radix_tree itself [1]. xarray properly handles cleanup of
internal nodes — xa_destroy() frees all internal xarray nodes when the
qrtr_node is released, preventing the leak.

[1] https://lore.kernel.org/all/20260225071623.41275-1-jiayuan.chen@linux.dev/T/
Reported-by: syzbot+006987d1be3586e13555@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/all/000000000000bfba3a060bf4ffcf@google.com/T/
Fixes: 5fdeb0d372ab ("net: qrtr: Implement outgoing flow control")
Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260324080645.290197-1-jiayuan.chen@linux.dev
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Jiayuan Chen and committed by
Jakub Kicinski
24280831 04f27218

+13 -18
+13 -18
net/qrtr/af_qrtr.c
··· 118 118 * @ep: endpoint 119 119 * @ref: reference count for node 120 120 * @nid: node id 121 - * @qrtr_tx_flow: tree of qrtr_tx_flow, keyed by node << 32 | port 121 + * @qrtr_tx_flow: xarray of qrtr_tx_flow, keyed by node << 32 | port 122 122 * @qrtr_tx_lock: lock for qrtr_tx_flow inserts 123 123 * @rx_queue: receive queue 124 124 * @item: list item for broadcast list ··· 129 129 struct kref ref; 130 130 unsigned int nid; 131 131 132 - struct radix_tree_root qrtr_tx_flow; 132 + struct xarray qrtr_tx_flow; 133 133 struct mutex qrtr_tx_lock; /* for qrtr_tx_flow */ 134 134 135 135 struct sk_buff_head rx_queue; ··· 172 172 struct qrtr_tx_flow *flow; 173 173 unsigned long flags; 174 174 void __rcu **slot; 175 + unsigned long index; 175 176 176 177 spin_lock_irqsave(&qrtr_nodes_lock, flags); 177 178 /* If the node is a bridge for other nodes, there are possibly ··· 190 189 skb_queue_purge(&node->rx_queue); 191 190 192 191 /* Free tx flow counters */ 193 - radix_tree_for_each_slot(slot, &node->qrtr_tx_flow, &iter, 0) { 194 - flow = *slot; 195 - radix_tree_iter_delete(&node->qrtr_tx_flow, &iter, slot); 192 + xa_for_each(&node->qrtr_tx_flow, index, flow) 196 193 kfree(flow); 197 - } 194 + xa_destroy(&node->qrtr_tx_flow); 198 195 kfree(node); 199 196 } 200 197 ··· 227 228 228 229 key = remote_node << 32 | remote_port; 229 230 230 - rcu_read_lock(); 231 - flow = radix_tree_lookup(&node->qrtr_tx_flow, key); 232 - rcu_read_unlock(); 231 + flow = xa_load(&node->qrtr_tx_flow, key); 233 232 if (flow) { 234 233 spin_lock(&flow->resume_tx.lock); 235 234 flow->pending = 0; ··· 266 269 return 0; 267 270 268 271 mutex_lock(&node->qrtr_tx_lock); 269 - flow = radix_tree_lookup(&node->qrtr_tx_flow, key); 272 + flow = xa_load(&node->qrtr_tx_flow, key); 270 273 if (!flow) { 271 274 flow = kzalloc_obj(*flow); 272 275 if (flow) { 273 276 init_waitqueue_head(&flow->resume_tx); 274 - if (radix_tree_insert(&node->qrtr_tx_flow, key, flow)) { 277 + if (xa_err(xa_store(&node->qrtr_tx_flow, key, flow, 278 + GFP_KERNEL))) { 275 279 kfree(flow); 276 280 flow = NULL; 277 281 } ··· 324 326 unsigned long key = (u64)dest_node << 32 | dest_port; 325 327 struct qrtr_tx_flow *flow; 326 328 327 - rcu_read_lock(); 328 - flow = radix_tree_lookup(&node->qrtr_tx_flow, key); 329 - rcu_read_unlock(); 329 + flow = xa_load(&node->qrtr_tx_flow, key); 330 330 if (flow) { 331 331 spin_lock_irq(&flow->resume_tx.lock); 332 332 flow->tx_failed = 1; ··· 595 599 node->nid = QRTR_EP_NID_AUTO; 596 600 node->ep = ep; 597 601 598 - INIT_RADIX_TREE(&node->qrtr_tx_flow, GFP_KERNEL); 602 + xa_init(&node->qrtr_tx_flow); 599 603 mutex_init(&node->qrtr_tx_lock); 600 604 601 605 qrtr_node_assign(node, nid); ··· 623 627 struct qrtr_tx_flow *flow; 624 628 struct sk_buff *skb; 625 629 unsigned long flags; 630 + unsigned long index; 626 631 void __rcu **slot; 627 632 628 633 mutex_lock(&node->ep_lock); ··· 646 649 647 650 /* Wake up any transmitters waiting for resume-tx from the node */ 648 651 mutex_lock(&node->qrtr_tx_lock); 649 - radix_tree_for_each_slot(slot, &node->qrtr_tx_flow, &iter, 0) { 650 - flow = *slot; 652 + xa_for_each(&node->qrtr_tx_flow, index, flow) 651 653 wake_up_interruptible_all(&flow->resume_tx); 652 - } 653 654 mutex_unlock(&node->qrtr_tx_lock); 654 655 655 656 qrtr_node_release(node);