diff options
author | Russell Bryant <russell@russellbryant.com> | 2007-10-30 21:22:48 +0000 |
---|---|---|
committer | Russell Bryant <russell@russellbryant.com> | 2007-10-30 21:22:48 +0000 |
commit | 3978cb6f1c42af28d40fe13137ecbb091cbf4078 (patch) | |
tree | c49084c4d7107806e98de1e77c4b184a71a8a1ad /channels/chan_iax2.c | |
parent | 9b207275fabb857a8ccf0337d13cc8e9b42a42a7 (diff) |
Merged revisions 87686 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r87686 | russell | 2007-10-30 16:19:09 -0500 (Tue, 30 Oct 2007) | 11 lines
Merge the changes from team/russell/iax2_poke_fix and iax2-poke-fix-trunk
There was a race condition related to the handling of POKEing peers. Essentially,
a reference to a peer is held by the scheduler when there are pending callbacks,
but the reference count didn't reflect it. So, it was possible for a peer to hit
a reference count of zero and have its destructor begin to be called at the same
time that the scheduler thread ran a POKE related callback. If that happened,
a crash would likely occur.
(closes issue #11082, closes issue #11094)
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@87687 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'channels/chan_iax2.c')
-rw-r--r-- | channels/chan_iax2.c | 134 |
1 files changed, 96 insertions, 38 deletions
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 6b456f626..d2707bc4d 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -2288,7 +2288,7 @@ static char *handle_cli_iax2_prune_realtime(struct ast_cli_entry *e, int cmd, st } else if ((peer = find_peer(a->argv[3], 0))) { if(ast_test_flag(peer, IAX_RTCACHEFRIENDS)) { ast_set_flag(peer, IAX_RTAUTOCLEAR); - expire_registry((const void *) peer->name); + expire_registry(peer_ref(peer)); ast_cli(a->fd, "Peer %s was removed from the cache.\n", a->argv[3]); } else { ast_cli(a->fd, "Peer %s is not eligible for this operation.\n", a->argv[3]); @@ -3006,8 +3006,15 @@ static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in if (ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS)) { ast_copy_flags(peer, &globalflags, IAX_RTAUTOCLEAR|IAX_RTCACHEFRIENDS); if (ast_test_flag(peer, IAX_RTAUTOCLEAR)) { - peer->expire = iax2_sched_replace(peer->expire, sched, - (global_rtautoclear) * 1000, expire_registry, (const void *)peer->name); + if (peer->expire > -1) { + if (!ast_sched_del(sched, peer->expire)) { + peer->expire = -1; + peer_unref(peer); + } + } + peer->expire = iax2_sched_add(sched, (global_rtautoclear) * 1000, expire_registry, peer_ref(peer)); + if (peer->expire == -1) + peer_unref(peer); } ao2_link(peers, peer_ref(peer)); if (ast_test_flag(peer, IAX_DYNAMIC)) @@ -4818,8 +4825,19 @@ static char *handle_cli_iax2_unregister(struct ast_cli_entry *e, int cmd, struct p = find_peer(a->argv[2], 1); if (p) { if (p->expire > 0) { - expire_registry(a->argv[2]); - ast_cli(a->fd, "Peer %s unregistered\n", a->argv[2]); + struct iax2_peer tmp_peer = { + .name = a->argv[2], + }; + struct iax2_peer *peer; + + peer = ao2_find(peers, &tmp_peer, OBJ_POINTER); + if (peer) { + expire_registry(peer_ref(peer)); /* will release its own reference when done */ + peer_unref(peer); /* ref from ao2_find() */ + ast_cli(a->fd, "Peer %s unregistered\n", a->argv[2]); + } else { + ast_cli(a->fd, "Peer %s not found\n", a->argv[2]); + } } else { ast_cli(a->fd, "Peer %s not registered\n", a->argv[2]); } @@ -6324,15 +6342,29 @@ static void register_peer_exten(struct iax2_peer *peer, int onoff) } static void prune_peers(void); +static void unlink_peer(struct iax2_peer *peer) +{ + if (peer->expire > -1) { + if (!ast_sched_del(sched, peer->expire)) { + peer->expire = -1; + peer_unref(peer); + } + } + + if (peer->pokeexpire > -1) { + if (!ast_sched_del(sched, peer->pokeexpire)) { + peer->pokeexpire = -1; + peer_unref(peer); + } + } + + unlink_peer(peer); +} + static void __expire_registry(const void *data) { - const char *name = data; - struct iax2_peer *peer = NULL; - struct iax2_peer tmp_peer = { - .name = name, - }; + struct iax2_peer *peer = (struct iax2_peer *) data; - peer = ao2_find(peers, &tmp_peer, OBJ_POINTER); if (!peer) return; @@ -6354,7 +6386,7 @@ static void __expire_registry(const void *data) iax2_regfunk(peer->name, 0); if (ast_test_flag(peer, IAX_RTAUTOCLEAR)) - ao2_unlink(peers, peer); + unlink_peer(peer); peer_unref(peer); } @@ -6393,9 +6425,16 @@ static void reg_source_db(struct iax2_peer *p) p->addr.sin_family = AF_INET; p->addr.sin_addr = in; p->addr.sin_port = htons(atoi(c)); - ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */ - p->expire = iax2_sched_replace(p->expire, sched, - (p->expiry + 10) * 1000, expire_registry, (const void *)p->name); + if (p->expire > -1) { + if (!ast_sched_del(sched, p->expire)) { + p->expire = -1; + peer_unref(p); + } + } + ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */ + p->expire = iax2_sched_add(sched, (p->expiry + 10) * 1000, expire_registry, peer_ref(p)); + if (p->expire == -1) + peer_unref(p); if (iax2_regfunk) iax2_regfunk(p->name, 1); register_peer_exten(p, 1); @@ -6482,8 +6521,12 @@ static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, i /* Store socket fd */ p->sockfd = fd; /* Setup the expiry */ - if (p->expire > -1) - ast_sched_del(sched, p->expire); + if (p->expire > -1) { + if (!ast_sched_del(sched, p->expire)) { + p->expire = -1; + peer_unref(p); + } + } /* treat an unspecified refresh interval as the minimum */ if (!refresh) refresh = min_reg_expire; @@ -6498,8 +6541,11 @@ static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, i } else { p->expiry = refresh; } - if (p->expiry && sin->sin_addr.s_addr) - p->expire = iax2_sched_add(sched, (p->expiry + 10) * 1000, expire_registry, (const void *)p->name); + if (p->expiry && sin->sin_addr.s_addr) { + p->expire = iax2_sched_add(sched, (p->expiry + 10) * 1000, expire_registry, peer_ref(p)); + if (p->expire == -1) + peer_unref(p); + } iax_ie_append_str(&ied, IAX_IE_USERNAME, p->name); iax_ie_append_int(&ied, IAX_IE_DATETIME, iax2_datetime(p->zonetag)); if (sin->sin_addr.s_addr) { @@ -6760,6 +6806,7 @@ static void __iax2_poke_peer_s(const void *data) { struct iax2_peer *peer = (struct iax2_peer *)data; iax2_poke_peer(peer, 0); + peer_unref(peer); } static int iax2_poke_peer_s(const void *data) @@ -8301,13 +8348,19 @@ retryowner2: peer->historicms = iaxs[fr->callno]->pingtime; /* Remove scheduled iax2_poke_noanswer */ - if (peer->pokeexpire > -1) - ast_sched_del(sched, peer->pokeexpire); + if (peer->pokeexpire > -1) { + if (!ast_sched_del(sched, peer->pokeexpire)) { + peer_unref(peer); + peer->pokeexpire = -1; + } + } /* Schedule the next cycle */ if ((peer->lastms < 0) || (peer->historicms > peer->maxms)) - peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_peer_s, peer); + peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_peer_s, peer_ref(peer)); else - peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqok, iax2_poke_peer_s, peer); + peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqok, iax2_poke_peer_s, peer_ref(peer)); + if (peer->pokeexpire == -1) + peer_unref(peer); /* and finally send the ack */ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); /* And wrap up the qualify call */ @@ -9245,7 +9298,9 @@ static void __iax2_poke_noanswer(const void *data) peer->callno = 0; peer->lastms = -1; /* Try again quickly */ - peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_peer_s, peer); + peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_peer_s, peer_ref(peer)); + if (peer->pokeexpire == -1) + peer_unref(peer); } static int iax2_poke_noanswer(const void *data) @@ -9256,6 +9311,7 @@ static int iax2_poke_noanswer(const void *data) if (schedule_action(__iax2_poke_noanswer, data)) #endif __iax2_poke_noanswer(data); + peer_unref(peer); return 0; } @@ -9298,16 +9354,23 @@ static int iax2_poke_peer(struct iax2_peer *peer, int heldcall) /* Speed up retransmission times for this qualify call */ iaxs[peer->callno]->pingtime = peer->maxms / 4 + 1; iaxs[peer->callno]->peerpoke = peer; - + + if (peer->pokeexpire > -1) { + if (!ast_sched_del(sched, peer->pokeexpire)) { + peer->pokeexpire = -1; + peer_unref(peer); + } + } + /* Queue up a new task to handle no reply */ /* If the host is already unreachable then use the unreachable interval instead */ - if (peer->lastms < 0) { - peer->pokeexpire = iax2_sched_replace(peer->pokeexpire, - sched, peer->pokefreqnotok, iax2_poke_noanswer, peer); - } else { - peer->pokeexpire = iax2_sched_replace(peer->pokeexpire, - sched, DEFAULT_MAXMS * 2, iax2_poke_noanswer, peer); - } + if (peer->lastms < 0) + peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_noanswer, peer_ref(peer)); + else + peer->pokeexpire = iax2_sched_add(sched, DEFAULT_MAXMS * 2, iax2_poke_noanswer, peer_ref(peer)); + + if (peer->pokeexpire == -1) + peer_unref(peer); /* And send the poke */ send_command(iaxs[peer->callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, NULL, 0, -1); @@ -9658,11 +9721,6 @@ static void peer_destructor(void *obj) ast_free_ha(peer->ha); - /* Delete it, it needs to disappear */ - if (peer->expire > -1) - ast_sched_del(sched, peer->expire); - if (peer->pokeexpire > -1) - ast_sched_del(sched, peer->pokeexpire); if (peer->callno > 0) { ast_mutex_lock(&iaxsl[peer->callno]); iax2_destroy(peer->callno); @@ -10209,7 +10267,7 @@ static void prune_peers(void) i = ao2_iterator_init(peers, 0); while ((peer = ao2_iterator_next(&i))) { if (ast_test_flag(peer, IAX_DELME)) - ao2_unlink(peers, peer); + unlink_peer(peer); peer_unref(peer); } } |