[ovs-dev] [memory fixes 3/6] vswitchd: Fix dependency on DP_MAX_PORTS for allocating "struct dst"s.

Ben Pfaff blp at nicira.com
Mon Dec 13 21:10:48 UTC 2010


Until now, compose_actions() has allocated enough "struct dst"s on the
stack for a worst-case flow, one that floods packets with the maximum
number of ports and mirrors.  When the code was written this was correct.
However, now the number of ports is no longer known at compile time.  The
maximum number, 65535, would require (65536 * (32 + 1) * 4) == 8 MB of
stack space, which is a lot.  So this commit fixes the problem a different
way, by allocating the "struct dst"s dynamically when necessary.

This is a bug fix, but not a very serious one, because it could only
become a buffer overflow with a large number of mirrors.
---
 vswitchd/bridge.c |  133 ++++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 90 insertions(+), 43 deletions(-)

diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 81c54ef..2cd7cb4 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -82,6 +82,16 @@ struct dst {
     uint16_t dp_ifidx;
 };
 
+struct dst_set {
+    struct dst builtin[32];
+    struct dst *dsts;
+    size_t n, allocated;
+};
+
+static void dst_set_init(struct dst_set *);
+static void dst_set_add(struct dst_set *, const struct dst *);
+static void dst_set_free(struct dst_set *);
+
 struct iface {
     /* These members are always valid. */
     struct port *port;          /* Containing port. */
@@ -2227,16 +2237,16 @@ bond_wait(struct bridge *br)
 }
 
 static bool
