diff options
| author | Steve Wise <swise@opengridcomputing.com> | 2013-11-21 16:40:14 -0500 |
|---|---|---|
| committer | Roland Dreier <roland@purestorage.com> | 2013-12-15 19:47:47 -0500 |
| commit | 6b59ba609bb61e4fa2ecca7827f170ac07842d64 (patch) | |
| tree | cb7f4cc3a619bf13bc01f0f7175e7f61c36ec9d6 | |
| parent | 374b105797c3d4f29c685f3be535c35f5689b30e (diff) | |
RDMA/iwcm: Don't touch cm_id after deref in rem_ref
rem_ref() calls iwcm_deref_id(), which will wake up any blockers on
cm_id_priv->destroy_comp if the refcnt hits 0. That will unblock
someone in iw_destroy_cm_id() which will free the cmid. If that
happens before rem_ref() calls test_bit(IWCM_F_CALLBACK_DESTROY,
&cm_id_priv->flags), then the test_bit() will touch freed memory.
The fix is to read the bit first, then deref. We should never be in
iw_destroy_cm_id() with IWCM_F_CALLBACK_DESTROY set, and there is a
BUG_ON() to make sure of that.
Signed-off-by: Steve Wise <swise@opengridcomputing.com>
Signed-off-by: Roland Dreier <roland@purestorage.com>
| -rw-r--r-- | drivers/infiniband/core/iwcm.c | 11 |
1 files changed, 9 insertions, 2 deletions
diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c index c47c2034ca71..0717940ec3b5 100644 --- a/drivers/infiniband/core/iwcm.c +++ b/drivers/infiniband/core/iwcm.c | |||
| @@ -181,9 +181,16 @@ static void add_ref(struct iw_cm_id *cm_id) | |||
| 181 | static void rem_ref(struct iw_cm_id *cm_id) | 181 | static void rem_ref(struct iw_cm_id *cm_id) |
| 182 | { | 182 | { |
| 183 | struct iwcm_id_private *cm_id_priv; | 183 | struct iwcm_id_private *cm_id_priv; |
| 184 | int cb_destroy; | ||
| 185 | |||
| 184 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); | 186 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); |
| 185 | if (iwcm_deref_id(cm_id_priv) && | 187 | |
| 186 | test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)) { | 188 | /* |
| 189 | * Test bit before deref in case the cm_id gets freed on another | ||
| 190 | * thread. | ||
| 191 | */ | ||
| 192 | cb_destroy = test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags); | ||
| 193 | if (iwcm_deref_id(cm_id_priv) && cb_destroy) { | ||
| 187 | BUG_ON(!list_empty(&cm_id_priv->work_list)); | 194 | BUG_ON(!list_empty(&cm_id_priv->work_list)); |
| 188 | free_cm_id(cm_id_priv); | 195 | free_cm_id(cm_id_priv); |
| 189 | } | 196 | } |
