]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bgpd: Ensure community data is freed in some cases.
authorDonald Sharp <sharpd@nvidia.com>
Sat, 2 Mar 2024 14:50:38 +0000 (09:50 -0500)
committerMergify <37929162+mergify[bot]@users.noreply.github.com>
Thu, 14 Mar 2024 08:26:15 +0000 (08:26 +0000)
Customer has this valgrind trace:

Direct leak of 2829120 byte(s) in 70728 object(s) allocated from:
  0 in community_new ../bgpd/bgp_community.c:39
  1 in community_uniq_sort ../bgpd/bgp_community.c:170
  2 in route_set_community ../bgpd/bgp_routemap.c:2342
  3 in route_map_apply_ext ../lib/routemap.c:2673
  4 in subgroup_announce_check ../bgpd/bgp_route.c:2367
  5 in subgroup_process_announce_selected ../bgpd/bgp_route.c:2914
  6 in group_announce_route_walkcb ../bgpd/bgp_updgrp_adv.c:199
  7 in hash_walk ../lib/hash.c:285
  8 in update_group_af_walk ../bgpd/bgp_updgrp.c:2061
  9 in group_announce_route ../bgpd/bgp_updgrp_adv.c:1059
 10 in bgp_process_main_one ../bgpd/bgp_route.c:3221
 11 in bgp_process_wq ../bgpd/bgp_route.c:3221
 12 in work_queue_run ../lib/workqueue.c:282

The above leak detected by valgrind was from a screenshot so I copied it
by hand.  Any mistakes in line numbers are purely from my transcription.
Additionally this is against a slightly modified 8.5.1 version of FRR.
Code inspection of 8.5.1 -vs- latest master shows the same problem
exists.  Code should be able to be followed from there to here.

What is happening:

There is a route-map being applied that modifes the outgoing community
to a peer.  This is saved in the attr copy created in
subgroup_process_announce_selected.  This community pointer is not
interned.  So the community->refcount is still 0.  Normally when
a prefix is announced, the attr and the prefix are placed on a
adjency out structure where the attribute is interned.  This will
cause the community to be saved in the community hash list as well.
In a non-normal operation when the decision to send is aborted after
the route-map application, the attribute is just dropped and the
pointer to the community is just dropped too, leading to situations
where the memory is leaked.  The usage of bgp suppress-fib would
would be a case where the community is caused to be leaked.
Additionally the previous commit where an unsuppress-map is used
to modify the outgoing attribute but since unsuppress-map was
not considered part of outgoing policy the attribute would be dropped as
well.  This pointer drop also extends to any dynamically allocated
memory saved by the attribute pointer that was not interned yet as well.

So let's modify the return case where the decision is made to
not send the prefix to the peer to always just flush the attribute
to ensure memory is not leaked.

Fixes: #15459
Signed-off-by: Donald Sharp <sharpd@nvidia.com>
(cherry picked from commit addff17a555240a4ccb5d0c5733a780256837651)

bgpd/bgp_conditional_adv.c
bgpd/bgp_route.c
bgpd/bgp_updgrp.h
bgpd/bgp_updgrp_adv.c

index 6ed0dd797b5bb26ac8c8eb535c260c6abe814d09..2d96b444b6c535faeeeac9a8e6172c2ee37bc834 100644 (file)
@@ -122,8 +122,9 @@ static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi,
                        if (update_type == UPDATE_TYPE_ADVERTISE &&
                            subgroup_announce_check(dest, pi, subgrp, dest_p,
                                                    &attr, &advmap_attr)) {
-                               bgp_adj_out_set_subgroup(dest, subgrp, &attr,
-                                                        pi);
+                               if (!bgp_adj_out_set_subgroup(dest, subgrp,
+                                                             &attr, pi))
+                                       bgp_attr_flush(&attr);
                        } else {
                                /* If default originate is enabled for
                                 * the peer, do not send explicit
index 5cfcf8051a163e8a411b650243ed168451a88c7c..0c1a00c2d42a35889c0970dab81fc51ee0e7ce1e 100644 (file)
@@ -2986,7 +2986,7 @@ void subgroup_process_announce_selected(struct update_subgroup *subgrp,
 {
        const struct prefix *p;
        struct peer *onlypeer;
-       struct attr attr;
+       struct attr attr = { 0 }, *pattr = &attr;
        struct bgp *bgp;
        bool advertise;
 
@@ -3014,26 +3014,30 @@ void subgroup_process_announce_selected(struct update_subgroup *subgrp,
        advertise = bgp_check_advertise(bgp, dest, safi);
 
        if (selected) {
-               if (subgroup_announce_check(dest, selected, subgrp, p, &attr,
+               if (subgroup_announce_check(dest, selected, subgrp, p, pattr,
                                            NULL)) {
                        /* Route is selected, if the route is already installed
                         * in FIB, then it is advertised
                         */
                        if (advertise) {
                                if (!bgp_check_withdrawal(bgp, dest, safi)) {
-                                       struct attr *adv_attr =
-                                               bgp_attr_intern(&attr);
-
-                                       bgp_adj_out_set_subgroup(dest, subgrp,
-                                                                adv_attr,
-                                                                selected);
-                               } else
+                                       if (!bgp_adj_out_set_subgroup(dest,
+                                                                     subgrp,
+                                                                     pattr,
+                                                                     selected))
+                                               bgp_attr_flush(pattr);
+                               } else {
                                        bgp_adj_out_unset_subgroup(
                                                dest, subgrp, 1, addpath_tx_id);
-                       }
-               } else
+                                       bgp_attr_flush(pattr);
+                               }
+                       } else
+                               bgp_attr_flush(pattr);
+               } else {
                        bgp_adj_out_unset_subgroup(dest, subgrp, 1,
                                                   addpath_tx_id);
+                       bgp_attr_flush(pattr);
+               }
        }
 
        /* If selected is NULL we must withdraw the path using addpath_tx_id */
