pbx_dundi.c: Misc memory management fixes when destroying peers
authorRichard Mudgett <rmudgett@digium.com>
Thu, 16 Aug 2018 02:31:45 +0000 (21:31 -0500)
committerRichard Mudgett <rmudgett@digium.com>
Fri, 17 Aug 2018 19:25:27 +0000 (14:25 -0500)
* In destroy_peer(), fixed memory leaks of lookup history strings and
qualify transactions when destroying peers.

* In destroy_peer(), fixed leaving the registerexpire scheduled callback
active when a peer is destroyed on a reload.  The reload marks and sweeps
peers so any peers not explicitly configured get destroyed.  Peers created
dynamically from the '*' peer will not exist until they re-register after
the reload.  These destroyed peers caused memory corruption when the
registerexpire timer expired.

* Made build_peer() not schedule any callbacks on the '*' peer
(empty_eid).  It is a special peer that is cloned to dynamically created
peers so it doesn't actually get involved in any message transactions.

* Made do_register_expire() remove the dundi/dpeers AstDB entry when a
peer registration expires.

* Fix deep_copy_peer() to not copy some things that cannot be copied to
the cloned peer structure.  Timers, message transactions, and lookup
history are specific to a peer instance.

* Made set_config() lock around processing the mappings configuration.

* Reordered unload_module() to handle load_module() declining the load due
to error.

Change-Id: Ib846b2b60d027f3a2c2b3b563d9a83a357dce1d6

pbx/pbx_dundi.c

index a94d71c..f02029b 100644 (file)
@@ -1318,7 +1318,9 @@ static int do_register_expire(const void *data)
 {
        struct dundi_peer *peer = (struct dundi_peer *)data;
        char eid_str[20];
+
        ast_debug(1, "Register expired for '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+       ast_db_del("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid));
        peer->registerexpire = -1;
        peer->lastms = 0;
        memset(&peer->addr, 0, sizeof(peer->addr));
@@ -1540,7 +1542,18 @@ static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer
 {
        struct permission *cur, *perm;
 
-       memcpy(peer_dst, peer_src, sizeof(*peer_dst));
+       *peer_dst = *peer_src;
+       AST_LIST_NEXT(peer_dst, list) = NULL;
+
+       /* Scheduled items cannot go with the copy */
+       peer_dst->registerid = -1;
+       peer_dst->qualifyid = -1;
+       peer_dst->registerexpire = -1;
+
+       /* Transactions and lookup history cannot go with the copy either */
+       peer_dst->regtrans = NULL;
+       peer_dst->qualtrans = NULL;
+       memset(&peer_dst->lookups, 0, sizeof(peer_dst->lookups));
 
        memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
        memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
@@ -2367,8 +2380,7 @@ static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *
                AST_LIST_LOCK(&peers);
                AST_LIST_TRAVERSE(&peers, p, list) {
                        for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
-                               if (p->lookups[x])
-                                       ast_free(p->lookups[x]);
+                               ast_free(p->lookups[x]);
                                p->lookups[x] = NULL;
                                p->lookuptimes[x] = 0;
                        }
@@ -3180,8 +3192,7 @@ static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
                                        if (!ast_eid_cmp(&trans->them_eid, &peer->eid)) {
                                                peer->avgms = 0;
                                                cnt = 0;
-                                               if (peer->lookups[DUNDI_TIMING_HISTORY-1])
-                                                       ast_free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
+                                               ast_free(peer->lookups[DUNDI_TIMING_HISTORY - 1]);
                                                for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
                                                        peer->lookuptimes[x] = peer->lookuptimes[x-1];
                                                        peer->lookups[x] = peer->lookups[x-1];
@@ -4327,19 +4338,31 @@ static void destroy_permissions(struct permissionlist *permlist)
 
 static void destroy_peer(struct dundi_peer *peer)
 {
+       int idx;
+
+       AST_SCHED_DEL(sched, peer->registerexpire);
        AST_SCHED_DEL(sched, peer->registerid);
-       if (peer->regtrans)
+       if (peer->regtrans) {
                destroy_trans(peer->regtrans, 0);
+       }
        AST_SCHED_DEL(sched, peer->qualifyid);
+       if (peer->qualtrans) {
+               destroy_trans(peer->qualtrans, 0);
+       }
        destroy_permissions(&peer->permit);
        destroy_permissions(&peer->include);
+
+       /* Release lookup history */
+       for (idx = 0; idx < ARRAY_LEN(peer->lookups); ++idx) {
+               ast_free(peer->lookups[idx]);
+       }
+
        ast_free(peer);
 }
 
 static void destroy_map(struct dundi_mapping *map)
 {
-       if (map->weightstr)
-               ast_free(map->weightstr);
+       ast_free(map->weightstr);
        ast_free(map);
 }
 
@@ -4683,10 +4706,11 @@ static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode
                ast_log(LOG_WARNING, "Peer '%s' is supposed to have permission for some inbound searches but isn't an inbound peer or outbound precache!\n",
                        ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
        } else {
-               if (needregister) {
-                       peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
-               }
                if (ast_eid_cmp(&peer->eid, &empty_eid)) {
+                       /* Schedule any items for explicitly configured peers. */
+                       if (needregister) {
+                               peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
+                       }
                        qualify_peer(peer, 1);
                }
        }
@@ -4925,13 +4949,17 @@ static int set_config(char *config_file, struct sockaddr_in* sin, int reload)
                v = v->next;
        }
        AST_LIST_UNLOCK(&peers);
+
        mark_mappings();
        v = ast_variable_browse(cfg, "mappings");
-       while(v) {
+       while (v) {
+               AST_LIST_LOCK(&peers);
                build_mapping(v->name, v->value);
+               AST_LIST_UNLOCK(&peers);
                v = v->next;
        }
        prune_mappings();
+
        mark_peers();
        cat = ast_category_browse(cfg, NULL);
        while(cat) {
@@ -4948,6 +4976,7 @@ static int set_config(char *config_file, struct sockaddr_in* sin, int reload)
                cat = ast_category_browse(cfg, cat);
        }
        prune_peers();
+
        ast_config_destroy(cfg);
        load_password();
        if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
@@ -4980,15 +5009,24 @@ static int unload_module(void)
                pthread_join(previous_clearcachethreadid, NULL);
        }
 
-       close(netsocket);
-       io_context_destroy(io);
-
        mark_mappings();
        prune_mappings();
        mark_peers();
        prune_peers();
 
-       ast_sched_context_destroy(sched);
+       if (-1 < netsocket) {
+               close(netsocket);
+               netsocket = -1;
+       }
+       if (io) {
+               io_context_destroy(io);
+               io = NULL;
+       }
+
+       if (sched) {
+               ast_sched_context_destroy(sched);
+               sched = NULL;
+       }
 
        return 0;
 }