Skip to content

Commit a7809ff

Browse files
Mani-Sadhasivamdavem330
authored andcommitted
net: qrtr: ns: Protect radix_tree_deref_slot() using rcu read locks
The rcu read locks are needed to avoid potential race condition while dereferencing radix tree from multiple threads. The issue was identified by syzbot. Below is the crash report: ============================= WARNING: suspicious RCU usage 5.7.0-syzkaller #0 Not tainted ----------------------------- include/linux/radix-tree.h:176 suspicious rcu_dereference_check() usage! other info that might help us debug this: rcu_scheduler_active = 2, debug_locks = 1 2 locks held by kworker/u4:1/21: #0: ffff88821b097938 ((wq_completion)qrtr_ns_handler){+.+.}-{0:0}, at: spin_unlock_irq include/linux/spinlock.h:403 [inline] #0: ffff88821b097938 ((wq_completion)qrtr_ns_handler){+.+.}-{0:0}, at: process_one_work+0x6df/0xfd0 kernel/workqueue.c:2241 #1: ffffc90000dd7d80 ((work_completion)(&qrtr_ns.work)){+.+.}-{0:0}, at: process_one_work+0x71e/0xfd0 kernel/workqueue.c:2243 stack backtrace: CPU: 0 PID: 21 Comm: kworker/u4:1 Not tainted 5.7.0-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Workqueue: qrtr_ns_handler qrtr_ns_worker Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x1e9/0x30e lib/dump_stack.c:118 radix_tree_deref_slot include/linux/radix-tree.h:176 [inline] ctrl_cmd_new_lookup net/qrtr/ns.c:558 [inline] qrtr_ns_worker+0x2aff/0x4500 net/qrtr/ns.c:674 process_one_work+0x76e/0xfd0 kernel/workqueue.c:2268 worker_thread+0xa7f/0x1450 kernel/workqueue.c:2414 kthread+0x353/0x380 kernel/kthread.c:268 Fixes: 0c2204a ("net: qrtr: Migrate nameservice to kernel from userspace") Reported-and-tested-by: [email protected] Signed-off-by: Manivannan Sadhasivam <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 0ba56b8 commit a7809ff

File tree

1 file changed

+25
-9
lines changed

1 file changed

+25
-9
lines changed

net/qrtr/ns.c

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -193,24 +193,28 @@ static int announce_servers(struct sockaddr_qrtr *sq)
193193
struct qrtr_server *srv;
194194
struct qrtr_node *node;
195195
void __rcu **slot;
196-
int ret;
196+
int ret = 0;
197197

198198
node = node_get(qrtr_ns.local_node);
199199
if (!node)
200200
return 0;
201201

202+
rcu_read_lock();
202203
/* Announce the list of servers registered in this node */
203204
radix_tree_for_each_slot(slot, &node->servers, &iter, 0) {
204205
srv = radix_tree_deref_slot(slot);
205206

206207
ret = service_announce_new(sq, srv);
207208
if (ret < 0) {
208209
pr_err("failed to announce new service\n");
209-
return ret;
210+
goto err_out;
210211
}
211212
}
212213

213-
return 0;
214+
err_out:
215+
rcu_read_unlock();
216+
217+
return ret;
214218
}
215219

216220
static struct qrtr_server *server_add(unsigned int service,
@@ -335,7 +339,7 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from)
335339
struct qrtr_node *node;
336340
void __rcu **slot;
337341
struct kvec iv;
338-
int ret;
342+
int ret = 0;
339343

340344
iv.iov_base = &pkt;
341345
iv.iov_len = sizeof(pkt);
@@ -344,11 +348,13 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from)
344348
if (!node)
345349
return 0;
346350

351+
rcu_read_lock();
347352
/* Advertise removal of this client to all servers of remote node */
348353
radix_tree_for_each_slot(slot, &node->servers, &iter, 0) {
349354
srv = radix_tree_deref_slot(slot);
350355
server_del(node, srv->port);
351356
}
357+
rcu_read_unlock();
352358

353359
/* Advertise the removal of this client to all local servers */
354360
local_node = node_get(qrtr_ns.local_node);
@@ -359,6 +365,7 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from)
359365
pkt.cmd = cpu_to_le32(QRTR_TYPE_BYE);
360366
pkt.client.node = cpu_to_le32(from->sq_node);
361367

368+
rcu_read_lock();
362369
radix_tree_for_each_slot(slot, &local_node->servers, &iter, 0) {
363370
srv = radix_tree_deref_slot(slot);
364371

@@ -372,11 +379,14 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from)
372379
ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
373380
if (ret < 0) {
374381
pr_err("failed to send bye cmd\n");
375-
return ret;
382+
goto err_out;
376383
}
377384
}
378385

379-
return 0;
386+
err_out:
387+
rcu_read_unlock();
388+
389+
return ret;
380390
}
381391

382392
static int ctrl_cmd_del_client(struct sockaddr_qrtr *from,
@@ -394,7 +404,7 @@ static int ctrl_cmd_del_client(struct sockaddr_qrtr *from,
394404
struct list_head *li;
395405
void __rcu **slot;
396406
struct kvec iv;
397-
int ret;
407+
int ret = 0;
398408

399409
iv.iov_base = &pkt;
400410
iv.iov_len = sizeof(pkt);
@@ -434,6 +444,7 @@ static int ctrl_cmd_del_client(struct sockaddr_qrtr *from,
434444
pkt.client.node = cpu_to_le32(node_id);
435445
pkt.client.port = cpu_to_le32(port);
436446

447+
rcu_read_lock();
437448
radix_tree_for_each_slot(slot, &local_node->servers, &iter, 0) {
438449
srv = radix_tree_deref_slot(slot);
439450

@@ -447,11 +458,14 @@ static int ctrl_cmd_del_client(struct sockaddr_qrtr *from,
447458
ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
448459
if (ret < 0) {
449460
pr_err("failed to send del client cmd\n");
450-
return ret;
461+
goto err_out;
451462
}
452463
}
453464

454-
return 0;
465+
err_out:
466+
rcu_read_unlock();
467+
468+
return ret;
455469
}
456470

457471
static int ctrl_cmd_new_server(struct sockaddr_qrtr *from,
@@ -554,6 +568,7 @@ static int ctrl_cmd_new_lookup(struct sockaddr_qrtr *from,
554568
filter.service = service;
555569
filter.instance = instance;
556570

571+
rcu_read_lock();
557572
radix_tree_for_each_slot(node_slot, &nodes, &node_iter, 0) {
558573
node = radix_tree_deref_slot(node_slot);
559574

@@ -568,6 +583,7 @@ static int ctrl_cmd_new_lookup(struct sockaddr_qrtr *from,
568583
lookup_notify(from, srv, true);
569584
}
570585
}
586+
rcu_read_unlock();
571587

572588
/* Empty notification, to indicate end of listing */
573589
lookup_notify(from, NULL, true);

0 commit comments

Comments
 (0)