index d4c6ecfdbbc25367f1d500e731d322bed15f9953..5e8b5374d6513c099d3d4a9b27ce19db88048c88 100644 (file)
@@ -441,7 +441,7 @@ extern struct bgp_adj_out *bgp_adj_out_alloc(struct update_subgroup *subgrp,
 extern void bgp_adj_out_remove_subgroup(struct bgp_dest *dest,
                                        struct bgp_adj_out *adj,
                                        struct update_subgroup *subgrp);
-extern void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
+extern bool bgp_adj_out_set_subgroup(struct bgp_dest *dest,
                                     struct update_subgroup *subgrp,
                                     struct attr *attr,
                                     struct bgp_path_info *path);
index e0f59c42e6f76fad432f162a3fefff1e4af3b46b..267a7567dc9be8610737e069c848362ae639bb40 100644 (file)
@@ -496,7 +496,7 @@ bgp_advertise_clean_subgroup(struct update_subgroup *subgrp,
        return next;
 }
 
-void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
+bool bgp_adj_out_set_subgroup(struct bgp_dest *dest,
                              struct update_subgroup *subgrp, struct attr *attr,
                              struct bgp_path_info *path)
 {
@@ -516,7 +516,7 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
        bgp = SUBGRP_INST(subgrp);
 
        if (DISABLE_BGP_ANNOUNCE)
-               return;
+               return false;
 
        /* Look for adjacency information. */
        adj = adj_lookup(
@@ -532,7 +532,7 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
                        bgp_addpath_id_for_peer(peer, afi, safi,
                                                &path->tx_addpath));
                if (!adj)
-                       return;
+                       return false;
 
                subgrp->pscount++;
        }
@@ -571,7 +571,7 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
                 * will never be able to coalesce the 3rd peer down
                 */
                subgrp->version = MAX(subgrp->version, dest->version);
-               return;
+               return false;
        }
 
        if (adj->adv)
@@ -619,6 +619,8 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
        bgp_adv_fifo_add_tail(&subgrp->sync->update, adv);
 
        subgrp->version = MAX(subgrp->version, dest->version);
+
+       return true;
 }
 
 /* The only time 'withdraw' will be false is if we are sending
@@ -828,7 +830,7 @@ void subgroup_announce_route(struct update_subgroup *subgrp)
 void subgroup_default_originate(struct update_subgroup *subgrp, bool withdraw)
 {
        struct bgp *bgp;
-       struct attr attr;
+       struct attr attr = { 0 };
        struct attr *new_attr = &attr;
        struct aspath *aspath;
        struct prefix p;
@@ -969,18 +971,19 @@ void subgroup_default_originate(struct update_subgroup *subgrp, bool withdraw)
                if (dest) {
                        for (pi = bgp_dest_get_bgp_path_info(dest); pi;
                             pi = pi->next) {
-                               if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
-                                       if (subgroup_announce_check(
-                                                   dest, pi, subgrp,
-                                                   bgp_dest_get_prefix(dest),
-                                                   &attr, NULL)) {
-                                               struct attr *default_attr =
-                                                       bgp_attr_intern(&attr);
-
-                                               bgp_adj_out_set_subgroup(
-                                                       dest, subgrp,
-                                                       default_attr, pi);
-                                       }
+                               if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+                                       continue;
+
+                               if (subgroup_announce_check(dest, pi, subgrp,
+                                                           bgp_dest_get_prefix(
+                                                                   dest),
+                                                           &attr, NULL)) {
+                                       if (!bgp_adj_out_set_subgroup(dest,
+                                                                     subgrp,
+                                                                     &attr, pi))
+                                               bgp_attr_flush(&attr);
+                               } else
+                                       bgp_attr_flush(&attr);
                        }
                        bgp_dest_unlock_node(dest);
                }