mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-20 03:04:01 +08:00
net: qrtr: ns: Fix the incorrect usage of rcu_read_lock()
The rcu_read_lock() is not supposed to lock the kernel_sendmsg() API
since it has the lock_sock() in qrtr_sendmsg() which will sleep. Hence,
fix it by excluding the locking for kernel_sendmsg().
While at it, let's also use radix_tree_deref_retry() to confirm the
validity of the pointer returned by radix_tree_deref_slot() and use
radix_tree_iter_resume() to resume iterating the tree properly before
releasing the lock as suggested by Doug.
Fixes: a7809ff90c
("net: qrtr: ns: Protect radix_tree_deref_slot() using rcu read locks")
Reported-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Alex Elder <elder@linaro.org>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
7575fdda56
commit
082bb94fe1
@ -193,7 +193,7 @@ static int announce_servers(struct sockaddr_qrtr *sq)
|
|||||||
struct qrtr_server *srv;
|
struct qrtr_server *srv;
|
||||||
struct qrtr_node *node;
|
struct qrtr_node *node;
|
||||||
void __rcu **slot;
|
void __rcu **slot;
|
||||||
int ret = 0;
|
int ret;
|
||||||
|
|
||||||
node = node_get(qrtr_ns.local_node);
|
node = node_get(qrtr_ns.local_node);
|
||||||
if (!node)
|
if (!node)
|
||||||
@ -203,18 +203,27 @@ static int announce_servers(struct sockaddr_qrtr *sq)
|
|||||||
/* Announce the list of servers registered in this node */
|
/* Announce the list of servers registered in this node */
|
||||||
radix_tree_for_each_slot(slot, &node->servers, &iter, 0) {
|
radix_tree_for_each_slot(slot, &node->servers, &iter, 0) {
|
||||||
srv = radix_tree_deref_slot(slot);
|
srv = radix_tree_deref_slot(slot);
|
||||||
|
if (!srv)
|
||||||
|
continue;
|
||||||
|
if (radix_tree_deref_retry(srv)) {
|
||||||
|
slot = radix_tree_iter_retry(&iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
slot = radix_tree_iter_resume(slot, &iter);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
ret = service_announce_new(sq, srv);
|
ret = service_announce_new(sq, srv);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
pr_err("failed to announce new service\n");
|
pr_err("failed to announce new service\n");
|
||||||
goto err_out;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
err_out:
|
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct qrtr_server *server_add(unsigned int service,
|
static struct qrtr_server *server_add(unsigned int service,
|
||||||
@ -339,7 +348,7 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from)
|
|||||||
struct qrtr_node *node;
|
struct qrtr_node *node;
|
||||||
void __rcu **slot;
|
void __rcu **slot;
|
||||||
struct kvec iv;
|
struct kvec iv;
|
||||||
int ret = 0;
|
int ret;
|
||||||
|
|
||||||
iv.iov_base = &pkt;
|
iv.iov_base = &pkt;
|
||||||
iv.iov_len = sizeof(pkt);
|
iv.iov_len = sizeof(pkt);
|
||||||
@ -352,7 +361,16 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from)
|
|||||||
/* Advertise removal of this client to all servers of remote node */
|
/* Advertise removal of this client to all servers of remote node */
|
||||||
radix_tree_for_each_slot(slot, &node->servers, &iter, 0) {
|
radix_tree_for_each_slot(slot, &node->servers, &iter, 0) {
|
||||||
srv = radix_tree_deref_slot(slot);
|
srv = radix_tree_deref_slot(slot);
|
||||||
|
if (!srv)
|
||||||
|
continue;
|
||||||
|
if (radix_tree_deref_retry(srv)) {
|
||||||
|
slot = radix_tree_iter_retry(&iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
slot = radix_tree_iter_resume(slot, &iter);
|
||||||
|
rcu_read_unlock();
|
||||||
server_del(node, srv->port);
|
server_del(node, srv->port);
|
||||||
|
rcu_read_lock();
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
@ -368,6 +386,14 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from)
|
|||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
radix_tree_for_each_slot(slot, &local_node->servers, &iter, 0) {
|
radix_tree_for_each_slot(slot, &local_node->servers, &iter, 0) {
|
||||||
srv = radix_tree_deref_slot(slot);
|
srv = radix_tree_deref_slot(slot);
|
||||||
|
if (!srv)
|
||||||
|
continue;
|
||||||
|
if (radix_tree_deref_retry(srv)) {
|
||||||
|
slot = radix_tree_iter_retry(&iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
slot = radix_tree_iter_resume(slot, &iter);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
sq.sq_family = AF_QIPCRTR;
|
sq.sq_family = AF_QIPCRTR;
|
||||||
sq.sq_node = srv->node;
|
sq.sq_node = srv->node;
|
||||||
@ -379,14 +405,14 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from)
|
|||||||
ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
|
ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
pr_err("failed to send bye cmd\n");
|
pr_err("failed to send bye cmd\n");
|
||||||
goto err_out;
|
return ret;
|
||||||
}
|
}
|
||||||
|
rcu_read_lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
err_out:
|
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ctrl_cmd_del_client(struct sockaddr_qrtr *from,
|
static int ctrl_cmd_del_client(struct sockaddr_qrtr *from,
|
||||||
@ -404,7 +430,7 @@ static int ctrl_cmd_del_client(struct sockaddr_qrtr *from,
|
|||||||
struct list_head *li;
|
struct list_head *li;
|
||||||
void __rcu **slot;
|
void __rcu **slot;
|
||||||
struct kvec iv;
|
struct kvec iv;
|
||||||
int ret = 0;
|
int ret;
|
||||||
|
|
||||||
iv.iov_base = &pkt;
|
iv.iov_base = &pkt;
|
||||||
iv.iov_len = sizeof(pkt);
|
iv.iov_len = sizeof(pkt);
|
||||||
@ -447,6 +473,14 @@ static int ctrl_cmd_del_client(struct sockaddr_qrtr *from,
|
|||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
radix_tree_for_each_slot(slot, &local_node->servers, &iter, 0) {
|
radix_tree_for_each_slot(slot, &local_node->servers, &iter, 0) {
|
||||||
srv = radix_tree_deref_slot(slot);
|
srv = radix_tree_deref_slot(slot);
|
||||||
|
if (!srv)
|
||||||
|
continue;
|
||||||
|
if (radix_tree_deref_retry(srv)) {
|
||||||
|
slot = radix_tree_iter_retry(&iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
slot = radix_tree_iter_resume(slot, &iter);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
sq.sq_family = AF_QIPCRTR;
|
sq.sq_family = AF_QIPCRTR;
|
||||||
sq.sq_node = srv->node;
|
sq.sq_node = srv->node;
|
||||||
@ -458,14 +492,14 @@ static int ctrl_cmd_del_client(struct sockaddr_qrtr *from,
|
|||||||
ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
|
ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
pr_err("failed to send del client cmd\n");
|
pr_err("failed to send del client cmd\n");
|
||||||
goto err_out;
|
return ret;
|
||||||
}
|
}
|
||||||
|
rcu_read_lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
err_out:
|
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ctrl_cmd_new_server(struct sockaddr_qrtr *from,
|
static int ctrl_cmd_new_server(struct sockaddr_qrtr *from,
|
||||||
@ -571,16 +605,34 @@ static int ctrl_cmd_new_lookup(struct sockaddr_qrtr *from,
|
|||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
radix_tree_for_each_slot(node_slot, &nodes, &node_iter, 0) {
|
radix_tree_for_each_slot(node_slot, &nodes, &node_iter, 0) {
|
||||||
node = radix_tree_deref_slot(node_slot);
|
node = radix_tree_deref_slot(node_slot);
|
||||||
|
if (!node)
|
||||||
|
continue;
|
||||||
|
if (radix_tree_deref_retry(node)) {
|
||||||
|
node_slot = radix_tree_iter_retry(&node_iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
node_slot = radix_tree_iter_resume(node_slot, &node_iter);
|
||||||
|
|
||||||
radix_tree_for_each_slot(srv_slot, &node->servers,
|
radix_tree_for_each_slot(srv_slot, &node->servers,
|
||||||
&srv_iter, 0) {
|
&srv_iter, 0) {
|
||||||
struct qrtr_server *srv;
|
struct qrtr_server *srv;
|
||||||
|
|
||||||
srv = radix_tree_deref_slot(srv_slot);
|
srv = radix_tree_deref_slot(srv_slot);
|
||||||
|
if (!srv)
|
||||||
|
continue;
|
||||||
|
if (radix_tree_deref_retry(srv)) {
|
||||||
|
srv_slot = radix_tree_iter_retry(&srv_iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!server_match(srv, &filter))
|
if (!server_match(srv, &filter))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
srv_slot = radix_tree_iter_resume(srv_slot, &srv_iter);
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
lookup_notify(from, srv, true);
|
lookup_notify(from, srv, true);
|
||||||
|
rcu_read_lock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
Loading…
Reference in New Issue
Block a user