* Resolving use-after-free in ib_nl_send_msg @ 2020-05-07 18:34 Divya Indi 2020-05-07 18:34 ` [PATCH 1/2] IB/sa: " Divya Indi 0 siblings, 1 reply; 16+ messages in thread From: Divya Indi @ 2020-05-07 18:34 UTC (permalink / raw) To: linux-kernel, linux-rdma, Jason Gunthorpe, Kaike Wan Cc: Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford [PATCH] IB/sa: Resolving use-after-free in ib_nl_send_msg. Hi, This patch is in reply to - https://lkml.org/lkml/2020/4/24/1076 We have a use-after-free possibility in the ibacm code path - when the timer(ib_nl_request_timeout) kicks in before ib_nl_snd_msg has completed sending the query out to ibacm via netlink. The timeout handler ie ib_nl_request_timeout may result in releasing the query while ib_nl_snd_msg is still accessing query. Since the issue appears to be specific to the ibacm code path, we are trying to resolve it for the life cycle of sa_query in the ibacm code path. Please review the proposed fix ie the patch that follows. Would appreciate your thoughts and feedback on the same. Let me know if you have any questions! Thanks, Divya ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. 2020-05-07 18:34 Resolving use-after-free in ib_nl_send_msg Divya Indi @ 2020-05-07 18:34 ` Divya Indi 2020-05-07 19:06 ` Wan, Kaike ` (3 more replies) 0 siblings, 4 replies; 16+ messages in thread From: Divya Indi @ 2020-05-07 18:34 UTC (permalink / raw) To: linux-kernel, linux-rdma, Jason Gunthorpe, Kaike Wan Cc: Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford, Divya Indi This patch fixes commit - commit 3ebd2fd0d011 ("IB/sa: Put netlink request into the request list before sending")' Above commit adds the query to the request list before ib_nl_snd_msg. However, if there is a delay in sending out the request (For eg: Delay due to low memory situation) the timer to handle request timeout might kick in before the request is sent out to ibacm via netlink. ib_nl_request_timeout may release the query if it fails to send it to SA as well causing a use after free situation. Call Trace for the above race: [<ffffffffa02f43cb>] ? ib_pack+0x17b/0x240 [ib_core] [<ffffffffa032aef1>] ib_sa_path_rec_get+0x181/0x200 [ib_sa] [<ffffffffa0379db0>] rdma_resolve_route+0x3c0/0x8d0 [rdma_cm] [<ffffffffa0374450>] ? cma_bind_port+0xa0/0xa0 [rdma_cm] [<ffffffffa040f850>] ? rds_rdma_cm_event_handler_cmn+0x850/0x850 [rds_rdma] [<ffffffffa040f22c>] rds_rdma_cm_event_handler_cmn+0x22c/0x850 [rds_rdma] [<ffffffffa040f860>] rds_rdma_cm_event_handler+0x10/0x20 [rds_rdma] [<ffffffffa037778e>] addr_handler+0x9e/0x140 [rdma_cm] [<ffffffffa026cdb4>] process_req+0x134/0x190 [ib_addr] [<ffffffff810a02f9>] process_one_work+0x169/0x4a0 [<ffffffff810a0b2b>] worker_thread+0x5b/0x560 [<ffffffff810a0ad0>] ? flush_delayed_work+0x50/0x50 [<ffffffff810a68fb>] kthread+0xcb/0xf0 [<ffffffff816ec49a>] ? __schedule+0x24a/0x810 [<ffffffff816ec49a>] ? __schedule+0x24a/0x810 [<ffffffff810a6830>] ? kthread_create_on_node+0x180/0x180 [<ffffffff816f25a7>] ret_from_fork+0x47/0x90 [<ffffffff810a6830>] ? kthread_create_on_node+0x180/0x180 .... RIP [<ffffffffa03296cd>] send_mad+0x33d/0x5d0 [ib_sa] To resolve this issue, we introduce a new flag IB_SA_NL_QUERY_SENT. This flag Indicates if the request has been sent out to ibacm yet. If this flag is not set for a query and the query times out, we add it back to the list with the original delay. To handle the case where a response is received before we could set this flag, the response handler waits for the flag to be set before proceeding with the query. Signed-off-by: Divya Indi <divya.indi@oracle.com> --- drivers/infiniband/core/sa_query.c | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index 30d4c12..ffbae2f 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -59,6 +59,9 @@ #define IB_SA_LOCAL_SVC_TIMEOUT_MAX 200000 #define IB_SA_CPI_MAX_RETRY_CNT 3 #define IB_SA_CPI_RETRY_WAIT 1000 /*msecs */ + +DECLARE_WAIT_QUEUE_HEAD(wait_queue); + static int sa_local_svc_timeout_ms = IB_SA_LOCAL_SVC_TIMEOUT_DEFAULT; struct ib_sa_sm_ah { @@ -122,6 +125,7 @@ struct ib_sa_query { #define IB_SA_ENABLE_LOCAL_SERVICE 0x00000001 #define IB_SA_CANCEL 0x00000002 #define IB_SA_QUERY_OPA 0x00000004 +#define IB_SA_NL_QUERY_SENT 0x00000008 struct ib_sa_service_query { void (*callback)(int, struct ib_sa_service_rec *, void *); @@ -746,6 +750,11 @@ static inline int ib_sa_query_cancelled(struct ib_sa_query *query) return (query->flags & IB_SA_CANCEL); } +static inline int ib_sa_nl_query_sent(struct ib_sa_query *query) +{ + return (query->flags & IB_SA_NL_QUERY_SENT); +} + static void ib_nl_set_path_rec_attrs(struct sk_buff *skb, struct ib_sa_query *query) { @@ -889,6 +898,15 @@ static int ib_nl_make_request(struct ib_sa_query *query, gfp_t gfp_mask) spin_lock_irqsave(&ib_nl_request_lock, flags); list_del(&query->list); spin_unlock_irqrestore(&ib_nl_request_lock, flags); + } else { + query->flags |= IB_SA_NL_QUERY_SENT; + + /* + * If response is received before this flag was set + * someone is waiting to process the response and release the + * query. + */ + wake_up(&wait_queue); } return ret; @@ -994,6 +1012,21 @@ static void ib_nl_request_timeout(struct work_struct *work) } list_del(&query->list); + + /* + * If IB_SA_NL_QUERY_SENT is not set, this query has not been + * sent to ibacm yet. Reset the timer. + */ + if (!ib_sa_nl_query_sent(query)) { + delay = msecs_to_jiffies(sa_local_svc_timeout_ms); + query->timeout = delay + jiffies; + list_add_tail(&query->list, &ib_nl_request_list); + /* Start the timeout if this is the only request */ + if (ib_nl_request_list.next == &query->list) + queue_delayed_work(ib_nl_wq, &ib_nl_timed_work, + delay); + break; + } ib_sa_disable_local_svc(query); /* Hold the lock to protect against query cancellation */ if (ib_sa_query_cancelled(query)) @@ -1123,6 +1156,18 @@ int ib_nl_handle_resolve_resp(struct sk_buff *skb, send_buf = query->mad_buf; + /* + * Make sure the IB_SA_NL_QUERY_SENT flag is set before + * processing this query. If flag is not set, query can be accessed in + * another context while setting the flag and processing the query will + * eventually release it causing a possible use-after-free. + */ + if (unlikely(!ib_sa_nl_query_sent(query))) { + spin_unlock_irqrestore(&ib_nl_request_lock, flags); + wait_event(wait_queue, ib_sa_nl_query_sent(query)); + spin_lock_irqsave(&ib_nl_request_lock, flags); + } + if (!ib_nl_is_good_resolve_resp(nlh)) { /* if the result is a failure, send out the packet via IB */ ib_sa_disable_local_svc(query); -- 1.8.3.1 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* RE: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. 2020-05-07 18:34 ` [PATCH 1/2] IB/sa: " Divya Indi @ 2020-05-07 19:06 ` Wan, Kaike 2020-05-07 19:36 ` Mark Bloch ` (2 subsequent siblings) 3 siblings, 0 replies; 16+ messages in thread From: Wan, Kaike @ 2020-05-07 19:06 UTC (permalink / raw) To: Divya Indi, linux-kernel, linux-rdma, Jason Gunthorpe Cc: Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford > This patch fixes commit - > commit 3ebd2fd0d011 ("IB/sa: Put netlink request into the request list > before sending")' > > Above commit adds the query to the request list before ib_nl_snd_msg. > > However, if there is a delay in sending out the request (For > eg: Delay due to low memory situation) the timer to handle request timeout > might kick in before the request is sent out to ibacm via netlink. > ib_nl_request_timeout may release the query if it fails to send it to SA as > well causing a use after free situation. > > Call Trace for the above race: > > [<ffffffffa02f43cb>] ? ib_pack+0x17b/0x240 [ib_core] [<ffffffffa032aef1>] > ib_sa_path_rec_get+0x181/0x200 [ib_sa] [<ffffffffa0379db0>] > rdma_resolve_route+0x3c0/0x8d0 [rdma_cm] [<ffffffffa0374450>] ? > cma_bind_port+0xa0/0xa0 [rdma_cm] [<ffffffffa040f850>] ? > rds_rdma_cm_event_handler_cmn+0x850/0x850 > [rds_rdma] > [<ffffffffa040f22c>] rds_rdma_cm_event_handler_cmn+0x22c/0x850 > [rds_rdma] > [<ffffffffa040f860>] rds_rdma_cm_event_handler+0x10/0x20 [rds_rdma] > [<ffffffffa037778e>] addr_handler+0x9e/0x140 [rdma_cm] > [<ffffffffa026cdb4>] process_req+0x134/0x190 [ib_addr] [<ffffffff810a02f9>] > process_one_work+0x169/0x4a0 [<ffffffff810a0b2b>] > worker_thread+0x5b/0x560 [<ffffffff810a0ad0>] ? > flush_delayed_work+0x50/0x50 [<ffffffff810a68fb>] kthread+0xcb/0xf0 > [<ffffffff816ec49a>] ? __schedule+0x24a/0x810 [<ffffffff816ec49a>] ? > __schedule+0x24a/0x810 [<ffffffff810a6830>] ? > kthread_create_on_node+0x180/0x180 > [<ffffffff816f25a7>] ret_from_fork+0x47/0x90 [<ffffffff810a6830>] ? > kthread_create_on_node+0x180/0x180 > .... > RIP [<ffffffffa03296cd>] send_mad+0x33d/0x5d0 [ib_sa] > > To resolve this issue, we introduce a new flag IB_SA_NL_QUERY_SENT. > This flag Indicates if the request has been sent out to ibacm yet. > > If this flag is not set for a query and the query times out, we add it back to > the list with the original delay. > > To handle the case where a response is received before we could set this flag, > the response handler waits for the flag to be set before proceeding with the > query. > > Signed-off-by: Divya Indi <divya.indi@oracle.com> > --- > drivers/infiniband/core/sa_query.c | 45 > ++++++++++++++++++++++++++++++++++++++ > 1 file changed, 45 insertions(+) > > diff --git a/drivers/infiniband/core/sa_query.c > b/drivers/infiniband/core/sa_query.c > index 30d4c12..ffbae2f 100644 > --- a/drivers/infiniband/core/sa_query.c > +++ b/drivers/infiniband/core/sa_query.c > @@ -59,6 +59,9 @@ > #define IB_SA_LOCAL_SVC_TIMEOUT_MAX 200000 > #define IB_SA_CPI_MAX_RETRY_CNT 3 > #define IB_SA_CPI_RETRY_WAIT 1000 /*msecs */ > + > +DECLARE_WAIT_QUEUE_HEAD(wait_queue); > + > static int sa_local_svc_timeout_ms = > IB_SA_LOCAL_SVC_TIMEOUT_DEFAULT; > > struct ib_sa_sm_ah { > @@ -122,6 +125,7 @@ struct ib_sa_query { > #define IB_SA_ENABLE_LOCAL_SERVICE 0x00000001 > #define IB_SA_CANCEL 0x00000002 > #define IB_SA_QUERY_OPA 0x00000004 > +#define IB_SA_NL_QUERY_SENT 0x00000008 > > struct ib_sa_service_query { > void (*callback)(int, struct ib_sa_service_rec *, void *); @@ -746,6 > +750,11 @@ static inline int ib_sa_query_cancelled(struct ib_sa_query > *query) > return (query->flags & IB_SA_CANCEL); > } > > +static inline int ib_sa_nl_query_sent(struct ib_sa_query *query) { > + return (query->flags & IB_SA_NL_QUERY_SENT); } > + > static void ib_nl_set_path_rec_attrs(struct sk_buff *skb, > struct ib_sa_query *query) > { > @@ -889,6 +898,15 @@ static int ib_nl_make_request(struct ib_sa_query > *query, gfp_t gfp_mask) > spin_lock_irqsave(&ib_nl_request_lock, flags); > list_del(&query->list); > spin_unlock_irqrestore(&ib_nl_request_lock, flags); > + } else { > + query->flags |= IB_SA_NL_QUERY_SENT; > + > + /* > + * If response is received before this flag was set > + * someone is waiting to process the response and release > the > + * query. > + */ > + wake_up(&wait_queue); > } > > return ret; > @@ -994,6 +1012,21 @@ static void ib_nl_request_timeout(struct > work_struct *work) > } > > list_del(&query->list); > + > + /* > + * If IB_SA_NL_QUERY_SENT is not set, this query has not > been > + * sent to ibacm yet. Reset the timer. > + */ > + if (!ib_sa_nl_query_sent(query)) { > + delay = msecs_to_jiffies(sa_local_svc_timeout_ms); > + query->timeout = delay + jiffies; > + list_add_tail(&query->list, &ib_nl_request_list); > + /* Start the timeout if this is the only request */ > + if (ib_nl_request_list.next == &query->list) > + queue_delayed_work(ib_nl_wq, > &ib_nl_timed_work, > + delay); > + break; > + } > ib_sa_disable_local_svc(query); > /* Hold the lock to protect against query cancellation */ > if (ib_sa_query_cancelled(query)) > @@ -1123,6 +1156,18 @@ int ib_nl_handle_resolve_resp(struct sk_buff > *skb, > > send_buf = query->mad_buf; > > + /* > + * Make sure the IB_SA_NL_QUERY_SENT flag is set before > + * processing this query. If flag is not set, query can be accessed in > + * another context while setting the flag and processing the query > will > + * eventually release it causing a possible use-after-free. > + */ > + if (unlikely(!ib_sa_nl_query_sent(query))) { > + spin_unlock_irqrestore(&ib_nl_request_lock, flags); > + wait_event(wait_queue, ib_sa_nl_query_sent(query)); > + spin_lock_irqsave(&ib_nl_request_lock, flags); > + } > + > if (!ib_nl_is_good_resolve_resp(nlh)) { > /* if the result is a failure, send out the packet via IB */ > ib_sa_disable_local_svc(query); > -- > 1.8.3.1 Reviewd-by: Kaike Wan <Kaike.wan@intel.com> ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. 2020-05-07 18:34 ` [PATCH 1/2] IB/sa: " Divya Indi 2020-05-07 19:06 ` Wan, Kaike @ 2020-05-07 19:36 ` Mark Bloch 2020-05-07 20:16 ` Wan, Kaike 2020-05-08 0:08 ` Jason Gunthorpe [not found] ` <20200508110302.17872-1-hdanton@sina.com> 3 siblings, 1 reply; 16+ messages in thread From: Mark Bloch @ 2020-05-07 19:36 UTC (permalink / raw) To: Divya Indi, linux-kernel, linux-rdma, Jason Gunthorpe, Kaike Wan Cc: Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford On 5/7/2020 11:34, Divya Indi wrote: > This patch fixes commit - > commit 3ebd2fd0d011 ("IB/sa: Put netlink request into the request list before sending")' > > Above commit adds the query to the request list before ib_nl_snd_msg. > > However, if there is a delay in sending out the request (For > eg: Delay due to low memory situation) the timer to handle request timeout > might kick in before the request is sent out to ibacm via netlink. > ib_nl_request_timeout may release the query if it fails to send it to SA > as well causing a use after free situation. > > Call Trace for the above race: > > [<ffffffffa02f43cb>] ? ib_pack+0x17b/0x240 [ib_core] > [<ffffffffa032aef1>] ib_sa_path_rec_get+0x181/0x200 [ib_sa] > [<ffffffffa0379db0>] rdma_resolve_route+0x3c0/0x8d0 [rdma_cm] > [<ffffffffa0374450>] ? cma_bind_port+0xa0/0xa0 [rdma_cm] > [<ffffffffa040f850>] ? rds_rdma_cm_event_handler_cmn+0x850/0x850 > [rds_rdma] > [<ffffffffa040f22c>] rds_rdma_cm_event_handler_cmn+0x22c/0x850 > [rds_rdma] > [<ffffffffa040f860>] rds_rdma_cm_event_handler+0x10/0x20 [rds_rdma] > [<ffffffffa037778e>] addr_handler+0x9e/0x140 [rdma_cm] > [<ffffffffa026cdb4>] process_req+0x134/0x190 [ib_addr] > [<ffffffff810a02f9>] process_one_work+0x169/0x4a0 > [<ffffffff810a0b2b>] worker_thread+0x5b/0x560 > [<ffffffff810a0ad0>] ? flush_delayed_work+0x50/0x50 > [<ffffffff810a68fb>] kthread+0xcb/0xf0 > [<ffffffff816ec49a>] ? __schedule+0x24a/0x810 > [<ffffffff816ec49a>] ? __schedule+0x24a/0x810 > [<ffffffff810a6830>] ? kthread_create_on_node+0x180/0x180 > [<ffffffff816f25a7>] ret_from_fork+0x47/0x90 > [<ffffffff810a6830>] ? kthread_create_on_node+0x180/0x180 > .... > RIP [<ffffffffa03296cd>] send_mad+0x33d/0x5d0 [ib_sa] > > To resolve this issue, we introduce a new flag IB_SA_NL_QUERY_SENT. > This flag Indicates if the request has been sent out to ibacm yet. > > If this flag is not set for a query and the query times out, we add it > back to the list with the original delay. > > To handle the case where a response is received before we could set this > flag, the response handler waits for the flag to be > set before proceeding with the query. > > Signed-off-by: Divya Indi <divya.indi@oracle.com> > --- > drivers/infiniband/core/sa_query.c | 45 ++++++++++++++++++++++++++++++++++++++ > 1 file changed, 45 insertions(+) > > diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c > index 30d4c12..ffbae2f 100644 > --- a/drivers/infiniband/core/sa_query.c > +++ b/drivers/infiniband/core/sa_query.c > @@ -59,6 +59,9 @@ > #define IB_SA_LOCAL_SVC_TIMEOUT_MAX 200000 > #define IB_SA_CPI_MAX_RETRY_CNT 3 > #define IB_SA_CPI_RETRY_WAIT 1000 /*msecs */ > + > +DECLARE_WAIT_QUEUE_HEAD(wait_queue); > + > static int sa_local_svc_timeout_ms = IB_SA_LOCAL_SVC_TIMEOUT_DEFAULT; > > struct ib_sa_sm_ah { > @@ -122,6 +125,7 @@ struct ib_sa_query { > #define IB_SA_ENABLE_LOCAL_SERVICE 0x00000001 > #define IB_SA_CANCEL 0x00000002 > #define IB_SA_QUERY_OPA 0x00000004 > +#define IB_SA_NL_QUERY_SENT 0x00000008 > > struct ib_sa_service_query { > void (*callback)(int, struct ib_sa_service_rec *, void *); > @@ -746,6 +750,11 @@ static inline int ib_sa_query_cancelled(struct ib_sa_query *query) > return (query->flags & IB_SA_CANCEL); > } > > +static inline int ib_sa_nl_query_sent(struct ib_sa_query *query) > +{ > + return (query->flags & IB_SA_NL_QUERY_SENT); > +} > + > static void ib_nl_set_path_rec_attrs(struct sk_buff *skb, > struct ib_sa_query *query) > { > @@ -889,6 +898,15 @@ static int ib_nl_make_request(struct ib_sa_query *query, gfp_t gfp_mask) > spin_lock_irqsave(&ib_nl_request_lock, flags); > list_del(&query->list); > spin_unlock_irqrestore(&ib_nl_request_lock, flags); > + } else { > + query->flags |= IB_SA_NL_QUERY_SENT; > + > + /* > + * If response is received before this flag was set > + * someone is waiting to process the response and release the > + * query. > + */ > + wake_up(&wait_queue); > } > > return ret; > @@ -994,6 +1012,21 @@ static void ib_nl_request_timeout(struct work_struct *work) > } > > list_del(&query->list); > + > + /* > + * If IB_SA_NL_QUERY_SENT is not set, this query has not been > + * sent to ibacm yet. Reset the timer. > + */ > + if (!ib_sa_nl_query_sent(query)) { > + delay = msecs_to_jiffies(sa_local_svc_timeout_ms); > + query->timeout = delay + jiffies; > + list_add_tail(&query->list, &ib_nl_request_list); > + /* Start the timeout if this is the only request */ > + if (ib_nl_request_list.next == &query->list) > + queue_delayed_work(ib_nl_wq, &ib_nl_timed_work, > + delay); > + break; > + } > ib_sa_disable_local_svc(query); > /* Hold the lock to protect against query cancellation */ > if (ib_sa_query_cancelled(query)) > @@ -1123,6 +1156,18 @@ int ib_nl_handle_resolve_resp(struct sk_buff *skb, > > send_buf = query->mad_buf; > > + /* > + * Make sure the IB_SA_NL_QUERY_SENT flag is set before > + * processing this query. If flag is not set, query can be accessed in > + * another context while setting the flag and processing the query will > + * eventually release it causing a possible use-after-free. > + */ > + if (unlikely(!ib_sa_nl_query_sent(query))) { Can't there be a race here where you check the flag (it isn't set) and before you call wait_event() the flag is set and wake_up() is called which means you will wait here forever? or worse, a timeout will happen the query will be freed and them some other query will call wake_up() and we have again a use-after-free. > + spin_unlock_irqrestore(&ib_nl_request_lock, flags); > + wait_event(wait_queue, ib_sa_nl_query_sent(query)); What if there are two queries sent to userspace, shouldn't you check and make sure you got woken up by the right one setting the flag? Other than that, the entire solution makes it very complicated to reason with (flags set/checked without locking etc) maybe we should just revert and fix it the other way? Mark > + spin_lock_irqsave(&ib_nl_request_lock, flags); > + } > + > if (!ib_nl_is_good_resolve_resp(nlh)) { > /* if the result is a failure, send out the packet via IB */ > ib_sa_disable_local_svc(query); > ^ permalink raw reply [flat|nested] 16+ messages in thread
* RE: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. 2020-05-07 19:36 ` Mark Bloch @ 2020-05-07 20:16 ` Wan, Kaike 2020-05-07 21:40 ` Mark Bloch 2020-05-11 21:06 ` Divya Indi 0 siblings, 2 replies; 16+ messages in thread From: Wan, Kaike @ 2020-05-07 20:16 UTC (permalink / raw) To: Mark Bloch, Divya Indi, linux-kernel, linux-rdma, Jason Gunthorpe Cc: Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford > -----Original Message----- > From: Mark Bloch <markb@mellanox.com> > Sent: Thursday, May 07, 2020 3:36 PM > To: Divya Indi <divya.indi@oracle.com>; linux-kernel@vger.kernel.org; linux- > rdma@vger.kernel.org; Jason Gunthorpe <jgg@ziepe.ca>; Wan, Kaike > <kaike.wan@intel.com> > Cc: Gerd Rausch <gerd.rausch@oracle.com>; Håkon Bugge > <haakon.bugge@oracle.com>; Srinivas Eeda <srinivas.eeda@oracle.com>; > Rama Nichanamatlu <rama.nichanamatlu@oracle.com>; Doug Ledford > <dledford@redhat.com> > Subject: Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. > > > > @@ -1123,6 +1156,18 @@ int ib_nl_handle_resolve_resp(struct sk_buff > > *skb, > > > > send_buf = query->mad_buf; > > > > + /* > > + * Make sure the IB_SA_NL_QUERY_SENT flag is set before > > + * processing this query. If flag is not set, query can be accessed in > > + * another context while setting the flag and processing the query > will > > + * eventually release it causing a possible use-after-free. > > + */ > > + if (unlikely(!ib_sa_nl_query_sent(query))) { > > Can't there be a race here where you check the flag (it isn't set) and before > you call wait_event() the flag is set and wake_up() is called which means you > will wait here forever? Should wait_event() catch that? That is, if the flag is not set, wait_event() will sleep until the flag is set. or worse, a timeout will happen the query will be > freed and them some other query will call wake_up() and we have again a > use-after-free. The request has been deleted from the request list by this time and therefore the timeout should have no impact here. > > > + spin_unlock_irqrestore(&ib_nl_request_lock, flags); > > + wait_event(wait_queue, ib_sa_nl_query_sent(query)); > > What if there are two queries sent to userspace, shouldn't you check and > make sure you got woken up by the right one setting the flag? The wait_event() is conditioned on the specific query (ib_sa_nl_query_sent(query)), not on the wait_queue itself. > > Other than that, the entire solution makes it very complicated to reason with > (flags set/checked without locking etc) maybe we should just revert and fix it > the other way? The flag could certainly be set under the lock, which may reduce complications. Kaike ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. 2020-05-07 20:16 ` Wan, Kaike @ 2020-05-07 21:40 ` Mark Bloch 2020-05-11 21:10 ` Divya Indi 2020-05-11 21:06 ` Divya Indi 1 sibling, 1 reply; 16+ messages in thread From: Mark Bloch @ 2020-05-07 21:40 UTC (permalink / raw) To: Wan, Kaike, Divya Indi, linux-kernel, linux-rdma, Jason Gunthorpe Cc: Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford On 5/7/2020 13:16, Wan, Kaike wrote: > > >> -----Original Message----- >> From: Mark Bloch <markb@mellanox.com> >> Sent: Thursday, May 07, 2020 3:36 PM >> To: Divya Indi <divya.indi@oracle.com>; linux-kernel@vger.kernel.org; linux- >> rdma@vger.kernel.org; Jason Gunthorpe <jgg@ziepe.ca>; Wan, Kaike >> <kaike.wan@intel.com> >> Cc: Gerd Rausch <gerd.rausch@oracle.com>; Håkon Bugge >> <haakon.bugge@oracle.com>; Srinivas Eeda <srinivas.eeda@oracle.com>; >> Rama Nichanamatlu <rama.nichanamatlu@oracle.com>; Doug Ledford >> <dledford@redhat.com> >> Subject: Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. >> >> >>> @@ -1123,6 +1156,18 @@ int ib_nl_handle_resolve_resp(struct sk_buff >>> *skb, >>> >>> send_buf = query->mad_buf; >>> >>> + /* >>> + * Make sure the IB_SA_NL_QUERY_SENT flag is set before >>> + * processing this query. If flag is not set, query can be accessed in >>> + * another context while setting the flag and processing the query >> will >>> + * eventually release it causing a possible use-after-free. >>> + */ >>> + if (unlikely(!ib_sa_nl_query_sent(query))) { >> >> Can't there be a race here where you check the flag (it isn't set) and before >> you call wait_event() the flag is set and wake_up() is called which means you >> will wait here forever? > > Should wait_event() catch that? That is, if the flag is not set, wait_event() will sleep until the flag is set. > > or worse, a timeout will happen the query will be >> freed and them some other query will call wake_up() and we have again a >> use-after-free. > > The request has been deleted from the request list by this time and therefore the timeout should have no impact here. > > >> >>> + spin_unlock_irqrestore(&ib_nl_request_lock, flags); >>> + wait_event(wait_queue, ib_sa_nl_query_sent(query)); >> >> What if there are two queries sent to userspace, shouldn't you check and >> make sure you got woken up by the right one setting the flag? > > The wait_event() is conditioned on the specific query (ib_sa_nl_query_sent(query)), not on the wait_queue itself. Right, missed that this macro is expends into some inline code. Looking at the code a little more, I think this also fixes another issue. Lets say ib_nl_send_msg() returns an error but before we do the list_del() in ib_nl_make_request() there is also a timeout, so in ib_nl_request_timeout() we will do list_del() and then another one list_del() will be done in ib_nl_make_request(). > >> >> Other than that, the entire solution makes it very complicated to reason with >> (flags set/checked without locking etc) maybe we should just revert and fix it >> the other way? > > The flag could certainly be set under the lock, which may reduce complications. Anything that can help here with this. For me in ib_nl_make_request() the comment should also explain that not only ib_nl_handle_resolve_resp() is waiting for the flag to be set but also ib_nl_request_timeout() and that a timeout can't happen before the flag is set. Mark > > Kaike > ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. 2020-05-07 21:40 ` Mark Bloch @ 2020-05-11 21:10 ` Divya Indi 0 siblings, 0 replies; 16+ messages in thread From: Divya Indi @ 2020-05-11 21:10 UTC (permalink / raw) To: Mark Bloch, Wan, Kaike, linux-kernel, linux-rdma, Jason Gunthorpe Cc: Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford Hi Mark, Please find my comments inline - On 5/7/20 2:40 PM, Mark Bloch wrote: > > On 5/7/2020 13:16, Wan, Kaike wrote: >> >>> -----Original Message----- >>> From: Mark Bloch <markb@mellanox.com> >>> Sent: Thursday, May 07, 2020 3:36 PM >>> To: Divya Indi <divya.indi@oracle.com>; linux-kernel@vger.kernel.org; linux- >>> rdma@vger.kernel.org; Jason Gunthorpe <jgg@ziepe.ca>; Wan, Kaike >>> <kaike.wan@intel.com> >>> Cc: Gerd Rausch <gerd.rausch@oracle.com>; Håkon Bugge >>> <haakon.bugge@oracle.com>; Srinivas Eeda <srinivas.eeda@oracle.com>; >>> Rama Nichanamatlu <rama.nichanamatlu@oracle.com>; Doug Ledford >>> <dledford@redhat.com> >>> Subject: Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. >>> >>> >>>> @@ -1123,6 +1156,18 @@ int ib_nl_handle_resolve_resp(struct sk_buff >>>> *skb, >>>> >>>> send_buf = query->mad_buf; >>>> >>>> + /* >>>> + * Make sure the IB_SA_NL_QUERY_SENT flag is set before >>>> + * processing this query. If flag is not set, query can be accessed in >>>> + * another context while setting the flag and processing the query >>> will >>>> + * eventually release it causing a possible use-after-free. >>>> + */ >>>> + if (unlikely(!ib_sa_nl_query_sent(query))) { >>> Can't there be a race here where you check the flag (it isn't set) and before >>> you call wait_event() the flag is set and wake_up() is called which means you >>> will wait here forever? >> Should wait_event() catch that? That is, if the flag is not set, wait_event() will sleep until the flag is set. >> >> or worse, a timeout will happen the query will be >>> freed and them some other query will call wake_up() and we have again a >>> use-after-free. >> The request has been deleted from the request list by this time and therefore the timeout should have no impact here. >> >> >>>> + spin_unlock_irqrestore(&ib_nl_request_lock, flags); >>>> + wait_event(wait_queue, ib_sa_nl_query_sent(query)); >>> What if there are two queries sent to userspace, shouldn't you check and >>> make sure you got woken up by the right one setting the flag? >> The wait_event() is conditioned on the specific query (ib_sa_nl_query_sent(query)), not on the wait_queue itself. > Right, missed that this macro is expends into some inline code. > > Looking at the code a little more, I think this also fixes another issue. > Lets say ib_nl_send_msg() returns an error but before we do the list_del() in > ib_nl_make_request() there is also a timeout, so in ib_nl_request_timeout() > we will do list_del() and then another one list_del() will be done in ib_nl_make_request(). > >>> Other than that, the entire solution makes it very complicated to reason with >>> (flags set/checked without locking etc) maybe we should just revert and fix it >>> the other way? >> The flag could certainly be set under the lock, which may reduce complications. > Anything that can help here with this. > For me in ib_nl_make_request() the comment should also explain that not only ib_nl_handle_resolve_resp() > is waiting for the flag to be set but also ib_nl_request_timeout() and that a timeout can't happen > before the flag is set. ib_nl_request_timeout() would re-queue the query to the request list if the flag is not set. However, makes sense! Noted, il add the comment in ib_nl_make_request to make things more clear. Thanks, Divya > Mark > >> Kaike >> i ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. 2020-05-07 20:16 ` Wan, Kaike 2020-05-07 21:40 ` Mark Bloch @ 2020-05-11 21:06 ` Divya Indi 2020-05-12 11:15 ` Wan, Kaike 1 sibling, 1 reply; 16+ messages in thread From: Divya Indi @ 2020-05-11 21:06 UTC (permalink / raw) To: Wan, Kaike, Mark Bloch, linux-kernel, linux-rdma, Jason Gunthorpe Cc: Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford Hi, Thanks for taking the time to review. Please find my comments inline - On 5/7/20 1:16 PM, Wan, Kaike wrote: > >> -----Original Message----- >> From: Mark Bloch <markb@mellanox.com> >> Sent: Thursday, May 07, 2020 3:36 PM >> To: Divya Indi <divya.indi@oracle.com>; linux-kernel@vger.kernel.org; linux- >> rdma@vger.kernel.org; Jason Gunthorpe <jgg@ziepe.ca>; Wan, Kaike >> <kaike.wan@intel.com> >> Cc: Gerd Rausch <gerd.rausch@oracle.com>; Håkon Bugge >> <haakon.bugge@oracle.com>; Srinivas Eeda <srinivas.eeda@oracle.com>; >> Rama Nichanamatlu <rama.nichanamatlu@oracle.com>; Doug Ledford >> <dledford@redhat.com> >> Subject: Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. >> >> >>> @@ -1123,6 +1156,18 @@ int ib_nl_handle_resolve_resp(struct sk_buff >>> *skb, >>> >>> send_buf = query->mad_buf; >>> >>> + /* >>> + * Make sure the IB_SA_NL_QUERY_SENT flag is set before >>> + * processing this query. If flag is not set, query can be accessed in >>> + * another context while setting the flag and processing the query >> will >>> + * eventually release it causing a possible use-after-free. >>> + */ >>> + if (unlikely(!ib_sa_nl_query_sent(query))) { >> Can't there be a race here where you check the flag (it isn't set) and before >> you call wait_event() the flag is set and wake_up() is called which means you >> will wait here forever? > Should wait_event() catch that? That is, if the flag is not set, wait_event() will sleep until the flag is set. > > or worse, a timeout will happen the query will be >> freed and them some other query will call wake_up() and we have again a >> use-after-free. > The request has been deleted from the request list by this time and therefore the timeout should have no impact here. > > >>> + spin_unlock_irqrestore(&ib_nl_request_lock, flags); >>> + wait_event(wait_queue, ib_sa_nl_query_sent(query)); >> What if there are two queries sent to userspace, shouldn't you check and >> make sure you got woken up by the right one setting the flag? > The wait_event() is conditioned on the specific query (ib_sa_nl_query_sent(query)), not on the wait_queue itself. > >> Other than that, the entire solution makes it very complicated to reason with >> (flags set/checked without locking etc) maybe we should just revert and fix it >> the other way? > The flag could certainly be set under the lock, which may reduce complications. We could use a lock or use atomic operations. However, the reason for not doing so was that we have 1 writer and multiple readers of the IB_SA_NL_QUERY_SENT flag and the readers wouldnt mind reading a stale value. Would it still be better to have a lock for this flag? Thanks, Divya > > Kaike > ^ permalink raw reply [flat|nested] 16+ messages in thread
* RE: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. 2020-05-11 21:06 ` Divya Indi @ 2020-05-12 11:15 ` Wan, Kaike 0 siblings, 0 replies; 16+ messages in thread From: Wan, Kaike @ 2020-05-12 11:15 UTC (permalink / raw) To: Divya Indi, Mark Bloch, linux-kernel, linux-rdma, Jason Gunthorpe Cc: Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford > -----Original Message----- > From: Divya Indi <divya.indi@oracle.com> > Sent: Monday, May 11, 2020 5:06 PM > To: Wan, Kaike <kaike.wan@intel.com>; Mark Bloch > <markb@mellanox.com>; linux-kernel@vger.kernel.org; linux- > rdma@vger.kernel.org; Jason Gunthorpe <jgg@ziepe.ca> > Cc: Gerd Rausch <gerd.rausch@oracle.com>; Håkon Bugge > <haakon.bugge@oracle.com>; Srinivas Eeda <srinivas.eeda@oracle.com>; > Rama Nichanamatlu <rama.nichanamatlu@oracle.com>; Doug Ledford > <dledford@redhat.com> > Subject: Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. > > Hi, > > Thanks for taking the time to review. Please find my comments inline - > > On 5/7/20 1:16 PM, Wan, Kaike wrote: > > > >> -----Original Message----- > >> From: Mark Bloch <markb@mellanox.com> > >> Sent: Thursday, May 07, 2020 3:36 PM > >> To: Divya Indi <divya.indi@oracle.com>; linux-kernel@vger.kernel.org; > >> linux- rdma@vger.kernel.org; Jason Gunthorpe <jgg@ziepe.ca>; Wan, > >> Kaike <kaike.wan@intel.com> > >> Cc: Gerd Rausch <gerd.rausch@oracle.com>; Håkon Bugge > >> <haakon.bugge@oracle.com>; Srinivas Eeda <srinivas.eeda@oracle.com>; > >> Rama Nichanamatlu <rama.nichanamatlu@oracle.com>; Doug Ledford > >> <dledford@redhat.com> > >> Subject: Re: [PATCH 1/2] IB/sa: Resolving use-after-free in > ib_nl_send_msg. > >> > >> > >>> @@ -1123,6 +1156,18 @@ int ib_nl_handle_resolve_resp(struct sk_buff > >>> *skb, > >>> > >>> send_buf = query->mad_buf; > >>> > >>> + /* > >>> + * Make sure the IB_SA_NL_QUERY_SENT flag is set before > >>> + * processing this query. If flag is not set, query can be accessed in > >>> + * another context while setting the flag and processing the query > >> will > >>> + * eventually release it causing a possible use-after-free. > >>> + */ > >>> + if (unlikely(!ib_sa_nl_query_sent(query))) { > >> Can't there be a race here where you check the flag (it isn't set) > >> and before you call wait_event() the flag is set and wake_up() is > >> called which means you will wait here forever? > > Should wait_event() catch that? That is, if the flag is not set, wait_event() > will sleep until the flag is set. > > > > or worse, a timeout will happen the query will be > >> freed and them some other query will call wake_up() and we have again > >> a use-after-free. > > The request has been deleted from the request list by this time and > therefore the timeout should have no impact here. > > > > > >>> + spin_unlock_irqrestore(&ib_nl_request_lock, flags); > >>> + wait_event(wait_queue, ib_sa_nl_query_sent(query)); > >> What if there are two queries sent to userspace, shouldn't you check > >> and make sure you got woken up by the right one setting the flag? > > The wait_event() is conditioned on the specific query > (ib_sa_nl_query_sent(query)), not on the wait_queue itself. > > > >> Other than that, the entire solution makes it very complicated to > >> reason with (flags set/checked without locking etc) maybe we should > >> just revert and fix it the other way? > > The flag could certainly be set under the lock, which may reduce > complications. > > We could use a lock or use atomic operations. However, the reason for not > doing so was that we have 1 writer and multiple readers of the > IB_SA_NL_QUERY_SENT flag and the readers wouldnt mind reading a stale > value. > > Would it still be better to have a lock for this flag? > Yes. It will make the code less complicated and easier to maintain. Kaike ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. 2020-05-07 18:34 ` [PATCH 1/2] IB/sa: " Divya Indi 2020-05-07 19:06 ` Wan, Kaike 2020-05-07 19:36 ` Mark Bloch @ 2020-05-08 0:08 ` Jason Gunthorpe 2020-05-11 21:26 ` Divya Indi [not found] ` <20200508110302.17872-1-hdanton@sina.com> 3 siblings, 1 reply; 16+ messages in thread From: Jason Gunthorpe @ 2020-05-08 0:08 UTC (permalink / raw) To: Divya Indi Cc: linux-kernel, linux-rdma, Kaike Wan, Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford On Thu, May 07, 2020 at 11:34:47AM -0700, Divya Indi wrote: > This patch fixes commit - > commit 3ebd2fd0d011 ("IB/sa: Put netlink request into the request list before sending")' > > Above commit adds the query to the request list before ib_nl_snd_msg. > > However, if there is a delay in sending out the request (For > eg: Delay due to low memory situation) the timer to handle request timeout > might kick in before the request is sent out to ibacm via netlink. > ib_nl_request_timeout may release the query if it fails to send it to SA > as well causing a use after free situation. > > Call Trace for the above race: > > [<ffffffffa02f43cb>] ? ib_pack+0x17b/0x240 [ib_core] > [<ffffffffa032aef1>] ib_sa_path_rec_get+0x181/0x200 [ib_sa] > [<ffffffffa0379db0>] rdma_resolve_route+0x3c0/0x8d0 [rdma_cm] > [<ffffffffa0374450>] ? cma_bind_port+0xa0/0xa0 [rdma_cm] > [<ffffffffa040f850>] ? rds_rdma_cm_event_handler_cmn+0x850/0x850 > [rds_rdma] > [<ffffffffa040f22c>] rds_rdma_cm_event_handler_cmn+0x22c/0x850 > [rds_rdma] > [<ffffffffa040f860>] rds_rdma_cm_event_handler+0x10/0x20 [rds_rdma] > [<ffffffffa037778e>] addr_handler+0x9e/0x140 [rdma_cm] > [<ffffffffa026cdb4>] process_req+0x134/0x190 [ib_addr] > [<ffffffff810a02f9>] process_one_work+0x169/0x4a0 > [<ffffffff810a0b2b>] worker_thread+0x5b/0x560 > [<ffffffff810a0ad0>] ? flush_delayed_work+0x50/0x50 > [<ffffffff810a68fb>] kthread+0xcb/0xf0 > [<ffffffff816ec49a>] ? __schedule+0x24a/0x810 > [<ffffffff816ec49a>] ? __schedule+0x24a/0x810 > [<ffffffff810a6830>] ? kthread_create_on_node+0x180/0x180 > [<ffffffff816f25a7>] ret_from_fork+0x47/0x90 > [<ffffffff810a6830>] ? kthread_create_on_node+0x180/0x180 > .... > RIP [<ffffffffa03296cd>] send_mad+0x33d/0x5d0 [ib_sa] > > To resolve this issue, we introduce a new flag IB_SA_NL_QUERY_SENT. > This flag Indicates if the request has been sent out to ibacm yet. > > If this flag is not set for a query and the query times out, we add it > back to the list with the original delay. > > To handle the case where a response is received before we could set this > flag, the response handler waits for the flag to be > set before proceeding with the query. > > Signed-off-by: Divya Indi <divya.indi@oracle.com> > drivers/infiniband/core/sa_query.c | 45 ++++++++++++++++++++++++++++++++++++++ > 1 file changed, 45 insertions(+) > > diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c > index 30d4c12..ffbae2f 100644 > +++ b/drivers/infiniband/core/sa_query.c > @@ -59,6 +59,9 @@ > #define IB_SA_LOCAL_SVC_TIMEOUT_MAX 200000 > #define IB_SA_CPI_MAX_RETRY_CNT 3 > #define IB_SA_CPI_RETRY_WAIT 1000 /*msecs */ > + > +DECLARE_WAIT_QUEUE_HEAD(wait_queue); > + > static int sa_local_svc_timeout_ms = IB_SA_LOCAL_SVC_TIMEOUT_DEFAULT; > > struct ib_sa_sm_ah { > @@ -122,6 +125,7 @@ struct ib_sa_query { > #define IB_SA_ENABLE_LOCAL_SERVICE 0x00000001 > #define IB_SA_CANCEL 0x00000002 > #define IB_SA_QUERY_OPA 0x00000004 > +#define IB_SA_NL_QUERY_SENT 0x00000008 > > struct ib_sa_service_query { > void (*callback)(int, struct ib_sa_service_rec *, void *); > @@ -746,6 +750,11 @@ static inline int ib_sa_query_cancelled(struct ib_sa_query *query) > return (query->flags & IB_SA_CANCEL); > } > > +static inline int ib_sa_nl_query_sent(struct ib_sa_query *query) > +{ > + return (query->flags & IB_SA_NL_QUERY_SENT); > +} > + > static void ib_nl_set_path_rec_attrs(struct sk_buff *skb, > struct ib_sa_query *query) > { > @@ -889,6 +898,15 @@ static int ib_nl_make_request(struct ib_sa_query *query, gfp_t gfp_mask) > spin_lock_irqsave(&ib_nl_request_lock, flags); > list_del(&query->list); > spin_unlock_irqrestore(&ib_nl_request_lock, flags); > + } else { > + query->flags |= IB_SA_NL_QUERY_SENT; > + > + /* > + * If response is received before this flag was set > + * someone is waiting to process the response and release the > + * query. > + */ > + wake_up(&wait_queue); > } > > return ret; > @@ -994,6 +1012,21 @@ static void ib_nl_request_timeout(struct work_struct *work) > } > > list_del(&query->list); > + > + /* > + * If IB_SA_NL_QUERY_SENT is not set, this query has not been > + * sent to ibacm yet. Reset the timer. > + */ > + if (!ib_sa_nl_query_sent(query)) { > + delay = msecs_to_jiffies(sa_local_svc_timeout_ms); > + query->timeout = delay + jiffies; > + list_add_tail(&query->list, &ib_nl_request_list); > + /* Start the timeout if this is the only request */ > + if (ib_nl_request_list.next == &query->list) > + queue_delayed_work(ib_nl_wq, &ib_nl_timed_work, > + delay); > + break; > + } > ib_sa_disable_local_svc(query); > /* Hold the lock to protect against query cancellation */ > if (ib_sa_query_cancelled(query)) > @@ -1123,6 +1156,18 @@ int ib_nl_handle_resolve_resp(struct sk_buff *skb, > > send_buf = query->mad_buf; > > + /* > + * Make sure the IB_SA_NL_QUERY_SENT flag is set before > + * processing this query. If flag is not set, query can be accessed in > + * another context while setting the flag and processing the query will > + * eventually release it causing a possible use-after-free. > + */ This comment doesn't really make sense, flags insige the memory being freed inherently can't prevent use after free. Jason ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. 2020-05-08 0:08 ` Jason Gunthorpe @ 2020-05-11 21:26 ` Divya Indi 2020-05-13 15:00 ` Jason Gunthorpe 0 siblings, 1 reply; 16+ messages in thread From: Divya Indi @ 2020-05-11 21:26 UTC (permalink / raw) To: Jason Gunthorpe Cc: linux-kernel, linux-rdma, Kaike Wan, Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford Hi Jason, On 5/7/20 5:08 PM, Jason Gunthorpe wrote: > On Thu, May 07, 2020 at 11:34:47AM -0700, Divya Indi wrote: >> This patch fixes commit - >> commit 3ebd2fd0d011 ("IB/sa: Put netlink request into the request list before sending")' >> >> Above commit adds the query to the request list before ib_nl_snd_msg. >> >> However, if there is a delay in sending out the request (For >> eg: Delay due to low memory situation) the timer to handle request timeout >> might kick in before the request is sent out to ibacm via netlink. >> ib_nl_request_timeout may release the query if it fails to send it to SA >> as well causing a use after free situation. >> >> Call Trace for the above race: >> >> [<ffffffffa02f43cb>] ? ib_pack+0x17b/0x240 [ib_core] >> [<ffffffffa032aef1>] ib_sa_path_rec_get+0x181/0x200 [ib_sa] >> [<ffffffffa0379db0>] rdma_resolve_route+0x3c0/0x8d0 [rdma_cm] >> [<ffffffffa0374450>] ? cma_bind_port+0xa0/0xa0 [rdma_cm] >> [<ffffffffa040f850>] ? rds_rdma_cm_event_handler_cmn+0x850/0x850 >> [rds_rdma] >> [<ffffffffa040f22c>] rds_rdma_cm_event_handler_cmn+0x22c/0x850 >> [rds_rdma] >> [<ffffffffa040f860>] rds_rdma_cm_event_handler+0x10/0x20 [rds_rdma] >> [<ffffffffa037778e>] addr_handler+0x9e/0x140 [rdma_cm] >> [<ffffffffa026cdb4>] process_req+0x134/0x190 [ib_addr] >> [<ffffffff810a02f9>] process_one_work+0x169/0x4a0 >> [<ffffffff810a0b2b>] worker_thread+0x5b/0x560 >> [<ffffffff810a0ad0>] ? flush_delayed_work+0x50/0x50 >> [<ffffffff810a68fb>] kthread+0xcb/0xf0 >> [<ffffffff816ec49a>] ? __schedule+0x24a/0x810 >> [<ffffffff816ec49a>] ? __schedule+0x24a/0x810 >> [<ffffffff810a6830>] ? kthread_create_on_node+0x180/0x180 >> [<ffffffff816f25a7>] ret_from_fork+0x47/0x90 >> [<ffffffff810a6830>] ? kthread_create_on_node+0x180/0x180 >> .... >> RIP [<ffffffffa03296cd>] send_mad+0x33d/0x5d0 [ib_sa] >> >> To resolve this issue, we introduce a new flag IB_SA_NL_QUERY_SENT. >> This flag Indicates if the request has been sent out to ibacm yet. >> >> If this flag is not set for a query and the query times out, we add it >> back to the list with the original delay. >> >> To handle the case where a response is received before we could set this >> flag, the response handler waits for the flag to be >> set before proceeding with the query. >> >> Signed-off-by: Divya Indi <divya.indi@oracle.com> >> drivers/infiniband/core/sa_query.c | 45 ++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 45 insertions(+) >> >> diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c >> index 30d4c12..ffbae2f 100644 >> +++ b/drivers/infiniband/core/sa_query.c >> @@ -59,6 +59,9 @@ >> #define IB_SA_LOCAL_SVC_TIMEOUT_MAX 200000 >> #define IB_SA_CPI_MAX_RETRY_CNT 3 >> #define IB_SA_CPI_RETRY_WAIT 1000 /*msecs */ >> + >> +DECLARE_WAIT_QUEUE_HEAD(wait_queue); >> + >> static int sa_local_svc_timeout_ms = IB_SA_LOCAL_SVC_TIMEOUT_DEFAULT; >> >> struct ib_sa_sm_ah { >> @@ -122,6 +125,7 @@ struct ib_sa_query { >> #define IB_SA_ENABLE_LOCAL_SERVICE 0x00000001 >> #define IB_SA_CANCEL 0x00000002 >> #define IB_SA_QUERY_OPA 0x00000004 >> +#define IB_SA_NL_QUERY_SENT 0x00000008 >> >> struct ib_sa_service_query { >> void (*callback)(int, struct ib_sa_service_rec *, void *); >> @@ -746,6 +750,11 @@ static inline int ib_sa_query_cancelled(struct ib_sa_query *query) >> return (query->flags & IB_SA_CANCEL); >> } >> >> +static inline int ib_sa_nl_query_sent(struct ib_sa_query *query) >> +{ >> + return (query->flags & IB_SA_NL_QUERY_SENT); >> +} >> + >> static void ib_nl_set_path_rec_attrs(struct sk_buff *skb, >> struct ib_sa_query *query) >> { >> @@ -889,6 +898,15 @@ static int ib_nl_make_request(struct ib_sa_query *query, gfp_t gfp_mask) >> spin_lock_irqsave(&ib_nl_request_lock, flags); >> list_del(&query->list); >> spin_unlock_irqrestore(&ib_nl_request_lock, flags); >> + } else { >> + query->flags |= IB_SA_NL_QUERY_SENT; >> + >> + /* >> + * If response is received before this flag was set >> + * someone is waiting to process the response and release the >> + * query. >> + */ >> + wake_up(&wait_queue); >> } >> >> return ret; >> @@ -994,6 +1012,21 @@ static void ib_nl_request_timeout(struct work_struct *work) >> } >> >> list_del(&query->list); >> + >> + /* >> + * If IB_SA_NL_QUERY_SENT is not set, this query has not been >> + * sent to ibacm yet. Reset the timer. >> + */ >> + if (!ib_sa_nl_query_sent(query)) { >> + delay = msecs_to_jiffies(sa_local_svc_timeout_ms); >> + query->timeout = delay + jiffies; >> + list_add_tail(&query->list, &ib_nl_request_list); >> + /* Start the timeout if this is the only request */ >> + if (ib_nl_request_list.next == &query->list) >> + queue_delayed_work(ib_nl_wq, &ib_nl_timed_work, >> + delay); >> + break; >> + } >> ib_sa_disable_local_svc(query); >> /* Hold the lock to protect against query cancellation */ >> if (ib_sa_query_cancelled(query)) >> @@ -1123,6 +1156,18 @@ int ib_nl_handle_resolve_resp(struct sk_buff *skb, >> >> send_buf = query->mad_buf; >> >> + /* >> + * Make sure the IB_SA_NL_QUERY_SENT flag is set before >> + * processing this query. If flag is not set, query can be accessed in >> + * another context while setting the flag and processing the query will >> + * eventually release it causing a possible use-after-free. >> + */ > This comment doesn't really make sense, flags insige the memory being > freed inherently can't prevent use after free. I can definitely re-phrase here to make things clearer. But, the idea here is in the unlikely/rare case where a response for a query comes in before the flag has been set in ib_nl_make_request, we want to wait for the flag to be sent before proceeding. The response handler will eventually release the query so this wait avoids that if the flag has not been set else "query->flags |= IB_SA_NL_QUERY_SENT;" will be accessing a query which was freed due to the above mentioned race. It is unlikely since getting a response => We have actually sent out the query to ibacm. How about this - "Getting a response is indicative of having sent out the query, but in an unlikely race when the response comes in before setting IB_SA_NL_QUERY_SENT, we need to wait till the flag is set to avoid accessing a query that has been released." Thoughts? Thanks, Divya Jason ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. 2020-05-11 21:26 ` Divya Indi @ 2020-05-13 15:00 ` Jason Gunthorpe 2020-05-13 21:02 ` Divya Indi 0 siblings, 1 reply; 16+ messages in thread From: Jason Gunthorpe @ 2020-05-13 15:00 UTC (permalink / raw) To: Divya Indi Cc: linux-kernel, linux-rdma, Kaike Wan, Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford On Mon, May 11, 2020 at 02:26:30PM -0700, Divya Indi wrote: > >> @@ -1123,6 +1156,18 @@ int ib_nl_handle_resolve_resp(struct sk_buff *skb, > >> > >> send_buf = query->mad_buf; > >> > >> + /* > >> + * Make sure the IB_SA_NL_QUERY_SENT flag is set before > >> + * processing this query. If flag is not set, query can be accessed in > >> + * another context while setting the flag and processing the query will > >> + * eventually release it causing a possible use-after-free. > >> + */ > > This comment doesn't really make sense, flags insige the memory being > > freed inherently can't prevent use after free. > > I can definitely re-phrase here to make things clearer. But, the idea here is > in the unlikely/rare case where a response for a query comes in before the flag has been > set in ib_nl_make_request, we want to wait for the flag to be sent before proceeding. > The response handler will eventually release the query so this wait avoids that if the flag has not been set > else > "query->flags |= IB_SA_NL_QUERY_SENT;" > will be accessing a query which was freed due to the above mentioned race. > > It is unlikely since getting a response => We have actually sent out the query to ibacm. > > How about this - > > "Getting a response is indicative of having sent out the query, but in an unlikely race when > the response comes in before setting IB_SA_NL_QUERY_SENT, we need to wait till the flag is set to > avoid accessing a query that has been released." It still makes no sense, a flag that is set before freeing the memory is fundamentally useless to prevent races. Jason ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. 2020-05-13 15:00 ` Jason Gunthorpe @ 2020-05-13 21:02 ` Divya Indi 2020-05-19 23:30 ` Divya Indi 0 siblings, 1 reply; 16+ messages in thread From: Divya Indi @ 2020-05-13 21:02 UTC (permalink / raw) To: Jason Gunthorpe Cc: linux-kernel, linux-rdma, Kaike Wan, Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford Hi Jason, Please find my comments inline - On 5/13/20 8:00 AM, Jason Gunthorpe wrote: > On Mon, May 11, 2020 at 02:26:30PM -0700, Divya Indi wrote: >>>> @@ -1123,6 +1156,18 @@ int ib_nl_handle_resolve_resp(struct sk_buff *skb, >>>> >>>> send_buf = query->mad_buf; >>>> >>>> + /* >>>> + * Make sure the IB_SA_NL_QUERY_SENT flag is set before >>>> + * processing this query. If flag is not set, query can be accessed in >>>> + * another context while setting the flag and processing the query will >>>> + * eventually release it causing a possible use-after-free. >>>> + */ >>> This comment doesn't really make sense, flags insige the memory being >>> freed inherently can't prevent use after free. >> I can definitely re-phrase here to make things clearer. But, the idea here is >> in the unlikely/rare case where a response for a query comes in before the flag has been >> set in ib_nl_make_request, we want to wait for the flag to be sent before proceeding. >> The response handler will eventually release the query so this wait avoids that if the flag has not been set >> else >> "query->flags |= IB_SA_NL_QUERY_SENT;" >> will be accessing a query which was freed due to the above mentioned race. >> >> It is unlikely since getting a response => We have actually sent out the query to ibacm. >> >> How about this - >> >> "Getting a response is indicative of having sent out the query, but in an unlikely race when >> the response comes in before setting IB_SA_NL_QUERY_SENT, we need to wait till the flag is set to >> avoid accessing a query that has been released." > It still makes no sense, a flag that is set before freeing the memory > is fundamentally useless to prevent races. Here the race is - 1. ib_nl_send_msg() 2. query->flags |= IB_SA_NL_QUERY_SENT 3. return; ------------- response_handler() { wait till flag is set. .... kfree(query); } Here, if the response handler was called => Query was sent and flag should have been set. However if response handler kicks in before line 2, we want to wait and make sure the flag is set and then free the query. Ideally after ib_nl_send_msg, we should not be accessing the query because we can be getting a response/timeout asynchronously and cant be sure when. The only places we access a query after it is successfully sent [response handler getting called => sending was successful] - 1. ib_nl_request_timeout 2. While setting the flag. 1. is taken care of because the request list access is protected by a lock and whoever gets the lock first deletes it from the request list and hence we can only have the response handler OR the timeout handler operate on the query. 2. To handle this is why we wait in the response handler. Once the flag is set we are no longer accessing the query and hence safe to release it. I have modified the comment for v2 as follows - /* + * In case of a quick response ie when a response comes in before + * setting IB_SA_NL_QUERY_SENT, we can have an unlikely race where the + * response handler will release the query, but we can still access the + * freed query while setting the flag. + * Hence, before proceeding and eventually releasing the query - + * wait till the flag is set. The flag should be set soon since getting + * a response is indicative of having successfully sent the query. + */ Thanks, Divya > > Jason ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. 2020-05-13 21:02 ` Divya Indi @ 2020-05-19 23:30 ` Divya Indi 2020-05-20 0:10 ` Jason Gunthorpe 0 siblings, 1 reply; 16+ messages in thread From: Divya Indi @ 2020-05-19 23:30 UTC (permalink / raw) To: Jason Gunthorpe Cc: linux-kernel, linux-rdma, Kaike Wan, Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford Hi Jason, I wanted to follow up to see if you got a chance to review the following reply? Let me know if it addresses your concern and if you have any questions! Thanks, Divya On 5/13/20 2:02 PM, Divya Indi wrote: > Hi Jason, > > Please find my comments inline - > > On 5/13/20 8:00 AM, Jason Gunthorpe wrote: >> On Mon, May 11, 2020 at 02:26:30PM -0700, Divya Indi wrote: >>>>> @@ -1123,6 +1156,18 @@ int ib_nl_handle_resolve_resp(struct sk_buff *skb, >>>>> >>>>> send_buf = query->mad_buf; >>>>> >>>>> + /* >>>>> + * Make sure the IB_SA_NL_QUERY_SENT flag is set before >>>>> + * processing this query. If flag is not set, query can be accessed in >>>>> + * another context while setting the flag and processing the query will >>>>> + * eventually release it causing a possible use-after-free. >>>>> + */ >>>> This comment doesn't really make sense, flags insige the memory being >>>> freed inherently can't prevent use after free. >>> I can definitely re-phrase here to make things clearer. But, the idea here is >>> in the unlikely/rare case where a response for a query comes in before the flag has been >>> set in ib_nl_make_request, we want to wait for the flag to be sent before proceeding. >>> The response handler will eventually release the query so this wait avoids that if the flag has not been set >>> else >>> "query->flags |= IB_SA_NL_QUERY_SENT;" >>> will be accessing a query which was freed due to the above mentioned race. >>> >>> It is unlikely since getting a response => We have actually sent out the query to ibacm. >>> >>> How about this - >>> >>> "Getting a response is indicative of having sent out the query, but in an unlikely race when >>> the response comes in before setting IB_SA_NL_QUERY_SENT, we need to wait till the flag is set to >>> avoid accessing a query that has been released." >> It still makes no sense, a flag that is set before freeing the memory >> is fundamentally useless to prevent races. > Here the race is - > > 1. ib_nl_send_msg() > 2. query->flags |= IB_SA_NL_QUERY_SENT > 3. return; > > ------------- > > response_handler() { > wait till flag is set. > .... > kfree(query); > > } > > Here, if the response handler was called => Query was sent > and flag should have been set. However if response handler kicks in > before line 2, we want to wait and make sure the flag is set and > then free the query. > > Ideally after ib_nl_send_msg, we should not be accessing the query > because we can be getting a response/timeout asynchronously and cant be > sure when. > > The only places we access a query after it is successfully sent [response handler getting called > => sending was successful] - > 1. ib_nl_request_timeout > 2. While setting the flag. > > 1. is taken care of because the request list access is protected by a lock > and whoever gets the lock first deletes it from the request list and > hence we can only have the response handler OR the timeout handler operate on the > query. > > 2. To handle this is why we wait in the response handler. Once the flag is > set we are no longer accessing the query and hence safe to release it. > > I have modified the comment for v2 as follows - > > /* > + * In case of a quick response ie when a response comes in before > + * setting IB_SA_NL_QUERY_SENT, we can have an unlikely race where the > + * response handler will release the query, but we can still access the > + * freed query while setting the flag. > + * Hence, before proceeding and eventually releasing the query - > + * wait till the flag is set. The flag should be set soon since getting > + * a response is indicative of having successfully sent the query. > + */ > > > Thanks, > Divya > >> Jason ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. 2020-05-19 23:30 ` Divya Indi @ 2020-05-20 0:10 ` Jason Gunthorpe 0 siblings, 0 replies; 16+ messages in thread From: Jason Gunthorpe @ 2020-05-20 0:10 UTC (permalink / raw) To: Divya Indi Cc: linux-kernel, linux-rdma, Kaike Wan, Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford On Tue, May 19, 2020 at 04:30:52PM -0700, Divya Indi wrote: > Hi Jason, > > I wanted to follow up to see if you got a chance to review the following reply? Not yet, it still seems bad to be doing code like this. If two threads are sharing memory they really need to use a refcount/kref not rely on some sketchy thing with flags. It is very hard to tell if the sketchy thing with flags is correct or not. Jason ^ permalink raw reply [flat|nested] 16+ messages in thread
[parent not found: <20200508110302.17872-1-hdanton@sina.com>]
* Re: [PATCH 1/2] IB/sa: Resolving use-after-free in ib_nl_send_msg. [not found] ` <20200508110302.17872-1-hdanton@sina.com> @ 2020-05-11 21:30 ` Divya Indi 0 siblings, 0 replies; 16+ messages in thread From: Divya Indi @ 2020-05-11 21:30 UTC (permalink / raw) To: Hillf Danton, Mark Bloch Cc: linux-kernel, linux-rdma, Jason Gunthorpe, Kaike Wan, Gerd Rausch, Håkon Bugge, Srinivas Eeda, Rama Nichanamatlu, Doug Ledford Hi Hillf, Please find my comments inline - On 5/8/20 4:03 AM, Hillf Danton wrote: > On Thu, 7 May 2020 12:36:29 Mark Bloch wrote: >> On 5/7/2020 11:34, Divya Indi wrote: >>> This patch fixes commit - >>> commit 3ebd2fd0d011 ("IB/sa: Put netlink request into the request list before sending")' >>> >>> Above commit adds the query to the request list before ib_nl_snd_msg. >>> >>> However, if there is a delay in sending out the request (For >>> eg: Delay due to low memory situation) the timer to handle request timeout >>> might kick in before the request is sent out to ibacm via netlink. >>> ib_nl_request_timeout may release the query if it fails to send it to SA >>> as well causing a use after free situation. >>> >>> Call Trace for the above race: >>> >>> [<ffffffffa02f43cb>] ? ib_pack+0x17b/0x240 [ib_core] >>> [<ffffffffa032aef1>] ib_sa_path_rec_get+0x181/0x200 [ib_sa] >>> [<ffffffffa0379db0>] rdma_resolve_route+0x3c0/0x8d0 [rdma_cm] >>> [<ffffffffa0374450>] ? cma_bind_port+0xa0/0xa0 [rdma_cm] >>> [<ffffffffa040f850>] ? rds_rdma_cm_event_handler_cmn+0x850/0x850 >>> [rds_rdma] >>> [<ffffffffa040f22c>] rds_rdma_cm_event_handler_cmn+0x22c/0x850 >>> [rds_rdma] >>> [<ffffffffa040f860>] rds_rdma_cm_event_handler+0x10/0x20 [rds_rdma] >>> [<ffffffffa037778e>] addr_handler+0x9e/0x140 [rdma_cm] >>> [<ffffffffa026cdb4>] process_req+0x134/0x190 [ib_addr] >>> [<ffffffff810a02f9>] process_one_work+0x169/0x4a0 >>> [<ffffffff810a0b2b>] worker_thread+0x5b/0x560 >>> [<ffffffff810a0ad0>] ? flush_delayed_work+0x50/0x50 >>> [<ffffffff810a68fb>] kthread+0xcb/0xf0 >>> [<ffffffff816ec49a>] ? __schedule+0x24a/0x810 >>> [<ffffffff816ec49a>] ? __schedule+0x24a/0x810 >>> [<ffffffff810a6830>] ? kthread_create_on_node+0x180/0x180 >>> [<ffffffff816f25a7>] ret_from_fork+0x47/0x90 >>> [<ffffffff810a6830>] ? kthread_create_on_node+0x180/0x180 >>> .... >>> RIP [<ffffffffa03296cd>] send_mad+0x33d/0x5d0 [ib_sa] >>> >>> To resolve this issue, we introduce a new flag IB_SA_NL_QUERY_SENT. >>> This flag Indicates if the request has been sent out to ibacm yet. >>> >>> If this flag is not set for a query and the query times out, we add it >>> back to the list with the original delay. >>> >>> To handle the case where a response is received before we could set this >>> flag, the response handler waits for the flag to be >>> set before proceeding with the query. >>> >>> Signed-off-by: Divya Indi <divya.indi@oracle.com> >>> --- >>> drivers/infiniband/core/sa_query.c | 45 ++++++++++++++++++++++++++++++++++++++ >>> 1 file changed, 45 insertions(+) >>> >>> diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c >>> index 30d4c12..ffbae2f 100644 >>> --- a/drivers/infiniband/core/sa_query.c >>> +++ b/drivers/infiniband/core/sa_query.c >>> @@ -59,6 +59,9 @@ >>> #define IB_SA_LOCAL_SVC_TIMEOUT_MAX 200000 >>> #define IB_SA_CPI_MAX_RETRY_CNT 3 >>> #define IB_SA_CPI_RETRY_WAIT 1000 /*msecs */ >>> + >>> +DECLARE_WAIT_QUEUE_HEAD(wait_queue); >>> + >>> static int sa_local_svc_timeout_ms = IB_SA_LOCAL_SVC_TIMEOUT_DEFAULT; >>> >>> struct ib_sa_sm_ah { >>> @@ -122,6 +125,7 @@ struct ib_sa_query { >>> #define IB_SA_ENABLE_LOCAL_SERVICE 0x00000001 >>> #define IB_SA_CANCEL 0x00000002 >>> #define IB_SA_QUERY_OPA 0x00000004 >>> +#define IB_SA_NL_QUERY_SENT 0x00000008 >>> >>> struct ib_sa_service_query { >>> void (*callback)(int, struct ib_sa_service_rec *, void *); >>> @@ -746,6 +750,11 @@ static inline int ib_sa_query_cancelled(struct ib_sa_query *query) >>> return (query->flags & IB_SA_CANCEL); >>> } >>> >>> +static inline int ib_sa_nl_query_sent(struct ib_sa_query *query) >>> +{ >>> + return (query->flags & IB_SA_NL_QUERY_SENT); >>> +} >>> + >>> static void ib_nl_set_path_rec_attrs(struct sk_buff *skb, >>> struct ib_sa_query *query) >>> { >>> @@ -889,6 +898,15 @@ static int ib_nl_make_request(struct ib_sa_query *query, gfp_t gfp_mask) >>> spin_lock_irqsave(&ib_nl_request_lock, flags); >>> list_del(&query->list); >>> spin_unlock_irqrestore(&ib_nl_request_lock, flags); > Here it also needs to do wakeup as sleeper might come while sending, no > matter the failure to send. We only sleep in the response handler. If we could not send a query, then response handler is not triggered and hence no one would be waiting on this query. Thanks, Divya >>> + } else { >>> + query->flags |= IB_SA_NL_QUERY_SENT; >>> + >>> + /* >>> + * If response is received before this flag was set >>> + * someone is waiting to process the response and release the >>> + * query. >>> + */ >>> + wake_up(&wait_queue); >>> } >>> >>> return ret; > With minor changes added on top of your work it now looks like the diff > below (only including the wakeup part). > > --- a/drivers/infiniband/core/sa_query.c > +++ b/drivers/infiniband/core/sa_query.c > @@ -863,6 +863,11 @@ static int ib_nl_send_msg(struct ib_sa_q > return rdma_nl_multicast(&init_net, skb, RDMA_NL_GROUP_LS, gfp_mask); > } > > +static inline bool ib_sa_nl_query_sending(struct ib_sa_query *query) > +{ > + return query->flags & IB_SA_NL_QUERY_SENDING; > +} > + > static int ib_nl_make_request(struct ib_sa_query *query, gfp_t gfp_mask) > { > unsigned long flags; > @@ -874,6 +879,8 @@ static int ib_nl_make_request(struct ib_ > > /* Put the request on the list first.*/ > spin_lock_irqsave(&ib_nl_request_lock, flags); > + /* info others we're having work to do */ > + query->flags |= IB_SA_NL_QUERY_SENDING; > delay = msecs_to_jiffies(sa_local_svc_timeout_ms); > query->timeout = delay + jiffies; > list_add_tail(&query->list, &ib_nl_request_list); > @@ -883,13 +890,15 @@ static int ib_nl_make_request(struct ib_ > spin_unlock_irqrestore(&ib_nl_request_lock, flags); > > ret = ib_nl_send_msg(query, gfp_mask); > + /* safe to delete it with IB_SA_NL_QUERY_SENDING set */ > + spin_lock_irqsave(&ib_nl_request_lock, flags); > if (ret) { > ret = -EIO; > - /* Remove the request */ > - spin_lock_irqsave(&ib_nl_request_lock, flags); > list_del(&query->list); > - spin_unlock_irqrestore(&ib_nl_request_lock, flags); > } > + query->flags &= ~IB_SA_NL_QUERY_SENDING; > + wake_up(&wait_queue); > + spin_unlock_irqrestore(&ib_nl_request_lock, flags); > > return ret; > } > > >>> @@ -994,6 +1012,21 @@ static void ib_nl_request_timeout(struct work_struct *work) >>> } >>> >>> list_del(&query->list); >>> + >>> + /* >>> + * If IB_SA_NL_QUERY_SENT is not set, this query has not been >>> + * sent to ibacm yet. Reset the timer. >>> + */ >>> + if (!ib_sa_nl_query_sent(query)) { >>> + delay = msecs_to_jiffies(sa_local_svc_timeout_ms); >>> + query->timeout = delay + jiffies; >>> + list_add_tail(&query->list, &ib_nl_request_list); >>> + /* Start the timeout if this is the only request */ >>> + if (ib_nl_request_list.next == &query->list) >>> + queue_delayed_work(ib_nl_wq, &ib_nl_timed_work, >>> + delay); >>> + break; >>> + } >>> ib_sa_disable_local_svc(query); >>> /* Hold the lock to protect against query cancellation */ >>> if (ib_sa_query_cancelled(query)) >>> @@ -1123,6 +1156,18 @@ int ib_nl_handle_resolve_resp(struct sk_buff *skb, >>> >>> send_buf = query->mad_buf; >>> >>> + /* >>> + * Make sure the IB_SA_NL_QUERY_SENT flag is set before >>> + * processing this query. If flag is not set, query can be accessed in >>> + * another context while setting the flag and processing the query will >>> + * eventually release it causing a possible use-after-free. >>> + */ >>> + if (unlikely(!ib_sa_nl_query_sent(query))) { >> Can't there be a race here where you check the flag (it isn't set) >> and before you call wait_event() the flag is set and wake_up() is called >> which means you will wait here forever? or worse, a timeout will happen >> the query will be freed and them some other query will call wake_up() >> and we have again a use-after-free. >> >>> + spin_unlock_irqrestore(&ib_nl_request_lock, flags); >>> + wait_event(wait_queue, ib_sa_nl_query_sent(query)); >> What if there are two queries sent to userspace, shouldn't you check and make sure >> you got woken up by the right one setting the flag? >> >> Other than that, the entire solution makes it very complicated to reason with (flags set/checked without locking etc) >> maybe we should just revert and fix it the other way? >> >> Mark >> >>> + spin_lock_irqsave(&ib_nl_request_lock, flags); >>> + } >>> + >>> if (!ib_nl_is_good_resolve_resp(nlh)) { >>> /* if the result is a failure, send out the packet via IB */ >>> ib_sa_disable_local_svc(query); >>> ^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2020-05-20 0:10 UTC | newest] Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-05-07 18:34 Resolving use-after-free in ib_nl_send_msg Divya Indi 2020-05-07 18:34 ` [PATCH 1/2] IB/sa: " Divya Indi 2020-05-07 19:06 ` Wan, Kaike 2020-05-07 19:36 ` Mark Bloch 2020-05-07 20:16 ` Wan, Kaike 2020-05-07 21:40 ` Mark Bloch 2020-05-11 21:10 ` Divya Indi 2020-05-11 21:06 ` Divya Indi 2020-05-12 11:15 ` Wan, Kaike 2020-05-08 0:08 ` Jason Gunthorpe 2020-05-11 21:26 ` Divya Indi 2020-05-13 15:00 ` Jason Gunthorpe 2020-05-13 21:02 ` Divya Indi 2020-05-19 23:30 ` Divya Indi 2020-05-20 0:10 ` Jason Gunthorpe [not found] ` <20200508110302.17872-1-hdanton@sina.com> 2020-05-11 21:30 ` Divya Indi
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).