From: Eli Britstein <[email protected]>

A group has a hash_map that contains the array of its buckets.
Revalidator threads access it to check if some bucket is alive, while
the main thread may modify the group, re-allocating this array. This race
condition may lead to invalid memory access.

Introduce a rw-lock to take a write-lock while modifying a group and a
read-lock for revalidators.

Fixes: 0a8f6beb54ab ("ofproto-dpif: Fix dp_hash mapping after select group 
modification.")
Signed-off-by: Eli Britstein <[email protected]>
Acked-by: Roi Dayan <[email protected]>
---
 ofproto/ofproto-dpif-xlate.c | 3 +++
 ofproto/ofproto-dpif.c       | 9 +++++++++
 ofproto/ofproto-dpif.h       | 1 +
 3 files changed, 13 insertions(+)

diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 2c8197fb7307..945506bdf86a 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -4998,13 +4998,16 @@ pick_dp_hash_select_group(struct xlate_ctx *ctx, struct 
group_dpif *group)
          * are quasi-randomly spread over the hash values, this maintains
          * a distribution according to bucket weights even when some buckets
          * are non-live. */
+        ovs_rwlock_rdlock(&group->hash_map_rwlock);
         for (int i = 0; i <= hash_mask; i++) {
             struct ofputil_bucket *b =
                     group->hash_map[(dp_hash + i) & hash_mask];
             if (bucket_is_alive(ctx, group, b, 0)) {
+                ovs_rwlock_unlock(&group->hash_map_rwlock);
                 return b;
             }
         }
+        ovs_rwlock_unlock(&group->hash_map_rwlock);
 
         return NULL;
     }
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index ed9e44ce2b03..e8b38d1e5355 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -5301,6 +5301,7 @@ group_dpif_credit_stats(struct group_dpif *group,
 
 static bool
 group_setup_dp_hash_table(struct group_dpif *group, size_t max_hash)
+    OVS_REQ_WRLOCK(group->hash_map_rwlock)
 {
     struct ofputil_bucket *bucket;
     uint32_t n_buckets = group->up.n_buckets;
@@ -5388,6 +5389,7 @@ group_setup_dp_hash_table(struct group_dpif *group, 
size_t max_hash)
 
 static void
 group_set_selection_method(struct group_dpif *group)
+    OVS_REQ_WRLOCK(group->hash_map_rwlock)
 {
     const struct ofputil_group_props *props = &group->up.props;
     const char *selection_method = props->selection_method;
@@ -5456,11 +5458,14 @@ group_construct(struct ofgroup *group_)
     ovs_mutex_init_adaptive(&group->stats_mutex);
     ovs_mutex_lock(&group->stats_mutex);
     group_construct_stats(group);
+    ovs_rwlock_init(&group->hash_map_rwlock);
+    ovs_rwlock_wrlock(&group->hash_map_rwlock);
     group->hash_map = NULL;
     if (group->up.type == OFPGT11_SELECT) {
         VLOG_DBG("Constructing select group %"PRIu32, group->up.group_id);
         group_set_selection_method(group);
     }
+    ovs_rwlock_unlock(&group->hash_map_rwlock);
     ovs_mutex_unlock(&group->stats_mutex);
     return 0;
 }
@@ -5469,7 +5474,9 @@ static void
 group_destruct(struct ofgroup *group_)
 {
     struct group_dpif *group = group_dpif_cast(group_);
+
     ovs_mutex_destroy(&group->stats_mutex);
+    ovs_rwlock_destroy(&group->hash_map_rwlock);
     if (group->hash_map) {
         free(group->hash_map);
         group->hash_map = NULL;
@@ -5481,6 +5488,7 @@ group_modify(struct ofgroup *group_)
 {
     struct group_dpif *group = group_dpif_cast(group_);
 
+    ovs_rwlock_wrlock(&group->hash_map_rwlock);
     if (group->hash_map) {
         free(group->hash_map);
         group->hash_map = NULL;
@@ -5489,6 +5497,7 @@ group_modify(struct ofgroup *group_)
         VLOG_DBG("Modifying select group %"PRIu32, group->up.group_id);
         group_set_selection_method(group);
     }
+    ovs_rwlock_unlock(&group->hash_map_rwlock);
 }
 
 static enum ofperr
diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
index f8d3df5ab5a6..3a0f4e0a29ed 100644
--- a/ofproto/ofproto-dpif.h
+++ b/ofproto/ofproto-dpif.h
@@ -142,6 +142,7 @@ struct group_dpif {
     enum ovs_hash_alg hash_alg;         /* dp_hash algorithm to be applied. */
     uint32_t hash_basis;                /* Basis for dp_hash. */
     uint32_t hash_mask;                 /* Used to mask dp_hash (2^N - 1).*/
+    struct ovs_rwlock hash_map_rwlock;
     struct ofputil_bucket **hash_map;   /* Map hash values to buckets. */
 };
 
-- 
2.47.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to