-set_dst(struct dst *p, const struct flow *flow,
+set_dst(struct dst *dst, const struct flow *flow,
         const struct port *in_port, const struct port *out_port,
         tag_type *tags)
 {
-    p->vlan = (out_port->vlan >= 0 ? OFP_VLAN_NONE
+    dst->vlan = (out_port->vlan >= 0 ? OFP_VLAN_NONE
               : in_port->vlan >= 0 ? in_port->vlan
               : flow->vlan_tci == 0 ? OFP_VLAN_NONE
               : vlan_tci_to_vid(flow->vlan_tci));
-    return choose_output_iface(out_port, flow->dl_src, p->vlan,
-                               &p->dp_ifidx, tags);
+    return choose_output_iface(out_port, flow->dl_src, dst->vlan,
+                               &dst->dp_ifidx, tags);
 }
 
 static void
@@ -2253,10 +2263,10 @@ swap_dst(struct dst *p, struct dst *q)
  * vlan, but in most cases there are at most two different vlan tags so that's
  * possibly overkill.) */
 static void
-partition_dsts(struct dst *dsts, size_t n_dsts, int vlan)
+partition_dsts(struct dst_set *set, int vlan)
 {
-    struct dst *first = dsts;
-    struct dst *last = dsts + n_dsts;
+    struct dst *first = set->dsts;
+    struct dst *last = set->dsts + set->n;
 
     while (first != last) {
         /* Invariants:
@@ -2291,13 +2301,48 @@ mirror_mask_ffs(mirror_mask_t mask)
     return ffs(mask);
 }
 
+static void
+dst_set_init(struct dst_set *set)
+{
+    set->dsts = set->builtin;
+    set->n = 0;
+    set->allocated = ARRAY_SIZE(set->builtin);
+}
+
+static void
+dst_set_add(struct dst_set *set, const struct dst *dst)
+{
+    if (set->n >= set->allocated) {
+        size_t new_allocated;
+        struct dst *new_dsts;
+
+        new_allocated = set->allocated * 2;
+        new_dsts = xmalloc(new_allocated * sizeof *new_dsts);
+        memcpy(new_dsts, set->dsts, set->n * sizeof *new_dsts);
+
+        free(set->dsts);
+
+        set->dsts = new_dsts;
+        set->allocated = new_allocated;
+    }
+    set->dsts[set->n++] = *dst;
+}
+
+static void
+dst_set_free(struct dst_set *set)
+{
+    if (set->dsts != set->builtin) {
+        free(set->dsts);
+    }
+}
+
 static bool
-dst_is_duplicate(const struct dst *dsts, size_t n_dsts,
-                 const struct dst *test)
+dst_is_duplicate(const struct dst_set *set, const struct dst *test)
 {
     size_t i;
-    for (i = 0; i < n_dsts; i++) {
-        if (dsts[i].vlan == test->vlan && dsts[i].dp_ifidx == test->dp_ifidx) {
+    for (i = 0; i < set->n; i++) {
+        if (set->dsts[i].vlan == test->vlan
+            && set->dsts[i].dp_ifidx == test->dp_ifidx) {
             return true;
         }
     }
@@ -2331,14 +2376,14 @@ port_is_floodable(const struct port *port)
     return true;
 }
 
-static size_t
+static void
 compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
              const struct port *in_port, const struct port *out_port,
-             struct dst dsts[], tag_type *tags, uint16_t *nf_output_iface)
+             struct dst_set *set, tag_type *tags, uint16_t *nf_output_iface)
 {
     mirror_mask_t mirrors = in_port->src_mirrors;
+    struct dst dst;
     int flow_vlan;
-    struct dst *dst = dsts;
     size_t i;
 
     flow_vlan = vlan_tci_to_vid(flow->vlan_tci);
@@ -2347,45 +2392,42 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
     }
 
     if (out_port == FLOOD_PORT) {
-        /* XXX use ODP_FLOOD if no vlans or bonding. */
-        /* XXX even better, define each VLAN as a datapath port group */
         for (i = 0; i < br->n_ports; i++) {
             struct port *port = br->ports[i];
             if (port != in_port
                 && port_is_floodable(port)
                 && port_includes_vlan(port, vlan)
                 && !port->is_mirror_output_port
-                && set_dst(dst, flow, in_port, port, tags)) {
+                && set_dst(&dst, flow, in_port, port, tags)) {
                 mirrors |= port->dst_mirrors;
-                dst++;
+                dst_set_add(set, &dst);
             }
         }
         *nf_output_iface = NF_OUT_FLOOD;
-    } else if (out_port && set_dst(dst, flow, in_port, out_port, tags)) {
-        *nf_output_iface = dst->dp_ifidx;
+    } else if (out_port && set_dst(&dst, flow, in_port, out_port, tags)) {
+        dst_set_add(set, &dst);
+        *nf_output_iface = dst.dp_ifidx;
         mirrors |= out_port->dst_mirrors;
-        dst++;
     }
 
     while (mirrors) {
         struct mirror *m = br->mirrors[mirror_mask_ffs(mirrors) - 1];
         if (!m->n_vlans || vlan_is_mirrored(m, vlan)) {
             if (m->out_port) {
-                if (set_dst(dst, flow, in_port, m->out_port, tags)
-                    && !dst_is_duplicate(dsts, dst - dsts, dst)) {
-                    dst++;
+                if (set_dst(&dst, flow, in_port, m->out_port, tags)
+                    && !dst_is_duplicate(set, &dst)) {
+                    dst_set_add(set, &dst);
                 }
             } else {
                 for (i = 0; i < br->n_ports; i++) {
                     struct port *port = br->ports[i];
                     if (port_includes_vlan(port, m->out_vlan)
-                        && set_dst(dst, flow, in_port, port, tags))
+                        && set_dst(&dst, flow, in_port, port, tags))
                     {
-
                         if (port->vlan < 0) {
-                            dst->vlan = m->out_vlan;
+                            dst.vlan = m->out_vlan;
                         }
-                        if (dst_is_duplicate(dsts, dst - dsts, dst)) {
+                        if (dst_is_duplicate(set, &dst)) {
                             continue;
                         }
 
@@ -2395,11 +2437,11 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
                          * tagging tags place. This is necessary because
                          * dst->vlan is the final vlan, after removing implicit
                          * tags. */
-                        if (port == in_port && dst->vlan == flow_vlan) {
+                        if (port == in_port && dst.vlan == flow_vlan) {
                             /* Don't send out input port on same VLAN. */
                             continue;
                         }
-                        dst++;
+                        dst_set_add(set, &dst);
                     }
                 }
             }
@@ -2407,17 +2449,20 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
         mirrors &= mirrors - 1;
     }
 
-    partition_dsts(dsts, dst - dsts, flow_vlan);
-    return dst - dsts;
+    partition_dsts(set, flow_vlan);
 }
 
 static void OVS_UNUSED
-print_dsts(const struct dst *dsts, size_t n)
+print_dsts(const struct dst_set *set)
 {
-    for (; n--; dsts++) {
-        printf(">p%"PRIu16, dsts->dp_ifidx);
-        if (dsts->vlan != OFP_VLAN_NONE) {
-            printf("v%"PRIu16, dsts->vlan);
+    size_t i;
+
+    for (i = 0; i < set->n; i++) {
+        const struct dst *dst = &set->dsts[i];
+
+        printf(">p%"PRIu16, dst->dp_ifidx);
+        if (dst->vlan != OFP_VLAN_NONE) {
+            printf("v%"PRIu16, dst->vlan);
         }
     }
 }
@@ -2428,19 +2473,20 @@ compose_actions(struct bridge *br, const struct flow *flow, uint16_t vlan,
                 tag_type *tags, struct ofpbuf *actions,
                 uint16_t *nf_output_iface)
 {
-    struct dst dsts[DP_MAX_PORTS * (MAX_MIRRORS + 1)];
-    size_t n_dsts;
-    const struct dst *p;
+    struct dst_set set;
     uint16_t cur_vlan;
+    size_t i;
 
-    n_dsts = compose_dsts(br, flow, vlan, in_port, out_port, dsts, tags,
-                          nf_output_iface);
+    dst_set_init(&set);
+    compose_dsts(br, flow, vlan, in_port, out_port, &set, tags,
+                 nf_output_iface);
 
     cur_vlan = vlan_tci_to_vid(flow->vlan_tci);
     if (cur_vlan == 0) {
         cur_vlan = OFP_VLAN_NONE;
     }
-    for (p = dsts; p < &dsts[n_dsts]; p++) {
+    for (i = 0; i < set.n; i++) {
+        const struct dst *p = &set.dsts[i];
         if (p->vlan != cur_vlan) {
             if (p->vlan == OFP_VLAN_NONE) {
                 nl_msg_put_flag(actions, ODPAT_STRIP_VLAN);
@@ -2454,6 +2500,7 @@ compose_actions(struct bridge *br, const struct flow *flow, uint16_t vlan,
         }
         nl_msg_put_u32(actions, ODPAT_OUTPUT, p->dp_ifidx);
     }
+    dst_set_free(&set);
 }
 
 /* Returns the effective vlan of a packet, taking into account both the
-- 
1.7.1





More information about the dev mailing list