]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
bridge_softmix.c: Fix crash if channel fails to join mixing tech. 89/2589/2
authorRichard Mudgett <rmudgett@digium.com>
Wed, 13 Apr 2016 18:20:23 +0000 (13:20 -0500)
committerRichard Mudgett <rmudgett@digium.com>
Fri, 22 Apr 2016 20:45:47 +0000 (15:45 -0500)
softmix_bridge_join() failed because of an allocation failure.  To address
this, the softmix bridge technology now checks if the channel failed to
join softmix successfully.  In addition, the bridge now begins the process
of kicking the channel out of the bridge so we don't have channels
partially in the bridge for very long.

* Fix the test_channel_feature_hooks.c unit tests.  The test channel must
have a valid codec to join the simple_bridge technology.  This patch makes
joining a bridge more strict by not allowing partially joined channels to
remain in the bridge.

Change-Id: I97e2ade6a2bcd1214f24fb839fda948825b61a2b

bridges/bridge_softmix.c
include/asterisk/bridge_technology.h
main/bridge.c
tests/test_channel_feature_hooks.c

index bad3e43c442f6503749e79feb7e8f31525102219..0991e2897002e2f0aea4ff2f8f677be848366804 100644 (file)
@@ -359,6 +359,9 @@ static void set_softmix_bridge_data(int rate, int interval, struct ast_bridge_ch
        struct ast_format *slin_format;
        int setup_fail;
 
+       /* The callers have already ensured that sc is never NULL. */
+       ast_assert(sc != NULL);
+
        slin_format = ast_format_cache_get_slin_by_rate(rate);
 
        ast_mutex_lock(&sc->lock);
@@ -714,7 +717,7 @@ static int softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_cha
 {
        int res = 0;
 
-       if (!bridge->tech_pvt || (bridge_channel && !bridge_channel->tech_pvt)) {
+       if (!bridge->tech_pvt || !bridge_channel || !bridge_channel->tech_pvt) {
                /* "Accept" the frame and discard it. */
                return 0;
        }
@@ -984,6 +987,11 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
                AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
                        struct softmix_channel *sc = bridge_channel->tech_pvt;
 
+                       if (!sc) {
+                               /* This channel failed to join successfully. */
+                               continue;
+                       }
+
                        /* Update the sample rate to match the bridge's native sample rate if necessary. */
                        if (update_all_rates) {
                                set_softmix_bridge_data(softmix_data->internal_rate, softmix_data->internal_mixing_interval, bridge_channel, 1);
@@ -1019,7 +1027,8 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
                AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
                        struct softmix_channel *sc = bridge_channel->tech_pvt;
 
-                       if (bridge_channel->suspended) {
+                       if (!sc || bridge_channel->suspended) {
+                               /* This channel failed to join successfully or is suspended. */
                                continue;
                        }
 
index b83a51b69d27e6236375fab41f7d361946f0d400..8df19d9e97eebf354c9de3aad28bbf411a5e806d 100644 (file)
@@ -107,6 +107,9 @@ struct ast_bridge_technology {
         * \retval -1 on failure
         *
         * \note On entry, bridge is already locked.
+        *
+        * \note The bridge technology must tollerate a failed to join channel
+        * until it can be kicked from the bridge.
         */
        int (*join)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
        /*!
index b9a436e86a5efcebe2d5a22a2fceb7ff39b24149..a56555bc9faf151fd7db638d70d27d4277886b7f 100644 (file)
@@ -420,10 +420,12 @@ static void bridge_channel_complete_join(struct ast_bridge *bridge, struct ast_b
                bridge->technology->name);
        if (bridge->technology->join
                && bridge->technology->join(bridge, bridge_channel)) {
-               ast_debug(1, "Bridge %s: %p(%s) failed to join %s technology\n",
+               /* We cannot leave the channel partially in the bridge so we must kick it out */
+               ast_debug(1, "Bridge %s: %p(%s) failed to join %s technology (Kicking it out)\n",
                        bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
                        bridge->technology->name);
                bridge_channel->just_joined = 1;
+               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
                return;
        }
 
index ad7268822532ac7e3b5dcbeda16d059d67166417..94037e2fa50c6b4bdc81fe06f4d650fb761777ec 100644 (file)
@@ -40,6 +40,7 @@ ASTERISK_REGISTER_FILE()
 #include "asterisk/bridge.h"
 #include "asterisk/bridge_basic.h"
 #include "asterisk/features.h"
+#include "asterisk/format_cache.h"
 
 #define TEST_CATEGORY "/channels/features/"
 
@@ -47,6 +48,8 @@ ASTERISK_REGISTER_FILE()
 
 #define TEST_BACKEND_NAME "Features Test Logging"
 
+#define TEST_CHANNEL_FORMAT            ast_format_slin
+
 /*! \brief A channel technology used for the unit tests */
 static struct ast_channel_tech test_features_chan_tech = {
        .type = CHANNEL_TECH_NAME,
@@ -94,6 +97,11 @@ static void wait_for_unbridged(struct ast_channel *channel)
 #define START_CHANNEL(channel, name, number) do { \
        channel = ast_channel_alloc(0, AST_STATE_UP, number, name, number, number, \
                "default", NULL, NULL, 0, CHANNEL_TECH_NAME "/" name); \
+       ast_channel_nativeformats_set(channel, test_features_chan_tech.capabilities); \
+       ast_channel_set_rawwriteformat(channel, TEST_CHANNEL_FORMAT); \
+       ast_channel_set_rawreadformat(channel, TEST_CHANNEL_FORMAT); \
+       ast_channel_set_writeformat(channel, TEST_CHANNEL_FORMAT); \
+       ast_channel_set_readformat(channel, TEST_CHANNEL_FORMAT); \
        ast_channel_unlock(channel); \
        } while (0)
 
@@ -329,12 +337,19 @@ static int unload_module(void)
        AST_TEST_UNREGISTER(test_features_channel_interval);
 
        ast_channel_unregister(&test_features_chan_tech);
+       ao2_cleanup(test_features_chan_tech.capabilities);
+       test_features_chan_tech.capabilities = NULL;
 
        return 0;
 }
 
 static int load_module(void)
 {
+       test_features_chan_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+       if (!test_features_chan_tech.capabilities) {
+               return AST_MODULE_LOAD_FAILURE;
+       }
+       ast_format_cap_append(test_features_chan_tech.capabilities, TEST_CHANNEL_FORMAT, 0);
        ast_channel_register(&test_features_chan_tech);
 
        AST_TEST_REGISTER(test_features_channel_dtmf);