[asterisk-commits] russell: trunk r87687 - in /trunk: ./ channels/chan_iax2.c

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Oct 30 16:22:49 CDT 2007


Author: russell
Date: Tue Oct 30 16:22:48 2007
New Revision: 87687

URL: http://svn.digium.com/view/asterisk?view=rev&rev=87687
Log:
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)

........

Modified:
    trunk/   (props changed)
    trunk/channels/chan_iax2.c

Propchange: trunk/
------------------------------------------------------------------------------
Binary property 'branch-1.4-merged' - no diff available.

Modified: trunk/channels/chan_iax2.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/chan_iax2.c?view=diff&rev=87687&r1=87686&r2=87687
==============================================================================
--- trunk/channels/chan_iax2.c (original)
+++ trunk/channels/chan_iax2.c Tue Oct 30 16:22:48 2007
@@ -2288,7 +2288,7 @@
 	} 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 @@
 	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 @@
 	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 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,
-	};
-
-	peer = ao2_find(peers, &tmp_peer, OBJ_POINTER);
+	struct iax2_peer *peer = (struct iax2_peer *) data;
+
 	if (!peer)
 		return;
 
@@ -6354,7 +6386,7 @@
 		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 @@
 					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 @@
 	/* 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 @@
 	} 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 @@
 {
 	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 @@
 						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 @@
 	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 @@
 	if (schedule_action(__iax2_poke_noanswer, data))
 #endif		
 		__iax2_poke_noanswer(data);
+	peer_unref(peer);
 	return 0;
 }
 
@@ -9298,16 +9354,23 @@
 	/* 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 @@
 
 	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 @@
 	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);
 	}
 }




More information about the asterisk-commits mailing list